Add a Stopping state for voices
This currently doesn't do much, except have the mixer progress it to Stopped. It's valid to have without a source or buffers, and in the future will allow fading out when a source is paused or stopped.
This commit is contained in:
+7
-3
@@ -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. */
|
||||
|
||||
+7
-12
@@ -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).
|
||||
*/
|
||||
|
||||
+44
-11
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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. */
|
||||
|
||||
+38
-11
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user