]> git.mxchange.org Git - simgear.git/blob - simgear/sound/sample_group.cxx
OpenAL buffer management; add a buffer cache to prevent loading the same sample in...
[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             _smgr->release_buffer( sample );
80         }
81     }
82
83     _smgr = 0;
84 }
85
86 void SGSampleGroup::update( double dt ) {
87
88     if ( !_active ) return;
89
90     // testForALError("start of update!!\n");
91
92     sample_map_iterator sample_current = _samples.begin();
93     sample_map_iterator sample_end = _samples.end();
94     for ( ; sample_current != sample_end; ++sample_current ) {
95         SGSoundSample *sample = sample_current->second;
96
97         if ( !sample->is_valid_source() && sample->is_playing() ) {
98             //
99             // a request to start playing a sound has been filed.
100             //
101             if ( _smgr->request_buffer(sample) == SGSoundMgr::NO_BUFFER )
102                 continue;
103
104             if ( _tied_to_listener && _smgr->has_changed() ) {
105                 sample->set_base_position( _smgr->get_position_vec() );
106                 sample->set_orientation( _smgr->get_direction() );
107                 sample->set_velocity( _smgr->get_velocity() );
108             }
109
110             // start playing the sample
111             ALboolean looping = sample->get_looping() ? AL_TRUE : AL_FALSE;
112             ALuint buffer = sample->get_buffer();
113             ALuint source = _smgr->request_source();
114             if (alIsSource(source) == AL_TRUE && alIsBuffer(buffer) == AL_TRUE)
115             {
116                 sample->set_source( source );
117                 
118                 alSourcei( source, AL_BUFFER, buffer );
119                 testForALError("assign buffer to source");
120
121                 sample->set_source( source );
122                 update_sample_config( sample );
123
124                 alSourcei( source, AL_SOURCE_RELATIVE, AL_FALSE );
125                 alSourcei( source, AL_LOOPING, looping );
126                 alSourcef( source, AL_ROLLOFF_FACTOR, 1.2 );
127                 alSourcePlay( source );
128                 testForALError("sample play");
129             } else {
130                 if (alIsBuffer(buffer) == AL_FALSE) 
131                    SG_LOG( SG_GENERAL, SG_ALERT, "No such buffer!\n");
132                 // sample->no_valid_source();
133                 // sadly, no free source available at this time
134             }
135
136         } else if ( sample->is_valid_source() && sample->has_changed() ) {
137             if ( !sample->is_playing() ) {
138                 // a request to stop playing the sound has been filed.
139
140                 sample->no_valid_source();
141                 sample->stop();
142                 _smgr->release_source( sample->get_source() );
143             } else  {
144                 update_sample_config( sample );
145             }
146
147         } else if ( sample->is_valid_source() ) {
148             // check if the sound has stopped by itself
149
150             unsigned int source = sample->get_source();
151             int result;
152
153             alGetSourcei( source, AL_SOURCE_STATE, &result );
154             if ( result == AL_STOPPED ) {
155                 // sample is stoped because it wasn't looping
156                 sample->no_valid_source();
157                 sample->stop();
158                 _smgr->release_source( source );
159             }
160         }
161         testForALError("update");
162     }
163 }
164
165 // add a sound effect, return true if successful
166 bool SGSampleGroup::add( SGSoundSample *sound, const string& refname ) {
167
168     sample_map_iterator sample_it = _samples.find( refname );
169     if ( sample_it != _samples.end() ) {
170         // sample name already exists
171         return false;
172     }
173
174     _samples[refname] = sound;
175     return true;
176 }
177
178
179 // remove a sound effect, return true if successful
180 bool SGSampleGroup::remove( const string &refname ) {
181
182     sample_map_iterator sample_it = _samples.find( refname );
183     if ( sample_it == _samples.end() ) {
184         // sample was not found
185         return false;
186     }
187
188     // remove the sources buffer
189     _smgr->release_buffer( sample_it->second );
190     _samples.erase( refname );
191
192     return true;
193 }
194
195
196 // return true of the specified sound exists in the sound manager system
197 bool SGSampleGroup::exists( const string &refname ) {
198     sample_map_iterator sample_it = _samples.find( refname );
199     if ( sample_it == _samples.end() ) {
200         // sample was not found
201         return false;
202     }
203
204     return true;
205 }
206
207
208 // return a pointer to the SGSoundSample if the specified sound exists
209 // in the sound manager system, otherwise return NULL
210 SGSoundSample *SGSampleGroup::find( const string &refname ) {
211     sample_map_iterator sample_it = _samples.find( refname );
212     if ( sample_it == _samples.end() ) {
213         // sample was not found
214         return NULL;
215     }
216
217     return sample_it->second;
218 }
219
220
221 // stop playing all associated samples
222 void
223 SGSampleGroup::suspend ()
224 {
225     _active = false;
226     sample_map_iterator sample_current = _samples.begin();
227     sample_map_iterator sample_end = _samples.end();
228     for ( ; sample_current != sample_end; ++sample_current ) {
229         SGSoundSample *sample = sample_current->second;
230
231         if ( sample->is_valid_source() && sample->is_playing() ) {
232             unsigned int source = sample->get_source();
233             alSourcePause( source );
234         }
235     }
236     testForALError("suspend");
237 }
238
239 // resume playing all associated samples
240 void
241 SGSampleGroup::resume ()
242 {
243     sample_map_iterator sample_current = _samples.begin();
244     sample_map_iterator sample_end = _samples.end();
245     for ( ; sample_current != sample_end; ++sample_current ) {
246         SGSoundSample *sample = sample_current->second;
247
248         if ( sample->is_valid_source() && sample->is_playing() ) {
249             unsigned int source = sample->get_source();
250             alSourcePlay( source );
251         }
252     }
253     testForALError("resume");
254     _active = true;
255 }
256
257
258 // tell the scheduler to play the indexed sample in a continuous loop
259 bool SGSampleGroup::play( const string &refname, bool looping = false ) {
260     SGSoundSample *sample = find( refname );
261
262     if ( sample == NULL ) {
263         return false;
264     }
265
266     sample->play( looping );
267     return true;
268 }
269
270
271 // return true of the specified sound is currently being played
272 bool SGSampleGroup::is_playing( const string& refname ) {
273     SGSoundSample *sample = find( refname );
274
275     if ( sample == NULL ) {
276         return false;
277     }
278
279     return ( sample->is_playing() ) ? true : false;
280 }
281
282 // immediate stop playing the sound
283 bool SGSampleGroup::stop( const string& refname ) {
284     SGSoundSample *sample  = find( refname );
285
286     if ( sample == NULL ) {
287         return false;
288     }
289
290     sample->stop();
291     return true;
292 }
293
294
295 // set source position of all managed sounds
296 void SGSampleGroup::set_position( SGVec3d pos ) {
297     if ( isnan(pos.data()[0]) || isnan(pos.data()[1]) || isnan(pos.data()[2]) )
298     {
299         SG_LOG( SG_GENERAL, SG_ALERT, "NAN's found in SampleGroup postion");
300         return;
301     }
302
303     if ( !_tied_to_listener ) {
304         sample_map_iterator sample_current = _samples.begin();
305         sample_map_iterator sample_end = _samples.end();
306         for ( ; sample_current != sample_end; ++sample_current ) {
307             SGSoundSample *sample = sample_current->second;
308             sample->set_base_position( pos );
309         }
310     }
311 }
312
313 // set source velocity of all managed sounds
314 void SGSampleGroup::set_velocity( SGVec3f vel ) {
315     if ( isnan(vel.data()[0]) || isnan(vel.data()[1]) || isnan(vel.data()[2]) )
316     {
317         SG_LOG( SG_GENERAL, SG_ALERT, "NAN's found in SampleGroup velocity");
318         return;
319     }
320
321     sample_map_iterator sample_current = _samples.begin();
322     sample_map_iterator sample_end = _samples.end();
323     for ( ; sample_current != sample_end; ++sample_current ) {
324         SGSoundSample *sample = sample_current->second;
325         sample->set_velocity( vel );
326     }
327 }
328
329 // ste the source orientation of all managed sounds
330 void SGSampleGroup::set_orientation( SGVec3f ori ) {
331     if ( isnan(ori.data()[0]) || isnan(ori.data()[1]) || isnan(ori.data()[2]) )
332     {
333         SG_LOG( SG_GENERAL, SG_ALERT, "NAN's found in SampleGroup orientation");
334         return;
335     }
336
337     sample_map_iterator sample_current = _samples.begin();
338     sample_map_iterator sample_end = _samples.end();
339     for ( ; sample_current != sample_end; ++sample_current ) {
340         SGSoundSample *sample = sample_current->second;
341         sample->set_orientation( ori );
342     }
343 }
344
345 void SGSampleGroup::update_sample_config( SGSoundSample *sample ) {
346     if ( sample->is_valid_source() ) {
347         unsigned int source = sample->get_source();
348
349         alSourcefv( source, AL_POSITION, sample->get_position());
350         alSourcefv( source, AL_DIRECTION, sample->get_direction() );
351         alSourcefv( source, AL_VELOCITY, sample->get_velocity() );
352         testForALError("position and orientation");
353
354         alSourcef( source, AL_PITCH, sample->get_pitch() );
355         alSourcef( source, AL_GAIN, sample->get_volume() );
356         testForALError("pitch and gain");
357
358         if ( sample->has_static_data_changed() ) {
359             alSourcef( source, AL_CONE_INNER_ANGLE, sample->get_innerangle() );
360             alSourcef( source, AL_CONE_OUTER_ANGLE, sample->get_outerangle() );
361             alSourcef( source, AL_CONE_OUTER_GAIN, sample->get_outergain() );
362             testForALError("audio cone");
363
364             alSourcef( source, AL_MAX_DISTANCE, sample->get_max_dist() );
365             alSourcef( source, AL_REFERENCE_DISTANCE,
366                                sample->get_reference_dist() );
367             testForALError("distance rolloff");
368         }
369     }
370 }
371
372 void SGSampleGroup::set_volume( float vol )
373 {
374     _volume = vol;
375     if (_volume < 0.0) _volume = 0.0;
376     if (_volume > 1.0) _volume = 1.0;
377
378     sample_map_iterator sample_current = _samples.begin();
379     sample_map_iterator sample_end = _samples.end();
380     for ( ; sample_current != sample_end; ++sample_current ) {
381         SGSoundSample *sample = sample_current->second;
382         sample->set_master_volume( _volume );
383     }
384 }
385
386 bool SGSampleGroup::testForError(void *p, string s)
387 {
388    if (p == NULL) {
389       SG_LOG( SG_GENERAL, SG_ALERT, "Error (sample group): " << s);
390       return true;
391    }
392    return false;
393 }
394
395 bool SGSampleGroup::testForALError(string s)
396 {
397     ALenum error = alGetError();
398     if (error != AL_NO_ERROR)  {
399        SG_LOG( SG_GENERAL, SG_ALERT, "AL Error (" << _refname << "): "
400                                       << alGetString(error) << " at " << s);
401        return true;
402     }
403     return false;
404 }
405