1 #include <algorithm>
2 #include <cstring>
3 #include <vector>
4 #include <memory>
5 #include <array>
6 #include <atomic>
7 #include <condition_variable>
8 #include <thread>
9 #include <mutex>
10 #include <chrono>
11 
12 #include <stdint.h>
13 
14 #include <components/debug/debuglog.hpp>
15 #include <components/misc/constants.hpp>
16 #include <components/vfs/manager.hpp>
17 
18 #include "openal_output.hpp"
19 #include "sound_decoder.hpp"
20 #include "sound.hpp"
21 #include "soundmanagerimp.hpp"
22 #include "loudness.hpp"
23 
24 #include "efx-presets.h"
25 
26 #ifndef ALC_ALL_DEVICES_SPECIFIER
27 #define ALC_ALL_DEVICES_SPECIFIER 0x1013
28 #endif
29 
30 
31 #define MAKE_PTRID(id) ((void*)(uintptr_t)id)
32 #define GET_PTRID(ptr) ((ALuint)(uintptr_t)ptr)
33 
34 namespace
35 {
36 
37 const int sLoudnessFPS = 20; // loudness values per second of audio
38 
checkALCError(ALCdevice * device,const char * func,int line)39 ALCenum checkALCError(ALCdevice *device, const char *func, int line)
40 {
41     ALCenum err = alcGetError(device);
42     if(err != ALC_NO_ERROR)
43         Log(Debug::Error) << "ALC error "<< alcGetString(device, err) << " (" << err << ") @ " << func << ":" << line;
44     return err;
45 }
46 #define getALCError(d) checkALCError((d), __FUNCTION__, __LINE__)
47 
checkALError(const char * func,int line)48 ALenum checkALError(const char *func, int line)
49 {
50     ALenum err = alGetError();
51     if(err != AL_NO_ERROR)
52         Log(Debug::Error) << "AL error " << alGetString(err) << " (" << err << ") @ " << func << ":" << line;
53     return err;
54 }
55 #define getALError() checkALError(__FUNCTION__, __LINE__)
56 
57 // Helper to get an OpenAL extension function
58 template<typename T, typename R>
convertPointer(T & dest,R src)59 void convertPointer(T& dest, R src)
60 {
61     memcpy(&dest, &src, sizeof(src));
62 }
63 
64 template<typename T>
getALCFunc(T & func,ALCdevice * device,const char * name)65 void getALCFunc(T& func, ALCdevice *device, const char *name)
66 {
67     void* funcPtr = alcGetProcAddress(device, name);
68     convertPointer(func, funcPtr);
69 }
70 
71 template<typename T>
getALFunc(T & func,const char * name)72 void getALFunc(T& func, const char *name)
73 {
74     void* funcPtr = alGetProcAddress(name);
75     convertPointer(func, funcPtr);
76 }
77 
78 // Effect objects
79 LPALGENEFFECTS alGenEffects;
80 LPALDELETEEFFECTS alDeleteEffects;
81 LPALISEFFECT alIsEffect;
82 LPALEFFECTI alEffecti;
83 LPALEFFECTIV alEffectiv;
84 LPALEFFECTF alEffectf;
85 LPALEFFECTFV alEffectfv;
86 LPALGETEFFECTI alGetEffecti;
87 LPALGETEFFECTIV alGetEffectiv;
88 LPALGETEFFECTF alGetEffectf;
89 LPALGETEFFECTFV alGetEffectfv;
90 // Filter objects
91 LPALGENFILTERS alGenFilters;
92 LPALDELETEFILTERS alDeleteFilters;
93 LPALISFILTER alIsFilter;
94 LPALFILTERI alFilteri;
95 LPALFILTERIV alFilteriv;
96 LPALFILTERF alFilterf;
97 LPALFILTERFV alFilterfv;
98 LPALGETFILTERI alGetFilteri;
99 LPALGETFILTERIV alGetFilteriv;
100 LPALGETFILTERF alGetFilterf;
101 LPALGETFILTERFV alGetFilterfv;
102 // Auxiliary slot objects
103 LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots;
104 LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots;
105 LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot;
106 LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti;
107 LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv;
108 LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf;
109 LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv;
110 LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti;
111 LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv;
112 LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf;
113 LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv;
114 
115 
LoadEffect(ALuint effect,const EFXEAXREVERBPROPERTIES & props)116 void LoadEffect(ALuint effect, const EFXEAXREVERBPROPERTIES &props)
117 {
118     ALint type = AL_NONE;
119     alGetEffecti(effect, AL_EFFECT_TYPE, &type);
120     if(type == AL_EFFECT_EAXREVERB)
121     {
122         alEffectf(effect, AL_EAXREVERB_DIFFUSION, props.flDiffusion);
123         alEffectf(effect, AL_EAXREVERB_DENSITY, props.flDensity);
124         alEffectf(effect, AL_EAXREVERB_GAIN, props.flGain);
125         alEffectf(effect, AL_EAXREVERB_GAINHF, props.flGainHF);
126         alEffectf(effect, AL_EAXREVERB_GAINLF, props.flGainLF);
127         alEffectf(effect, AL_EAXREVERB_DECAY_TIME, props.flDecayTime);
128         alEffectf(effect, AL_EAXREVERB_DECAY_HFRATIO, props.flDecayHFRatio);
129         alEffectf(effect, AL_EAXREVERB_DECAY_LFRATIO, props.flDecayLFRatio);
130         alEffectf(effect, AL_EAXREVERB_REFLECTIONS_GAIN, props.flReflectionsGain);
131         alEffectf(effect, AL_EAXREVERB_REFLECTIONS_DELAY, props.flReflectionsDelay);
132         alEffectfv(effect, AL_EAXREVERB_REFLECTIONS_PAN, props.flReflectionsPan);
133         alEffectf(effect, AL_EAXREVERB_LATE_REVERB_GAIN, props.flLateReverbGain);
134         alEffectf(effect, AL_EAXREVERB_LATE_REVERB_DELAY, props.flLateReverbDelay);
135         alEffectfv(effect, AL_EAXREVERB_LATE_REVERB_PAN, props.flLateReverbPan);
136         alEffectf(effect, AL_EAXREVERB_ECHO_TIME, props.flEchoTime);
137         alEffectf(effect, AL_EAXREVERB_ECHO_DEPTH, props.flEchoDepth);
138         alEffectf(effect, AL_EAXREVERB_MODULATION_TIME, props.flModulationTime);
139         alEffectf(effect, AL_EAXREVERB_MODULATION_DEPTH, props.flModulationDepth);
140         alEffectf(effect, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, props.flAirAbsorptionGainHF);
141         alEffectf(effect, AL_EAXREVERB_HFREFERENCE, props.flHFReference);
142         alEffectf(effect, AL_EAXREVERB_LFREFERENCE, props.flLFReference);
143         alEffectf(effect, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, props.flRoomRolloffFactor);
144         alEffecti(effect, AL_EAXREVERB_DECAY_HFLIMIT, props.iDecayHFLimit ? AL_TRUE : AL_FALSE);
145     }
146     else if(type == AL_EFFECT_REVERB)
147     {
148         alEffectf(effect, AL_REVERB_DIFFUSION, props.flDiffusion);
149         alEffectf(effect, AL_REVERB_DENSITY, props.flDensity);
150         alEffectf(effect, AL_REVERB_GAIN, props.flGain);
151         alEffectf(effect, AL_REVERB_GAINHF, props.flGainHF);
152         alEffectf(effect, AL_REVERB_DECAY_TIME, props.flDecayTime);
153         alEffectf(effect, AL_REVERB_DECAY_HFRATIO, props.flDecayHFRatio);
154         alEffectf(effect, AL_REVERB_REFLECTIONS_GAIN, props.flReflectionsGain);
155         alEffectf(effect, AL_REVERB_REFLECTIONS_DELAY, props.flReflectionsDelay);
156         alEffectf(effect, AL_REVERB_LATE_REVERB_GAIN, props.flLateReverbGain);
157         alEffectf(effect, AL_REVERB_LATE_REVERB_DELAY, props.flLateReverbDelay);
158         alEffectf(effect, AL_REVERB_AIR_ABSORPTION_GAINHF, props.flAirAbsorptionGainHF);
159         alEffectf(effect, AL_REVERB_ROOM_ROLLOFF_FACTOR, props.flRoomRolloffFactor);
160         alEffecti(effect, AL_REVERB_DECAY_HFLIMIT, props.iDecayHFLimit ? AL_TRUE : AL_FALSE);
161     }
162     getALError();
163 }
164 
165 }
166 
167 namespace MWSound
168 {
169 
getALFormat(ChannelConfig chans,SampleType type)170 static ALenum getALFormat(ChannelConfig chans, SampleType type)
171 {
172     struct FormatEntry {
173         ALenum format;
174         ChannelConfig chans;
175         SampleType type;
176     };
177     struct FormatEntryExt {
178         const char name[32];
179         ChannelConfig chans;
180         SampleType type;
181     };
182     static const std::array<FormatEntry,4> fmtlist{{
183         { AL_FORMAT_MONO16,   ChannelConfig_Mono,   SampleType_Int16 },
184         { AL_FORMAT_MONO8,    ChannelConfig_Mono,   SampleType_UInt8 },
185         { AL_FORMAT_STEREO16, ChannelConfig_Stereo, SampleType_Int16 },
186         { AL_FORMAT_STEREO8,  ChannelConfig_Stereo, SampleType_UInt8 },
187     }};
188 
189     for(auto &fmt : fmtlist)
190     {
191         if(fmt.chans == chans && fmt.type == type)
192             return fmt.format;
193     }
194 
195     if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
196     {
197         static const std::array<FormatEntryExt,6> mcfmtlist{{
198             { "AL_FORMAT_QUAD16",   ChannelConfig_Quad,    SampleType_Int16 },
199             { "AL_FORMAT_QUAD8",    ChannelConfig_Quad,    SampleType_UInt8 },
200             { "AL_FORMAT_51CHN16",  ChannelConfig_5point1, SampleType_Int16 },
201             { "AL_FORMAT_51CHN8",   ChannelConfig_5point1, SampleType_UInt8 },
202             { "AL_FORMAT_71CHN16",  ChannelConfig_7point1, SampleType_Int16 },
203             { "AL_FORMAT_71CHN8",   ChannelConfig_7point1, SampleType_UInt8 },
204         }};
205 
206         for(auto &fmt : mcfmtlist)
207         {
208             if(fmt.chans == chans && fmt.type == type)
209             {
210                 ALenum format = alGetEnumValue(fmt.name);
211                 if(format != 0 && format != -1)
212                     return format;
213             }
214         }
215     }
216     if(alIsExtensionPresent("AL_EXT_FLOAT32"))
217     {
218         static const std::array<FormatEntryExt,2> fltfmtlist{{
219             { "AL_FORMAT_MONO_FLOAT32",   ChannelConfig_Mono,   SampleType_Float32 },
220             { "AL_FORMAT_STEREO_FLOAT32", ChannelConfig_Stereo, SampleType_Float32 },
221         }};
222 
223         for(auto &fmt : fltfmtlist)
224         {
225             if(fmt.chans == chans && fmt.type == type)
226             {
227                 ALenum format = alGetEnumValue(fmt.name);
228                 if(format != 0 && format != -1)
229                     return format;
230             }
231         }
232 
233         if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
234         {
235             static const std::array<FormatEntryExt,3> fltmcfmtlist{{
236                 { "AL_FORMAT_QUAD32",  ChannelConfig_Quad,    SampleType_Float32 },
237                 { "AL_FORMAT_51CHN32", ChannelConfig_5point1, SampleType_Float32 },
238                 { "AL_FORMAT_71CHN32", ChannelConfig_7point1, SampleType_Float32 },
239             }};
240 
241             for(auto &fmt : fltmcfmtlist)
242             {
243                 if(fmt.chans == chans && fmt.type == type)
244                 {
245                     ALenum format = alGetEnumValue(fmt.name);
246                     if(format != 0 && format != -1)
247                         return format;
248                 }
249             }
250         }
251     }
252 
253     Log(Debug::Warning) << "Unsupported sound format (" << getChannelConfigName(chans) << ", " << getSampleTypeName(type) << ")";
254     return AL_NONE;
255 }
256 
257 
258 //
259 // A streaming OpenAL sound.
260 //
261 class OpenAL_SoundStream
262 {
263     static const ALfloat sBufferLength;
264 
265 private:
266     ALuint mSource;
267 
268     std::array<ALuint,6> mBuffers;
269     ALint mCurrentBufIdx;
270 
271     ALenum mFormat;
272     ALsizei mSampleRate;
273     ALuint mBufferSize;
274     ALuint mFrameSize;
275     ALint mSilence;
276 
277     DecoderPtr mDecoder;
278 
279     std::unique_ptr<Sound_Loudness> mLoudnessAnalyzer;
280 
281     std::atomic<bool> mIsFinished;
282 
283     void updateAll(bool local);
284 
285     OpenAL_SoundStream(const OpenAL_SoundStream &rhs);
286     OpenAL_SoundStream& operator=(const OpenAL_SoundStream &rhs);
287 
288     friend class OpenAL_Output;
289 
290 public:
291     OpenAL_SoundStream(ALuint src, DecoderPtr decoder);
292     ~OpenAL_SoundStream();
293 
294     bool init(bool getLoudnessData=false);
295 
296     bool isPlaying();
297     double getStreamDelay() const;
298     double getStreamOffset() const;
299 
300     float getCurrentLoudness() const;
301 
302     bool process();
303     ALint refillQueue();
304 };
305 const ALfloat OpenAL_SoundStream::sBufferLength = 0.125f;
306 
307 
308 //
309 // A background streaming thread (keeps active streams processed)
310 //
311 struct OpenAL_Output::StreamThread
312 {
313     typedef std::vector<OpenAL_SoundStream*> StreamVec;
314     StreamVec mStreams;
315 
316     std::atomic<bool> mQuitNow;
317     std::mutex mMutex;
318     std::condition_variable mCondVar;
319     std::thread mThread;
320 
StreamThreadMWSound::OpenAL_Output::StreamThread321     StreamThread()
322       : mQuitNow(false)
323       , mThread([this] { run(); })
324     {
325     }
~StreamThreadMWSound::OpenAL_Output::StreamThread326     ~StreamThread()
327     {
328         mQuitNow = true;
329         mMutex.lock(); mMutex.unlock();
330         mCondVar.notify_all();
331         mThread.join();
332     }
333 
334     // thread entry point
runMWSound::OpenAL_Output::StreamThread335     void run()
336     {
337         std::unique_lock<std::mutex> lock(mMutex);
338         while(!mQuitNow)
339         {
340             StreamVec::iterator iter = mStreams.begin();
341             while(iter != mStreams.end())
342             {
343                 if((*iter)->process() == false)
344                     iter = mStreams.erase(iter);
345                 else
346                     ++iter;
347             }
348 
349             mCondVar.wait_for(lock, std::chrono::milliseconds(50));
350         }
351     }
352 
addMWSound::OpenAL_Output::StreamThread353     void add(OpenAL_SoundStream *stream)
354     {
355         std::lock_guard<std::mutex> lock(mMutex);
356         if(std::find(mStreams.begin(), mStreams.end(), stream) == mStreams.end())
357         {
358             mStreams.push_back(stream);
359             mCondVar.notify_all();
360         }
361     }
362 
removeMWSound::OpenAL_Output::StreamThread363     void remove(OpenAL_SoundStream *stream)
364     {
365         std::lock_guard<std::mutex> lock(mMutex);
366         StreamVec::iterator iter = std::find(mStreams.begin(), mStreams.end(), stream);
367         if(iter != mStreams.end()) mStreams.erase(iter);
368     }
369 
removeAllMWSound::OpenAL_Output::StreamThread370     void removeAll()
371     {
372         std::lock_guard<std::mutex> lock(mMutex);
373         mStreams.clear();
374     }
375 
376 private:
377     StreamThread(const StreamThread &rhs);
378     StreamThread& operator=(const StreamThread &rhs);
379 };
380 
381 
OpenAL_SoundStream(ALuint src,DecoderPtr decoder)382 OpenAL_SoundStream::OpenAL_SoundStream(ALuint src, DecoderPtr decoder)
383   : mSource(src), mCurrentBufIdx(0), mFormat(AL_NONE), mSampleRate(0)
384   , mBufferSize(0), mFrameSize(0), mSilence(0), mDecoder(std::move(decoder))
385   , mLoudnessAnalyzer(nullptr), mIsFinished(true)
386 {
387     mBuffers.fill(0);
388 }
389 
~OpenAL_SoundStream()390 OpenAL_SoundStream::~OpenAL_SoundStream()
391 {
392     if(mBuffers[0] && alIsBuffer(mBuffers[0]))
393         alDeleteBuffers(mBuffers.size(), mBuffers.data());
394     alGetError();
395 
396     mDecoder->close();
397 }
398 
init(bool getLoudnessData)399 bool OpenAL_SoundStream::init(bool getLoudnessData)
400 {
401     alGenBuffers(mBuffers.size(), mBuffers.data());
402     ALenum err = getALError();
403     if(err != AL_NO_ERROR)
404         return false;
405 
406     ChannelConfig chans;
407     SampleType type;
408 
409     try {
410         mDecoder->getInfo(&mSampleRate, &chans, &type);
411         mFormat = getALFormat(chans, type);
412     }
413     catch(std::exception &e)
414     {
415         Log(Debug::Error) << "Failed to get stream info: " << e.what();
416         return false;
417     }
418 
419     switch(type)
420     {
421         case SampleType_UInt8: mSilence = 0x80; break;
422         case SampleType_Int16: mSilence = 0x00; break;
423         case SampleType_Float32: mSilence = 0x00; break;
424     }
425 
426     mFrameSize = framesToBytes(1, chans, type);
427     mBufferSize = static_cast<ALuint>(sBufferLength*mSampleRate);
428     mBufferSize *= mFrameSize;
429 
430     if (getLoudnessData)
431         mLoudnessAnalyzer.reset(new Sound_Loudness(sLoudnessFPS, mSampleRate, chans, type));
432 
433     mIsFinished = false;
434     return true;
435 }
436 
isPlaying()437 bool OpenAL_SoundStream::isPlaying()
438 {
439     ALint state;
440 
441     alGetSourcei(mSource, AL_SOURCE_STATE, &state);
442     getALError();
443 
444     if(state == AL_PLAYING || state == AL_PAUSED)
445         return true;
446     return !mIsFinished;
447 }
448 
getStreamDelay() const449 double OpenAL_SoundStream::getStreamDelay() const
450 {
451     ALint state = AL_STOPPED;
452     double d = 0.0;
453     ALint offset;
454 
455     alGetSourcei(mSource, AL_SAMPLE_OFFSET, &offset);
456     alGetSourcei(mSource, AL_SOURCE_STATE, &state);
457     if(state == AL_PLAYING || state == AL_PAUSED)
458     {
459         ALint queued;
460         alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued);
461         ALint inqueue = mBufferSize/mFrameSize*queued - offset;
462         d = (double)inqueue / (double)mSampleRate;
463     }
464 
465     getALError();
466     return d;
467 }
468 
getStreamOffset() const469 double OpenAL_SoundStream::getStreamOffset() const
470 {
471     ALint state = AL_STOPPED;
472     ALint offset;
473     double t;
474 
475     alGetSourcei(mSource, AL_SAMPLE_OFFSET, &offset);
476     alGetSourcei(mSource, AL_SOURCE_STATE, &state);
477     if(state == AL_PLAYING || state == AL_PAUSED)
478     {
479         ALint queued;
480         alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued);
481         ALint inqueue = mBufferSize/mFrameSize*queued - offset;
482         t = (double)(mDecoder->getSampleOffset() - inqueue) / (double)mSampleRate;
483     }
484     else
485     {
486         /* Underrun, or not started yet. The decoder offset is where we'll play
487          * next. */
488         t = (double)mDecoder->getSampleOffset() / (double)mSampleRate;
489     }
490 
491     getALError();
492     return t;
493 }
494 
getCurrentLoudness() const495 float OpenAL_SoundStream::getCurrentLoudness() const
496 {
497     if (!mLoudnessAnalyzer.get())
498         return 0.f;
499 
500     float time = getStreamOffset();
501     return mLoudnessAnalyzer->getLoudnessAtTime(time);
502 }
503 
process()504 bool OpenAL_SoundStream::process()
505 {
506     try {
507         if(refillQueue() > 0)
508         {
509             ALint state;
510             alGetSourcei(mSource, AL_SOURCE_STATE, &state);
511             if(state != AL_PLAYING && state != AL_PAUSED)
512             {
513                 // Ensure all processed buffers are removed so we don't replay them.
514                 refillQueue();
515 
516                 alSourcePlay(mSource);
517             }
518         }
519     }
520     catch(std::exception&) {
521         Log(Debug::Error) << "Error updating stream \"" << mDecoder->getName() << "\"";
522         mIsFinished = true;
523     }
524     return !mIsFinished;
525 }
526 
refillQueue()527 ALint OpenAL_SoundStream::refillQueue()
528 {
529     ALint processed;
530     alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed);
531     while(processed > 0)
532     {
533         ALuint buf;
534         alSourceUnqueueBuffers(mSource, 1, &buf);
535         --processed;
536     }
537 
538     ALint queued;
539     alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued);
540     if(!mIsFinished && (ALuint)queued < mBuffers.size())
541     {
542         std::vector<char> data(mBufferSize);
543         for(;!mIsFinished && (ALuint)queued < mBuffers.size();++queued)
544         {
545             size_t got = mDecoder->read(data.data(), data.size());
546             if(got < data.size())
547             {
548                 mIsFinished = true;
549                 std::fill(data.begin()+got, data.end(), mSilence);
550             }
551             if(got > 0)
552             {
553                 if (mLoudnessAnalyzer.get())
554                     mLoudnessAnalyzer->analyzeLoudness(data);
555 
556                 ALuint bufid = mBuffers[mCurrentBufIdx];
557                 alBufferData(bufid, mFormat, data.data(), data.size(), mSampleRate);
558                 alSourceQueueBuffers(mSource, 1, &bufid);
559                 mCurrentBufIdx = (mCurrentBufIdx+1) % mBuffers.size();
560             }
561         }
562     }
563 
564     return queued;
565 }
566 
567 
568 //
569 // An OpenAL output device
570 //
enumerate()571 std::vector<std::string> OpenAL_Output::enumerate()
572 {
573     std::vector<std::string> devlist;
574     const ALCchar *devnames;
575 
576     if(alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT"))
577         devnames = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER);
578     else
579         devnames = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
580     while(devnames && *devnames)
581     {
582         devlist.emplace_back(devnames);
583         devnames += strlen(devnames)+1;
584     }
585     return devlist;
586 }
587 
init(const std::string & devname,const std::string & hrtfname,HrtfMode hrtfmode)588 bool OpenAL_Output::init(const std::string &devname, const std::string &hrtfname, HrtfMode hrtfmode)
589 {
590     deinit();
591 
592     Log(Debug::Info) << "Initializing OpenAL...";
593 
594     mDevice = alcOpenDevice(devname.c_str());
595     if(!mDevice && !devname.empty())
596     {
597         Log(Debug::Warning) << "Failed to open \"" << devname << "\", trying default";
598         mDevice = alcOpenDevice(nullptr);
599     }
600 
601     if(!mDevice)
602     {
603         Log(Debug::Error) << "Failed to open default audio device";
604         return false;
605     }
606 
607     const ALCchar *name = nullptr;
608     if(alcIsExtensionPresent(mDevice, "ALC_ENUMERATE_ALL_EXT"))
609         name = alcGetString(mDevice, ALC_ALL_DEVICES_SPECIFIER);
610     if(alcGetError(mDevice) != AL_NO_ERROR || !name)
611         name = alcGetString(mDevice, ALC_DEVICE_SPECIFIER);
612     Log(Debug::Info) << "Opened \"" << name << "\"";
613 
614     ALCint major=0, minor=0;
615     alcGetIntegerv(mDevice, ALC_MAJOR_VERSION, 1, &major);
616     alcGetIntegerv(mDevice, ALC_MINOR_VERSION, 1, &minor);
617     Log(Debug::Info) << "  ALC Version: " << major << "." << minor <<"\n" <<
618                         "  ALC Extensions: " << alcGetString(mDevice, ALC_EXTENSIONS);
619 
620     ALC.EXT_EFX = alcIsExtensionPresent(mDevice, "ALC_EXT_EFX");
621     ALC.SOFT_HRTF = alcIsExtensionPresent(mDevice, "ALC_SOFT_HRTF");
622 
623     std::vector<ALCint> attrs;
624     attrs.reserve(15);
625     if(ALC.SOFT_HRTF)
626     {
627         LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr;
628         getALCFunc(alcGetStringiSOFT, mDevice, "alcGetStringiSOFT");
629 
630         attrs.push_back(ALC_HRTF_SOFT);
631         attrs.push_back(hrtfmode == HrtfMode::Disable ? ALC_FALSE :
632                         hrtfmode == HrtfMode::Enable ? ALC_TRUE :
633                         /*hrtfmode == HrtfMode::Auto ?*/ ALC_DONT_CARE_SOFT);
634         if(!hrtfname.empty())
635         {
636             ALCint index = -1;
637             ALCint num_hrtf;
638             alcGetIntegerv(mDevice, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf);
639             for(ALCint i = 0;i < num_hrtf;++i)
640             {
641                 const ALCchar *entry = alcGetStringiSOFT(mDevice, ALC_HRTF_SPECIFIER_SOFT, i);
642                 if(hrtfname == entry)
643                 {
644                     index = i;
645                     break;
646                 }
647             }
648 
649             if(index < 0)
650                 Log(Debug::Warning) << "Failed to find HRTF \"" << hrtfname << "\", using default";
651             else
652             {
653                 attrs.push_back(ALC_HRTF_ID_SOFT);
654                 attrs.push_back(index);
655             }
656         }
657     }
658     attrs.push_back(0);
659 
660     mContext = alcCreateContext(mDevice, attrs.data());
661     if(!mContext || alcMakeContextCurrent(mContext) == ALC_FALSE)
662     {
663         Log(Debug::Error) << "Failed to setup audio context: "<<alcGetString(mDevice, alcGetError(mDevice));
664         if(mContext)
665             alcDestroyContext(mContext);
666         mContext = nullptr;
667         alcCloseDevice(mDevice);
668         mDevice = nullptr;
669         return false;
670     }
671 
672     Log(Debug::Info) << "  Vendor: "<<alGetString(AL_VENDOR)<<"\n"<<
673                         "  Renderer: "<<alGetString(AL_RENDERER)<<"\n"<<
674                         "  Version: "<<alGetString(AL_VERSION)<<"\n"<<
675                         "  Extensions: "<<alGetString(AL_EXTENSIONS);
676 
677     if(!ALC.SOFT_HRTF)
678         Log(Debug::Warning) << "HRTF status unavailable";
679     else
680     {
681         ALCint hrtf_state;
682         alcGetIntegerv(mDevice, ALC_HRTF_SOFT, 1, &hrtf_state);
683         if(!hrtf_state)
684             Log(Debug::Info) << "HRTF disabled";
685         else
686         {
687             const ALCchar *hrtf = alcGetString(mDevice, ALC_HRTF_SPECIFIER_SOFT);
688             Log(Debug::Info) << "Enabled HRTF " << hrtf;
689         }
690     }
691 
692     AL.SOFT_source_spatialize = alIsExtensionPresent("AL_SOFT_source_spatialize");
693 
694     ALCuint maxtotal;
695     ALCint maxmono = 0, maxstereo = 0;
696     alcGetIntegerv(mDevice, ALC_MONO_SOURCES, 1, &maxmono);
697     alcGetIntegerv(mDevice, ALC_STEREO_SOURCES, 1, &maxstereo);
698     if(getALCError(mDevice) != ALC_NO_ERROR)
699         maxtotal = 256;
700     else
701     {
702         maxtotal = std::min<ALCuint>(maxmono+maxstereo, 256);
703         if (maxtotal == 0) // workaround for broken implementations
704             maxtotal = 256;
705     }
706     for(size_t i = 0;i < maxtotal;i++)
707     {
708         ALuint src = 0;
709         alGenSources(1, &src);
710         if(alGetError() != AL_NO_ERROR)
711             break;
712         mFreeSources.push_back(src);
713     }
714     if(mFreeSources.empty())
715     {
716         Log(Debug::Warning) << "Could not allocate any sound sourcess";
717         alcMakeContextCurrent(nullptr);
718         alcDestroyContext(mContext);
719         mContext = nullptr;
720         alcCloseDevice(mDevice);
721         mDevice = nullptr;
722         return false;
723     }
724     Log(Debug::Info) << "Allocated " << mFreeSources.size() << " sound sources";
725 
726     if(ALC.EXT_EFX)
727     {
728 #define LOAD_FUNC(x) getALFunc(x, #x)
729         LOAD_FUNC(alGenEffects);
730         LOAD_FUNC(alDeleteEffects);
731         LOAD_FUNC(alIsEffect);
732         LOAD_FUNC(alEffecti);
733         LOAD_FUNC(alEffectiv);
734         LOAD_FUNC(alEffectf);
735         LOAD_FUNC(alEffectfv);
736         LOAD_FUNC(alGetEffecti);
737         LOAD_FUNC(alGetEffectiv);
738         LOAD_FUNC(alGetEffectf);
739         LOAD_FUNC(alGetEffectfv);
740         LOAD_FUNC(alGenFilters);
741         LOAD_FUNC(alDeleteFilters);
742         LOAD_FUNC(alIsFilter);
743         LOAD_FUNC(alFilteri);
744         LOAD_FUNC(alFilteriv);
745         LOAD_FUNC(alFilterf);
746         LOAD_FUNC(alFilterfv);
747         LOAD_FUNC(alGetFilteri);
748         LOAD_FUNC(alGetFilteriv);
749         LOAD_FUNC(alGetFilterf);
750         LOAD_FUNC(alGetFilterfv);
751         LOAD_FUNC(alGenAuxiliaryEffectSlots);
752         LOAD_FUNC(alDeleteAuxiliaryEffectSlots);
753         LOAD_FUNC(alIsAuxiliaryEffectSlot);
754         LOAD_FUNC(alAuxiliaryEffectSloti);
755         LOAD_FUNC(alAuxiliaryEffectSlotiv);
756         LOAD_FUNC(alAuxiliaryEffectSlotf);
757         LOAD_FUNC(alAuxiliaryEffectSlotfv);
758         LOAD_FUNC(alGetAuxiliaryEffectSloti);
759         LOAD_FUNC(alGetAuxiliaryEffectSlotiv);
760         LOAD_FUNC(alGetAuxiliaryEffectSlotf);
761         LOAD_FUNC(alGetAuxiliaryEffectSlotfv);
762 #undef LOAD_FUNC
763         if(getALError() != AL_NO_ERROR)
764         {
765             ALC.EXT_EFX = false;
766             goto skip_efx;
767         }
768 
769         alGenFilters(1, &mWaterFilter);
770         if(alGetError() == AL_NO_ERROR)
771         {
772             alFilteri(mWaterFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
773             if(alGetError() == AL_NO_ERROR)
774             {
775                 Log(Debug::Info) << "Low-pass filter supported";
776                 alFilterf(mWaterFilter, AL_LOWPASS_GAIN, 0.9f);
777                 alFilterf(mWaterFilter, AL_LOWPASS_GAINHF, 0.125f);
778             }
779             else
780             {
781                 alDeleteFilters(1, &mWaterFilter);
782                 mWaterFilter = 0;
783             }
784             alGetError();
785         }
786 
787         alGenAuxiliaryEffectSlots(1, &mEffectSlot);
788         alGetError();
789 
790         alGenEffects(1, &mDefaultEffect);
791         if(alGetError() == AL_NO_ERROR)
792         {
793             alEffecti(mDefaultEffect, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB);
794             if(alGetError() == AL_NO_ERROR)
795                 Log(Debug::Info) << "EAX Reverb supported";
796             else
797             {
798                 alEffecti(mDefaultEffect, AL_EFFECT_TYPE, AL_EFFECT_REVERB);
799                 if(alGetError() == AL_NO_ERROR)
800                     Log(Debug::Info) << "Standard Reverb supported";
801             }
802             EFXEAXREVERBPROPERTIES props = EFX_REVERB_PRESET_LIVINGROOM;
803             props.flGain = 0.0f;
804             LoadEffect(mDefaultEffect, props);
805         }
806 
807         alGenEffects(1, &mWaterEffect);
808         if(alGetError() == AL_NO_ERROR)
809         {
810             alEffecti(mWaterEffect, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB);
811             if(alGetError() != AL_NO_ERROR)
812             {
813                 alEffecti(mWaterEffect, AL_EFFECT_TYPE, AL_EFFECT_REVERB);
814                 alGetError();
815             }
816             LoadEffect(mWaterEffect, EFX_REVERB_PRESET_UNDERWATER);
817         }
818 
819         alListenerf(AL_METERS_PER_UNIT, 1.0f / Constants::UnitsPerMeter);
820     }
821 skip_efx:
822     alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
823     // Speed of sound is in units per second. Take the sound speed in air (assumed
824     // meters per second), multiply by the units per meter to get the speed in u/s.
825     alSpeedOfSound(Constants::SoundSpeedInAir * Constants::UnitsPerMeter);
826     alGetError();
827 
828     mInitialized = true;
829     return true;
830 }
831 
deinit()832 void OpenAL_Output::deinit()
833 {
834     mStreamThread->removeAll();
835 
836     for(ALuint source : mFreeSources)
837         alDeleteSources(1, &source);
838     mFreeSources.clear();
839 
840     if(mEffectSlot)
841         alDeleteAuxiliaryEffectSlots(1, &mEffectSlot);
842     mEffectSlot = 0;
843     if(mDefaultEffect)
844         alDeleteEffects(1, &mDefaultEffect);
845     mDefaultEffect = 0;
846     if(mWaterEffect)
847         alDeleteEffects(1, &mWaterEffect);
848     mWaterEffect = 0;
849     if(mWaterFilter)
850         alDeleteFilters(1, &mWaterFilter);
851     mWaterFilter = 0;
852 
853     alcMakeContextCurrent(nullptr);
854     if(mContext)
855         alcDestroyContext(mContext);
856     mContext = nullptr;
857     if(mDevice)
858         alcCloseDevice(mDevice);
859     mDevice = nullptr;
860 
861     mInitialized = false;
862 }
863 
864 
enumerateHrtf()865 std::vector<std::string> OpenAL_Output::enumerateHrtf()
866 {
867     std::vector<std::string> ret;
868 
869     if(!mDevice || !ALC.SOFT_HRTF)
870         return ret;
871 
872     LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr;
873     getALCFunc(alcGetStringiSOFT, mDevice, "alcGetStringiSOFT");
874 
875     ALCint num_hrtf;
876     alcGetIntegerv(mDevice, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf);
877     ret.reserve(num_hrtf);
878     for(ALCint i = 0;i < num_hrtf;++i)
879     {
880         const ALCchar *entry = alcGetStringiSOFT(mDevice, ALC_HRTF_SPECIFIER_SOFT, i);
881         ret.emplace_back(entry);
882     }
883 
884     return ret;
885 }
886 
setHrtf(const std::string & hrtfname,HrtfMode hrtfmode)887 void OpenAL_Output::setHrtf(const std::string &hrtfname, HrtfMode hrtfmode)
888 {
889     if(!mDevice || !ALC.SOFT_HRTF)
890     {
891         Log(Debug::Info) << "HRTF extension not present";
892         return;
893     }
894 
895     LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr;
896     getALCFunc(alcGetStringiSOFT, mDevice, "alcGetStringiSOFT");
897 
898     LPALCRESETDEVICESOFT alcResetDeviceSOFT = nullptr;
899     getALCFunc(alcResetDeviceSOFT, mDevice, "alcResetDeviceSOFT");
900 
901     std::vector<ALCint> attrs;
902     attrs.reserve(15);
903 
904     attrs.push_back(ALC_HRTF_SOFT);
905     attrs.push_back(hrtfmode == HrtfMode::Disable ? ALC_FALSE :
906                     hrtfmode == HrtfMode::Enable ? ALC_TRUE :
907                     /*hrtfmode == HrtfMode::Auto ?*/ ALC_DONT_CARE_SOFT);
908     if(!hrtfname.empty())
909     {
910         ALCint index = -1;
911         ALCint num_hrtf;
912         alcGetIntegerv(mDevice, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtf);
913         for(ALCint i = 0;i < num_hrtf;++i)
914         {
915             const ALCchar *entry = alcGetStringiSOFT(mDevice, ALC_HRTF_SPECIFIER_SOFT, i);
916             if(hrtfname == entry)
917             {
918                 index = i;
919                 break;
920             }
921         }
922 
923         if(index < 0)
924             Log(Debug::Warning) << "Failed to find HRTF name \"" << hrtfname << "\", using default";
925         else
926         {
927             attrs.push_back(ALC_HRTF_ID_SOFT);
928             attrs.push_back(index);
929         }
930     }
931     attrs.push_back(0);
932     alcResetDeviceSOFT(mDevice, attrs.data());
933 
934     ALCint hrtf_state;
935     alcGetIntegerv(mDevice, ALC_HRTF_SOFT, 1, &hrtf_state);
936     if(!hrtf_state)
937         Log(Debug::Info) << "HRTF disabled";
938     else
939     {
940         const ALCchar *hrtf = alcGetString(mDevice, ALC_HRTF_SPECIFIER_SOFT);
941         Log(Debug::Info) << "Enabled HRTF " << hrtf;
942     }
943 }
944 
945 
loadSound(const std::string & fname)946 std::pair<Sound_Handle,size_t> OpenAL_Output::loadSound(const std::string &fname)
947 {
948     getALError();
949 
950     std::vector<char> data;
951     ALenum format = AL_NONE;
952     int srate = 0;
953 
954     try
955     {
956         DecoderPtr decoder = mManager.getDecoder();
957         // Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav.
958         if(decoder->mResourceMgr->exists(fname))
959             decoder->open(fname);
960         else
961         {
962             std::string file = fname;
963             std::string::size_type pos = file.rfind('.');
964             if(pos != std::string::npos)
965                 file = file.substr(0, pos)+".mp3";
966             decoder->open(file);
967         }
968 
969         ChannelConfig chans;
970         SampleType type;
971         decoder->getInfo(&srate, &chans, &type);
972         format = getALFormat(chans, type);
973         if(format) decoder->readAll(data);
974     }
975     catch(std::exception &e)
976     {
977         Log(Debug::Error) << "Failed to load audio from " << fname << ": " << e.what();
978     }
979 
980     if(data.empty())
981     {
982         // If we failed to get any usable audio, substitute with silence.
983         format = AL_FORMAT_MONO8;
984         srate = 8000;
985         data.assign(8000, -128);
986     }
987 
988     ALint size;
989     ALuint buf = 0;
990     alGenBuffers(1, &buf);
991     alBufferData(buf, format, data.data(), data.size(), srate);
992     alGetBufferi(buf, AL_SIZE, &size);
993     if(getALError() != AL_NO_ERROR)
994     {
995         if(buf && alIsBuffer(buf))
996             alDeleteBuffers(1, &buf);
997         getALError();
998         return std::make_pair(nullptr, 0);
999     }
1000     return std::make_pair(MAKE_PTRID(buf), size);
1001 }
1002 
unloadSound(Sound_Handle data)1003 size_t OpenAL_Output::unloadSound(Sound_Handle data)
1004 {
1005     ALuint buffer = GET_PTRID(data);
1006     if(!buffer) return 0;
1007 
1008     // Make sure no sources are playing this buffer before unloading it.
1009     SoundVec::const_iterator iter = mActiveSounds.begin();
1010     for(;iter != mActiveSounds.end();++iter)
1011     {
1012         if(!(*iter)->mHandle)
1013             continue;
1014 
1015         ALuint source = GET_PTRID((*iter)->mHandle);
1016         ALint srcbuf;
1017         alGetSourcei(source, AL_BUFFER, &srcbuf);
1018         if((ALuint)srcbuf == buffer)
1019         {
1020             alSourceStop(source);
1021             alSourcei(source, AL_BUFFER, 0);
1022         }
1023     }
1024     ALint size = 0;
1025     alGetBufferi(buffer, AL_SIZE, &size);
1026     alDeleteBuffers(1, &buffer);
1027     getALError();
1028     return size;
1029 }
1030 
1031 
initCommon2D(ALuint source,const osg::Vec3f & pos,ALfloat gain,ALfloat pitch,bool loop,bool useenv)1032 void OpenAL_Output::initCommon2D(ALuint source, const osg::Vec3f &pos, ALfloat gain, ALfloat pitch, bool loop, bool useenv)
1033 {
1034     alSourcef(source, AL_REFERENCE_DISTANCE, 1.0f);
1035     alSourcef(source, AL_MAX_DISTANCE, 1000.0f);
1036     alSourcef(source, AL_ROLLOFF_FACTOR, 0.0f);
1037     alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
1038     alSourcei(source, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
1039     if(AL.SOFT_source_spatialize)
1040         alSourcei(source, AL_SOURCE_SPATIALIZE_SOFT, AL_FALSE);
1041 
1042     if(useenv)
1043     {
1044         if(mWaterFilter)
1045             alSourcei(source, AL_DIRECT_FILTER,
1046                 (mListenerEnv == Env_Underwater) ? mWaterFilter : AL_FILTER_NULL
1047             );
1048         else if(mListenerEnv == Env_Underwater)
1049         {
1050             gain *= 0.9f;
1051             pitch *= 0.7f;
1052         }
1053         if(mEffectSlot)
1054             alSource3i(source, AL_AUXILIARY_SEND_FILTER, mEffectSlot, 0, AL_FILTER_NULL);
1055     }
1056     else
1057     {
1058         if(mWaterFilter)
1059             alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL);
1060         if(mEffectSlot)
1061             alSource3i(source, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL);
1062     }
1063 
1064     alSourcef(source, AL_GAIN, gain);
1065     alSourcef(source, AL_PITCH, pitch);
1066     alSourcefv(source, AL_POSITION, pos.ptr());
1067     alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
1068     alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
1069 }
1070 
initCommon3D(ALuint source,const osg::Vec3f & pos,ALfloat mindist,ALfloat maxdist,ALfloat gain,ALfloat pitch,bool loop,bool useenv)1071 void OpenAL_Output::initCommon3D(ALuint source, const osg::Vec3f &pos, ALfloat mindist, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool loop, bool useenv)
1072 {
1073     alSourcef(source, AL_REFERENCE_DISTANCE, mindist);
1074     alSourcef(source, AL_MAX_DISTANCE, maxdist);
1075     alSourcef(source, AL_ROLLOFF_FACTOR, 1.0f);
1076     alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE);
1077     alSourcei(source, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
1078     if(AL.SOFT_source_spatialize)
1079         alSourcei(source, AL_SOURCE_SPATIALIZE_SOFT, AL_TRUE);
1080 
1081     if((pos - mListenerPos).length2() > maxdist*maxdist)
1082         gain = 0.0f;
1083     if(useenv)
1084     {
1085         if(mWaterFilter)
1086             alSourcei(source, AL_DIRECT_FILTER,
1087                 (mListenerEnv == Env_Underwater) ? mWaterFilter : AL_FILTER_NULL
1088             );
1089         else if(mListenerEnv == Env_Underwater)
1090         {
1091             gain *= 0.9f;
1092             pitch *= 0.7f;
1093         }
1094         if(mEffectSlot)
1095             alSource3i(source, AL_AUXILIARY_SEND_FILTER, mEffectSlot, 0, AL_FILTER_NULL);
1096     }
1097     else
1098     {
1099         if(mWaterFilter)
1100             alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL);
1101         if(mEffectSlot)
1102             alSource3i(source, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL);
1103     }
1104 
1105     alSourcef(source, AL_GAIN, gain);
1106     alSourcef(source, AL_PITCH, pitch);
1107     alSourcefv(source, AL_POSITION, pos.ptr());
1108     alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
1109     alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
1110 }
1111 
updateCommon(ALuint source,const osg::Vec3f & pos,ALfloat maxdist,ALfloat gain,ALfloat pitch,bool useenv,bool is3d)1112 void OpenAL_Output::updateCommon(ALuint source, const osg::Vec3f& pos, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv, bool is3d)
1113 {
1114     if(is3d)
1115     {
1116         if((pos - mListenerPos).length2() > maxdist*maxdist)
1117             gain = 0.0f;
1118     }
1119     if(useenv && mListenerEnv == Env_Underwater && !mWaterFilter)
1120     {
1121         gain *= 0.9f;
1122         pitch *= 0.7f;
1123     }
1124 
1125     alSourcef(source, AL_GAIN, gain);
1126     alSourcef(source, AL_PITCH, pitch);
1127     alSourcefv(source, AL_POSITION, pos.ptr());
1128     alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
1129     alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
1130 }
1131 
1132 
playSound(Sound * sound,Sound_Handle data,float offset)1133 bool OpenAL_Output::playSound(Sound *sound, Sound_Handle data, float offset)
1134 {
1135     ALuint source;
1136 
1137     if(mFreeSources.empty())
1138     {
1139         Log(Debug::Warning) << "No free sources!";
1140         return false;
1141     }
1142     source = mFreeSources.front();
1143 
1144     initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(),
1145                  sound->getIsLooping(), sound->getUseEnv());
1146     alSourcei(source, AL_BUFFER, GET_PTRID(data));
1147     alSourcef(source, AL_SEC_OFFSET, offset);
1148     if(getALError() != AL_NO_ERROR)
1149     {
1150         alSourceRewind(source);
1151         alSourcei(source, AL_BUFFER, 0);
1152         alGetError();
1153         return false;
1154     }
1155 
1156     alSourcePlay(source);
1157     if(getALError() != AL_NO_ERROR)
1158     {
1159         alSourceRewind(source);
1160         alSourcei(source, AL_BUFFER, 0);
1161         alGetError();
1162         return false;
1163     }
1164 
1165     mFreeSources.pop_front();
1166     sound->mHandle = MAKE_PTRID(source);
1167     mActiveSounds.push_back(sound);
1168 
1169     return true;
1170 }
1171 
playSound3D(Sound * sound,Sound_Handle data,float offset)1172 bool OpenAL_Output::playSound3D(Sound *sound, Sound_Handle data, float offset)
1173 {
1174     ALuint source;
1175 
1176     if(mFreeSources.empty())
1177     {
1178         Log(Debug::Warning) << "No free sources!";
1179         return false;
1180     }
1181     source = mFreeSources.front();
1182 
1183     initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(),
1184                  sound->getRealVolume(), sound->getPitch(), sound->getIsLooping(),
1185                  sound->getUseEnv());
1186     alSourcei(source, AL_BUFFER, GET_PTRID(data));
1187     alSourcef(source, AL_SEC_OFFSET, offset);
1188     if(getALError() != AL_NO_ERROR)
1189     {
1190         alSourceRewind(source);
1191         alSourcei(source, AL_BUFFER, 0);
1192         alGetError();
1193         return false;
1194     }
1195 
1196     alSourcePlay(source);
1197     if(getALError() != AL_NO_ERROR)
1198     {
1199         alSourceRewind(source);
1200         alSourcei(source, AL_BUFFER, 0);
1201         alGetError();
1202         return false;
1203     }
1204 
1205     mFreeSources.pop_front();
1206     sound->mHandle = MAKE_PTRID(source);
1207     mActiveSounds.push_back(sound);
1208 
1209     return true;
1210 }
1211 
finishSound(Sound * sound)1212 void OpenAL_Output::finishSound(Sound *sound)
1213 {
1214     if(!sound->mHandle) return;
1215     ALuint source = GET_PTRID(sound->mHandle);
1216     sound->mHandle = nullptr;
1217 
1218     // Rewind the stream to put the source back into an AL_INITIAL state, for
1219     // the next time it's used.
1220     alSourceRewind(source);
1221     alSourcei(source, AL_BUFFER, 0);
1222     getALError();
1223 
1224     mFreeSources.push_back(source);
1225     mActiveSounds.erase(std::find(mActiveSounds.begin(), mActiveSounds.end(), sound));
1226 }
1227 
isSoundPlaying(Sound * sound)1228 bool OpenAL_Output::isSoundPlaying(Sound *sound)
1229 {
1230     if(!sound->mHandle) return false;
1231     ALuint source = GET_PTRID(sound->mHandle);
1232     ALint state = AL_STOPPED;
1233 
1234     alGetSourcei(source, AL_SOURCE_STATE, &state);
1235     getALError();
1236 
1237     return state == AL_PLAYING || state == AL_PAUSED;
1238 }
1239 
updateSound(Sound * sound)1240 void OpenAL_Output::updateSound(Sound *sound)
1241 {
1242     if(!sound->mHandle) return;
1243     ALuint source = GET_PTRID(sound->mHandle);
1244 
1245     updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(),
1246                  sound->getPitch(), sound->getUseEnv(), sound->getIs3D());
1247     getALError();
1248 }
1249 
1250 
streamSound(DecoderPtr decoder,Stream * sound,bool getLoudnessData)1251 bool OpenAL_Output::streamSound(DecoderPtr decoder, Stream *sound, bool getLoudnessData)
1252 {
1253     if(mFreeSources.empty())
1254     {
1255         Log(Debug::Warning) << "No free sources!";
1256         return false;
1257     }
1258     ALuint source = mFreeSources.front();
1259 
1260     if(sound->getIsLooping())
1261         Log(Debug::Warning) << "Warning: cannot loop stream \"" << decoder->getName() << "\"";
1262 
1263     initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(),
1264                  false, sound->getUseEnv());
1265     if(getALError() != AL_NO_ERROR)
1266         return false;
1267 
1268     OpenAL_SoundStream *stream = new OpenAL_SoundStream(source, std::move(decoder));
1269     if(!stream->init(getLoudnessData))
1270     {
1271         delete stream;
1272         return false;
1273     }
1274     mStreamThread->add(stream);
1275 
1276     mFreeSources.pop_front();
1277     sound->mHandle = stream;
1278     mActiveStreams.push_back(sound);
1279     return true;
1280 }
1281 
streamSound3D(DecoderPtr decoder,Stream * sound,bool getLoudnessData)1282 bool OpenAL_Output::streamSound3D(DecoderPtr decoder, Stream *sound, bool getLoudnessData)
1283 {
1284     if(mFreeSources.empty())
1285     {
1286         Log(Debug::Warning) << "No free sources!";
1287         return false;
1288     }
1289     ALuint source = mFreeSources.front();
1290 
1291     if(sound->getIsLooping())
1292         Log(Debug::Warning) << "Warning: cannot loop stream \"" << decoder->getName() << "\"";
1293 
1294     initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(),
1295                  sound->getRealVolume(), sound->getPitch(), false, sound->getUseEnv());
1296     if(getALError() != AL_NO_ERROR)
1297         return false;
1298 
1299     OpenAL_SoundStream *stream = new OpenAL_SoundStream(source, std::move(decoder));
1300     if(!stream->init(getLoudnessData))
1301     {
1302         delete stream;
1303         return false;
1304     }
1305     mStreamThread->add(stream);
1306 
1307     mFreeSources.pop_front();
1308     sound->mHandle = stream;
1309     mActiveStreams.push_back(sound);
1310     return true;
1311 }
1312 
finishStream(Stream * sound)1313 void OpenAL_Output::finishStream(Stream *sound)
1314 {
1315     if(!sound->mHandle) return;
1316     OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);
1317     ALuint source = stream->mSource;
1318 
1319     sound->mHandle = nullptr;
1320     mStreamThread->remove(stream);
1321 
1322     // Rewind the stream to put the source back into an AL_INITIAL state, for
1323     // the next time it's used.
1324     alSourceRewind(source);
1325     alSourcei(source, AL_BUFFER, 0);
1326     getALError();
1327 
1328     mFreeSources.push_back(source);
1329     mActiveStreams.erase(std::find(mActiveStreams.begin(), mActiveStreams.end(), sound));
1330 
1331     delete stream;
1332 }
1333 
getStreamDelay(Stream * sound)1334 double OpenAL_Output::getStreamDelay(Stream *sound)
1335 {
1336     if(!sound->mHandle) return 0.0;
1337     OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);
1338     return stream->getStreamDelay();
1339 }
1340 
getStreamOffset(Stream * sound)1341 double OpenAL_Output::getStreamOffset(Stream *sound)
1342 {
1343     if(!sound->mHandle) return 0.0;
1344     OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);
1345     std::lock_guard<std::mutex> lock(mStreamThread->mMutex);
1346     return stream->getStreamOffset();
1347 }
1348 
getStreamLoudness(Stream * sound)1349 float OpenAL_Output::getStreamLoudness(Stream *sound)
1350 {
1351     if(!sound->mHandle) return 0.0;
1352     OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);
1353     std::lock_guard<std::mutex> lock(mStreamThread->mMutex);
1354     return stream->getCurrentLoudness();
1355 }
1356 
isStreamPlaying(Stream * sound)1357 bool OpenAL_Output::isStreamPlaying(Stream *sound)
1358 {
1359     if(!sound->mHandle) return false;
1360     OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);
1361     std::lock_guard<std::mutex> lock(mStreamThread->mMutex);
1362     return stream->isPlaying();
1363 }
1364 
updateStream(Stream * sound)1365 void OpenAL_Output::updateStream(Stream *sound)
1366 {
1367     if(!sound->mHandle) return;
1368     OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);
1369     ALuint source = stream->mSource;
1370 
1371     updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(),
1372                  sound->getPitch(), sound->getUseEnv(), sound->getIs3D());
1373     getALError();
1374 }
1375 
1376 
startUpdate()1377 void OpenAL_Output::startUpdate()
1378 {
1379     alcSuspendContext(alcGetCurrentContext());
1380 }
1381 
finishUpdate()1382 void OpenAL_Output::finishUpdate()
1383 {
1384     alcProcessContext(alcGetCurrentContext());
1385 }
1386 
1387 
updateListener(const osg::Vec3f & pos,const osg::Vec3f & atdir,const osg::Vec3f & updir,Environment env)1388 void OpenAL_Output::updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdir, const osg::Vec3f &updir, Environment env)
1389 {
1390     if(mContext)
1391     {
1392         ALfloat orient[6] = {
1393             atdir.x(), atdir.y(), atdir.z(),
1394             updir.x(), updir.y(), updir.z()
1395         };
1396         alListenerfv(AL_POSITION, pos.ptr());
1397         alListenerfv(AL_ORIENTATION, orient);
1398 
1399         if(env != mListenerEnv)
1400         {
1401             alSpeedOfSound(((env == Env_Underwater) ? Constants::SoundSpeedUnderwater : Constants::SoundSpeedInAir) * Constants::UnitsPerMeter);
1402 
1403             // Update active sources with the environment's direct filter
1404             if(mWaterFilter)
1405             {
1406                 ALuint filter = (env == Env_Underwater) ? mWaterFilter : AL_FILTER_NULL;
1407                 for(Sound *sound : mActiveSounds)
1408                 {
1409                     if(sound->getUseEnv())
1410                         alSourcei(GET_PTRID(sound->mHandle), AL_DIRECT_FILTER, filter);
1411                 }
1412                 for(Stream *sound : mActiveStreams)
1413                 {
1414                     if(sound->getUseEnv())
1415                         alSourcei(
1416                             reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle)->mSource,
1417                             AL_DIRECT_FILTER, filter
1418                         );
1419                 }
1420             }
1421             // Update the environment effect
1422             if(mEffectSlot)
1423                 alAuxiliaryEffectSloti(mEffectSlot, AL_EFFECTSLOT_EFFECT,
1424                     (env == Env_Underwater) ? mWaterEffect : mDefaultEffect
1425                 );
1426         }
1427         getALError();
1428     }
1429 
1430     mListenerPos = pos;
1431     mListenerEnv = env;
1432 }
1433 
1434 
pauseSounds(int types)1435 void OpenAL_Output::pauseSounds(int types)
1436 {
1437     std::vector<ALuint> sources;
1438     for(Sound *sound : mActiveSounds)
1439     {
1440         if((types&sound->getPlayType()))
1441             sources.push_back(GET_PTRID(sound->mHandle));
1442     }
1443     for(Stream *sound : mActiveStreams)
1444     {
1445         if((types&sound->getPlayType()))
1446         {
1447             OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);
1448             sources.push_back(stream->mSource);
1449         }
1450     }
1451     if(!sources.empty())
1452     {
1453         alSourcePausev(sources.size(), sources.data());
1454         getALError();
1455     }
1456 }
1457 
pauseActiveDevice()1458 void OpenAL_Output::pauseActiveDevice()
1459 {
1460     if (mDevice == nullptr)
1461         return;
1462 
1463     if(alcIsExtensionPresent(mDevice, "ALC_SOFT_PAUSE_DEVICE"))
1464     {
1465         LPALCDEVICEPAUSESOFT alcDevicePauseSOFT = nullptr;
1466         getALCFunc(alcDevicePauseSOFT, mDevice, "alcDevicePauseSOFT");
1467         alcDevicePauseSOFT(mDevice);
1468         getALCError(mDevice);
1469     }
1470 
1471     alListenerf(AL_GAIN, 0.0f);
1472 }
1473 
resumeActiveDevice()1474 void OpenAL_Output::resumeActiveDevice()
1475 {
1476     if (mDevice == nullptr)
1477         return;
1478 
1479     if(alcIsExtensionPresent(mDevice, "ALC_SOFT_PAUSE_DEVICE"))
1480     {
1481         LPALCDEVICERESUMESOFT alcDeviceResumeSOFT = nullptr;
1482         getALCFunc(alcDeviceResumeSOFT, mDevice, "alcDeviceResumeSOFT");
1483         alcDeviceResumeSOFT(mDevice);
1484         getALCError(mDevice);
1485     }
1486 
1487     alListenerf(AL_GAIN, 1.0f);
1488 }
1489 
resumeSounds(int types)1490 void OpenAL_Output::resumeSounds(int types)
1491 {
1492     std::vector<ALuint> sources;
1493     for(Sound *sound : mActiveSounds)
1494     {
1495         if((types&sound->getPlayType()))
1496             sources.push_back(GET_PTRID(sound->mHandle));
1497     }
1498     for(Stream *sound : mActiveStreams)
1499     {
1500         if((types&sound->getPlayType()))
1501         {
1502             OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);
1503             sources.push_back(stream->mSource);
1504         }
1505     }
1506     if(!sources.empty())
1507     {
1508         alSourcePlayv(sources.size(), sources.data());
1509         getALError();
1510     }
1511 }
1512 
1513 
OpenAL_Output(SoundManager & mgr)1514 OpenAL_Output::OpenAL_Output(SoundManager &mgr)
1515   : Sound_Output(mgr)
1516   , mDevice(nullptr), mContext(nullptr)
1517   , mListenerPos(0.0f, 0.0f, 0.0f), mListenerEnv(Env_Normal)
1518   , mWaterFilter(0), mWaterEffect(0), mDefaultEffect(0), mEffectSlot(0)
1519   , mStreamThread(new StreamThread)
1520 {
1521 }
1522 
~OpenAL_Output()1523 OpenAL_Output::~OpenAL_Output()
1524 {
1525     OpenAL_Output::deinit();
1526 }
1527 
1528 }
1529