]> git.mxchange.org Git - simgear.git/blob - simgear/sound/soundmgr_openal.cxx
385e918fcb1e3c3b99f25d70756747de40b22fd8
[simgear.git] / simgear / sound / soundmgr_openal.cxx
1 // soundmgr.cxx -- Sound effect management class
2 //
3 // Sound manager initially written by David Findlay
4 // <david_j_findlay@yahoo.com.au> 2001
5 //
6 // C++-ified by Curtis Olson, started March 2001.
7 // Modified for the new SoundSystem by Erik Hofman, October 2009
8 //
9 // Copyright (C) 2001  Curtis L. Olson - http://www.flightgear.org/~curt
10 // Copyright (C) 2009 Erik Hofman <erik@ehofman.com>
11 //
12 // This program is free software; you can redistribute it and/or
13 // modify it under the terms of the GNU General Public License as
14 // published by the Free Software Foundation; either version 2 of the
15 // License, or (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful, but
18 // WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 // General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software Foundation,
24 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
25 //
26 // $Id$
27
28 #ifdef HAVE_CONFIG_H
29 #  include <simgear_config.h>
30 #endif
31
32 #include <iostream>
33 #include <algorithm>
34 #include <cstring>
35
36 #include "soundmgr_openal.hxx"
37 #include "readwav.hxx"
38
39 #include <simgear/structure/exception.hxx>
40 #include <simgear/debug/logstream.hxx>
41 #include <simgear/misc/sg_path.hxx>
42
43 using std::string;
44 using std::vector;
45
46 extern bool isNaN(float *v);
47
48 #define MAX_SOURCES     128
49
50
51 #ifndef ALC_ALL_DEVICES_SPECIFIER
52 # define ALC_ALL_DEVICES_SPECIFIER      0x1013
53 #endif
54
55 //
56 // Sound Manager
57 //
58
59 // constructor
60 SGSoundMgr::SGSoundMgr() :
61     _working(false),
62     _active(false),
63     _changed(true),
64     _volume(0.0),
65     _device(NULL),
66     _context(NULL),
67     _absolute_pos(SGVec3d::zeros()),
68     _offset_pos(SGVec3d::zeros()),
69     _base_pos(SGVec3d::zeros()),
70     _geod_pos(SGGeod::fromCart(SGVec3d::zeros())),
71     _velocity(SGVec3d::zeros()),
72     _orientation(SGQuatd::zeros()),
73     _bad_doppler(false),
74     _renderer("unknown"),
75     _vendor("unknown")
76 {
77 }
78
79 // destructor
80
81 SGSoundMgr::~SGSoundMgr() {
82
83     stop();
84 }
85
86 // initialize the sound manager
87 void SGSoundMgr::init(const char *devname) {
88
89     SG_LOG( SG_SOUND, SG_INFO, "Initializing OpenAL sound manager" );
90
91     ALCdevice *device = alcOpenDevice(devname);
92     if ( testForError(device, "Audio device not available, trying default") ) {
93         device = alcOpenDevice(NULL);
94         if (testForError(device, "Default Audio device not available.") ) {
95            return;
96         }
97     }
98
99     _device = device;
100     ALCcontext *context = alcCreateContext(device, NULL);
101     testForALCError("context creation.");
102     if ( testForError(context, "Unable to create a valid context.") ) {
103         alcCloseDevice (device);
104         return;
105     }
106
107     if ( !alcMakeContextCurrent(context) ) {
108         testForALCError("context initialization");
109         alcDestroyContext (context);
110         alcCloseDevice (device);
111         return;
112     }
113
114     if (_context != NULL)
115         SG_LOG(SG_SOUND, SG_ALERT, "context is already assigned");
116     _context = context;
117     _working = true;
118
119     _at_up_vec[0] = 0.0; _at_up_vec[1] = 0.0; _at_up_vec[2] = -1.0;
120     _at_up_vec[3] = 0.0; _at_up_vec[4] = 1.0; _at_up_vec[5] = 0.0;
121
122     alListenerf( AL_GAIN, 0.0f );
123     alListenerfv( AL_ORIENTATION, _at_up_vec );
124     alListenerfv( AL_POSITION, SGVec3f::zeros().data() );
125     alListenerfv( AL_VELOCITY, SGVec3f::zeros().data() );
126
127     alDopplerFactor(1.0);
128     alDopplerVelocity(340.3);   // speed of sound in meters per second.
129
130     // gain = AL_REFERENCE_DISTANCE / (AL_REFERENCE_DISTANCE +
131     //        AL_ROLLOFF_FACTOR * (distance - AL_REFERENCE_DISTANCE));
132     alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
133
134     testForALError("listener initialization");
135
136     // get a free source one at a time
137     // if an error is returned no more (hardware) sources are available
138     for (unsigned int i=0; i<MAX_SOURCES; i++) {
139         ALuint source;
140         ALenum error;
141
142         alGetError();
143         alGenSources(1, &source);
144         error = alGetError();
145         if ( error == AL_NO_ERROR ) {
146             _free_sources.push_back( source );
147         }
148         else break;
149     }
150
151     _vendor = (const char *)alGetString(AL_VENDOR);
152     _renderer = (const char *)alGetString(AL_RENDERER);
153
154     if (_vendor == "Creative Labs Inc.") {
155        _bad_doppler = true;
156
157     } else if (_vendor == "OpenAL Community" && _renderer == "OpenAL Soft") {
158        _bad_doppler = true;
159     }
160
161     if (_free_sources.size() == 0) {
162         SG_LOG(SG_SOUND, SG_ALERT, "Unable to grab any OpenAL sources!");
163     }
164 }
165
166 void SGSoundMgr::activate() {
167     if ( _working ) {
168         _active = true;
169         sample_group_map_iterator sample_grp_current = _sample_groups.begin();
170         sample_group_map_iterator sample_grp_end = _sample_groups.end();
171         for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) {
172             SGSampleGroup *sgrp = sample_grp_current->second;
173             sgrp->activate();
174         }
175     }
176 }
177
178 // stop the sound manager
179 void SGSoundMgr::stop() {
180
181     // first stop all sample groups
182     sample_group_map_iterator sample_grp_current = _sample_groups.begin();
183     sample_group_map_iterator sample_grp_end = _sample_groups.end();
184     for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) {
185         SGSampleGroup *sgrp = sample_grp_current->second;
186         sgrp->stop();
187     }
188
189     // clear all OpenAL sources
190     for (unsigned int i=0; i<_free_sources.size(); i++) {
191         ALuint source = _free_sources[i];
192         alDeleteSources( 1 , &source );
193         testForALError("SGSoundMgr::stop: delete sources");
194     }
195     _free_sources.clear();
196
197     // clear any OpenAL buffers before shutting down
198     buffer_map_iterator buffers_current = _buffers.begin();
199     buffer_map_iterator buffers_end = _buffers.end();
200     for ( ; buffers_current != buffers_end; ++buffers_current ) {
201         refUint ref = buffers_current->second;
202         ALuint buffer = ref.id;
203         alDeleteBuffers(1, &buffer);
204         testForALError("SGSoundMgr::stop: delete buffers");
205     }
206     _buffers.clear();
207
208     if (_working) {
209         _working = false;
210         _active = false;
211         _context = alcGetCurrentContext();
212         _device = alcGetContextsDevice(_context);
213         alcDestroyContext(_context);
214         alcCloseDevice(_device);
215         _context = NULL;
216
217         _renderer = "unknown";
218         _vendor = "unknown";
219     }
220 }
221
222 void SGSoundMgr::suspend() {
223     if (_working) {
224         sample_group_map_iterator sample_grp_current = _sample_groups.begin();
225         sample_group_map_iterator sample_grp_end = _sample_groups.end();
226         for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) {
227             SGSampleGroup *sgrp = sample_grp_current->second;
228             sgrp->stop();
229         }
230         _active = false;
231     }
232 }
233
234 void SGSoundMgr::resume() {
235     if (_working) {
236         sample_group_map_iterator sample_grp_current = _sample_groups.begin();
237         sample_group_map_iterator sample_grp_end = _sample_groups.end();
238         for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) {
239             SGSampleGroup *sgrp = sample_grp_current->second;
240             sgrp->resume();
241         }
242         _active = true;
243     }
244 }
245
246 void SGSoundMgr::bind ()
247 {
248     _free_sources.clear();
249     _free_sources.reserve( MAX_SOURCES );
250     _sources_in_use.clear();
251     _sources_in_use.reserve( MAX_SOURCES );
252 }
253
254
255 void SGSoundMgr::unbind ()
256 {
257     _sample_groups.clear();
258
259     // delete free sources
260     for (unsigned int i=0; i<_free_sources.size(); i++) {
261         ALuint source = _free_sources[i];
262         alDeleteSources( 1 , &source );
263         testForALError("SGSoundMgr::unbind: delete sources");
264     }
265
266     _free_sources.clear();
267     _sources_in_use.clear();
268 }
269
270 // run the audio scheduler
271 void SGSoundMgr::update( double dt ) {
272     if (_active) {
273         alcSuspendContext(_context);
274
275         if (_changed) {
276             update_pos_and_orientation();
277         }
278
279         sample_group_map_iterator sample_grp_current = _sample_groups.begin();
280         sample_group_map_iterator sample_grp_end = _sample_groups.end();
281         for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) {
282             SGSampleGroup *sgrp = sample_grp_current->second;
283             sgrp->update(dt);
284         }
285
286         if (_changed) {
287 #if 0
288 if (isNaN(_at_up_vec)) printf("NaN in listener orientation\n");
289 if (isNaN(toVec3f(_absolute_pos).data())) printf("NaN in listener position\n");
290 if (isNaN(_velocity.data())) printf("NaN in listener velocity\n");
291 #endif
292             alListenerf( AL_GAIN, _volume );
293             alListenerfv( AL_ORIENTATION, _at_up_vec );
294             // alListenerfv( AL_POSITION, toVec3f(_absolute_pos).data() );
295
296             SGQuatd hlOr = SGQuatd::fromLonLat( _geod_pos );
297             SGVec3d velocity = SGVec3d::zeros();
298             if ( _velocity[0] || _velocity[1] || _velocity[2] ) {
299                 velocity = hlOr.backTransform(_velocity*SG_FEET_TO_METER);
300             }
301
302             if ( _bad_doppler ) {
303                 velocity *= 100.0f;
304             }
305
306             alListenerfv( AL_VELOCITY, toVec3f(velocity).data() );
307             // alDopplerVelocity(340.3);        // TODO: altitude dependent
308             testForALError("update");
309             _changed = false;
310         }
311
312         alcProcessContext(_context);
313     }
314 }
315
316 // add a sample group, return true if successful
317 bool SGSoundMgr::add( SGSampleGroup *sgrp, const string& refname )
318 {
319     sample_group_map_iterator sample_grp_it = _sample_groups.find( refname );
320     if ( sample_grp_it != _sample_groups.end() ) {
321         // sample group already exists
322         return false;
323     }
324
325     if (_active) sgrp->activate();
326     _sample_groups[refname] = sgrp;
327
328     return true;
329 }
330
331
332 // remove a sound effect, return true if successful
333 bool SGSoundMgr::remove( const string &refname )
334 {
335     sample_group_map_iterator sample_grp_it = _sample_groups.find( refname );
336     if ( sample_grp_it == _sample_groups.end() ) {
337         // sample group was not found.
338         return false;
339     }
340
341     _sample_groups.erase( sample_grp_it );
342
343     return true;
344 }
345
346
347 // return true of the specified sound exists in the sound manager system
348 bool SGSoundMgr::exists( const string &refname ) {
349     sample_group_map_iterator sample_grp_it = _sample_groups.find( refname );
350     if ( sample_grp_it == _sample_groups.end() ) {
351         // sample group was not found.
352         return false;
353     }
354
355     return true;
356 }
357
358
359 // return a pointer to the SGSampleGroup if the specified sound exists
360 // in the sound manager system, otherwise return NULL
361 SGSampleGroup *SGSoundMgr::find( const string &refname, bool create ) {
362     sample_group_map_iterator sample_grp_it = _sample_groups.find( refname );
363     if ( sample_grp_it == _sample_groups.end() ) {
364         // sample group was not found.
365         if (create) {
366             SGSampleGroup* sgrp = new SGSampleGroup(this, refname);
367             add( sgrp, refname );
368             return sgrp;
369         }
370         else 
371             return NULL;
372     }
373
374     return sample_grp_it->second;
375 }
376
377
378 void SGSoundMgr::set_volume( float v )
379 {
380     _volume = v;
381     if (_volume > 1.0) _volume = 1.0;
382     if (_volume < 0.0) _volume = 0.0;
383     _changed = true;
384 }
385
386 // Get an unused source id
387 //
388 // The Sound Manager should keep track of the sources in use, the distance
389 // of these sources to the listener and the volume (also based on audio cone
390 // and hence orientation) of the sources.
391 //
392 // The Sound Manager is (and should be) the only one knowing about source
393 // management. Sources further away should be suspended to free resources for
394 // newly added sounds close by.
395 unsigned int SGSoundMgr::request_source()
396 {
397     unsigned int source = NO_SOURCE;
398
399     if (_free_sources.size() > 0) {
400        source = _free_sources.back();
401        _free_sources.pop_back();
402        _sources_in_use.push_back(source);
403     }
404     else
405        SG_LOG( SG_SOUND, SG_BULK, "Sound manager: No more free sources available!\n");
406
407     return source;
408 }
409
410 // Free up a source id for further use
411 void SGSoundMgr::release_source( unsigned int source )
412 {
413     vector<ALuint>::iterator it;
414
415     it = std::find(_sources_in_use.begin(), _sources_in_use.end(), source);
416     if ( it != _sources_in_use.end() ) {
417         ALint result;
418
419         alGetSourcei( source, AL_SOURCE_STATE, &result );
420         if ( result == AL_PLAYING || result == AL_PAUSED ) {
421             alSourceStop( source );
422         }
423
424         alSourcei( source, AL_BUFFER, 0 );      // detach the associated buffer
425         testForALError("release_source");
426         _free_sources.push_back( source );
427         _sources_in_use.erase( it );
428     }
429 }
430
431 unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample)
432 {
433     ALuint buffer = NO_BUFFER;
434
435     if ( !sample->is_valid_buffer() ) {
436         // sample was not yet loaded or removed again
437         string sample_name = sample->get_sample_name();
438         void *sample_data = NULL;
439
440         // see if the sample name is already cached
441         buffer_map_iterator buffer_it = _buffers.find( sample_name );
442         if ( buffer_it != _buffers.end() ) {
443             buffer_it->second.refctr++;
444             buffer = buffer_it->second.id;
445             sample->set_buffer( buffer );
446             return buffer;
447         }
448
449         // sample name was not found in the buffer cache.
450         if ( sample->is_file() ) {
451             int freq, format;
452             size_t size;
453
454             try {
455               bool res = load(sample_name, &sample_data, &format, &size, &freq);
456               if (res == false) return NO_BUFFER;
457             } catch (sg_exception& e) {
458               SG_LOG(SG_SOUND, SG_ALERT,
459                     "failed to load sound buffer: " << e.getFormattedMessage());
460               sample->set_buffer( SGSoundMgr::FAILED_BUFFER );
461               return FAILED_BUFFER;
462             }
463             
464             sample->set_frequency( freq );
465             sample->set_format( format );
466             sample->set_size( size );
467
468         } else {
469             sample_data = sample->get_data();
470         }
471
472         // create an OpenAL buffer handle
473         alGenBuffers(1, &buffer);
474         if ( !testForALError("generate buffer") ) {
475             // Copy data to the internal OpenAL buffer
476
477             ALenum format = sample->get_format();
478             ALsizei size = sample->get_size();
479             ALsizei freq = sample->get_frequency();
480             alBufferData( buffer, format, sample_data, size, freq );
481
482             if ( !testForALError("buffer add data") ) {
483                 sample->set_buffer(buffer);
484                 _buffers[sample_name] = refUint(buffer);
485             }
486         }
487
488         if ( sample->is_file() ) free(sample_data);
489     }
490     else {
491         buffer = sample->get_buffer();
492     }
493
494     return buffer;
495 }
496
497 void SGSoundMgr::release_buffer(SGSoundSample *sample)
498 {
499     if ( !sample->is_queue() )
500     {
501         string sample_name = sample->get_sample_name();
502         buffer_map_iterator buffer_it = _buffers.find( sample_name );
503         if ( buffer_it == _buffers.end() ) {
504             // buffer was not found
505             return;
506         }
507
508         sample->no_valid_buffer();
509         buffer_it->second.refctr--;
510         if (buffer_it->second.refctr == 0) {
511             ALuint buffer = buffer_it->second.id;
512             alDeleteBuffers(1, &buffer);
513             _buffers.erase( buffer_it );
514             testForALError("release buffer");
515         }
516     }
517 }
518
519 void SGSoundMgr::update_pos_and_orientation() {
520     /**
521      * Description: ORIENTATION is a pair of 3-tuples representing the
522      * 'at' direction vector and 'up' direction of the Object in
523      * Cartesian space. AL expects two vectors that are orthogonal to
524      * each other. These vectors are not expected to be normalized. If
525      * one or more vectors have zero length, implementation behavior
526      * is undefined. If the two vectors are linearly dependent,
527      * behavior is undefined.
528      *
529      * This is in the same coordinate system as OpenGL; y=up, z=back, x=right.
530      */
531     SGVec3d sgv_at = _orientation.backTransform(-SGVec3d::e3());
532     SGVec3d sgv_up = _orientation.backTransform(SGVec3d::e2());
533     _at_up_vec[0] = sgv_at[0];
534     _at_up_vec[1] = sgv_at[1];
535     _at_up_vec[2] = sgv_at[2];
536     _at_up_vec[3] = sgv_up[0];
537     _at_up_vec[4] = sgv_up[1];
538     _at_up_vec[5] = sgv_up[2];
539
540     _absolute_pos = _base_pos;
541 }
542
543 bool SGSoundMgr::load(const string &samplepath, void **dbuf, int *fmt,
544                                           size_t *sz, int *frq )
545 {
546     if ( !_working ) return false;
547
548     ALenum format;
549     ALsizei size;
550     ALsizei freq;
551     ALvoid *data;
552
553     ALfloat freqf;
554     // ignore previous errors to prevent the system from halting on silly errors
555     alGetError();
556     alcGetError(_device);
557     data = simgear::loadWAVFromFile(samplepath, format, size, freqf );
558     freq = (ALsizei)freqf;
559     if (data == NULL) {
560         throw sg_io_exception("Failed to load wav file", sg_location(samplepath));
561     }
562
563     if (format == AL_FORMAT_STEREO8 || format == AL_FORMAT_STEREO16) {
564         free(data);
565         throw sg_io_exception("Warning: STEREO files are not supported for 3D audio effects: " + samplepath);
566     }
567
568     *dbuf = (void *)data;
569     *fmt = (int)format;
570     *sz = (size_t)size;
571     *frq = (int)freq;
572
573     return true;
574 }
575
576 vector<const char*> SGSoundMgr::get_available_devices()
577 {
578     vector<const char*> devices;
579     const ALCchar *s;
580
581     if (alcIsExtensionPresent(NULL, "ALC_enumerate_all_EXT") == AL_TRUE) {
582         s = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
583     } else {
584         s = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
585     }
586
587     if (s) {
588         ALCchar *nptr, *ptr = (ALCchar *)s;
589
590         nptr = ptr;
591         while (*(nptr += strlen(ptr)+1) != 0)
592         {
593             devices.push_back(ptr);
594             ptr = nptr;
595         }
596         devices.push_back(ptr);
597     }
598
599     return devices;
600 }
601
602
603 bool SGSoundMgr::testForError(void *p, string s)
604 {
605    if (p == NULL) {
606       SG_LOG( SG_SOUND, SG_ALERT, "Error: " << s);
607       return true;
608    }
609    return false;
610 }
611
612
613 bool SGSoundMgr::testForALError(string s)
614 {
615     ALenum error = alGetError();
616     if (error != AL_NO_ERROR)  {
617        SG_LOG( SG_SOUND, SG_ALERT, "AL Error (sound manager): "
618                                       << alGetString(error) << " at " << s);
619        return true;
620     }
621     return false;
622 }
623
624 bool SGSoundMgr::testForALCError(string s)
625 {
626     ALCenum error;
627     error = alcGetError(_device);
628     if (error != ALC_NO_ERROR) {
629         SG_LOG( SG_SOUND, SG_ALERT, "ALC Error (sound manager): "
630                                        << alcGetString(_device, error) << " at "
631                                        << s);
632         return true;
633     }
634     return false;
635 }