]> git.mxchange.org Git - simgear.git/blob - simgear/sound/sample_group.cxx
183a4894e37d2ec37ad90d678c989eff844ed4d5
[simgear.git] / simgear / sound / sample_group.cxx
1 #ifdef HAVE_CONFIG_H
2 #  include <simgear_config.h>
3 #endif
4
5 #include <simgear/compiler.h>
6
7 #if defined (__APPLE__)
8 #  ifdef __GNUC__
9 #    if ( __GNUC__ >= 3 ) && ( __GNUC_MINOR__ >= 3 )
10 //  #        include <math.h>
11 inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
12 #    else
13     // any C++ header file undefines isinf and isnan
14     // so this should be included before <iostream>
15     // the functions are STILL in libm (libSystem on mac os x)
16 extern "C" int isnan (double);
17 extern "C" int isinf (double);
18 #    endif
19 #  else
20 //    inline int (isinf)(double r) { return isinf(r); }
21 //    inline int (isnan)(double r) { return isnan(r); }
22 #  endif
23 #endif
24
25 #if defined (__FreeBSD__)
26 #  if __FreeBSD_version < 500000
27      extern "C" {
28        inline int isnan(double r) { return !(r <= 0 || r >= 0); }
29      }
30 #  endif
31 #endif
32
33 #if defined (__CYGWIN__)
34 #  include <ieeefp.h>
35 #endif
36
37 #if defined(__MINGW32__)
38 #  define isnan(x) _isnan(x)
39 #endif
40
41 #include "soundmgr_openal.hxx"
42 #include "sample_group.hxx"
43
44 SGSampleGroup::SGSampleGroup () :
45     _smgr(NULL),
46     _active(false),
47     _position(SGVec3d::zeros().data()),
48     _orientation(SGVec3f::zeros().data()),
49     _tied_to_listener(false)
50 {
51     _samples.clear();
52 }
53
54 SGSampleGroup::SGSampleGroup ( SGSoundMgr *smgr, const string &refname ) :
55     _smgr(smgr),
56     _active(false), 
57     _position(SGVec3d::zeros().data()),
58     _orientation(SGVec3f::zeros().data()),
59     _tied_to_listener(false)
60 {
61     _smgr->add(this, refname);
62     _active = _smgr->is_working();
63     _refname = refname;
64     _samples.clear();
65 }
66
67 SGSampleGroup::~SGSampleGroup ()
68 {
69     _active = false;
70
71     sample_map_iterator sample_current = _samples.begin();
72     sample_map_iterator sample_end = _samples.end();
73     for ( ; sample_current != sample_end; ++sample_current ) {
74         SGSoundSample *sample = sample_current->second;
75
76         if ( sample->is_valid_source() && sample->is_playing() ) {
77             sample->no_valid_source();
78             _smgr->release_source( sample->get_source() );
79         }
80     }
81
82     _smgr = 0;
83 }
84
85 void SGSampleGroup::update( double dt ) {
86
87     if ( !_active ) return;
88
89     // testForALError("start of update!!\n");
90
91     sample_map_iterator sample_current = _samples.begin();
92     sample_map_iterator sample_end = _samples.end();
93     for ( ; sample_current != sample_end; ++sample_current ) {
94         SGSoundSample *sample = sample_current->second;
95
96         if ( !sample->is_valid_source() && sample->is_playing() ) {
97             //
98             // a request to start playing a sound has been filed.
99             //
100             ALboolean looping = sample->get_looping() ? AL_TRUE : AL_FALSE;
101
102             if ( !sample->is_valid_buffer() ) {
103                 // sample was not yet loaded or removed again
104
105 // TODO: Create a buffer cache that checks whether a file is already present
106 //       as an OpenAL buffer since buffers can be shared among sources.
107                 load_file(sample);
108                 if ( testForALError("load sample") ) {
109                     throw sg_exception("Failed to load sound sample.");
110                     continue;
111                 }
112
113                 // create an OpenAL buffer handle
114                 ALuint buffer;
115                 alGenBuffers(1, &buffer);
116                 if ( testForALError("generate buffer") ) {
117                     throw sg_exception("Failed to generate OpenAL buffer.");
118                     continue;
119                 }
120
121                 // Copy data to the internal OpenAL buffer
122                 const ALvoid *data = sample->get_data();
123                 ALenum format = sample->get_format();
124                 ALsizei size = sample->get_size();
125                 ALsizei freq = sample->get_frequency();
126                 alBufferData( buffer, format, data, size, freq );
127                 sample->free_data();
128                 if ( testForALError("buffer add data") ) {
129                     continue;
130                 }
131
132                 sample->set_buffer(buffer);
133             }
134
135             if ( _tied_to_listener && _smgr->has_changed() ) {
136                 sample->set_base_position( _smgr->get_position_vec() );
137                 sample->set_orientation( _smgr->get_direction() );
138                 sample->set_velocity( _smgr->get_velocity() );
139             }
140
141             // start playing the sample
142             ALuint buffer = sample->get_buffer();
143             ALuint source = _smgr->request_source();
144             if (alIsSource(source) == AL_TRUE && alIsBuffer(buffer) == AL_TRUE)
145             {
146                 sample->set_source( source );
147                 
148                 alSourcei( source, AL_BUFFER, buffer );
149                 testForALError("assign buffer to source");
150
151                 sample->set_source( source );
152                 update_sample_config( sample );
153
154                 alSourcei( source, AL_SOURCE_RELATIVE, AL_FALSE );
155                 alSourcei( source, AL_LOOPING, looping );
156                 alSourcef( source, AL_ROLLOFF_FACTOR, 1.2 );
157                 alSourcePlay( source );
158                 testForALError("sample play");
159             } else {
160                 if (alIsBuffer(buffer) == AL_FALSE) 
161                    SG_LOG( SG_GENERAL, SG_ALERT, "No such buffer!\n");
162                 // sample->no_valid_source();
163                 // sadly, no free source available at this time
164             }
165
166         } else if ( sample->is_valid_source() && sample->has_changed() ) {
167             if ( !sample->is_playing() ) {
168                 // a request to stop playing the sound has been filed.
169
170                 sample->no_valid_source();
171                 sample->stop();
172                 _smgr->release_source( sample->get_source() );
173             } else  {
174                 update_sample_config( sample );
175             }
176
177         } else if ( sample->is_valid_source() ) {
178             // check if the sound has stopped by itself
179
180             unsigned int source = sample->get_source();
181             int result;
182
183             alGetSourcei( source, AL_SOURCE_STATE, &result );
184             if ( result == AL_STOPPED ) {
185                 // sample is stoped because it wasn't looping
186                 sample->no_valid_source();
187                 sample->stop();
188                 _smgr->release_source( source );
189
190             }
191         }
192         testForALError("update");
193     }
194 }
195
196 // add a sound effect, return true if successful
197 bool SGSampleGroup::add( SGSoundSample *sound, const string& refname ) {
198
199     sample_map_iterator sample_it = _samples.find( refname );
200     if ( sample_it != _samples.end() ) {
201         // sample name already exists
202         return false;
203     }
204
205     _samples[refname] = sound;
206     return true;
207 }
208
209
210 // remove a sound effect, return true if successful
211 bool SGSampleGroup::remove( const string &refname ) {
212
213     sample_map_iterator sample_it = _samples.find( refname );
214     if ( sample_it == _samples.end() ) {
215         // sample was not found
216         return false;
217     }
218
219     _samples.erase( sample_it );
220     return true;
221 }
222
223
224 // return true of the specified sound exists in the sound manager system
225 bool SGSampleGroup::exists( const string &refname ) {
226     sample_map_iterator sample_it = _samples.find( refname );
227     if ( sample_it == _samples.end() ) {
228         // sample was not found
229         return false;
230     }
231
232     return true;
233 }
234
235
236 // return a pointer to the SGSoundSample if the specified sound exists
237 // in the sound manager system, otherwise return NULL
238 SGSoundSample *SGSampleGroup::find( const string &refname ) {
239     sample_map_iterator sample_it = _samples.find( refname );
240     if ( sample_it == _samples.end() ) {
241         // sample was not found
242         return NULL;
243     }
244
245     return sample_it->second;
246 }
247
248
249 // stop playing all associated samples
250 void
251 SGSampleGroup::suspend ()
252 {
253     _active = false;
254     sample_map_iterator sample_current = _samples.begin();
255     sample_map_iterator sample_end = _samples.end();
256     for ( ; sample_current != sample_end; ++sample_current ) {
257         SGSoundSample *sample = sample_current->second;
258
259         if ( sample->is_valid_source() && sample->is_playing() ) {
260             unsigned int source = sample->get_source();
261             alSourcePause( source );
262         }
263     }
264     testForALError("suspend");
265 }
266
267 // resume playing all associated samples
268 void
269 SGSampleGroup::resume ()
270 {
271     sample_map_iterator sample_current = _samples.begin();
272     sample_map_iterator sample_end = _samples.end();
273     for ( ; sample_current != sample_end; ++sample_current ) {
274         SGSoundSample *sample = sample_current->second;
275
276         if ( sample->is_valid_source() && sample->is_playing() ) {
277             unsigned int source = sample->get_source();
278             alSourcePlay( source );
279         }
280     }
281     testForALError("resume");
282     _active = true;
283 }
284
285
286 // tell the scheduler to play the indexed sample in a continuous loop
287 bool SGSampleGroup::play( const string &refname, bool looping = false ) {
288     SGSoundSample *sample = find( refname );
289
290     if ( sample == NULL ) {
291         return false;
292     }
293
294     sample->play( looping );
295     return true;
296 }
297
298
299 // return true of the specified sound is currently being played
300 bool SGSampleGroup::is_playing( const string& refname ) {
301     SGSoundSample *sample = find( refname );
302
303     if ( sample == NULL ) {
304         return false;
305     }
306
307     return ( sample->is_playing() ) ? true : false;
308 }
309
310 // immediate stop playing the sound
311 bool SGSampleGroup::stop( const string& refname ) {
312     SGSoundSample *sample  = find( refname );
313
314     if ( sample == NULL ) {
315         return false;
316     }
317
318     sample->stop();
319     return true;
320 }
321
322
323 // set source position of all managed sounds
324 void SGSampleGroup::set_position( SGVec3d pos ) {
325     if ( isnan(pos.data()[0]) || isnan(pos.data()[1]) || isnan(pos.data()[2]) )
326     {
327         SG_LOG( SG_GENERAL, SG_ALERT, "NAN's found in SampleGroup postion");
328         return;
329     }
330
331     if ( !_tied_to_listener ) {
332         sample_map_iterator sample_current = _samples.begin();
333         sample_map_iterator sample_end = _samples.end();
334         for ( ; sample_current != sample_end; ++sample_current ) {
335             SGSoundSample *sample = sample_current->second;
336             sample->set_base_position( pos );
337         }
338     }
339 }
340
341 // set source velocity of all managed sounds
342 void SGSampleGroup::set_velocity( SGVec3f vel ) {
343     if ( isnan(vel.data()[0]) || isnan(vel.data()[1]) || isnan(vel.data()[2]) )
344     {
345         SG_LOG( SG_GENERAL, SG_ALERT, "NAN's found in SampleGroup velocity");
346         return;
347     }
348
349     sample_map_iterator sample_current = _samples.begin();
350     sample_map_iterator sample_end = _samples.end();
351     for ( ; sample_current != sample_end; ++sample_current ) {
352         SGSoundSample *sample = sample_current->second;
353         sample->set_velocity( vel );
354     }
355 }
356
357 // ste the source orientation of all managed sounds
358 void SGSampleGroup::set_orientation( SGVec3f ori ) {
359     if ( isnan(ori.data()[0]) || isnan(ori.data()[1]) || isnan(ori.data()[2]) )
360     {
361         SG_LOG( SG_GENERAL, SG_ALERT, "NAN's found in SampleGroup orientation");
362         return;
363     }
364
365     sample_map_iterator sample_current = _samples.begin();
366     sample_map_iterator sample_end = _samples.end();
367     for ( ; sample_current != sample_end; ++sample_current ) {
368         SGSoundSample *sample = sample_current->second;
369         sample->set_orientation( ori );
370     }
371 }
372
373 void SGSampleGroup::update_sample_config( SGSoundSample *sample ) {
374     if ( sample->is_valid_source() ) {
375         unsigned int source = sample->get_source();
376
377         alSourcefv( source, AL_POSITION, sample->get_position());
378         alSourcefv( source, AL_DIRECTION, sample->get_direction() );
379         alSourcefv( source, AL_VELOCITY, sample->get_velocity() );
380         testForALError("position and orientation");
381
382         alSourcef( source, AL_PITCH, sample->get_pitch() );
383         alSourcef( source, AL_GAIN, sample->get_volume() );
384         testForALError("pitch and gain");
385
386         if ( sample->has_static_data_changed() ) {
387             alSourcef( source, AL_CONE_INNER_ANGLE, sample->get_innerangle() );
388             alSourcef( source, AL_CONE_OUTER_ANGLE, sample->get_outerangle() );
389             alSourcef( source, AL_CONE_OUTER_GAIN, sample->get_outergain() );
390             testForALError("audio cone");
391
392             alSourcef( source, AL_MAX_DISTANCE, sample->get_max_dist() );
393             alSourcef( source, AL_REFERENCE_DISTANCE,
394                                sample->get_reference_dist() );
395             testForALError("distance rolloff");
396         }
397     }
398 }
399
400 ALvoid
401 SGSampleGroup::load_file(SGSoundSample *sample) {
402     if (sample->is_file()) {
403         unsigned int size;
404         int freq, format;
405         void *data;
406
407         string sample_name = sample->get_sample_name();
408         _smgr->load(sample_name, &data, &format, &size, &freq);
409
410         sample->set_data( (unsigned char *)data );
411         sample->set_frequency( freq );
412         sample->set_format( format );
413         sample->set_size( size );
414     }
415 }
416
417 void SGSampleGroup::set_volume( float vol )
418 {
419     _volume = vol;
420     if (_volume < 0.0) _volume = 0.0;
421     if (_volume > 1.0) _volume = 1.0;
422
423     sample_map_iterator sample_current = _samples.begin();
424     sample_map_iterator sample_end = _samples.end();
425     for ( ; sample_current != sample_end; ++sample_current ) {
426         SGSoundSample *sample = sample_current->second;
427         sample->set_master_volume( _volume );
428     }
429 }
430
431 bool SGSampleGroup::testForError(void *p, string s)
432 {
433    if (p == NULL) {
434       SG_LOG( SG_GENERAL, SG_ALERT, "Error (sample group): " << s);
435       return true;
436    }
437    return false;
438 }
439
440 bool SGSampleGroup::testForALError(string s)
441 {
442     ALenum error = alGetError();
443     if (error != AL_NO_ERROR)  {
444        SG_LOG( SG_GENERAL, SG_ALERT, "AL Error (" << _refname << "): "
445                                       << alGetString(error) << " at " << s);
446        return true;
447     }
448     return false;
449 }
450