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