From 05a29c7e6cd621bcb5e05adea5175da84a30c3d8 Mon Sep 17 00:00:00 2001 From: erorcun Date: Sat, 26 Jun 2021 23:59:40 +0300 Subject: Multi-threaded audio fixes --- src/audio/oal/stream.cpp | 93 ++++++++++++++++++++++-------------- src/audio/oal/stream.h | 7 ++- src/audio/sampman_oal.cpp | 118 +++++++++++++++++----------------------------- src/core/config.h | 3 +- 4 files changed, 107 insertions(+), 114 deletions(-) diff --git a/src/audio/oal/stream.cpp b/src/audio/oal/stream.cpp index c46f836c..13fbcd34 100644 --- a/src/audio/oal/stream.cpp +++ b/src/audio/oal/stream.cpp @@ -1089,42 +1089,23 @@ CStream::FlagAsToBeProcessed(bool close) gAudioThreadCv.notify_one(); } -extern CStream *aStream[]; void audioFileOpsThread() { - std::queue m_streamsToDelete; - do { CStream *stream; { // Just a semaphore std::unique_lock queueMutex(gAudioThreadQueueMutex); - gAudioThreadCv.wait(queueMutex, [m_streamsToDelete] { return gStreamsToProcess.size() > 0 || m_streamsToDelete.size() > 0 || gAudioThreadTerm; }); + gAudioThreadCv.wait(queueMutex, [] { return gStreamsToProcess.size() > 0 || gAudioThreadTerm; }); if (gAudioThreadTerm) return; if (!gStreamsToProcess.empty()) { stream = gStreamsToProcess.front(); gStreamsToProcess.pop(); - } else { - // End of streams. Perform deleting streams - while(!m_streamsToDelete.empty()) { - CStream *stream = m_streamsToDelete.front(); - m_streamsToDelete.pop(); - if (stream->m_pSoundFile) { - delete stream->m_pSoundFile; - stream->m_pSoundFile = nil; - } - - if (stream->m_pBuffer) { - free(stream->m_pBuffer); - stream->m_pBuffer = nil; - } - delete stream; - } + } else continue; - } } std::unique_lock lock(stream->m_mutex); @@ -1133,16 +1114,21 @@ void audioFileOpsThread() bool insertBufsAfterCheck = false; do { - if (stream->m_nDeleteMe == 1) { - m_streamsToDelete.push(stream); - stream->m_nDeleteMe = 2; - break; - } else if (stream->m_nDeleteMe == 2) { - break; - } + if (!stream->IsOpened()) { + // We MUST do that here, because we release mutex for m_pSoundFile->Seek() and m_pSoundFile->Decode() since they're costly + if (stream->m_pSoundFile) { + delete stream->m_pSoundFile; + stream->m_pSoundFile = nil; + } - if (!stream->IsOpened()) + if (stream->m_pBuffer) { + free(stream->m_pBuffer); + stream->m_pBuffer = nil; + } + lock.unlock(); + stream->m_closeCv.notify_one(); break; + } if (stream->m_bReset) break; @@ -1223,14 +1209,14 @@ void CStream::Terminate() #endif } -CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS], uint32 overrideSampleRate) : +CStream::CStream(ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS]) : m_pAlSources(sources), m_alBuffers(buffers), m_pBuffer(nil), m_bPaused(false), m_bActive(false), #ifdef MULTITHREADED_AUDIO - m_nDeleteMe(false), + m_bIExist(false), m_bDoSeek(false), m_SeekPos(0), #endif @@ -1242,6 +1228,31 @@ CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBU m_nLoopCount(1) { +} + +bool CStream::Open(const char* filename, uint32 overrideSampleRate) +{ + if (IsOpened()) return false; + +#ifdef MULTITHREADED_AUDIO + std::unique_lock lock(m_mutex); + + CStream *stream = this; + // Wait for thread to close old one. We can't close it here, because the thread might be running Decode() or Seek(), while mutex is released + m_closeCv.wait(lock, [this] { return m_pSoundFile == nil && m_pBuffer == nil; }); + + m_bDoSeek = false; + m_SeekPos = 0; +#endif + + m_bPaused = false; + m_bActive = false; + m_bReset = false; + m_nVolume = 0; + m_nPan = 0; + m_nPosBeforeReset = 0; + m_nLoopCount = 1; + // Be case-insensitive on linux (from https://github.com/OneSadCookie/fcaseopen/) #if !defined(_WIN32) char *real = casepath(filename); @@ -1278,7 +1289,7 @@ CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBU else m_pSoundFile = nil; - if ( IsOpened() ) + if ( m_pSoundFile && m_pSoundFile->IsOpened() ) { uint32 bufSize = m_pSoundFile->GetBufferSize(); if(bufSize != 0) { // Otherwise it's deferred @@ -1293,8 +1304,12 @@ CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBU DEV("Buffer sec: %f\n", (float(m_pSoundFile->GetBufferSamples()) / float(m_pSoundFile->GetChannels())/ float(m_pSoundFile->GetSampleRate()))); DEV("Length MS: %02d:%02d\n", (m_pSoundFile->GetLength() / 1000) / 60, (m_pSoundFile->GetLength() / 1000) % 60); } - return; +#ifdef MULTITHREADED_AUDIO + m_bIExist = true; +#endif + return true; } + return false; } CStream::~CStream() @@ -1304,18 +1319,21 @@ CStream::~CStream() void CStream::Close() { + if(!IsOpened()) return; + #ifdef MULTITHREADED_AUDIO { std::lock_guard lock(m_mutex); Stop(); ClearBuffers(); - m_nDeleteMe = true; - // clearing buffer queues are not needed. after m_nDeleteMe set, this stream is ded + m_bIExist = false; + // clearing buffer queues are not needed. after m_bIExist is cleared, this stream is ded } FlagAsToBeProcessed(true); #else + Stop(); ClearBuffers(); @@ -1338,9 +1356,14 @@ bool CStream::HasSource() return (m_pAlSources[0] != AL_NONE) && (m_pAlSources[1] != AL_NONE); } +// m_bIExist only written in main thread, thus mutex is not needed on main thread bool CStream::IsOpened() { +#ifdef MULTITHREADED_AUDIO + return m_bIExist; +#else return m_pSoundFile && m_pSoundFile->IsOpened(); +#endif } bool CStream::IsPlaying() diff --git a/src/audio/oal/stream.h b/src/audio/oal/stream.h index bdbf19e0..10b595c1 100644 --- a/src/audio/oal/stream.h +++ b/src/audio/oal/stream.h @@ -127,9 +127,10 @@ public: std::mutex m_mutex; std::queue> m_fillBuffers; // left and right buffer tsQueue> m_queueBuffers; + std::condition_variable m_closeCv; bool m_bDoSeek; uint32 m_SeekPos; - uint8 m_nDeleteMe; // 1: add to delete list 2: already on delete list + bool m_bIExist; #endif void *m_pBuffer; @@ -163,8 +164,10 @@ public: static void Initialise(); static void Terminate(); - CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS], uint32 overrideSampleRate = 32000); + CStream(ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS]); ~CStream(); + void Delete(); + bool Open(const char *filename, uint32 overrideSampleRate = 32000); void Close(); bool IsOpened(); diff --git a/src/audio/sampman_oal.cpp b/src/audio/sampman_oal.cpp index cd2fa50a..2c7d4cec 100644 --- a/src/audio/sampman_oal.cpp +++ b/src/audio/sampman_oal.cpp @@ -39,6 +39,7 @@ #ifdef MULTITHREADED_AUDIO #include #include +#include #endif #include "oal/stream.h" @@ -522,13 +523,10 @@ _FindMP3s(void) } else bShortcut = FALSE; - aStream[0] = new CStream(filepath, ALStreamSources[0], ALStreamBuffers[0]); - - if (aStream[0] && aStream[0]->IsOpened()) + if (aStream[0] && aStream[0]->Open(filepath)) { total_ms = aStream[0]->GetLengthMS(); aStream[0]->Close(); - aStream[0] = NULL; OutputDebugString(fd.cFileName); @@ -596,13 +594,10 @@ _FindMP3s(void) continue; } } - aStream[0] = new CStream(filepath, ALStreamSources[0], ALStreamBuffers[0]); - - if (aStream[0] && aStream[0]->IsOpened()) + if (aStream[0] && aStream[0]->Open(filepath)) { total_ms = aStream[0]->GetLengthMS(); aStream[0]->Close(); - aStream[0] = NULL; OutputDebugString(fd.cFileName); @@ -656,13 +651,10 @@ _FindMP3s(void) } else bShortcut = FALSE; - aStream[0] = new CStream(filepath, ALStreamSources[0], ALStreamBuffers[0]); - - if (aStream[0] && aStream[0]->IsOpened()) + if (aStream[0] && aStream[0]->Open(filepath)) { total_ms = aStream[0]->GetLengthMS(); aStream[0]->Close(); - aStream[0] = NULL; OutputDebugString(fd.cFileName); @@ -818,6 +810,9 @@ cSampleManager::Initialise(void) EFXInit(); + for(int i = 0; i < MAX_STREAMS; i++) + aStream[i] = new CStream(ALStreamSources[i], ALStreamBuffers[i]); + CStream::Initialise(); { @@ -973,13 +968,10 @@ cSampleManager::Initialise(void) for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ ) { - aStream[0] = new CStream(StreamedNameTable[i], ALStreamSources[0], ALStreamBuffers[0], IsThisTrackAt16KHz(i) ? 16000 : 32000); - - if ( aStream[0] && aStream[0]->IsOpened() ) + if ( aStream[0] && aStream[0]->Open(StreamedNameTable[i], IsThisTrackAt16KHz(i) ? 16000 : 32000) ) { uint32 tatalms = aStream[0]->GetLengthMS(); aStream[0]->Close(); - aStream[0] = NULL; nStreamLength[i] = tatalms; } @@ -1023,7 +1015,8 @@ cSampleManager::Initialise(void) { for ( int32 i = 0; i < MAX_STREAMS; i++ ) { - aStream[i] = NULL; + aStream[i]->Close(); + nStreamVolume[i] = 100; nStreamPan[i] = 63; } @@ -1110,14 +1103,7 @@ void cSampleManager::Terminate(void) { for (int32 i = 0; i < MAX_STREAMS; i++) - { - CStream *stream = aStream[i]; - if (stream) - { - stream->Close(); - aStream[i] = NULL; - } - } + aStream[i]->Close(); for ( int32 i = 0; i < NUM_CHANNELS; i++ ) aChannel[i].Term(); @@ -1168,6 +1154,9 @@ cSampleManager::Terminate(void) CStream::Terminate(); + for(int32 i = 0; i < MAX_STREAMS; i++) + delete aStream[i]; + if ( nSampleBankMemoryStartAddress[SFX_BANK_0] != 0 ) { free((void *)nSampleBankMemoryStartAddress[SFX_BANK_0]); @@ -1693,22 +1682,16 @@ cSampleManager::PreloadStreamedFile(uint32 nFile, uint8 nStream) if ( nFile < TOTAL_STREAMED_SOUNDS ) { - if ( aStream[nStream] ) - { - aStream[nStream]->Close(); - aStream[nStream] = NULL; - } - + CStream *stream = aStream[nStream]; + + stream->Close(); + strcpy(filename, StreamedNameTable[nFile]); - CStream *stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); - ASSERT(stream != NULL); - - aStream[nStream] = stream; + stream->Open(filename, IsThisTrackAt16KHz(nFile) ? 16000 : 32000); if ( !stream->Setup() ) { stream->Close(); - aStream[nStream] = NULL; } } } @@ -1720,7 +1703,7 @@ cSampleManager::PauseStream(bool8 nPauseFlag, uint8 nStream) CStream *stream = aStream[nStream]; - if ( stream ) + if ( stream->IsOpened() ) { stream->SetPause(nPauseFlag != FALSE); } @@ -1733,12 +1716,9 @@ cSampleManager::StartPreloadedStreamedFile(uint8 nStream) CStream *stream = aStream[nStream]; - if ( stream ) + if ( stream->IsOpened() ) { - if ( stream->IsOpened() ) - { - stream->Start(); - } + stream->Start(); } } @@ -1752,11 +1732,8 @@ cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream) if ( nFile >= TOTAL_STREAMED_SOUNDS ) return FALSE; - if ( aStream[nStream] ) - { - aStream[nStream]->Close(); - aStream[nStream] = NULL; - } + aStream[nStream]->Close(); + if ( nFile == STREAMED_SOUND_RADIO_MP3_PLAYER ) { do @@ -1773,11 +1750,10 @@ cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream) nFile = 0; strcpy(filename, StreamedNameTable[nFile]); - CStream* stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); - aStream[nStream] = stream; - - if (stream->Setup()) { + CStream *stream = aStream[nStream]; + stream->Open(filename, IsThisTrackAt16KHz(nFile) ? 16000 : 32000); + if ( stream->Setup() ) { stream->SetLoopCount(nStreamLoopedFlag[nStream] ? 0 : 1); nStreamLoopedFlag[nStream] = TRUE; if (position != 0) @@ -1788,19 +1764,18 @@ cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream) return TRUE; } else { stream->Close(); - aStream[nStream] = NULL; } return FALSE; } else { if (e->pLinkPath != NULL) - aStream[nStream] = new CStream(e->pLinkPath, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); + aStream[nStream]->Open(e->pLinkPath, IsThisTrackAt16KHz(nFile) ? 16000 : 32000); else { strcpy(filename, _mp3DirectoryPath); strcat(filename, e->aFilename); - aStream[nStream] = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream]); + aStream[nStream]->Open(filename); } if (aStream[nStream]->Setup()) { @@ -1813,7 +1788,6 @@ cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream) return TRUE; } else { aStream[nStream]->Close(); - aStream[nStream] = NULL; } // fall through, start playing from another song } @@ -1832,9 +1806,8 @@ cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream) _bIsMp3Active = 0; strcpy(filename, StreamedNameTable[nFile]); - CStream* stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); - - aStream[nStream] = stream; + CStream* stream = aStream[nStream]; + stream->Open(filename, IsThisTrackAt16KHz(nFile) ? 16000 : 32000); if (stream->Setup()) { stream->SetLoopCount(nStreamLoopedFlag[nStream] ? 0 : 1); @@ -1847,18 +1820,17 @@ cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream) return TRUE; } else { stream->Close(); - aStream[nStream] = NULL; } return FALSE; } } if (mp3->pLinkPath != NULL) - aStream[nStream] = new CStream(mp3->pLinkPath, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); + aStream[nStream]->Open(mp3->pLinkPath, IsThisTrackAt16KHz(nFile) ? 16000 : 32000); else { strcpy(filename, _mp3DirectoryPath); strcat(filename, mp3->aFilename); - aStream[nStream] = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); + aStream[nStream]->Open(filename, IsThisTrackAt16KHz(nFile) ? 16000 : 32000); } if (aStream[nStream]->Setup()) { @@ -1869,7 +1841,6 @@ cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream) return TRUE; } else { aStream[nStream]->Close(); - aStream[nStream] = NULL; } } @@ -1881,9 +1852,9 @@ cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream) } strcpy(filename, StreamedNameTable[nFile]); - CStream *stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000); + CStream *stream = aStream[nStream]; - aStream[nStream] = stream; + aStream[nStream]->Open(filename, IsThisTrackAt16KHz(nFile) ? 16000 : 32000); if ( stream->Setup() ) { stream->SetLoopCount(nStreamLoopedFlag[nStream] ? 0 : 1); @@ -1896,7 +1867,6 @@ cSampleManager::StartStreamedFile(uint32 nFile, uint32 nPos, uint8 nStream) return TRUE; } else { stream->Close(); - aStream[nStream] = NULL; } return FALSE; } @@ -1908,14 +1878,10 @@ cSampleManager::StopStreamedFile(uint8 nStream) CStream *stream = aStream[nStream]; - if ( stream ) - { - stream->Close(); - aStream[nStream] = NULL; + stream->Close(); - if ( nStream == 0 ) - _bIsMp3Active = FALSE; - } + if ( nStream == 0 ) + _bIsMp3Active = FALSE; } int32 @@ -1925,7 +1891,7 @@ cSampleManager::GetStreamedFilePosition(uint8 nStream) CStream *stream = aStream[nStream]; - if ( stream ) + if ( stream->IsOpened() ) { if ( _bIsMp3Active ) { @@ -1968,7 +1934,7 @@ cSampleManager::SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, bool8 nEffect CStream *stream = aStream[nStream]; - if ( stream ) + if ( stream->IsOpened() ) { if ( nEffectFlag ) { if ( nStream == 1 || nStream == 2 ) @@ -1998,7 +1964,7 @@ cSampleManager::IsStreamPlaying(uint8 nStream) CStream *stream = aStream[nStream]; - if ( stream ) + if ( stream->IsOpened() ) { if ( stream->IsPlaying() ) return TRUE; @@ -2014,7 +1980,7 @@ cSampleManager::Service(void) { CStream *stream = aStream[i]; - if ( stream ) + if ( stream->IsOpened() ) stream->Update(); } int refCount = CChannel::channelsThatNeedService; diff --git a/src/core/config.h b/src/core/config.h index abc81834..4efe12f0 100644 --- a/src/core/config.h +++ b/src/core/config.h @@ -399,7 +399,7 @@ static_assert(false, "SUPPORT_XBOX_SCRIPT and SUPPORT_MOBILE_SCRIPT are mutually //#define PS2_AUDIO_PATHS // changes audio paths for cutscenes and radio to PS2 paths (needs vbdec on MSS builds) //#define AUDIO_OAL_USE_SNDFILE // use libsndfile to decode WAVs instead of our internal decoder #define AUDIO_OAL_USE_MPG123 // use mpg123 to support mp3 files -#define MULTITHREADED_AUDIO +#define MULTITHREADED_AUDIO // for streams. requires C++11 or later #ifdef AUDIO_OPUS #define AUDIO_OAL_USE_OPUS // enable support of opus files @@ -513,5 +513,6 @@ static_assert(false, "SUPPORT_XBOX_SCRIPT and SUPPORT_MOBILE_SCRIPT are mutually #undef FREE_CAM #undef BIG_IMG #undef PS2_AUDIO_CHANNELS +#undef MULTITHREADED_AUDIO #undef RADIO_SCROLL_TO_PREV_STATION #endif -- cgit v1.2.3