1 // soundmgr_openal.cxx -- Sound effect management class for OpenAL
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 <stdio.h>
33 
34 #include <iostream>
35 #include <algorithm>
36 #include <cstring>
37 #include <cassert>
38 
39 #include "soundmgr.hxx"
40 #include "readwav.hxx"
41 #include "sample_group.hxx"
42 
43 #include <simgear/sg_inlines.h>
44 #include <simgear/structure/exception.hxx>
45 #include <simgear/debug/logstream.hxx>
46 #include <simgear/misc/sg_path.hxx>
47 
48 #if defined(__APPLE__)
49 # include <OpenAL/al.h>
50 # include <OpenAL/alc.h>
51 #elif defined(OPENALSDK)
52 # include <al.h>
53 # include <alc.h>
54 #else
55 # include <AL/al.h>
56 # include <AL/alc.h>
57 #endif
58 
59 using std::vector;
60 
61 
62 #define MAX_SOURCES	128
63 
64 #ifndef ALC_ALL_DEVICES_SPECIFIER
65 # define ALC_ALL_DEVICES_SPECIFIER	0x1013
66 #endif
67 #ifndef AL_FORMAT_MONO_MULAW_EXT
68 # define AL_FORMAT_MONO_MULAW_EXT	0x10014
69 #endif
70 #ifndef AL_FORMAT_MONO_IMA4
71 # define AL_FORMAT_MONO_IMA4		0x1300
72 #endif
73 #ifndef AL_UNPACK_BLOCK_ALIGNMENT_SOFT
74 # define AL_UNPACK_BLOCK_ALIGNMENT_SOFT	0x200C
75 #endif
76 
77 
78 struct refUint {
79     unsigned int refctr;
80     ALuint id;
81 
refUintrefUint82     refUint() { refctr = 0; id = (ALuint)-1; };
refUintrefUint83     refUint(ALuint i) { refctr = 1; id = i; };
~refUintrefUint84     ~refUint() {};
85 };
86 
87 using buffer_map = std::map < std::string, refUint >;
88 using sample_group_map = std::map < std::string, SGSharedPtr<SGSampleGroup> >;
89 
isNaN(float * v)90 inline bool isNaN(float *v) {
91    return (SGMisc<float>::isNaN(v[0]) || SGMisc<float>::isNaN(v[1]) || SGMisc<float>::isNaN(v[2]));
92 }
93 
94 
95 class SGSoundMgr::SoundManagerPrivate
96 {
97 public:
98     SoundManagerPrivate() = default;
99     ~SoundManagerPrivate() = default;
100 
init()101     void init()
102     {
103         _at_up_vec[0] = 0.0; _at_up_vec[1] = 0.0; _at_up_vec[2] = -1.0;
104         _at_up_vec[3] = 0.0; _at_up_vec[4] = 1.0; _at_up_vec[5] = 0.0;
105     }
106 
update_pos_and_orientation()107     void update_pos_and_orientation()
108     {
109         /**
110          * Description: ORIENTATION is a pair of 3-tuples representing the
111          * 'at' direction vector and 'up' direction of the Object in
112          * Cartesian space. AL expects two vectors that are orthogonal to
113          * each other. These vectors are not expected to be normalized. If
114          * one or more vectors have zero length, implementation behavior
115          * is undefined. If the two vectors are linearly dependent,
116          * behavior is undefined.
117          *
118          * This is in the same coordinate system as OpenGL; y=up, z=back, x=right.
119          */
120         SGVec3d sgv_at = _orientation.backTransform(-SGVec3d::e3());
121         SGVec3d sgv_up = _orientation.backTransform(SGVec3d::e2());
122         _at_up_vec[0] = sgv_at[0];
123         _at_up_vec[1] = sgv_at[1];
124         _at_up_vec[2] = sgv_at[2];
125         _at_up_vec[3] = sgv_up[0];
126         _at_up_vec[4] = sgv_up[1];
127         _at_up_vec[5] = sgv_up[2];
128 
129         _absolute_pos = _base_pos;
130     }
131 
132     ALCdevice *_device = nullptr;
133     ALCcontext *_context = nullptr;
134 
135     std::vector<ALuint> _free_sources;
136     std::vector<ALuint> _sources_in_use;
137 
138     SGVec3d _absolute_pos = SGVec3d::zeros();
139     SGVec3d _base_pos = SGVec3d::zeros();
140     SGQuatd _orientation = SGQuatd::zeros();
141     // Orientation of the listener.
142     // first 3 elements are "at" vector, second 3 are "up" vector
143     ALfloat _at_up_vec[6];
144 
145     bool _bad_doppler = false;
146 
147     sample_group_map _sample_groups;
148     buffer_map _buffers;
149 };
150 
151 
152 //
153 // Sound Manager
154 //
155 
156 // constructor
SGSoundMgr()157 SGSoundMgr::SGSoundMgr() {
158     d.reset(new SoundManagerPrivate);
159     d->_base_pos = SGVec3d::fromGeod(_geod_pos);
160 
161     _block_support = alIsExtensionPresent((ALchar *)"AL_SOFT_block_alignment");
162 }
163 
164 // destructor
165 
~SGSoundMgr()166 SGSoundMgr::~SGSoundMgr() {
167 
168     if (is_working())
169         stop();
170     d->_sample_groups.clear();
171 }
172 
173 // initialize the sound manager
init()174 void SGSoundMgr::init()
175 {
176 #ifdef ENABLE_SOUND
177     if (is_working())
178     {
179         SG_LOG( SG_SOUND, SG_ALERT, "Oops, OpenAL sound manager is already initialized." );
180         return;
181     }
182 
183     SG_LOG( SG_SOUND, SG_INFO, "Initializing OpenAL sound manager" );
184 
185     d->_free_sources.clear();
186     d->_free_sources.reserve( MAX_SOURCES );
187     d->_sources_in_use.clear();
188     d->_sources_in_use.reserve( MAX_SOURCES );
189 
190     ALCdevice *device = nullptr;
191     const char* devname = _device_name.c_str();
192     if (_device_name == "")
193         devname = nullptr; // use default device
194     else
195     {
196         // try non-default device
197         device = alcOpenDevice(devname);
198     }
199 
200     if ((!devname)||(testForError(device, "Audio device not available, trying default.")) ) {
201         device = alcOpenDevice(nullptr);
202         if (testForError(device, "Default audio device not available.") ) {
203            return;
204         }
205     }
206 
207     ALCcontext *context = alcCreateContext(device, nullptr);
208     testForALCError("context creation.");
209     if ( testForError(context, "Unable to create a valid context.") ) {
210         alcCloseDevice (device);
211         return;
212     }
213 
214     if ( !alcMakeContextCurrent(context) ) {
215         testForALCError("context initialization");
216         alcDestroyContext (context);
217         alcCloseDevice (device);
218         return;
219     }
220 
221     if (d->_context != nullptr)
222     {
223         SG_LOG(SG_SOUND, SG_ALERT, "context is already assigned");
224     }
225 
226     d->_context = context;
227     d->_device = device;
228     d->init();
229 
230     alListenerf( AL_GAIN, 0.0f );
231     alListenerfv( AL_ORIENTATION, d->_at_up_vec );
232     alListenerfv( AL_POSITION, SGVec3f::zeros().data() );
233     alListenerfv( AL_VELOCITY, SGVec3f::zeros().data() );
234 
235     alDopplerFactor(1.0f);
236     alDopplerVelocity(_sound_velocity);
237 
238     // gain = AL_REFERENCE_DISTANCE / (AL_REFERENCE_DISTANCE +
239     //        AL_ROLLOFF_FACTOR * (distance - AL_REFERENCE_DISTANCE));
240     alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
241 
242     testForError("listener initialization");
243 
244     // get a free source one at a time
245     // if an error is returned no more (hardware) sources are available
246     for (unsigned int i=0; i<MAX_SOURCES; i++) {
247         ALuint source;
248         ALenum error;
249 
250         alGetError();
251         alGenSources(1, &source);
252         error = alGetError();
253         if ( error == AL_NO_ERROR ) {
254             d->_free_sources.push_back( source );
255         } else {
256             SG_LOG(SG_SOUND, SG_INFO, "allocating source failed:" << i);
257             break;
258         }
259     }
260 
261     _vendor = (const char *)alGetString(AL_VENDOR);
262     _renderer = (const char *)alGetString(AL_RENDERER);
263 
264     if (_vendor == "Creative Labs Inc.") {
265        alDopplerFactor(100.0f);
266        d->_bad_doppler = true;
267     } else if (_vendor == "OpenAL Community" && _renderer == "OpenAL Soft") {
268        alDopplerFactor(100.0f);
269        d->_bad_doppler = true;
270     }
271 
272     if (d->_free_sources.empty()) {
273         SG_LOG(SG_SOUND, SG_ALERT, "Unable to grab any OpenAL sources!");
274     }
275 #endif
276 }
277 
reinit()278 void SGSoundMgr::reinit()
279 {
280     bool was_active = _active;
281 
282     if (was_active)
283     {
284         suspend();
285     }
286 
287     SGSoundMgr::stop();
288     SGSoundMgr::init();
289 
290     if (was_active)
291         resume();
292 }
293 
activate()294 void SGSoundMgr::activate()
295 {
296 #ifdef ENABLE_SOUND
297     if ( is_working() ) {
298         _active = true;
299 
300         for ( auto current : d->_sample_groups ) {
301             current.second->activate();
302         }
303     }
304 #endif
305 }
306 
307 // stop the sound manager
stop()308 void SGSoundMgr::stop()
309 {
310 #ifdef ENABLE_SOUND
311     // first stop all sample groups
312     for ( auto current : d->_sample_groups ) {
313         current.second->stop();
314     }
315 
316     // clear all OpenAL sources
317     for(ALuint source : d->_free_sources) {
318         alDeleteSources( 1 , &source );
319         testForError("SGSoundMgr::stop: delete sources");
320     }
321     d->_free_sources.clear();
322 
323     // clear any OpenAL buffers before shutting down
324     for ( auto current : d->_buffers ) {
325         refUint ref = current.second;
326         ALuint buffer = ref.id;
327         alDeleteBuffers(1, &buffer);
328         testForError("SGSoundMgr::stop: delete buffers");
329     }
330 
331     d->_buffers.clear();
332     d->_sources_in_use.clear();
333 
334     if (is_working()) {
335         _active = false;
336         alcDestroyContext(d->_context);
337         alcCloseDevice(d->_device);
338         d->_context = nullptr;
339         d->_device = nullptr;
340 
341         _renderer = "unknown";
342         _vendor = "unknown";
343     }
344 #endif
345 }
346 
suspend()347 void SGSoundMgr::suspend()
348 {
349 #ifdef ENABLE_SOUND
350     if (is_working()) {
351         for ( auto current : d->_sample_groups ) {
352             current.second->suspend();
353         }
354         _active = false;
355     }
356 #endif
357 }
358 
resume()359 void SGSoundMgr::resume()
360 {
361 #ifdef ENABLE_SOUND
362     if (is_working()) {
363         for ( auto current : d->_sample_groups ) {
364             current.second->resume();
365         }
366         _active = true;
367     }
368 #endif
369 }
370 
371 // run the audio scheduler
update(double dt)372 void SGSoundMgr::update( double dt )
373 {
374 #ifdef ENABLE_SOUND
375     if (_active) {
376         alcSuspendContext(d->_context);
377 
378         if (_changed) {
379             d->update_pos_and_orientation();
380         }
381 
382         for ( auto current : d->_sample_groups ) {
383             current.second->update(dt);
384         }
385 
386         if (_changed) {
387 #if 0
388 if (isNaN(d->_at_up_vec)) printf("NaN in listener orientation\n");
389 if (isNaN(toVec3f(d->_absolute_pos).data())) printf("NaN in listener position\n");
390 if (isNaN(toVec3f(_velocity).data())) printf("NaN in listener velocity\n");
391 #endif
392             alListenerf( AL_GAIN, _volume );
393             alListenerfv( AL_ORIENTATION, d->_at_up_vec );
394             // alListenerfv( AL_POSITION, toVec3f(_absolute_pos).data() );
395 
396             SGQuatd hlOr = SGQuatd::fromLonLat( _geod_pos );
397             SGVec3d velocity = SGVec3d::zeros();
398             if ( _velocity[0] || _velocity[1] || _velocity[2] ) {
399                 velocity = hlOr.backTransform(_velocity*SG_FEET_TO_METER);
400 
401                 if ( d->_bad_doppler ) {
402                     float fact = 100.0f;
403                     float mag = length( velocity );
404 
405                     if (mag > _sound_velocity) {
406                         fact *= _sound_velocity / mag;
407                     }
408                     alDopplerFactor(fact);
409                 }
410             }
411 
412             alListenerfv( AL_VELOCITY, toVec3f(velocity).data() );
413 //          alDopplerVelocity(_sound_velocity);
414             testForError("update");
415             _changed = false;
416         }
417 
418         alcProcessContext(d->_context);
419     }
420 #endif
421 }
422 
423 // add a sample group, return true if successful
add(SGSampleGroup * sgrp,const std::string & refname)424 bool SGSoundMgr::add( SGSampleGroup *sgrp, const std::string& refname )
425 {
426     auto sample_grp_it = d->_sample_groups.find( refname );
427     if ( sample_grp_it != d->_sample_groups.end() ) {
428         // sample group already exists
429         return false;
430     }
431 
432     if (_active) sgrp->activate();
433     d->_sample_groups[refname] = sgrp;
434 
435     return true;
436 }
437 
438 
439 // remove a sound effect, return true if successful
remove(const std::string & refname)440 bool SGSoundMgr::remove( const std::string &refname )
441 {
442     auto sample_grp_it = d->_sample_groups.find( refname );
443     if ( sample_grp_it == d->_sample_groups.end() ) {
444         // sample group was not found.
445         return false;
446     }
447 
448     d->_sample_groups.erase( sample_grp_it );
449 
450     return true;
451 }
452 
453 
454 // return true of the specified sound exists in the sound manager system
exists(const std::string & refname)455 bool SGSoundMgr::exists( const std::string &refname ) {
456     auto sample_grp_it = d->_sample_groups.find( refname );
457     return ( sample_grp_it != d->_sample_groups.end() );
458 }
459 
460 
461 // return a pointer to the SGSampleGroup if the specified sound exists
462 // in the sound manager system, otherwise return nullptr
find(const std::string & refname,bool create)463 SGSampleGroup *SGSoundMgr::find( const std::string &refname, bool create ) {
464     auto sample_grp_it = d->_sample_groups.find( refname );
465     if ( sample_grp_it == d->_sample_groups.end() ) {
466         // sample group was not found.
467         if (create) {
468             SGSampleGroup* sgrp = new SGSampleGroup(this, refname);
469             add( sgrp, refname );
470             return sgrp;
471         }
472         else
473             return nullptr;
474     }
475 
476     return sample_grp_it->second;
477 }
478 
479 
set_volume(float v)480 void SGSoundMgr::set_volume( float v )
481 {
482     _volume = v;
483     SG_CLAMP_RANGE(_volume, 0.0f, 1.0f);
484     _changed = true;
485 }
486 
487 // Get an unused source id
488 //
489 // The Sound Manager should keep track of the sources in use, the distance
490 // of these sources to the listener and the volume (also based on audio cone
491 // and hence orientation) of the sources.
492 //
493 // The Sound Manager is (and should be) the only one knowing about source
494 // management. Sources further away should be suspended to free resources for
495 // newly added sounds close by.
request_source()496 unsigned int SGSoundMgr::request_source()
497 {
498     unsigned int source = NO_SOURCE;
499 
500     if (!d->_free_sources.empty()) {
501        source = d->_free_sources.back();
502        d->_free_sources.pop_back();
503        d->_sources_in_use.push_back(source);
504     }
505     else
506        SG_LOG( SG_SOUND, SG_BULK, "Sound manager: No more free sources available!\n");
507 
508     return source;
509 }
510 
511 // Free up a source id for further use
release_source(unsigned int source)512 void SGSoundMgr::release_source( unsigned int source )
513 {
514     auto it = std::find(d->_sources_in_use.begin(), d->_sources_in_use.end(), source);
515     if ( it != d->_sources_in_use.end() ) {
516   #ifdef ENABLE_SOUND
517         ALint result;
518 
519         alGetSourcei( source, AL_SOURCE_STATE, &result );
520         if ( result == AL_PLAYING || result == AL_PAUSED ) {
521             alSourceStop( source );
522         }
523 
524         alSourcei( source, AL_BUFFER, 0 );	// detach the associated buffer
525         testForError("release_source");
526   #endif
527         d->_free_sources.push_back( source );
528         d->_sources_in_use.erase( it );
529     }
530 }
531 
request_buffer(SGSoundSample * sample)532 unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample)
533 {
534     ALuint buffer = NO_BUFFER;
535 #ifdef ENABLE_SOUND
536     if ( !sample->is_valid_buffer() ) {
537         // sample was not yet loaded or removed again
538         std::string sample_name = sample->get_sample_name();
539         void* sample_data = nullptr;
540 
541         // see if the sample name is already cached
542         auto buffer_it = d->_buffers.find( sample_name );
543         if ( buffer_it != d->_buffers.end() ) {
544             buffer_it->second.refctr++;
545             buffer = buffer_it->second.id;
546             sample->set_buffer( buffer );
547             return buffer;
548         }
549 
550         // sample name was not found in the buffer cache.
551         if ( sample->is_file() ) {
552             int freq, format, block;
553             size_t size;
554 
555             try {
556               bool res = load(sample_name, &sample_data, &format, &size, &freq, &block);
557               if (res == false) return NO_BUFFER;
558             } catch (sg_exception& e) {
559               SG_LOG(SG_SOUND, SG_ALERT,
560                     "failed to load sound buffer:\n" << e.getFormattedMessage());
561               sample->set_buffer( SGSoundMgr::FAILED_BUFFER );
562               return FAILED_BUFFER;
563             }
564 
565             sample->set_block_align( block );
566             sample->set_frequency( freq );
567             sample->set_format( format );
568             sample->set_size( size );
569 
570         } else {
571             sample_data = sample->get_data();
572         }
573 
574         ALenum format = AL_NONE;
575         switch( sample->get_format() )
576         {
577         case SG_SAMPLE_MONO16:
578             format = AL_FORMAT_MONO16;
579             break;
580         case SG_SAMPLE_MONO8:
581             format = AL_FORMAT_MONO8;
582             break;
583         case SG_SAMPLE_MULAW:
584             format = AL_FORMAT_MONO_MULAW_EXT;
585             break;
586         case SG_SAMPLE_ADPCM:
587             format = AL_FORMAT_MONO_IMA4;
588             break;
589 
590         case SG_SAMPLE_STEREO16:
591             SG_LOG(SG_SOUND, SG_POPUP, "Stereo sound detected:\n" << sample->get_sample_name() << "\nUse two separate mono files instead if required.");
592             format = AL_FORMAT_STEREO16;
593             break;
594         case SG_SAMPLE_STEREO8:
595             SG_LOG(SG_SOUND, SG_POPUP, "Stereo sound detected:\n" << sample->get_sample_name() << "\nUse two separate mono files instead if required.");
596             format = AL_FORMAT_STEREO8;
597             break;
598         default:
599             SG_LOG(SG_SOUND, SG_ALERT, "unsupported audio format");
600             return buffer;
601         }
602 
603         // create an OpenAL buffer handle
604         alGenBuffers(1, &buffer);
605         if ( !testForError("generate buffer") ) {
606             // Copy data to the internal OpenAL buffer
607 
608             ALsizei size = sample->get_size();
609             ALsizei freq = sample->get_frequency();
610             alBufferData( buffer, format, sample_data, size, freq );
611 
612             if (format == AL_FORMAT_MONO_IMA4 && _block_support) {
613                 ALsizei samples_block = BLOCKSIZE_TO_SMP( sample->get_block_align() );
614                 alBufferi (buffer, AL_UNPACK_BLOCK_ALIGNMENT_SOFT, samples_block );
615             }
616 
617             if ( !testForError("buffer add data") ) {
618                 sample->set_buffer(buffer);
619                 d->_buffers[sample_name] = refUint(buffer);
620             }
621         }
622 
623         if ( sample->is_file() ) free(sample_data);
624     }
625     else {
626         buffer = sample->get_buffer();
627     }
628 #endif
629     return buffer;
630 }
631 
release_buffer(SGSoundSample * sample)632 void SGSoundMgr::release_buffer(SGSoundSample *sample)
633 {
634     if ( !sample->is_queue() )
635     {
636         std::string sample_name = sample->get_sample_name();
637         auto buffer_it = d->_buffers.find( sample_name );
638         if ( buffer_it == d->_buffers.end() ) {
639             // buffer was not found
640             return;
641         }
642 
643         sample->no_valid_buffer();
644         buffer_it->second.refctr--;
645         if (buffer_it->second.refctr == 0) {
646 #ifdef ENABLE_SOUND
647             ALuint buffer = buffer_it->second.id;
648             alDeleteBuffers(1, &buffer);
649 #endif
650             d->_buffers.erase( buffer_it );
651             testForError("release buffer");
652         }
653     }
654 }
655 
sample_suspend(SGSoundSample * sample)656 void SGSoundMgr::sample_suspend( SGSoundSample *sample )
657 {
658     if ( sample->is_valid_source() && sample->is_playing() ) {
659         alSourcePause( sample->get_source() );
660     }
661 }
662 
sample_resume(SGSoundSample * sample)663 void SGSoundMgr::sample_resume( SGSoundSample *sample )
664 {
665     if ( sample->is_valid_source() && sample->is_playing() ) {
666         alSourcePlay( sample->get_source() );
667     }
668 }
669 
sample_init(SGSoundSample * sample)670 void SGSoundMgr::sample_init( SGSoundSample *sample )
671 {
672 #ifdef ENABLE_SOUND
673     //
674     // a request to start playing a sound has been filed.
675     //
676     ALuint source = request_source();
677     if (alIsSource(source) == AL_FALSE ) {
678         return;
679     }
680 
681     sample->set_source( source );
682 #endif
683 }
684 
sample_play(SGSoundSample * sample)685 void SGSoundMgr::sample_play( SGSoundSample *sample )
686 {
687 #ifdef ENABLE_SOUND
688     ALboolean looping = sample->is_looping() ? AL_TRUE : AL_FALSE;
689     ALint source = sample->get_source();
690 
691     if ( !sample->is_queue() )
692     {
693         ALuint buffer = request_buffer(sample);
694         if (buffer == SGSoundMgr::FAILED_BUFFER ||
695             buffer == SGSoundMgr::NO_BUFFER)
696         {
697             release_source(source);
698             return;
699         }
700 
701         // start playing the sample
702         buffer = sample->get_buffer();
703         if ( alIsBuffer(buffer) == AL_TRUE )
704         {
705             alSourcei( source, AL_BUFFER, buffer );
706             testForError("assign buffer to source");
707         } else
708             SG_LOG( SG_SOUND, SG_ALERT, "No such buffer!");
709     }
710 
711     alSourcef( source, AL_ROLLOFF_FACTOR, 0.3 );
712     alSourcei( source, AL_LOOPING, looping );
713     alSourcei( source, AL_SOURCE_RELATIVE, AL_FALSE );
714     alSourcePlay( source );
715     testForError("sample play");
716 #endif
717 }
718 
sample_stop(SGSoundSample * sample)719 void SGSoundMgr::sample_stop( SGSoundSample *sample )
720 {
721     if ( sample->is_valid_source() ) {
722         int source = sample->get_source();
723 
724         bool stopped = is_sample_stopped(sample);
725         if ( sample->is_looping() && !stopped) {
726 #ifdef ENABLE_SOUND
727             alSourceStop( source );
728 #endif
729             stopped = is_sample_stopped(sample);
730         }
731 
732         if ( stopped ) {
733             sample->no_valid_source();
734             release_source( source );
735         }
736     }
737 }
738 
sample_destroy(SGSoundSample * sample)739 void SGSoundMgr::sample_destroy( SGSoundSample *sample )
740 {
741     if ( sample->is_valid_source() ) {
742 #ifdef ENABLE_SOUND
743         ALint source = sample->get_source();
744         if ( sample->is_playing() ) {
745             alSourceStop( source );
746             testForError("stop");
747         }
748         release_source( source );
749 #endif
750         sample->no_valid_source();
751     }
752 
753     if ( sample->is_valid_buffer() ) {
754         release_buffer( sample );
755         sample->no_valid_buffer();
756     }
757 }
758 
is_sample_stopped(SGSoundSample * sample)759 bool SGSoundMgr::is_sample_stopped(SGSoundSample *sample)
760 {
761 #ifdef ENABLE_SOUND
762     if ( sample->is_valid_source() ) {
763         ALint source = sample->get_source();
764         ALint result;
765         alGetSourcei( source, AL_SOURCE_STATE, &result );
766         return (result == AL_STOPPED);
767     }
768 #endif
769     return true;
770 }
771 
update_sample_config(SGSoundSample * sample,SGVec3d & position,SGVec3f & orientation,SGVec3f & velocity)772 void SGSoundMgr::update_sample_config( SGSoundSample *sample, SGVec3d& position, SGVec3f& orientation, SGVec3f& velocity )
773 {
774     unsigned int source = sample->get_source();
775     alSourcefv( source, AL_POSITION, toVec3f(position).data() );
776     alSourcefv( source, AL_VELOCITY, velocity.data() );
777     alSourcefv( source, AL_DIRECTION, orientation.data() );
778     testForError("position and orientation");
779 
780     alSourcef( source, AL_PITCH, sample->get_pitch() );
781     alSourcef( source, AL_GAIN, sample->get_volume() );
782     testForError("pitch and gain");
783 
784     if ( sample->has_static_data_changed() ) {
785         alSourcef( source, AL_CONE_INNER_ANGLE, sample->get_innerangle() );
786         alSourcef( source, AL_CONE_OUTER_ANGLE, sample->get_outerangle() );
787         alSourcef( source, AL_CONE_OUTER_GAIN, sample->get_outergain() );
788         testForError("audio cone");
789 
790         alSourcef( source, AL_MAX_DISTANCE, sample->get_max_dist() );
791         alSourcef( source, AL_REFERENCE_DISTANCE,
792                            sample->get_reference_dist() );
793         testForError("distance rolloff");
794     }
795 }
796 
797 
load(const std::string & samplepath,void ** dbuf,int * fmt,size_t * sz,int * frq,int * block)798 bool SGSoundMgr::load( const std::string &samplepath,
799                        void** dbuf,
800                        int *fmt,
801                        size_t *sz,
802                        int *frq,
803                        int *block )
804 {
805     if ( !is_working() )
806         return false;
807 
808     unsigned int format;
809     unsigned int blocksz;
810     ALsizei size;
811     ALsizei freq;
812     ALfloat freqf;
813 
814     auto data = simgear::loadWAVFromFile(samplepath, format, size, freqf, blocksz);
815     freq = (ALsizei)freqf;
816     if (!data) {
817         return false;
818     }
819 
820     if (format == AL_FORMAT_STEREO8 || format == AL_FORMAT_STEREO16) {
821          free(data);
822         SG_LOG(SG_IO, SG_DEV_ALERT, "Warning: STEREO files are not supported for 3D audio effects: " << samplepath);
823         return false;
824     }
825 
826     *dbuf = (void *)data;
827     *fmt = (int)format;
828     *block = (int)blocksz;
829     *sz = (size_t)size;
830     *frq = (int)freq;
831 
832     return true;
833 }
834 
get_available_devices()835 vector<std::string> SGSoundMgr::get_available_devices()
836 {
837     vector<std::string> devices;
838 #ifdef ENABLE_SOUND
839     const ALCchar *s;
840 
841     if (alcIsExtensionPresent(nullptr, "ALC_enumerate_all_EXT") == AL_TRUE) {
842         s = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER);
843     } else {
844         s = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
845     }
846 
847     if (s) {
848         ALCchar *nptr, *ptr = (ALCchar *)s;
849 
850         nptr = ptr;
851         while (*(nptr += strlen(ptr)+1) != 0)
852         {
853             devices.push_back(ptr);
854             ptr = nptr;
855         }
856         devices.push_back(ptr);
857     }
858 #endif
859     return devices;
860 }
861 
862 
testForError(void * p,std::string s)863 bool SGSoundMgr::testForError(void *p, std::string s)
864 {
865    if (p == nullptr) {
866       SG_LOG( SG_SOUND, SG_ALERT, "Error: " << s);
867       return true;
868    }
869    return false;
870 }
871 
872 
testForError(std::string s,std::string name)873 bool SGSoundMgr::testForError(std::string s, std::string name)
874 {
875 #ifdef ENABLE_SOUND
876     ALenum error = alGetError();
877     if (error != AL_NO_ERROR)  {
878        SG_LOG( SG_SOUND, SG_ALERT, "AL Error (" << name << "): "
879                                       << alGetString(error) << " at " << s);
880        return true;
881     }
882 #endif
883     return false;
884 }
885 
testForALCError(std::string s)886 bool SGSoundMgr::testForALCError(std::string s)
887 {
888 #ifdef ENABLE_SOUND
889     ALCenum error;
890     error = alcGetError(d->_device);
891     if (error != ALC_NO_ERROR) {
892         SG_LOG( SG_SOUND, SG_ALERT, "ALC Error (sound manager): "
893                                        << alcGetString(d->_device, error) << " at "
894                                        << s);
895         return true;
896     }
897 #endif
898     return false;
899 }
900 
is_working() const901 bool SGSoundMgr::is_working() const
902 {
903     return (d->_device != nullptr);
904 }
905 
get_orientation() const906 const SGQuatd& SGSoundMgr::get_orientation() const
907 {
908     return d->_orientation;
909 }
910 
set_orientation(const SGQuatd & ori)911 void SGSoundMgr::set_orientation( const SGQuatd& ori )
912 {
913     d->_orientation = ori;
914     _changed = true;
915 }
916 
get_position() const917 const SGVec3d& SGSoundMgr::get_position() const
918 {
919     return d->_absolute_pos;
920 }
921 
set_position(const SGVec3d & pos,const SGGeod & pos_geod)922 void SGSoundMgr::set_position( const SGVec3d& pos, const SGGeod& pos_geod )
923 {
924     d->_base_pos = pos; _geod_pos = pos_geod; _changed = true;
925 }
926 
get_direction() const927 SGVec3f SGSoundMgr::get_direction() const
928 {
929     return SGVec3f(d->_at_up_vec[0], d->_at_up_vec[1], d->_at_up_vec[2]);
930 }
931