diff options
-rw-r--r-- | al/source.cpp | 9 | ||||
-rw-r--r-- | alc/alcontext.h | 1 | ||||
-rw-r--r-- | alc/alu.cpp | 35 |
3 files changed, 32 insertions, 13 deletions
diff --git a/al/source.cpp b/al/source.cpp index 76f6c54b..dd79a1b6 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -2744,6 +2744,7 @@ START_API_FUNC case AL_PAUSED: assert(voice != nullptr); /* A source that's paused simply resumes. */ + cur->mOldVoice = nullptr; cur->mVoice = voice; cur->mSourceID = source->id; cur->mState = AL_PLAYING; @@ -2757,17 +2758,13 @@ START_API_FUNC * fades back to the beginning. */ voice->mPendingStop.store(true, std::memory_order_relaxed); - cur->mVoice = voice; - cur->mSourceID = source->id; - cur->mState = AL_STOPPED; + cur->mOldVoice = voice; voice = nullptr; - - cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed); - cur = cur->mNext.load(std::memory_order_relaxed); break; default: assert(voice == nullptr); + cur->mOldVoice = nullptr; break; } diff --git a/alc/alcontext.h b/alc/alcontext.h index 199f207d..aea2ea31 100644 --- a/alc/alcontext.h +++ b/alc/alcontext.h @@ -57,6 +57,7 @@ struct ALcontextProps { struct VoiceChange { + ALvoice *mOldVoice{nullptr}; ALvoice *mVoice{nullptr}; ALuint mSourceID{0}; ALenum mState{0}; diff --git a/alc/alu.cpp b/alc/alu.cpp index 08167663..46720cb0 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -1628,35 +1628,56 @@ void ProcessVoiceChanges(ALCcontext *ctx) do { cur = next; - bool success{false}; + bool sendevt{false}; if(cur->mState == AL_INITIAL || cur->mState == AL_STOPPED) { if(ALvoice *voice{cur->mVoice}) { voice->mCurrentBuffer.store(nullptr, std::memory_order_relaxed); voice->mLoopBuffer.store(nullptr, std::memory_order_relaxed); - voice->mSourceID.exchange(0u, std::memory_order_relaxed); + voice->mSourceID.store(0u, std::memory_order_relaxed); ALvoice::State oldvstate{ALvoice::Playing}; - success = voice->mPlayState.compare_exchange_strong(oldvstate, ALvoice::Stopping, + sendevt = voice->mPlayState.compare_exchange_strong(oldvstate, ALvoice::Stopping, std::memory_order_relaxed, std::memory_order_acquire); voice->mPendingStop.store(false, std::memory_order_release); } - success |= (cur->mState == AL_INITIAL); + /* AL_INITIAL state change events are always sent, even if the + * voice is already stopped or even if there is no voice. + */ + sendevt |= (cur->mState == AL_INITIAL); } else if(cur->mState == AL_PAUSED) { ALvoice *voice{cur->mVoice}; ALvoice::State oldvstate{ALvoice::Playing}; - success = voice->mPlayState.compare_exchange_strong(oldvstate, ALvoice::Stopping, + sendevt = voice->mPlayState.compare_exchange_strong(oldvstate, ALvoice::Stopping, std::memory_order_release, std::memory_order_acquire); } else if(cur->mState == AL_PLAYING) { + /* NOTE: When playing a voice, sending a source state change event + * depends if there's an old voice to stop and if that stop is + * successful. If there is no old voice, a playing event is always + * sent. If there is an old voice, an event is sent only if the + * voice is already stopped. + */ + if(ALvoice *oldvoice{cur->mOldVoice}) + { + oldvoice->mCurrentBuffer.store(nullptr, std::memory_order_relaxed); + oldvoice->mLoopBuffer.store(nullptr, std::memory_order_relaxed); + oldvoice->mSourceID.store(0u, std::memory_order_relaxed); + ALvoice::State oldvstate{ALvoice::Playing}; + sendevt = !oldvoice->mPlayState.compare_exchange_strong(oldvstate, + ALvoice::Stopping, std::memory_order_relaxed, std::memory_order_acquire); + oldvoice->mPendingStop.store(false, std::memory_order_release); + } + else + sendevt = true; + ALvoice *voice{cur->mVoice}; voice->mPlayState.store(ALvoice::Playing, std::memory_order_release); - success = true; } - if(success && (enabledevt&EventType_SourceStateChange) && cur->mSourceID != 0) + if(sendevt && (enabledevt&EventType_SourceStateChange)) SendSourceStateEvent(ctx, cur->mSourceID, cur->mState); } while((next=cur->mNext.load(std::memory_order_acquire))); ctx->mCurrentVoiceChange.store(cur, std::memory_order_release); |