]> git.mxchange.org Git - simgear.git/blob - simgear/sound/sample_group.cxx
cppbind.Ghost: clean up a bit
[simgear.git] / simgear / sound / sample_group.cxx
1 // sample_group.cxx -- Manage a group of samples relative to a base position
2 //
3 // Written for the new SoundSystem by Erik Hofman, October 2009
4 //
5 // Copyright (C) 2009 Erik Hofman <erik@ehofman.com>
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software Foundation,
19 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include <simgear_config.h>
25 #endif
26
27 #include <cassert>
28 #include <simgear/compiler.h>
29 #include <simgear/sg_inlines.h>
30 #include <simgear/debug/logstream.hxx>
31
32 #include "soundmgr_openal.hxx"
33 #include "soundmgr_openal_private.hxx"
34 #include "sample_group.hxx"
35
36 using std::string;
37
38 #ifdef HAVE_STD_ISNAN
39 using std::isnan;
40 #endif
41
42 bool isNaN(float *v) {
43    return (isnan(v[0]) || isnan(v[1]) || isnan(v[2]));
44 }
45
46 SGSampleGroup::SGSampleGroup () :
47     _smgr(NULL),
48     _refname(""),
49     _active(false),
50     _changed(false),
51     _pause(false),
52     _volume(1.0),
53     _tied_to_listener(false),
54     _velocity(SGVec3d::zeros()),
55     _orientation(SGQuatd::zeros())
56 {
57     _samples.clear();
58 }
59
60 SGSampleGroup::SGSampleGroup ( SGSoundMgr *smgr, const string &refname ) :
61     _smgr(smgr),
62     _refname(refname),
63     _active(false), 
64     _changed(false),
65     _pause(false),
66     _volume(1.0),
67     _tied_to_listener(false),
68     _velocity(SGVec3d::zeros()),
69     _orientation(SGQuatd::zeros())
70 {
71     _smgr->add(this, refname);
72     _samples.clear();
73 }
74
75 SGSampleGroup::~SGSampleGroup ()
76 {
77     _active = false;
78     stop();
79     _smgr = 0;
80 }
81
82 static bool is_sample_stopped(SGSoundSample *sample)
83 {
84 #ifdef ENABLE_SOUND
85     assert(sample->is_valid_source());
86     unsigned int source = sample->get_source();
87     int result;
88     alGetSourcei( source, AL_SOURCE_STATE, &result );
89     return (result == AL_STOPPED);
90 #else
91     return true;
92 #endif
93 }
94
95 void SGSampleGroup::cleanup_removed_samples()
96 {
97     // Delete any OpenAL buffers that might still be in use.
98     unsigned int size = _removed_samples.size();
99     for (unsigned int i=0; i<size; ) {
100         SGSoundSample *sample = _removed_samples[i];
101         bool stopped = is_sample_stopped(sample);
102         
103         if ( sample->is_valid_source() ) {
104             int source = sample->get_source();
105             
106             if ( sample->is_looping() && !stopped) {
107 #ifdef ENABLE_SOUND
108                 alSourceStop( source );
109 #endif
110                 stopped = is_sample_stopped(sample);
111             }
112             
113             if ( stopped ) {
114                 sample->no_valid_source();
115                 _smgr->release_source( source );
116             }
117         }
118         
119         if ( stopped ) {
120             sample->stop();
121             if (( !sample->is_queue() )&&
122                 (sample->is_valid_buffer()))
123             {
124                 _smgr->release_buffer(sample);
125             }
126             _removed_samples.erase( _removed_samples.begin()+i );
127             size--;
128             continue;
129         }
130         i++;
131     }
132 }
133
134 void SGSampleGroup::start_playing_sample(SGSoundSample *sample)
135 {
136 #ifdef ENABLE_SOUND
137     //
138     // a request to start playing a sound has been filed.
139     //
140     ALuint source = _smgr->request_source();
141     if (alIsSource(source) == AL_FALSE ) {
142         return;
143     }
144     
145     sample->set_source( source );
146     update_sample_config( sample );
147     ALboolean looping = sample->is_looping() ? AL_TRUE : AL_FALSE;
148     
149     if ( !sample->is_queue() )
150     {
151         ALuint buffer = _smgr->request_buffer(sample);
152         if (buffer == SGSoundMgr::FAILED_BUFFER ||
153             buffer == SGSoundMgr::NO_BUFFER)
154         {
155             _smgr->release_source(source);
156             return;
157         }
158         
159         // start playing the sample
160         buffer = sample->get_buffer();
161         if ( alIsBuffer(buffer) == AL_TRUE )
162         {
163             alSourcei( source, AL_BUFFER, buffer );
164             testForALError("assign buffer to source");
165         } else
166             SG_LOG( SG_SOUND, SG_ALERT, "No such buffer!");
167     }
168     
169     alSourcef( source, AL_ROLLOFF_FACTOR, 0.3 );
170     alSourcei( source, AL_LOOPING, looping );
171     alSourcei( source, AL_SOURCE_RELATIVE, AL_FALSE );
172     alSourcePlay( source );
173     testForALError("sample play");
174 #endif
175 }
176
177 void SGSampleGroup::check_playing_sample(SGSoundSample *sample)
178 {
179     // check if the sound has stopped by itself
180     
181     if (is_sample_stopped(sample)) {
182         // sample is stopped because it wasn't looping
183         sample->stop();
184         sample->no_valid_source();
185         _smgr->release_source( sample->get_source() );
186         _smgr->release_buffer( sample );
187         remove( sample->get_sample_name() );
188     } else if ( sample->has_changed() ) {
189         if ( !sample->is_playing() ) {
190             // a request to stop playing the sound has been filed.
191             sample->stop();
192             sample->no_valid_source();
193             _smgr->release_source( sample->get_source() );
194         } else if ( _smgr->has_changed() ) {
195             update_sample_config( sample );
196         }
197     }
198 }
199
200 void SGSampleGroup::update( double dt ) {
201
202     if ( !_active || _pause ) return;
203
204     testForALError("start of update!!\n");
205
206     cleanup_removed_samples();
207     
208     // Update the position and orientation information for all samples.
209     if ( _changed || _smgr->has_changed() ) {
210         update_pos_and_orientation();
211         _changed = false;
212     }
213
214     sample_map_iterator sample_current = _samples.begin();
215     sample_map_iterator sample_end = _samples.end();
216     for ( ; sample_current != sample_end; ++sample_current ) {
217         SGSoundSample *sample = sample_current->second;
218
219         if ( !sample->is_valid_source() && sample->is_playing() && !sample->test_out_of_range()) {
220             start_playing_sample(sample);
221
222         } else if ( sample->is_valid_source() ) {
223             check_playing_sample(sample);
224         }
225         testForALError("update");
226     }
227 }
228
229 // add a sound effect, return true if successful
230 bool SGSampleGroup::add( SGSharedPtr<SGSoundSample> sound, const string& refname ) {
231
232     sample_map_iterator sample_it = _samples.find( refname );
233     if ( sample_it != _samples.end() ) {
234         // sample name already exists
235         return false;
236     }
237
238     _samples[refname] = sound;
239     return true;
240 }
241
242
243 // remove a sound effect, return true if successful
244 bool SGSampleGroup::remove( const string &refname ) {
245
246     sample_map_iterator sample_it = _samples.find( refname );
247     if ( sample_it == _samples.end() ) {
248         // sample was not found
249         return false;
250     }
251
252     if ( sample_it->second->is_valid_buffer() )
253         _removed_samples.push_back( sample_it->second );
254
255     _samples.erase( sample_it );
256
257     return true;
258 }
259
260
261 // return true of the specified sound exists in the sound manager system
262 bool SGSampleGroup::exists( const string &refname ) {
263     sample_map_iterator sample_it = _samples.find( refname );
264     if ( sample_it == _samples.end() ) {
265         // sample was not found
266         return false;
267     }
268
269     return true;
270 }
271
272
273 // return a pointer to the SGSoundSample if the specified sound exists
274 // in the sound manager system, otherwise return NULL
275 SGSoundSample *SGSampleGroup::find( const string &refname ) {
276     sample_map_iterator sample_it = _samples.find( refname );
277     if ( sample_it == _samples.end() ) {
278         // sample was not found
279         return NULL;
280     }
281
282     return sample_it->second;
283 }
284
285
286 void
287 SGSampleGroup::stop ()
288 {
289     _pause = true;
290     sample_map_iterator sample_current = _samples.begin();
291     sample_map_iterator sample_end = _samples.end();
292     for ( ; sample_current != sample_end; ++sample_current ) {
293         SGSoundSample *sample = sample_current->second;
294
295         if ( sample->is_valid_source() ) {
296 #ifdef ENABLE_SOUND
297             ALint source = sample->get_source();
298             if ( sample->is_playing() ) {
299                 alSourceStop( source );
300                 testForALError("stop");
301             }
302             _smgr->release_source( source );
303 #endif
304             sample->no_valid_source();
305         }
306
307         if ( sample->is_valid_buffer() ) {
308             _smgr->release_buffer( sample );
309             sample->no_valid_buffer();
310         }
311     }
312 }
313
314 // stop playing all associated samples
315 void
316 SGSampleGroup::suspend ()
317 {
318     if (_active && _pause == false) {
319         _pause = true;
320         sample_map_iterator sample_current = _samples.begin();
321         sample_map_iterator sample_end = _samples.end();
322         for ( ; sample_current != sample_end; ++sample_current ) {
323 #ifdef ENABLE_SOUND
324             SGSoundSample *sample = sample_current->second;
325             if ( sample->is_valid_source() && sample->is_playing() ) {
326                 alSourcePause( sample->get_source() );
327             }
328 #endif
329         }
330         testForALError("suspend");
331     }
332 }
333
334 // resume playing all associated samples
335 void
336 SGSampleGroup::resume ()
337 {
338     if (_active && _pause == true) {
339 #ifdef ENABLE_SOUND
340         sample_map_iterator sample_current = _samples.begin();
341         sample_map_iterator sample_end = _samples.end();
342         for ( ; sample_current != sample_end; ++sample_current ) {
343             SGSoundSample *sample = sample_current->second;
344             if ( sample->is_valid_source() && sample->is_playing() ) {
345                 alSourcePlay( sample->get_source() );
346             }
347         }
348         testForALError("resume");
349 #endif
350         _pause = false;
351     }
352 }
353
354
355 // tell the scheduler to play the indexed sample in a continuous loop
356 bool SGSampleGroup::play( const string &refname, bool looping = false ) {
357     SGSoundSample *sample = find( refname );
358
359     if ( sample == NULL ) {
360         return false;
361     }
362
363     sample->play( looping );
364     return true;
365 }
366
367
368 // return true of the specified sound is currently being played
369 bool SGSampleGroup::is_playing( const string& refname ) {
370     SGSoundSample *sample = find( refname );
371
372     if ( sample == NULL ) {
373         return false;
374     }
375
376     return ( sample->is_playing() ) ? true : false;
377 }
378
379 // immediate stop playing the sound
380 bool SGSampleGroup::stop( const string& refname ) {
381     SGSoundSample *sample  = find( refname );
382
383     if ( sample == NULL ) {
384         return false;
385     }
386
387     sample->stop();
388     return true;
389 }
390
391 void SGSampleGroup::set_volume( float vol )
392 {
393     if (vol > _volume*1.01 || vol < _volume*0.99) {
394         _volume = vol;
395         SG_CLAMP_RANGE(_volume, 0.0f, 1.0f);
396         _changed = true;
397     }
398 }
399
400 // set the source position and orientation of all managed sounds
401 void SGSampleGroup::update_pos_and_orientation() {
402  
403     SGVec3d position = SGVec3d::fromGeod(_base_pos) - _smgr->get_position();
404     SGQuatd hlOr = SGQuatd::fromLonLat(_base_pos);
405     SGQuatd ec2body = hlOr*_orientation;
406
407     SGVec3f velocity = SGVec3f::zeros();
408     if ( _velocity[0] || _velocity[1] || _velocity[2] ) {
409        velocity = toVec3f( hlOr.backTransform(_velocity*SG_FEET_TO_METER) );
410     }
411
412     sample_map_iterator sample_current = _samples.begin();
413     sample_map_iterator sample_end = _samples.end();
414     for ( ; sample_current != sample_end; ++sample_current ) {
415         SGSoundSample *sample = sample_current->second;
416         sample->set_master_volume( _volume );
417         sample->set_orientation( _orientation );
418         sample->set_rotation( ec2body );
419         sample->set_position( position );
420         sample->set_velocity( velocity );
421
422         // Test if a sample is farther away than max distance, if so
423         // stop the sound playback and free it's source.
424         if (!_tied_to_listener) {
425             float max2 = sample->get_max_dist() * sample->get_max_dist();
426             float dist2 = position[0]*position[0]
427                           + position[1]*position[1] + position[2]*position[2];
428             if ((dist2 > max2) && !sample->test_out_of_range()) {
429                 sample->set_out_of_range(true);
430             } else if ((dist2 < max2) && sample->test_out_of_range()) {
431                 sample->set_out_of_range(false);
432             }
433         }
434     }
435 }
436
437 void SGSampleGroup::update_sample_config( SGSoundSample *sample )
438 {
439 #ifdef ENABLE_SOUND
440     SGVec3f orientation, velocity;
441     SGVec3d position;
442
443     if ( _tied_to_listener ) {
444         orientation = _smgr->get_direction();
445         position = SGVec3d::zeros();
446         velocity = _smgr->get_velocity();
447     } else {
448         sample->update_pos_and_orientation();
449         orientation = sample->get_orientation();
450         position = sample->get_position();
451         velocity = sample->get_velocity();
452     }
453
454     if (_smgr->bad_doppler_effect()) {
455         velocity *= 100.0f;
456     }
457
458 #if 0
459     if (length(position) > 20000)
460         printf("%s source and listener distance greater than 20km!\n",
461                _refname.c_str());
462     if (isNaN(toVec3f(position).data())) printf("NaN in source position\n");
463     if (isNaN(orientation.data())) printf("NaN in source orientation\n");
464     if (isNaN(velocity.data())) printf("NaN in source velocity\n");
465 #endif
466
467     unsigned int source = sample->get_source();
468     alSourcefv( source, AL_POSITION, toVec3f(position).data() );
469     alSourcefv( source, AL_VELOCITY, velocity.data() );
470     alSourcefv( source, AL_DIRECTION, orientation.data() );
471     testForALError("position and orientation");
472
473     alSourcef( source, AL_PITCH, sample->get_pitch() );
474     alSourcef( source, AL_GAIN, sample->get_volume() );
475     testForALError("pitch and gain");
476
477     if ( sample->has_static_data_changed() ) {
478         alSourcef( source, AL_CONE_INNER_ANGLE, sample->get_innerangle() );
479         alSourcef( source, AL_CONE_OUTER_ANGLE, sample->get_outerangle() );
480         alSourcef( source, AL_CONE_OUTER_GAIN, sample->get_outergain() );
481         testForALError("audio cone");
482
483         alSourcef( source, AL_MAX_DISTANCE, sample->get_max_dist() );
484         alSourcef( source, AL_REFERENCE_DISTANCE,
485                            sample->get_reference_dist() );
486         testForALError("distance rolloff");
487     }
488 #endif
489 }
490
491 bool SGSampleGroup::testForError(void *p, string s)
492 {
493    if (p == NULL) {
494       SG_LOG( SG_SOUND, SG_ALERT, "Error (sample group): " << s);
495       return true;
496    }
497    return false;
498 }
499
500 bool SGSampleGroup::testForALError(string s)
501 {
502 #ifdef SG_C
503     ALenum error = alGetError();
504     if (error != AL_NO_ERROR)  {
505        SG_LOG( SG_SOUND, SG_ALERT, "AL Error (" << _refname << "): "
506                                       << alGetString(error) << " at " << s);
507        return true;
508     }
509 #endif
510     return false;
511 }
512