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