]> git.mxchange.org Git - simgear.git/blob - simgear/sound/sample_group.cxx
Optionally use HTTP repository.
[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 #ifdef HAVE_STD_ISNAN
37 using std::isnan;
38 #endif
39
40 bool isNaN(float *v) {
41    return (isnan(v[0]) || isnan(v[1]) || isnan(v[2]));
42 }
43
44 SGSampleGroup::SGSampleGroup () :
45     _smgr(NULL),
46     _refname(""),
47     _active(false),
48     _changed(false),
49     _pause(false),
50     _volume(1.0),
51     _tied_to_listener(false),
52     _velocity(SGVec3d::zeros()),
53     _orientation(SGQuatd::zeros())
54 {
55     _samples.clear();
56 }
57
58 SGSampleGroup::SGSampleGroup ( SGSoundMgr *smgr,
59                                const std::string &refname ):
60     _smgr(smgr),
61     _refname(refname),
62     _active(false), 
63     _changed(false),
64     _pause(false),
65     _volume(1.0),
66     _tied_to_listener(false),
67     _velocity(SGVec3d::zeros()),
68     _orientation(SGQuatd::zeros())
69 {
70     _smgr->add(this, refname);
71     _samples.clear();
72 }
73
74 SGSampleGroup::~SGSampleGroup ()
75 {
76     _active = false;
77     stop();
78     _smgr = 0;
79 }
80
81 static bool is_sample_stopped(SGSoundSample *sample)
82 {
83 #ifdef ENABLE_SOUND
84     assert(sample->is_valid_source());
85     unsigned int source = sample->get_source();
86     int result;
87     alGetSourcei( source, AL_SOURCE_STATE, &result );
88     return (result == AL_STOPPED);
89 #else
90     return true;
91 #endif
92 }
93
94 void SGSampleGroup::cleanup_removed_samples()
95 {
96     // Delete any OpenAL buffers that might still be in use.
97     unsigned int size = _removed_samples.size();
98     for (unsigned int i=0; i<size; ) {
99         SGSoundSample *sample = _removed_samples[i];
100         bool stopped = is_sample_stopped(sample);
101         
102         if ( sample->is_valid_source() ) {
103             int source = sample->get_source();
104             
105             if ( sample->is_looping() && !stopped) {
106 #ifdef ENABLE_SOUND
107                 alSourceStop( source );
108 #endif
109                 stopped = is_sample_stopped(sample);
110             }
111             
112             if ( stopped ) {
113                 sample->no_valid_source();
114                 _smgr->release_source( source );
115             }
116         }
117         
118         if ( stopped ) {
119             sample->stop();
120             if (( !sample->is_queue() )&&
121                 (sample->is_valid_buffer()))
122             {
123                 _smgr->release_buffer(sample);
124             }
125             _removed_samples.erase( _removed_samples.begin()+i );
126             size--;
127             continue;
128         }
129         i++;
130     }
131 }
132
133 void SGSampleGroup::start_playing_sample(SGSoundSample *sample)
134 {
135 #ifdef ENABLE_SOUND
136     //
137     // a request to start playing a sound has been filed.
138     //
139     ALuint source = _smgr->request_source();
140     if (alIsSource(source) == AL_FALSE ) {
141         return;
142     }
143     
144     sample->set_source( source );
145     update_sample_config( sample );
146     ALboolean looping = sample->is_looping() ? AL_TRUE : AL_FALSE;
147     
148     if ( !sample->is_queue() )
149     {
150         ALuint buffer = _smgr->request_buffer(sample);
151         if (buffer == SGSoundMgr::FAILED_BUFFER ||
152             buffer == SGSoundMgr::NO_BUFFER)
153         {
154             _smgr->release_source(source);
155             return;
156         }
157         
158         // start playing the sample
159         buffer = sample->get_buffer();
160         if ( alIsBuffer(buffer) == AL_TRUE )
161         {
162             alSourcei( source, AL_BUFFER, buffer );
163             testForALError("assign buffer to source");
164         } else
165             SG_LOG( SG_SOUND, SG_ALERT, "No such buffer!");
166     }
167     
168     alSourcef( source, AL_ROLLOFF_FACTOR, 0.3 );
169     alSourcei( source, AL_LOOPING, looping );
170     alSourcei( source, AL_SOURCE_RELATIVE, AL_FALSE );
171     alSourcePlay( source );
172     testForALError("sample play");
173 #endif
174 }
175
176 void SGSampleGroup::check_playing_sample(SGSoundSample *sample)
177 {
178     // check if the sound has stopped by itself
179     
180     if (is_sample_stopped(sample)) {
181         // sample is stopped because it wasn't looping
182         sample->stop();
183         sample->no_valid_source();
184         _smgr->release_source( sample->get_source() );
185         _smgr->release_buffer( sample );
186         remove( sample->get_sample_name() );
187     } else if ( sample->has_changed() ) {
188         if ( !sample->is_playing() ) {
189             // a request to stop playing the sound has been filed.
190             sample->stop();
191             sample->no_valid_source();
192             _smgr->release_source( sample->get_source() );
193         } else if ( _smgr->has_changed() ) {
194             update_sample_config( sample );
195         }
196     }
197 }
198
199 void SGSampleGroup::update( double dt ) {
200
201     if ( !_active || _pause ) return;
202
203     testForALError("start of update!!\n");
204
205     cleanup_removed_samples();
206     
207     // Update the position and orientation information for all samples.
208     if ( _changed || _smgr->has_changed() ) {
209         update_pos_and_orientation();
210         _changed = false;
211     }
212
213     sample_map_iterator sample_current = _samples.begin();
214     sample_map_iterator sample_end = _samples.end();
215     for ( ; sample_current != sample_end; ++sample_current ) {
216         SGSoundSample *sample = sample_current->second;
217
218         if ( !sample->is_valid_source() && sample->is_playing() && !sample->test_out_of_range()) {
219             start_playing_sample(sample);
220
221         } else if ( sample->is_valid_source() ) {
222             check_playing_sample(sample);
223         }
224         testForALError("update");
225     }
226 }
227
228 // add a sound effect, return true if successful
229 bool SGSampleGroup::add( SGSharedPtr<SGSoundSample> sound,
230                          const std::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 std::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 std::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 std::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 std::string &refname,
357                           bool looping )
358 {
359     SGSoundSample *sample = find( refname );
360
361     if ( sample == NULL ) {
362         return false;
363     }
364
365     sample->play( looping );
366     return true;
367 }
368
369
370 // return true of the specified sound is currently being played
371 bool SGSampleGroup::is_playing( const std::string& refname ) {
372     SGSoundSample *sample = find( refname );
373
374     if ( sample == NULL ) {
375         return false;
376     }
377
378     return ( sample->is_playing() ) ? true : false;
379 }
380
381 // immediate stop playing the sound
382 bool SGSampleGroup::stop( const std::string& refname ) {
383     SGSoundSample *sample  = find( refname );
384
385     if ( sample == NULL ) {
386         return false;
387     }
388
389     sample->stop();
390     return true;
391 }
392
393 void SGSampleGroup::set_volume( float vol )
394 {
395     if (vol > _volume*1.01 || vol < _volume*0.99) {
396         _volume = vol;
397         SG_CLAMP_RANGE(_volume, 0.0f, 1.0f);
398         _changed = true;
399     }
400 }
401
402 // set the source position and orientation of all managed sounds
403 void SGSampleGroup::update_pos_and_orientation() {
404
405     SGVec3d base_position = SGVec3d::fromGeod(_base_pos);
406     SGVec3d smgr_position = _smgr->get_position();
407     SGQuatd hlOr = SGQuatd::fromLonLat(_base_pos);
408     SGQuatd ec2body = hlOr*_orientation;
409
410     SGVec3f velocity = SGVec3f::zeros();
411     if ( _velocity[0] || _velocity[1] || _velocity[2] ) {
412        velocity = toVec3f( hlOr.backTransform(_velocity*SG_FEET_TO_METER) );
413     }
414
415     sample_map_iterator sample_current = _samples.begin();
416     sample_map_iterator sample_end = _samples.end();
417     for ( ; sample_current != sample_end; ++sample_current ) {
418         SGSoundSample *sample = sample_current->second;
419         sample->set_master_volume( _volume );
420         sample->set_orientation( _orientation );
421         sample->set_rotation( ec2body );
422         sample->set_position(base_position);
423         sample->set_velocity( velocity );
424
425         // Test if a sample is farther away than max distance, if so
426         // stop the sound playback and free it's source.
427         if (!_tied_to_listener) {
428             sample->update_pos_and_orientation();
429             SGVec3d position = sample->get_position() - smgr_position;
430             float max2 = sample->get_max_dist() * sample->get_max_dist();
431             float dist2 = position[0]*position[0]
432                           + position[1]*position[1] + position[2]*position[2];
433             if ((dist2 > max2) && !sample->test_out_of_range()) {
434                 sample->set_out_of_range(true);
435             } else if ((dist2 < max2) && sample->test_out_of_range()) {
436                 sample->set_out_of_range(false);
437             }
438         }
439     }
440 }
441
442 void SGSampleGroup::update_sample_config( SGSoundSample *sample )
443 {
444 #ifdef ENABLE_SOUND
445     SGVec3f orientation, velocity;
446     SGVec3d position;
447
448     if ( _tied_to_listener ) {
449         orientation = _smgr->get_direction();
450         position = SGVec3d::zeros();
451         velocity = _smgr->get_velocity();
452     } else {
453         sample->update_pos_and_orientation();
454         orientation = sample->get_orientation();
455         position = sample->get_position() - _smgr->get_position();
456         velocity = sample->get_velocity();
457     }
458
459     if (_smgr->bad_doppler_effect()) {
460         velocity *= 100.0f;
461     }
462
463 #if 0
464     if (length(position) > 20000)
465         printf("%s source and listener distance greater than 20km!\n",
466                _refname.c_str());
467     if (isNaN(toVec3f(position).data())) printf("NaN in source position\n");
468     if (isNaN(orientation.data())) printf("NaN in source orientation\n");
469     if (isNaN(velocity.data())) printf("NaN in source velocity\n");
470 #endif
471
472     unsigned int source = sample->get_source();
473     alSourcefv( source, AL_POSITION, toVec3f(position).data() );
474     alSourcefv( source, AL_VELOCITY, velocity.data() );
475     alSourcefv( source, AL_DIRECTION, orientation.data() );
476     testForALError("position and orientation");
477
478     alSourcef( source, AL_PITCH, sample->get_pitch() );
479     alSourcef( source, AL_GAIN, sample->get_volume() );
480     testForALError("pitch and gain");
481
482     if ( sample->has_static_data_changed() ) {
483         alSourcef( source, AL_CONE_INNER_ANGLE, sample->get_innerangle() );
484         alSourcef( source, AL_CONE_OUTER_ANGLE, sample->get_outerangle() );
485         alSourcef( source, AL_CONE_OUTER_GAIN, sample->get_outergain() );
486         testForALError("audio cone");
487
488         alSourcef( source, AL_MAX_DISTANCE, sample->get_max_dist() );
489         alSourcef( source, AL_REFERENCE_DISTANCE,
490                            sample->get_reference_dist() );
491         testForALError("distance rolloff");
492     }
493 #endif
494 }
495
496 bool SGSampleGroup::testForError(void *p, std::string s)
497 {
498    if (p == NULL) {
499       SG_LOG( SG_SOUND, SG_ALERT, "Error (sample group): " << s);
500       return true;
501    }
502    return false;
503 }
504
505 bool SGSampleGroup::testForALError(std::string s)
506 {
507 #ifdef SG_C
508     ALenum error = alGetError();
509     if (error != AL_NO_ERROR)  {
510        SG_LOG( SG_SOUND, SG_ALERT, "AL Error (" << _refname << "): "
511                                       << alGetString(error) << " at " << s);
512        return true;
513     }
514 #endif
515     return false;
516 }
517