diff --git a/src/d_main.cpp b/src/d_main.cpp index 84e03cdaa..4c0d2537d 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -913,7 +913,6 @@ void D_Display () } while (diff < 1); wipestart = nowtime; done = screen->WipeDo (1); - S_UpdateMusic(); // OpenAL needs this to keep the music running, thanks to a complete lack of a sane streaming implementation using callbacks. :( C_DrawConsole (hw2d); // console and M_Drawer (); // menu are drawn even on top of wipes screen->Update (); // page flip or blit buffer @@ -1012,7 +1011,6 @@ void D_DoomLoop () // Update display, next frame, with current state. I_StartTic (); D_Display (); - S_UpdateMusic(); // OpenAL needs this to keep the music running, thanks to a complete lack of a sane streaming implementation using callbacks. :( if (wantToRestart) { wantToRestart = false; diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 1a9210c2a..7915fdccc 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -2660,17 +2660,6 @@ void S_StopMusic (bool force) } } -//========================================================================== -// -// -// -//========================================================================== - -void S_UpdateMusic() -{ - GSnd->UpdateMusic(); -} - //========================================================================== // // CCMD playsound diff --git a/src/s_sound.h b/src/s_sound.h index acdee2a2c..e157bbaa1 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -332,7 +332,6 @@ int S_GetMusic (char **name); // Stops the music for sure. void S_StopMusic (bool force); -void S_UpdateMusic(); // Stop and resume music, during game PAUSE. void S_PauseSound (bool notmusic, bool notsfx); diff --git a/src/sound/except.h b/src/sound/except.h index 023ddb6d4..f6bef4fc6 100644 --- a/src/sound/except.h +++ b/src/sound/except.h @@ -23,19 +23,6 @@ inline int CheckException(DWORD code) return EXCEPTION_CONTINUE_SEARCH; } - -#else - -#ifdef __try -#undef __try -#endif -#define __try - -#ifdef __except -#undef __except -#endif -#define __except(a) if (0) - #endif -#endif \ No newline at end of file +#endif diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index 72e3c0a05..1088818ce 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -3192,8 +3192,8 @@ bool IsFModExPresent() { #ifdef NO_FMOD return false; -#elif !defined _WIN32 - return true; // on non-Windows we cannot delay load the library so it has to be present. +#elif !defined _MSC_VER + return true; // on non-MSVC we cannot delay load the library so it has to be present. #else static bool cached_result; static bool done = false; diff --git a/src/sound/i_sound.h b/src/sound/i_sound.h index ef0743c05..4e565e885 100644 --- a/src/sound/i_sound.h +++ b/src/sound/i_sound.h @@ -148,7 +148,6 @@ public: virtual void UpdateListener (SoundListener *) = 0; virtual void UpdateSounds () = 0; - virtual void UpdateMusic() {} virtual bool IsValid () = 0; virtual void PrintStatus () = 0; diff --git a/src/sound/mpg123_decoder.cpp b/src/sound/mpg123_decoder.cpp index 4deab7e5b..9d1fff97e 100644 --- a/src/sound/mpg123_decoder.cpp +++ b/src/sound/mpg123_decoder.cpp @@ -56,17 +56,18 @@ bool MPG123Decoder::open(FileReader *reader) { if(!inited) { - __try - { +#ifdef _MSC_VER + __try { +#endif if(mpg123_init() != MPG123_OK) return false; inited = true; - } - __except (CheckException(GetExceptionCode())) - { +#ifdef _MSC_VER + } __except (CheckException(GetExceptionCode())) { // this means that the delay loaded decoder DLL was not found. return false; } +#endif } Reader = reader; diff --git a/src/sound/oalsound.cpp b/src/sound/oalsound.cpp index ac6a7aedb..8ac41a04b 100644 --- a/src/sound/oalsound.cpp +++ b/src/sound/oalsound.cpp @@ -221,7 +221,7 @@ class OpenALSoundStream : public SoundStream ALuint Buffers[BufferCount]; ALuint Source; - bool Playing; + std::atomic Playing; bool Looping; ALfloat Volume; @@ -287,12 +287,14 @@ public: OpenALSoundStream(OpenALSoundRenderer *renderer) : Renderer(renderer), Source(0), Playing(false), Looping(false), Volume(1.0f), Reader(NULL), Decoder(NULL) { - Renderer->Streams.Push(this); memset(Buffers, 0, sizeof(Buffers)); + Renderer->AddStream(this); } virtual ~OpenALSoundStream() { + Renderer->RemoveStream(this); + if(Source) { alSourceRewind(Source); @@ -309,9 +311,6 @@ public: } getALError(); - Renderer->Streams.Delete(Renderer->Streams.Find(this)); - Renderer = NULL; - delete Decoder; delete Reader; } @@ -321,7 +320,7 @@ public: { SetVolume(vol); - if(Playing) + if(Playing.load()) return true; /* Clear the buffer queue, then fill and queue each buffer */ @@ -342,21 +341,24 @@ public: return false; alSourcePlay(Source); - Playing = (getALError()==AL_NO_ERROR); + if(getALError() != AL_NO_ERROR) + return false; - return Playing; + Playing.store(true); + return true; } virtual void Stop() { - if(!Playing) + if(!Playing.load()) return; + std::unique_lock lock(Renderer->StreamLock); alSourceStop(Source); alSourcei(Source, AL_BUFFER, 0); getALError(); - Playing = false; + Playing.store(false); } virtual void SetVolume(float vol) @@ -382,21 +384,25 @@ public: virtual bool SetPosition(unsigned int ms_pos) { + std::unique_lock lock(Renderer->StreamLock); if(!Decoder->seek(ms_pos)) return false; - if(!Playing) + if(!Playing.load()) return true; - // Stop the source so that all buffers become processed, then call - // IsEnded() to refill and restart the source queue with the new + // Stop the source so that all buffers become processed, which will + // allow the next update to restart the source queue with the new // position. alSourceStop(Source); getALError(); - return !IsEnded(); + lock.unlock(); + Renderer->StreamWake.notify_all(); + return true; } virtual unsigned int GetPosition() { + std::unique_lock lock(Renderer->StreamLock); ALint offset, queued, state; alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset); alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); @@ -405,6 +411,8 @@ public: return 0; size_t pos = Decoder->getSampleOffset(); + lock.unlock(); + if(state != AL_STOPPED) { size_t rem = queued*(Data.Size()/FrameSize) - offset; @@ -416,54 +424,10 @@ public: virtual bool IsEnded() { - if(!Playing) - return true; - - ALint state, processed; - alGetSourcei(Source, AL_SOURCE_STATE, &state); - alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); - - Playing = (getALError()==AL_NO_ERROR); - if(!Playing) - return true; - - // For each processed buffer in the queue... - while(processed > 0) - { - ALuint bufid; - - // Unqueue the oldest buffer, fill it with more data, and queue it - // on the end - alSourceUnqueueBuffers(Source, 1, &bufid); - processed--; - - if(Callback(this, &Data[0], Data.Size(), UserData)) - { - alBufferData(bufid, Format, &Data[0], Data.Size(), SampleRate); - alSourceQueueBuffers(Source, 1, &bufid); - } - } - - // If the source is not playing or paused, and there are buffers queued, - // then there was an underrun. Restart the source. - Playing = (getALError()==AL_NO_ERROR); - if(Playing && state != AL_PLAYING && state != AL_PAUSED) - { - ALint queued = 0; - alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); - - Playing = (getALError() == AL_NO_ERROR) && (queued > 0); - if(Playing) - { - alSourcePlay(Source); - Playing = (getALError()==AL_NO_ERROR); - } - } - - return !Playing; + return !Playing.load(); } - FString GetStats() + virtual FString GetStats() { FString stats; size_t pos, len; @@ -474,6 +438,7 @@ public: ALint state; ALenum err; + std::unique_lock lock(Renderer->StreamLock); alGetSourcef(Source, AL_GAIN, &volume); alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset); alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); @@ -481,16 +446,19 @@ public: alGetSourcei(Source, AL_SOURCE_STATE, &state); if((err=alGetError()) != AL_NO_ERROR) { + lock.unlock(); stats = "Error getting stats: "; stats += alGetString(err); return stats; } + pos = Decoder->getSampleOffset(); + len = Decoder->getSampleLength(); + lock.unlock(); + stats = (state == AL_INITIAL) ? "Buffering" : (state == AL_STOPPED) ? "Underrun" : (state == AL_PLAYING || state == AL_PAUSED) ? "Ready" : "Unknown state"; - pos = Decoder->getSampleOffset(); - len = Decoder->getSampleLength(); if(state == AL_STOPPED) offset = BufferCount * (Data.Size()/FrameSize); else @@ -516,6 +484,57 @@ public: return stats; } + bool Process() + { + if(!Playing.load()) + return false; + + ALint state, processed; + alGetSourcei(Source, AL_SOURCE_STATE, &state); + alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); + if(getALError() != AL_NO_ERROR) + { + Playing.store(false); + return false; + } + + // For each processed buffer in the queue... + while(processed > 0) + { + ALuint bufid; + + // Unqueue the oldest buffer, fill it with more data, and queue it + // on the end + alSourceUnqueueBuffers(Source, 1, &bufid); + processed--; + + if(Callback(this, &Data[0], Data.Size(), UserData)) + { + alBufferData(bufid, Format, &Data[0], Data.Size(), SampleRate); + alSourceQueueBuffers(Source, 1, &bufid); + } + } + + // If the source is not playing or paused, and there are buffers queued, + // then there was an underrun. Restart the source. + bool ok = (getALError()==AL_NO_ERROR); + if(ok && state != AL_PLAYING && state != AL_PAUSED) + { + ALint queued = 0; + alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); + + ok = (getALError() == AL_NO_ERROR) && (queued > 0); + if(ok) + { + alSourcePlay(Source); + ok = (getALError()==AL_NO_ERROR); + } + } + + Playing.store(ok); + return ok; + } + bool Init(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) { if(!SetupSource()) @@ -707,7 +726,7 @@ static void LoadALCFunc(ALCdevice *device, const char *name, T *x) #define LOAD_FUNC(x) (LoadALFunc(#x, &x)) #define LOAD_DEV_FUNC(d, x) (LoadALCFunc(d, #x, &x)) OpenALSoundRenderer::OpenALSoundRenderer() - : Device(NULL), Context(NULL), SFXPaused(0), PrevEnvironment(NULL), EnvSlot(0) + : QuitThread(false), Device(NULL), Context(NULL), SFXPaused(0), PrevEnvironment(NULL), EnvSlot(0) { EnvFilters[0] = EnvFilters[1] = 0; @@ -940,6 +959,15 @@ OpenALSoundRenderer::~OpenALSoundRenderer() if(!Device) return; + if(StreamThread.joinable()) + { + std::unique_lock lock(StreamLock); + QuitThread.store(true); + lock.unlock(); + StreamWake.notify_all(); + StreamThread.join(); + } + while(Streams.Size() > 0) delete Streams[0]; @@ -974,6 +1002,43 @@ OpenALSoundRenderer::~OpenALSoundRenderer() Device = NULL; } +void OpenALSoundRenderer::BackgroundProc() +{ + std::unique_lock lock(StreamLock); + while(!QuitThread.load()) + { + if(Streams.Size() == 0) + { + // If there's nothing to play, wait indefinitely. + StreamWake.wait(lock); + } + else + { + // Else, process all active streams and sleep for 100ms + for(size_t i = 0;i < Streams.Size();i++) + Streams[i]->Process(); + StreamWake.wait_for(lock, std::chrono::milliseconds(100)); + } + } +} + +void OpenALSoundRenderer::AddStream(OpenALSoundStream *stream) +{ + std::unique_lock lock(StreamLock); + Streams.Push(stream); + lock.unlock(); + // There's a stream to play, make sure the background thread is aware + StreamWake.notify_all(); +} + +void OpenALSoundRenderer::RemoveStream(OpenALSoundStream *stream) +{ + std::unique_lock lock(StreamLock); + unsigned int idx = Streams.Find(stream); + if(idx < Streams.Size()) + Streams.Delete(idx); +} + void OpenALSoundRenderer::SetSfxVolume(float volume) { SfxVolume = volume; @@ -1252,6 +1317,8 @@ void OpenALSoundRenderer::UnloadSound(SoundHandle sfx) SoundStream *OpenALSoundRenderer::CreateStream(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) { + if(StreamThread.get_id() == std::thread::id()) + StreamThread = std::thread(std::mem_fn(&OpenALSoundRenderer::BackgroundProc), this); OpenALSoundStream *stream = new OpenALSoundStream(this); if (!stream->Init(callback, buffbytes, flags, samplerate, userdata)) { @@ -1263,6 +1330,8 @@ SoundStream *OpenALSoundRenderer::CreateStream(SoundStreamCallback callback, int SoundStream *OpenALSoundRenderer::OpenStream(FileReader *reader, int flags) { + if(StreamThread.get_id() == std::thread::id()) + StreamThread = std::thread(std::mem_fn(&OpenALSoundRenderer::BackgroundProc), this); OpenALSoundStream *stream = new OpenALSoundStream(this); if (!stream->Init(reader, !!(flags&SoundStream::Loop))) { @@ -1880,13 +1949,6 @@ void OpenALSoundRenderer::UpdateSounds() PurgeStoppedSources(); } -void OpenALSoundRenderer::UpdateMusic() -{ - // For some reason this isn't being called? - for(uint32 i = 0;i < Streams.Size();++i) - Streams[i]->IsEnded(); -} - bool OpenALSoundRenderer::IsValid() { return Device != NULL; diff --git a/src/sound/oalsound.h b/src/sound/oalsound.h index 88dcbc238..09fe64d92 100644 --- a/src/sound/oalsound.h +++ b/src/sound/oalsound.h @@ -1,6 +1,11 @@ #ifndef OALSOUND_H #define OALSOUND_H +#include +#include +#include +#include + #include "i_sound.h" #include "s_sound.h" #include "menu/menu.h" @@ -114,7 +119,6 @@ public: virtual void UpdateListener(SoundListener *); virtual void UpdateSounds(); - virtual void UpdateMusic(); virtual void MarkStartTime(FISoundChannel*); virtual float GetAudibility(FISoundChannel*); @@ -184,10 +188,19 @@ private: void (ALC_APIENTRY*alcDevicePauseSOFT)(ALCdevice *device); void (ALC_APIENTRY*alcDeviceResumeSOFT)(ALCdevice *device); + void BackgroundProc(); + void AddStream(OpenALSoundStream *stream); + void RemoveStream(OpenALSoundStream *stream); + void LoadReverb(const ReverbContainer *env); void PurgeStoppedSources(); static FSoundChan *FindLowestChannel(); + std::thread StreamThread; + std::mutex StreamLock; + std::condition_variable StreamWake; + std::atomic QuitThread; + ALCdevice *Device; ALCcontext *Context; diff --git a/src/sound/sndfile_decoder.cpp b/src/sound/sndfile_decoder.cpp index 382b22ab6..b0c35c2a0 100644 --- a/src/sound/sndfile_decoder.cpp +++ b/src/sound/sndfile_decoder.cpp @@ -54,8 +54,9 @@ SndFileDecoder::~SndFileDecoder() bool SndFileDecoder::open(FileReader *reader) { - __try - { +#ifdef _MSC_VER + __try { +#endif SF_VIRTUAL_IO sfio = { file_get_filelen, file_seek, file_read, file_write, file_tell }; Reader = reader; @@ -68,11 +69,11 @@ bool SndFileDecoder::open(FileReader *reader) sf_close(SndFile); SndFile = 0; } - } - __except (CheckException(GetExceptionCode())) - { +#ifdef _MSC_VER + } __except (CheckException(GetExceptionCode())) { // this means that the delay loaded decoder DLL was not found. } +#endif return false; }