diff options
-rw-r--r-- | Alc/alc.cpp | 10 | ||||
-rw-r--r-- | Alc/alu.cpp | 19 | ||||
-rw-r--r-- | Alc/mixvoice.cpp | 55 | ||||
-rw-r--r-- | OpenAL32/Include/alu.h | 10 | ||||
-rw-r--r-- | OpenAL32/alSource.cpp | 49 |
5 files changed, 104 insertions, 39 deletions
diff --git a/Alc/alc.cpp b/Alc/alc.cpp index 9bba1b52..9891fec5 100644 --- a/Alc/alc.cpp +++ b/Alc/alc.cpp @@ -2148,7 +2148,11 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList) { delete voice->Update.exchange(nullptr, std::memory_order_acq_rel); - if(voice->SourceID.load(std::memory_order_acquire) == 0u) + /* Force the voice to stopped if it was stopping. */ + ALvoice::State vstate{ALvoice::Stopping}; + voice->PlayState.compare_exchange_strong(vstate, ALvoice::Stopped, + std::memory_order_acquire, std::memory_order_acquire); + if(voice->SourceID.load(std::memory_order_relaxed) == 0u) return; if(device->AvgSpeakerDist > 0.0f) @@ -2625,8 +2629,8 @@ void AllocateVoices(ALCcontext *context, ALsizei num_voices, ALsizei old_sends) voice->SourceID.store(old_voice->SourceID.load(std::memory_order_relaxed), std::memory_order_relaxed); - voice->Playing.store(old_voice->Playing.load(std::memory_order_relaxed), - std::memory_order_relaxed); + voice->PlayState.store(old_voice->PlayState.load(std::memory_order_relaxed), + std::memory_order_relaxed); voice->Props = old_voice->Props; /* Clear extraneous property set sends. */ diff --git a/Alc/alu.cpp b/Alc/alu.cpp index 28f6346e..bf401638 100644 --- a/Alc/alu.cpp +++ b/Alc/alu.cpp @@ -1451,18 +1451,12 @@ void ProcessContext(ALCcontext *ctx, const ALsizei SamplesToDo) std::for_each(ctx->Voices, ctx->Voices+ctx->VoiceCount.load(std::memory_order_acquire), [SamplesToDo,ctx](ALvoice *voice) -> void { - if(!voice->Playing.load(std::memory_order_acquire)) return; + if(voice->PlayState.load(std::memory_order_acquire) == ALvoice::Stopped) + return; ALuint sid{voice->SourceID.load(std::memory_order_relaxed)}; - if(!sid || voice->Step < 1) return; + if(voice->Step < 1) return; - if(!MixSource(voice, sid, ctx, SamplesToDo)) - { - voice->current_buffer.store(nullptr, std::memory_order_relaxed); - voice->loop_buffer.store(nullptr, std::memory_order_relaxed); - voice->SourceID.store(0u, std::memory_order_relaxed); - voice->Playing.store(false, std::memory_order_release); - SendSourceStoppedEvent(ctx, sid); - } + MixSource(voice, sid, ctx, SamplesToDo); } ); @@ -1814,14 +1808,15 @@ void aluHandleDisconnect(ALCdevice *device, const char *msg, ...) auto stop_voice = [ctx](ALvoice *voice) -> void { - if(!voice->Playing.load(std::memory_order_acquire)) return; + if(voice->PlayState.load(std::memory_order_acquire) == ALvoice::Playing) + return; ALuint sid{voice->SourceID.load(std::memory_order_relaxed)}; if(!sid) return; voice->current_buffer.store(nullptr, std::memory_order_relaxed); voice->loop_buffer.store(nullptr, std::memory_order_relaxed); voice->SourceID.store(0u, std::memory_order_relaxed); - voice->Playing.store(false, std::memory_order_release); + voice->PlayState.store(ALvoice::Stopped, std::memory_order_release); /* If the source's voice was playing, it's now effectively stopped * (the source state will be updated the next time it's checked). */ diff --git a/Alc/mixvoice.cpp b/Alc/mixvoice.cpp index 2cc26d7b..9602ac72 100644 --- a/Alc/mixvoice.cpp +++ b/Alc/mixvoice.cpp @@ -198,6 +198,23 @@ void aluInitMixer() namespace { +void SendSourceStoppedEvent(ALCcontext *context, ALuint id) +{ + ALbitfieldSOFT enabledevt{context->EnabledEvts.load(std::memory_order_acquire)}; + if(!(enabledevt&EventType_SourceStateChange)) return; + + RingBuffer *ring{context->AsyncEvents.get()}; + auto evt_vec = ring->getWriteVector(); + if(evt_vec.first.len < 1) return; + + AsyncEvent *evt{new (evt_vec.first.buf) AsyncEvent{EventType_SourceStateChange}}; + evt->u.srcstate.id = id; + evt->u.srcstate.state = AL_STOPPED; + + ring->writeAdvance(1); + context->EventSem.post(); +} + /* Base template left undefined. Should be marked =delete, but Clang 3.8.1 * chokes on that given the inline specializations. */ @@ -283,20 +300,20 @@ const ALfloat *DoFilters(BiquadFilter *lpfilter, BiquadFilter *hpfilter, } // namespace -ALboolean MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, const ALsizei SamplesToDo) +void MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, const ALsizei SamplesToDo) { ASSUME(SamplesToDo > 0); /* Get source info */ - bool isplaying{true}; /* Will only be called while playing. */ - bool isstatic{(voice->Flags&VOICE_IS_STATIC) != 0}; - ALsizei DataPosInt{static_cast<ALsizei>(voice->position.load(std::memory_order_acquire))}; + ALvoice::State vstate{voice->PlayState.load(std::memory_order_acquire)}; + const bool isstatic{(voice->Flags&VOICE_IS_STATIC) != 0}; + ALsizei DataPosInt{static_cast<ALsizei>(voice->position.load(std::memory_order_relaxed))}; ALsizei DataPosFrac{voice->position_fraction.load(std::memory_order_relaxed)}; ALbufferlistitem *BufferListItem{voice->current_buffer.load(std::memory_order_relaxed)}; ALbufferlistitem *BufferLoopItem{voice->loop_buffer.load(std::memory_order_relaxed)}; - ALsizei NumChannels{voice->NumChannels}; - ALsizei SampleSize{voice->SampleSize}; - ALint increment{voice->Step}; + const ALsizei NumChannels{voice->NumChannels}; + const ALsizei SampleSize{voice->SampleSize}; + const ALint increment{voice->Step}; ASSUME(DataPosInt >= 0); ASSUME(DataPosFrac >= 0); @@ -304,6 +321,15 @@ ALboolean MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, ASSUME(SampleSize > 0); ASSUME(increment > 0); + /* TODO: Use stored previous samples to fade out without incrementing when + * stopping (buffers may not be available). + */ + if(UNLIKELY(vstate == ALvoice::Stopping)) + { + voice->PlayState.store(ALvoice::Stopped, std::memory_order_release); + return; + } + ALCdevice *Device{Context->Device}; const ALsizei IrSize{Device->mHrtf ? Device->mHrtf->irSize : 0}; const int OutLIdx{GetChannelIdxByName(Device->RealOut, FrontLeft)}; @@ -704,7 +730,7 @@ ALboolean MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, /* Handle non-looping static source */ if(DataPosInt >= BufferListItem->max_samples) { - isplaying = false; + vstate = ALvoice::Stopped; BufferListItem = nullptr; DataPosInt = 0; DataPosFrac = 0; @@ -724,13 +750,13 @@ ALboolean MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, BufferListItem = BufferListItem->next.load(std::memory_order_relaxed); if(!BufferListItem && !(BufferListItem=BufferLoopItem)) { - isplaying = false; + vstate = ALvoice::Stopped; DataPosInt = 0; DataPosFrac = 0; break; } } - } while(isplaying && OutPos < SamplesToDo); + } while(vstate != ALvoice::Stopped && OutPos < SamplesToDo); voice->Flags |= VOICE_IS_FADING; @@ -755,5 +781,12 @@ ALboolean MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, } } - return isplaying; + if(vstate == ALvoice::Stopped) + { + voice->current_buffer.store(nullptr, std::memory_order_relaxed); + voice->loop_buffer.store(nullptr, std::memory_order_relaxed); + voice->SourceID.store(0u, std::memory_order_relaxed); + voice->PlayState.store(ALvoice::Stopped, std::memory_order_release); + SendSourceStoppedEvent(Context, SourceID); + } } diff --git a/OpenAL32/Include/alu.h b/OpenAL32/Include/alu.h index 13362dea..fbf21c47 100644 --- a/OpenAL32/Include/alu.h +++ b/OpenAL32/Include/alu.h @@ -207,10 +207,16 @@ struct ALvoiceProps : public ALvoicePropsBase { #define VOICE_HAS_NFC (1u<<4) struct ALvoice { + enum State { + Stopped = 0, + Playing = 1, + Stopping = 2 + }; + std::atomic<ALvoiceProps*> Update{nullptr}; std::atomic<ALuint> SourceID{0u}; - std::atomic<bool> Playing{false}; + std::atomic<State> PlayState{Stopped}; ALvoicePropsBase Props; @@ -440,7 +446,7 @@ inline std::array<ALfloat,MAX_AMBI_CHANNELS> GetAmbiIdentityRow(size_t i) noexce } -ALboolean MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, const ALsizei SamplesToDo); +void MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, const ALsizei SamplesToDo); void aluMixData(ALCdevice *device, ALvoid *OutBuffer, ALsizei NumSamples); /* Caller must lock the device state, and the mixer must not be running. */ diff --git a/OpenAL32/alSource.cpp b/OpenAL32/alSource.cpp index e014a388..feab5213 100644 --- a/OpenAL32/alSource.cpp +++ b/OpenAL32/alSource.cpp @@ -534,7 +534,13 @@ void FreeSource(ALCcontext *context, ALsource *source) voice->current_buffer.store(nullptr, std::memory_order_relaxed); voice->loop_buffer.store(nullptr, std::memory_order_relaxed); voice->SourceID.store(0u, std::memory_order_relaxed); - voice->Playing.store(false, std::memory_order_release); + std::atomic_thread_fence(std::memory_order_release); + /* Don't set the voice to stopping if it was already stopped or + * stopping. + */ + ALvoice::State oldvstate{ALvoice::Playing}; + voice->PlayState.compare_exchange_strong(oldvstate, ALvoice::Stopping, + std::memory_order_acq_rel, std::memory_order_acquire); } backlock.unlock(); @@ -2724,7 +2730,12 @@ AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) auto voices_end = context->Voices + context->VoiceCount.load(std::memory_order_relaxed); auto free_voices = std::accumulate(context->Voices, voices_end, ALsizei{0}, [](const ALsizei count, const ALvoice *voice) noexcept -> ALsizei - { return (voice->SourceID.load(std::memory_order_relaxed) == 0u) ? count+1 : count; } + { + if(voice->PlayState.load(std::memory_order_acquire) == ALvoice::Stopped && + voice->SourceID.load(std::memory_order_relaxed) == 0u) + return count + 1; + return count; + } ); if(UNLIKELY(n > free_voices)) { @@ -2790,7 +2801,7 @@ AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) case AL_PAUSED: assert(voice != nullptr); /* A source that's paused simply resumes. */ - voice->Playing.store(true, std::memory_order_release); + voice->PlayState.store(ALvoice::Playing, std::memory_order_release); source->state = AL_PLAYING; SendStateChangeEvent(context.get(), source->id, AL_PLAYING); return; @@ -2804,12 +2815,15 @@ AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) auto voices_end = context->Voices + context->VoiceCount.load(std::memory_order_relaxed); auto voice_iter = std::find_if(context->Voices, voices_end, [](const ALvoice *voice) noexcept -> bool - { return voice->SourceID.load(std::memory_order_relaxed) == 0u; } + { + return voice->PlayState.load(std::memory_order_acquire) == ALvoice::Stopped && + voice->SourceID.load(std::memory_order_relaxed) == 0u; + } ); assert(voice_iter != voices_end); auto vidx = static_cast<ALint>(std::distance(context->Voices, voice_iter)); voice = *voice_iter; - voice->Playing.store(false, std::memory_order_release); + voice->PlayState.store(ALvoice::Stopped, std::memory_order_release); source->PropsClean.test_and_set(std::memory_order_acquire); UpdateSourceProps(source, voice, context.get()); @@ -2840,8 +2854,9 @@ AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) } /* Clear previous samples. */ - for(auto &samples : voice->PrevSamples) - std::fill(std::begin(samples), std::end(samples), 0.0f); + std::for_each(voice->PrevSamples.begin(), voice->PrevSamples.begin()+voice->NumChannels, + [](std::array<ALfloat,MAX_RESAMPLE_PADDING> &samples) -> void + { std::fill(std::begin(samples), std::end(samples), 0.0f); }); /* Clear the stepping value so the mixer knows not to mix this until * the update gets applied. @@ -2881,7 +2896,7 @@ AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) } voice->SourceID.store(source->id, std::memory_order_relaxed); - voice->Playing.store(true, std::memory_order_release); + voice->PlayState.store(ALvoice::Playing, std::memory_order_release); source->state = AL_PLAYING; source->VoiceIdx = vidx; @@ -2916,7 +2931,13 @@ AL_API ALvoid AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources) { ALsource *source{LookupSource(context.get(), sources[i])}; ALvoice *voice{GetSourceVoice(source, context.get())}; - if(voice) voice->Playing.store(false, std::memory_order_release); + if(voice) + { + std::atomic_thread_fence(std::memory_order_release); + ALvoice::State oldvstate{ALvoice::Playing}; + voice->PlayState.compare_exchange_strong(oldvstate, ALvoice::Stopping, + std::memory_order_acq_rel, std::memory_order_acquire); + } if(GetSourceState(source, voice) == AL_PLAYING) { source->state = AL_PAUSED; @@ -2956,7 +2977,10 @@ AL_API ALvoid AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources) voice->current_buffer.store(nullptr, std::memory_order_relaxed); voice->loop_buffer.store(nullptr, std::memory_order_relaxed); voice->SourceID.store(0u, std::memory_order_relaxed); - voice->Playing.store(false, std::memory_order_release); + std::atomic_thread_fence(std::memory_order_release); + ALvoice::State oldvstate{ALvoice::Playing}; + voice->PlayState.compare_exchange_strong(oldvstate, ALvoice::Stopping, + std::memory_order_acq_rel, std::memory_order_acquire); voice = nullptr; } ALenum oldstate{GetSourceState(source, voice)}; @@ -3001,7 +3025,10 @@ AL_API ALvoid AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources) voice->current_buffer.store(nullptr, std::memory_order_relaxed); voice->loop_buffer.store(nullptr, std::memory_order_relaxed); voice->SourceID.store(0u, std::memory_order_relaxed); - voice->Playing.store(false, std::memory_order_release); + std::atomic_thread_fence(std::memory_order_release); + ALvoice::State oldvstate{ALvoice::Playing}; + voice->PlayState.compare_exchange_strong(oldvstate, ALvoice::Stopping, + std::memory_order_acq_rel, std::memory_order_acquire); voice = nullptr; } if(GetSourceState(source, voice) != AL_INITIAL) |