aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--al/source.cpp9
-rw-r--r--alc/alcontext.h1
-rw-r--r--alc/alu.cpp35
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);