]> git.mxchange.org Git - simgear.git/blob - simgear/sound/soundmgr_openal.cxx
e91cf767785c64cf1493a1269068220bcb6d0986
[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 #if defined( __APPLE__ )
33 # include <ALUT/alut.h>
34 #else
35 # include <AL/alut.h>
36 #endif
37
38 #include <iostream>
39 #include <algorithm>
40
41 #include "soundmgr_openal.hxx"
42
43 #include <simgear/structure/exception.hxx>
44 #include <simgear/debug/logstream.hxx>
45 #include <simgear/misc/sg_path.hxx>
46 #include <simgear/math/SGMath.hxx>
47
48 using std::string;
49
50 extern bool isNaN(float *v);
51
52 #define MAX_SOURCES     128
53
54
55 #ifndef ALC_ALL_DEVICES_SPECIFIER
56 # define ALC_ALL_DEVICES_SPECIFIER      0x1013
57 #endif
58
59 //
60 // Sound Manager
61 //
62
63 int SGSoundMgr::_alut_init = 0;
64
65 // constructor
66 SGSoundMgr::SGSoundMgr() :
67     _working(false),
68     _active(false),
69     _changed(true),
70     _volume(0.0),
71     _device(NULL),
72     _context(NULL),
73     _absolute_pos(SGVec3d::zeros()),
74     _offset_pos(SGVec3d::zeros()),
75     _base_pos(SGVec3d::zeros()),
76     _geod_pos(SGGeod::fromCart(SGVec3d::zeros())),
77     _velocity(SGVec3d::zeros()),
78     _orientation(SGQuatd::zeros()),
79     _bad_doppler(false),
80     _renderer("unknown"),
81     _vendor("unknown")
82 {
83 #if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1
84     if (_alut_init == 0) {
85         if ( !alutInitWithoutContext(NULL, NULL) ) {
86             testForALUTError("alut initialization");
87             return;
88         }
89     }
90     _alut_init++;
91 #else
92   //#error ALUT 1.1 required, ALUT 1.0 is no longer supported, please upgrade
93 #endif
94 }
95
96 // destructor
97
98 SGSoundMgr::~SGSoundMgr() {
99
100     stop();
101 #if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1
102     _alut_init--;
103     if (_alut_init == 0) {
104         alutExit ();
105     }
106 #endif
107 }
108
109 // initialize the sound manager
110 void SGSoundMgr::init(const char *devname) {
111
112     SG_LOG( SG_GENERAL, SG_INFO, "Initializing OpenAL sound manager" );
113
114     ALCdevice *device = alcOpenDevice(devname);
115     if ( testForError(device, "Audio device not available, trying default") ) {
116         device = alcOpenDevice(NULL);
117         if (testForError(device, "Default Audio device not available.") ) {
118            return;
119         }
120     }
121
122     _device = device;
123     ALCcontext *context = alcCreateContext(device, NULL);
124     testForALCError("context creation.");
125     if ( testForError(context, "Unable to create a valid context.") ) {
126         alcCloseDevice (device);
127         return;
128     }
129
130     if ( !alcMakeContextCurrent(context) ) {
131         testForALCError("context initialization");
132         alcDestroyContext (context);
133         alcCloseDevice (device);
134         return;
135     }
136
137     if (_context != NULL)
138         SG_LOG(SG_GENERAL, SG_ALERT, "context is already assigned");
139     _context = context;
140     _working = true;
141
142     _at_up_vec[0] = 0.0; _at_up_vec[1] = 0.0; _at_up_vec[2] = -1.0;
143     _at_up_vec[3] = 0.0; _at_up_vec[4] = 1.0; _at_up_vec[5] = 0.0;
144
145     alListenerf( AL_GAIN, 0.0f );
146     alListenerfv( AL_ORIENTATION, _at_up_vec );
147     alListenerfv( AL_POSITION, SGVec3f::zeros().data() );
148     alListenerfv( AL_VELOCITY, SGVec3f::zeros().data() );
149
150     alDopplerFactor(1.0);
151     alDopplerVelocity(340.3);   // speed of sound in meters per second.
152
153     // gain = AL_REFERENCE_DISTANCE / (AL_REFERENCE_DISTANCE +
154     //        AL_ROLLOFF_FACTOR * (distance - AL_REFERENCE_DISTANCE));
155     alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
156
157     testForALError("listener initialization");
158
159     // get a free source one at a time
160     // if an error is returned no more (hardware) sources are available
161     for (unsigned int i=0; i<MAX_SOURCES; i++) {
162         ALuint source;
163         ALenum error;
164
165         alGetError();
166         alGenSources(1, &source);
167         error = alGetError();
168         if ( error == AL_NO_ERROR ) {
169             _free_sources.push_back( source );
170         }
171         else break;
172     }
173
174     _vendor = (const char *)alGetString(AL_VENDOR);
175     _renderer = (const char *)alGetString(AL_RENDERER);
176     if ( (_vendor != "Adalin" && _vendor != "Apple Computer Inc.") &&
177           (_vendor != "OpenAL Community" || (_renderer != "Software" &&
178                         _renderer != "OpenAL Sample Implementation"))
179        )
180     {
181        _bad_doppler = true;
182     }
183
184     if (_free_sources.size() == 0) {
185         SG_LOG(SG_GENERAL, SG_ALERT, "Unable to grab any OpenAL sources!");
186     }
187 }
188
189 void SGSoundMgr::activate() {
190     if ( _working ) {
191         _active = true;
192         sample_group_map_iterator sample_grp_current = _sample_groups.begin();
193         sample_group_map_iterator sample_grp_end = _sample_groups.end();
194         for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) {
195             SGSampleGroup *sgrp = sample_grp_current->second;
196             sgrp->activate();
197         }
198     }
199 }
200
201 // stop the sound manager
202 void SGSoundMgr::stop() {
203
204     // first stop all sample groups
205     sample_group_map_iterator sample_grp_current = _sample_groups.begin();
206     sample_group_map_iterator sample_grp_end = _sample_groups.end();
207     for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) {
208         SGSampleGroup *sgrp = sample_grp_current->second;
209         sgrp->stop();
210     }
211
212     // clear all OpenAL sources
213     for (unsigned int i=0; i<_free_sources.size(); i++) {
214         ALuint source = _free_sources[i];
215         alDeleteSources( 1 , &source );
216     }
217     _free_sources.clear();
218
219     // clear any OpenAL buffers before shutting down
220     buffer_map_iterator buffers_current = _buffers.begin();
221     buffer_map_iterator buffers_end = _buffers.end();
222     for ( ; buffers_current != buffers_end; ++buffers_current ) {
223         refUint ref = buffers_current->second;
224         ALuint buffer = ref.id;
225         alDeleteBuffers(1, &buffer);
226     }
227     _buffers.clear();
228
229     if (_working) {
230         _working = false;
231         _active = false;
232         _context = alcGetCurrentContext();
233         _device = alcGetContextsDevice(_context);
234         alcDestroyContext(_context);
235         alcCloseDevice(_device);
236         _context = NULL;
237
238         _renderer = "unknown";
239         _vendor = "unknown";
240     }
241 }
242
243 void SGSoundMgr::suspend() {
244     if (_working) {
245         sample_group_map_iterator sample_grp_current = _sample_groups.begin();
246         sample_group_map_iterator sample_grp_end = _sample_groups.end();
247         for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) {
248             SGSampleGroup *sgrp = sample_grp_current->second;
249             sgrp->stop();
250         }
251         _active = false;
252     }
253 }
254
255 void SGSoundMgr::resume() {
256     if (_working) {
257         sample_group_map_iterator sample_grp_current = _sample_groups.begin();
258         sample_group_map_iterator sample_grp_end = _sample_groups.end();
259         for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) {
260             SGSampleGroup *sgrp = sample_grp_current->second;
261             sgrp->resume();
262         }
263         _active = true;
264     }
265 }
266
267 void SGSoundMgr::bind ()
268 {
269     _free_sources.clear();
270     _free_sources.reserve( MAX_SOURCES );
271     _sources_in_use.clear();
272     _sources_in_use.reserve( MAX_SOURCES );
273 }
274
275
276 void SGSoundMgr::unbind ()
277 {
278     _sample_groups.clear();
279
280     // delete free sources
281     for (unsigned int i=0; i<_free_sources.size(); i++) {
282         ALuint source = _free_sources[i];
283         alDeleteSources( 1 , &source );
284     }
285
286     _free_sources.clear();
287     _sources_in_use.clear();
288 }
289
290 // run the audio scheduler
291 void SGSoundMgr::update( double dt ) {
292     if (_active) {
293         alcSuspendContext(_context);
294
295         if (_changed) {
296             update_pos_and_orientation();
297         }
298
299         sample_group_map_iterator sample_grp_current = _sample_groups.begin();
300         sample_group_map_iterator sample_grp_end = _sample_groups.end();
301         for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) {
302             SGSampleGroup *sgrp = sample_grp_current->second;
303             sgrp->update(dt);
304         }
305
306         if (_changed) {
307 #if 0
308 if (isNaN(_at_up_vec)) printf("NaN in listener orientation\n");
309 if (isNaN(toVec3f(_absolute_pos).data())) printf("NaN in listener position\n");
310 if (isNaN(_velocity.data())) printf("NaN in listener velocity\n");
311 #endif
312             alListenerf( AL_GAIN, _volume );
313             alListenerfv( AL_ORIENTATION, _at_up_vec );
314             // alListenerfv( AL_POSITION, toVec3f(_absolute_pos).data() );
315
316             SGQuatd hlOr = SGQuatd::fromLonLat( _geod_pos );
317             SGVec3d velocity = SGVec3d::zeros();
318             if ( _velocity[0] || _velocity[1] || _velocity[2] ) {
319                 velocity = hlOr.backTransform(_velocity*SG_FEET_TO_METER);
320             }
321
322             if ( _bad_doppler ) {
323                 velocity *= 100.0f;
324             }
325
326             alListenerfv( AL_VELOCITY, toVec3f(velocity).data() );
327             // alDopplerVelocity(340.3);        // TODO: altitude dependent
328             testForALError("update");
329             _changed = false;
330         }
331
332         alcProcessContext(_context);
333     }
334 }
335
336 // add a sample group, return true if successful
337 bool SGSoundMgr::add( SGSampleGroup *sgrp, const string& refname )
338 {
339     sample_group_map_iterator sample_grp_it = _sample_groups.find( refname );
340     if ( sample_grp_it != _sample_groups.end() ) {
341         // sample group already exists
342         return false;
343     }
344
345     if (_active) sgrp->activate();
346     _sample_groups[refname] = sgrp;
347
348     return true;
349 }
350
351
352 // remove a sound effect, return true if successful
353 bool SGSoundMgr::remove( const string &refname )
354 {
355     sample_group_map_iterator sample_grp_it = _sample_groups.find( refname );
356     if ( sample_grp_it == _sample_groups.end() ) {
357         // sample group was not found.
358         return false;
359     }
360
361     _sample_groups.erase( sample_grp_it );
362
363     return true;
364 }
365
366
367 // return true of the specified sound exists in the sound manager system
368 bool SGSoundMgr::exists( const string &refname ) {
369     sample_group_map_iterator sample_grp_it = _sample_groups.find( refname );
370     if ( sample_grp_it == _sample_groups.end() ) {
371         // sample group was not found.
372         return false;
373     }
374
375     return true;
376 }
377
378
379 // return a pointer to the SGSampleGroup if the specified sound exists
380 // in the sound manager system, otherwise return NULL
381 SGSampleGroup *SGSoundMgr::find( const string &refname, bool create ) {
382     sample_group_map_iterator sample_grp_it = _sample_groups.find( refname );
383     if ( sample_grp_it == _sample_groups.end() ) {
384         // sample group was not found.
385         if (create) {
386             SGSampleGroup* sgrp = new SGSampleGroup(this, refname);
387             add( sgrp, refname );
388             return sgrp;
389         }
390         else 
391             return NULL;
392     }
393
394     return sample_grp_it->second;
395 }
396
397
398 void SGSoundMgr::set_volume( float v )
399 {
400     _volume = v;
401     if (_volume > 1.0) _volume = 1.0;
402     if (_volume < 0.0) _volume = 0.0;
403     _changed = true;
404 }
405
406 // Get an unused source id
407 //
408 // The Sound Manager should keep track of the sources in use, the distance
409 // of these sources to the listener and the volume (also based on audio cone
410 // and hence orientation) of the sources.
411 //
412 // The Sound Manager is (and should be) the only one knowing about source
413 // management. Sources further away should be suspendped to free resources for
414 // newly added sounds close by.
415 unsigned int SGSoundMgr::request_source()
416 {
417     unsigned int source = NO_SOURCE;
418
419     if (_free_sources.size() > 0) {
420        source = _free_sources.back();
421        _free_sources.pop_back();
422        _sources_in_use.push_back(source);
423     }
424     else
425        SG_LOG( SG_GENERAL, SG_INFO, "No more free sources available\n");
426
427     return source;
428 }
429
430 // Free up a source id for further use
431 void SGSoundMgr::release_source( unsigned int source )
432 {
433     vector<ALuint>::iterator it;
434
435     it = std::find(_sources_in_use.begin(), _sources_in_use.end(), source);
436     if ( it != _sources_in_use.end() ) {
437         ALint result;
438
439         alGetSourcei( source, AL_SOURCE_STATE, &result );
440         if ( result == AL_PLAYING )
441             alSourceStop( source );
442         testForALError("release source");
443
444         alSourcei( source, AL_BUFFER, 0 );
445         _free_sources.push_back( source );
446         _sources_in_use.erase( it );
447     }
448 }
449
450 unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample)
451 {
452     ALuint buffer = NO_BUFFER;
453
454     if ( !sample->is_valid_buffer() ) {
455         // sample was not yet loaded or removed again
456         string sample_name = sample->get_sample_name();
457         void *sample_data = NULL;
458
459         // see if the sample name is already cached
460         buffer_map_iterator buffer_it = _buffers.find( sample_name );
461         if ( buffer_it != _buffers.end() ) {
462             buffer_it->second.refctr++;
463             buffer = buffer_it->second.id;
464             sample->set_buffer( buffer );
465             return buffer;
466         }
467
468         // sample name was not found in the buffer cache.
469         if ( sample->is_file() ) {
470             int freq, format;
471             size_t size;
472             bool res;
473
474             res = load(sample_name, &sample_data, &format, &size, &freq);
475             if (res == false) return buffer;
476
477             sample->set_frequency( freq );
478             sample->set_format( format );
479             sample->set_size( size );
480         }
481         else
482             sample_data = sample->get_data();
483
484         // create an OpenAL buffer handle
485         alGenBuffers(1, &buffer);
486         if ( !testForALError("generate buffer") ) {
487             // Copy data to the internal OpenAL buffer
488
489             ALenum format = sample->get_format();
490             ALsizei size = sample->get_size();
491             ALsizei freq = sample->get_frequency();
492             alBufferData( buffer, format, sample_data, size, freq );
493
494             if ( sample->is_file() ) free(sample_data);
495
496             if ( !testForALError("buffer add data") ) {
497                 sample->set_buffer(buffer);
498                 _buffers[sample_name] = refUint(buffer);
499             }
500         }
501     }
502     else {
503         buffer = sample->get_buffer();
504 }
505
506     return buffer;
507 }
508
509 void SGSoundMgr::release_buffer(SGSoundSample *sample)
510 {
511     if ( !sample->is_queue() )
512     {
513         string sample_name = sample->get_sample_name();
514         buffer_map_iterator buffer_it = _buffers.find( sample_name );
515         if ( buffer_it == _buffers.end() ) {
516             // buffer was not found
517             return;
518         }
519
520         sample->no_valid_buffer();
521         buffer_it->second.refctr--;
522         if (buffer_it->second.refctr == 0) {
523             ALuint buffer = buffer_it->second.id;
524             alDeleteBuffers(1, &buffer);
525             _buffers.erase( buffer_it );
526             testForALError("release buffer");
527         }
528     }
529 }
530
531 void SGSoundMgr::update_pos_and_orientation() {
532     /**
533      * Description: ORIENTATION is a pair of 3-tuples representing the
534      * 'at' direction vector and 'up' direction of the Object in
535      * Cartesian space. AL expects two vectors that are orthogonal to
536      * each other. These vectors are not expected to be normalized. If
537      * one or more vectors have zero length, implementation behavior
538      * is undefined. If the two vectors are linearly dependent,
539      * behavior is undefined.
540      *
541      * This is in the same coordinate system as OpenGL; y=up, z=back, x=right.
542      */
543     SGVec3d sgv_at = _orientation.backTransform(-SGVec3d::e3());
544     SGVec3d sgv_up = _orientation.backTransform(SGVec3d::e2());
545     _at_up_vec[0] = sgv_at[0];
546     _at_up_vec[1] = sgv_at[1];
547     _at_up_vec[2] = sgv_at[2];
548     _at_up_vec[3] = sgv_up[0];
549     _at_up_vec[4] = sgv_up[1];
550     _at_up_vec[5] = sgv_up[2];
551
552     // static const SGQuatd q(-0.5, -0.5, 0.5, 0.5);
553     // SGQuatd hlOr = SGQuatd::fromLonLat(SGGeod::fromCart(_base_pos));
554     // SGQuatd ec2body = hlOr*_orientation;
555     _absolute_pos = _base_pos; // + ec2body.backTransform( _offset_pos );
556 }
557
558 bool SGSoundMgr::load(string &samplepath, void **dbuf, int *fmt,
559                                           size_t *sz, int *frq )
560 {
561     if ( !_working ) return false;
562
563     ALenum format;
564     ALsizei size;
565     ALsizei freq;
566     ALvoid *data;
567
568 #if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1
569     ALfloat freqf;
570     // ignore previous errors to prevent the system from halting on silly errors
571     alGetError();
572     alcGetError(_device);
573     data = alutLoadMemoryFromFile(samplepath.c_str(), &format, &size, &freqf );
574     freq = (ALsizei)freqf;
575     int error = alutGetError();
576     if (data == NULL || error != ALUT_ERROR_NO_ERROR) {
577         string msg = "Failed to load wav file: ";
578          msg.append(alutGetErrorString(error));
579         throw sg_io_exception(msg.c_str(), sg_location(samplepath));
580         return false;
581     }
582
583 #else
584     ALbyte *fname = (ALbyte *)samplepath.c_str();
585 # if defined (__APPLE__)
586     alutLoadWAVFile( fname, &format, &data, &size, &freq );
587 # else
588     ALboolean loop;
589     alutLoadWAVFile( fname, &format, &data, &size, &freq, &loop );
590 # endif
591     ALenum error =  alGetError();
592     if ( error != AL_NO_ERROR ) {
593         string msg = "Failed to load wav file: ";
594         const ALchar *errorString = alGetString(error);
595         if (errorString) {
596             msg.append(errorString);
597         } else {
598             // alGetString returns NULL when an unexpected or OS specific error
599             // occurs: e.g. -43 on Mac when file is not found.
600             // In this case, alGetString() sets 'Invalid Enum' error, so
601             // showing with the original error number is helpful.
602             stringstream ss;
603             ss << alGetString(alGetError()) << "(" << error << ")";
604             msg.append(ss.str());
605         }
606         throw sg_io_exception(msg.c_str(), sg_location(samplepath));
607         return false;
608     }
609 #endif
610
611     *dbuf = (void *)data;
612     *fmt = (int)format;
613     *sz = (size_t)size;
614     *frq = (int)freq;
615
616     return true;
617 }
618
619 vector<const char*> SGSoundMgr::get_available_devices()
620 {
621     vector<const char*> devices;
622     const ALCchar *s;
623
624     if (alcIsExtensionPresent(NULL, "ALC_enumerate_all_EXT") == AL_TRUE) {
625         s = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
626     } else {
627         s = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
628     }
629
630     if (s) {
631         ALCchar *nptr, *ptr = (ALCchar *)s;
632
633         nptr = ptr;
634         while (*(nptr += strlen(ptr)+1) != 0)
635         {
636             devices.push_back(ptr);
637             ptr = nptr;
638         }
639         devices.push_back(ptr);
640     }
641
642     return devices;
643 }
644
645
646 bool SGSoundMgr::testForError(void *p, string s)
647 {
648    if (p == NULL) {
649       SG_LOG( SG_GENERAL, SG_ALERT, "Error: " << s);
650       return true;
651    }
652    return false;
653 }
654
655
656 bool SGSoundMgr::testForALError(string s)
657 {
658     ALenum error = alGetError();
659     if (error != AL_NO_ERROR)  {
660        SG_LOG( SG_GENERAL, SG_ALERT, "AL Error (sound manager): "
661                                       << alGetString(error) << " at " << s);
662        return true;
663     }
664     return false;
665 }
666
667 bool SGSoundMgr::testForALCError(string s)
668 {
669     ALCenum error;
670     error = alcGetError(_device);
671     if (error != ALC_NO_ERROR) {
672         SG_LOG( SG_GENERAL, SG_ALERT, "ALC Error (sound manager): "
673                                        << alcGetString(_device, error) << " at "
674                                        << s);
675         return true;
676     }
677     return false;
678 }
679
680 bool SGSoundMgr::testForALUTError(string s)
681 {
682 #if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1
683     ALenum error;
684     error =  alutGetError ();
685     if (error != ALUT_ERROR_NO_ERROR) {
686         SG_LOG( SG_GENERAL, SG_ALERT, "ALUT Error (sound manager): "
687                                        << alutGetErrorString(error) << " at "
688                                        << s);
689         return true;
690     }
691 #endif
692     return false;
693 }