Play dummy samples and force a fade out on stopping voices
This commit is contained in:
parent
ef0f335132
commit
87479b4b32
@ -302,6 +302,8 @@ const ALfloat *DoFilters(BiquadFilter *lpfilter, BiquadFilter *hpfilter,
|
|||||||
|
|
||||||
void MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, const ALsizei SamplesToDo)
|
void MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, const ALsizei SamplesToDo)
|
||||||
{
|
{
|
||||||
|
static constexpr ALfloat SilentTarget[MAX_OUTPUT_CHANNELS]{};
|
||||||
|
|
||||||
ASSUME(SamplesToDo > 0);
|
ASSUME(SamplesToDo > 0);
|
||||||
|
|
||||||
/* Get source info */
|
/* Get source info */
|
||||||
@ -321,15 +323,6 @@ void MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, const
|
|||||||
ASSUME(SampleSize > 0);
|
ASSUME(SampleSize > 0);
|
||||||
ASSUME(increment > 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};
|
ALCdevice *Device{Context->Device};
|
||||||
const ALsizei IrSize{Device->mHrtf ? Device->mHrtf->irSize : 0};
|
const ALsizei IrSize{Device->mHrtf ? Device->mHrtf->irSize : 0};
|
||||||
const int OutLIdx{GetChannelIdxByName(Device->RealOut, FrontLeft)};
|
const int OutLIdx{GetChannelIdxByName(Device->RealOut, FrontLeft)};
|
||||||
@ -415,19 +408,26 @@ void MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* It's impossible to have a buffer list item with no entries. */
|
/* It's impossible to have a buffer list item with no entries. */
|
||||||
assert(BufferListItem->num_buffers > 0);
|
assert(!BufferListItem || BufferListItem->num_buffers > 0);
|
||||||
|
|
||||||
for(ALsizei chan{0};chan < NumChannels;chan++)
|
for(ALsizei chan{0};chan < NumChannels;chan++)
|
||||||
{
|
{
|
||||||
auto &SrcData = Device->SourceData;
|
auto &SrcData = Device->SourceData;
|
||||||
|
|
||||||
/* Load the previous samples into the source data first, and clear the rest. */
|
/* Load the previous samples into the source data first, and clear the rest. */
|
||||||
auto srciter = std::copy(std::begin(voice->PrevSamples[chan]),
|
auto srciter = std::copy_n(voice->PrevSamples[chan].begin(), MAX_RESAMPLE_PADDING,
|
||||||
std::end(voice->PrevSamples[chan]), std::begin(SrcData));
|
std::begin(SrcData));
|
||||||
std::fill(srciter, std::end(SrcData), 0.0f);
|
std::fill(srciter, std::end(SrcData), 0.0f);
|
||||||
|
|
||||||
auto FilledAmt = static_cast<ALsizei>(voice->PrevSamples[chan].size());
|
ALsizei FilledAmt{MAX_RESAMPLE_PADDING};
|
||||||
if(isstatic)
|
if(vstate == ALvoice::Stopping)
|
||||||
|
{
|
||||||
|
srciter = std::copy(voice->PrevSamples[chan].begin()+MAX_RESAMPLE_PADDING,
|
||||||
|
voice->PrevSamples[chan].end(), srciter);
|
||||||
|
std::fill(srciter, std::begin(SrcData)+SrcBufferSize, *(srciter-1));
|
||||||
|
FilledAmt = SrcBufferSize;
|
||||||
|
}
|
||||||
|
else if(isstatic)
|
||||||
{
|
{
|
||||||
/* TODO: For static sources, loop points are taken from the
|
/* TODO: For static sources, loop points are taken from the
|
||||||
* first buffer (should be adjusted by any buffer offset, to
|
* first buffer (should be adjusted by any buffer offset, to
|
||||||
@ -589,29 +589,30 @@ void MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, const
|
|||||||
const ALfloat *samples{DoFilters(&parms.LowPass, &parms.HighPass,
|
const ALfloat *samples{DoFilters(&parms.LowPass, &parms.HighPass,
|
||||||
Device->FilteredData, ResampledData, DstBufferSize, voice->Direct.FilterType)};
|
Device->FilteredData, ResampledData, DstBufferSize, voice->Direct.FilterType)};
|
||||||
|
|
||||||
|
const ALfloat (&TargetGains)[MAX_OUTPUT_CHANNELS] =
|
||||||
|
UNLIKELY(vstate==ALvoice::Stopping) ? SilentTarget : parms.Gains.Target;
|
||||||
if(!(voice->Flags&VOICE_HAS_HRTF))
|
if(!(voice->Flags&VOICE_HAS_HRTF))
|
||||||
{
|
{
|
||||||
if(!(voice->Flags&VOICE_HAS_NFC))
|
if(!(voice->Flags&VOICE_HAS_NFC))
|
||||||
MixSamples(samples, voice->Direct.Channels, voice->Direct.Buffer,
|
MixSamples(samples, voice->Direct.Channels, voice->Direct.Buffer,
|
||||||
parms.Gains.Current, parms.Gains.Target, Counter, OutPos,
|
parms.Gains.Current, TargetGains, Counter, OutPos, DstBufferSize);
|
||||||
DstBufferSize);
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MixSamples(samples, voice->Direct.ChannelsPerOrder[0],
|
MixSamples(samples, voice->Direct.ChannelsPerOrder[0],
|
||||||
voice->Direct.Buffer, parms.Gains.Current, parms.Gains.Target, Counter,
|
voice->Direct.Buffer, parms.Gains.Current, TargetGains, Counter,
|
||||||
OutPos, DstBufferSize);
|
OutPos, DstBufferSize);
|
||||||
|
|
||||||
ALfloat (&nfcsamples)[BUFFERSIZE] = Device->NfcSampleData;
|
ALfloat (&nfcsamples)[BUFFERSIZE] = Device->NfcSampleData;
|
||||||
ALsizei chanoffset{voice->Direct.ChannelsPerOrder[0]};
|
ALsizei chanoffset{voice->Direct.ChannelsPerOrder[0]};
|
||||||
using FilterProc = void (NfcFilter::*)(float*,const float*,int);
|
using FilterProc = void (NfcFilter::*)(float*,const float*,int);
|
||||||
auto apply_nfc = [voice,&parms,samples,DstBufferSize,Counter,OutPos,&chanoffset,&nfcsamples](FilterProc process, ALsizei order) -> void
|
auto apply_nfc = [voice,&parms,samples,&TargetGains,DstBufferSize,Counter,OutPos,&chanoffset,&nfcsamples](FilterProc process, ALsizei order) -> void
|
||||||
{
|
{
|
||||||
if(voice->Direct.ChannelsPerOrder[order] < 1)
|
if(voice->Direct.ChannelsPerOrder[order] < 1)
|
||||||
return;
|
return;
|
||||||
(parms.NFCtrlFilter.*process)(nfcsamples, samples, DstBufferSize);
|
(parms.NFCtrlFilter.*process)(nfcsamples, samples, DstBufferSize);
|
||||||
MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[order],
|
MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[order],
|
||||||
voice->Direct.Buffer+chanoffset, parms.Gains.Current+chanoffset,
|
voice->Direct.Buffer+chanoffset, parms.Gains.Current+chanoffset,
|
||||||
parms.Gains.Target+chanoffset, Counter, OutPos, DstBufferSize);
|
TargetGains+chanoffset, Counter, OutPos, DstBufferSize);
|
||||||
chanoffset += voice->Direct.ChannelsPerOrder[order];
|
chanoffset += voice->Direct.ChannelsPerOrder[order];
|
||||||
};
|
};
|
||||||
apply_nfc(&NfcFilter::process1, 1);
|
apply_nfc(&NfcFilter::process1, 1);
|
||||||
@ -621,6 +622,8 @@ void MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, const
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
const ALfloat TargetGain{UNLIKELY(vstate==ALvoice::Stopping) ? 0.0f :
|
||||||
|
parms.Hrtf.Target.Gain};
|
||||||
ALsizei fademix{0};
|
ALsizei fademix{0};
|
||||||
/* If fading, the old gain is not silence, and this is the
|
/* If fading, the old gain is not silence, and this is the
|
||||||
* first mixing pass, fade between the IRs.
|
* first mixing pass, fade between the IRs.
|
||||||
@ -635,7 +638,7 @@ void MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, const
|
|||||||
* and new target gains given how much of the fade time
|
* and new target gains given how much of the fade time
|
||||||
* this mix handles.
|
* this mix handles.
|
||||||
*/
|
*/
|
||||||
ALfloat gain{lerp(parms.Hrtf.Old.Gain, parms.Hrtf.Target.Gain,
|
ALfloat gain{lerp(parms.Hrtf.Old.Gain, TargetGain,
|
||||||
minf(1.0f, static_cast<ALfloat>(fademix))/Counter)};
|
minf(1.0f, static_cast<ALfloat>(fademix))/Counter)};
|
||||||
MixHrtfParams hrtfparams;
|
MixHrtfParams hrtfparams;
|
||||||
hrtfparams.Coeffs = &parms.Hrtf.Target.Coeffs;
|
hrtfparams.Coeffs = &parms.Hrtf.Target.Coeffs;
|
||||||
@ -652,12 +655,14 @@ void MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, const
|
|||||||
parms.Hrtf.Old = parms.Hrtf.Target;
|
parms.Hrtf.Old = parms.Hrtf.Target;
|
||||||
if(fademix < Counter)
|
if(fademix < Counter)
|
||||||
parms.Hrtf.Old.Gain = hrtfparams.Gain;
|
parms.Hrtf.Old.Gain = hrtfparams.Gain;
|
||||||
|
else
|
||||||
|
parms.Hrtf.Old.Gain = TargetGain;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(fademix < DstBufferSize)
|
if(fademix < DstBufferSize)
|
||||||
{
|
{
|
||||||
const ALsizei todo{DstBufferSize - fademix};
|
const ALsizei todo{DstBufferSize - fademix};
|
||||||
ALfloat gain{parms.Hrtf.Target.Gain};
|
ALfloat gain{TargetGain};
|
||||||
|
|
||||||
/* Interpolate the target gain if the gain fading lasts
|
/* Interpolate the target gain if the gain fading lasts
|
||||||
* longer than this mix.
|
* longer than this mix.
|
||||||
@ -688,7 +693,7 @@ void MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
ALfloat (&FilterBuf)[BUFFERSIZE] = Device->FilteredData;
|
ALfloat (&FilterBuf)[BUFFERSIZE] = Device->FilteredData;
|
||||||
auto mix_send = [Counter,OutPos,DstBufferSize,chan,ResampledData,&FilterBuf](ALvoice::SendData &send) -> void
|
auto mix_send = [vstate,Counter,OutPos,DstBufferSize,chan,ResampledData,&FilterBuf](ALvoice::SendData &send) -> void
|
||||||
{
|
{
|
||||||
if(!send.Buffer)
|
if(!send.Buffer)
|
||||||
return;
|
return;
|
||||||
@ -697,8 +702,10 @@ void MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, const
|
|||||||
const ALfloat *samples{DoFilters(&parms.LowPass, &parms.HighPass,
|
const ALfloat *samples{DoFilters(&parms.LowPass, &parms.HighPass,
|
||||||
FilterBuf, ResampledData, DstBufferSize, send.FilterType)};
|
FilterBuf, ResampledData, DstBufferSize, send.FilterType)};
|
||||||
|
|
||||||
|
const ALfloat (&TargetGains)[MAX_OUTPUT_CHANNELS] =
|
||||||
|
UNLIKELY(vstate==ALvoice::Stopping) ? SilentTarget : parms.Gains.Target;
|
||||||
MixSamples(samples, send.Channels, send.Buffer, parms.Gains.Current,
|
MixSamples(samples, send.Channels, send.Buffer, parms.Gains.Current,
|
||||||
parms.Gains.Target, Counter, OutPos, DstBufferSize);
|
TargetGains, Counter, OutPos, DstBufferSize);
|
||||||
};
|
};
|
||||||
std::for_each(voice->Send.begin(), voice->Send.end(), mix_send);
|
std::for_each(voice->Send.begin(), voice->Send.end(), mix_send);
|
||||||
}
|
}
|
||||||
@ -711,7 +718,11 @@ void MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, const
|
|||||||
voice->Offset += DstBufferSize;
|
voice->Offset += DstBufferSize;
|
||||||
Counter = maxi(DstBufferSize, Counter) - DstBufferSize;
|
Counter = maxi(DstBufferSize, Counter) - DstBufferSize;
|
||||||
|
|
||||||
if(isstatic)
|
if(UNLIKELY(vstate == ALvoice::Stopping))
|
||||||
|
{
|
||||||
|
/* Do nothing. */
|
||||||
|
}
|
||||||
|
else if(isstatic)
|
||||||
{
|
{
|
||||||
if(BufferLoopItem)
|
if(BufferLoopItem)
|
||||||
{
|
{
|
||||||
@ -760,6 +771,13 @@ void MixSource(ALvoice *voice, const ALuint SourceID, ALCcontext *Context, const
|
|||||||
|
|
||||||
voice->Flags |= VOICE_IS_FADING;
|
voice->Flags |= VOICE_IS_FADING;
|
||||||
|
|
||||||
|
/* Don't update positions and buffers if we were stopping. */
|
||||||
|
if(UNLIKELY(vstate == ALvoice::Stopping))
|
||||||
|
{
|
||||||
|
voice->PlayState.store(ALvoice::Stopped, std::memory_order_release);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Update source info */
|
/* Update source info */
|
||||||
voice->position.store(DataPosInt, std::memory_order_relaxed);
|
voice->position.store(DataPosInt, std::memory_order_relaxed);
|
||||||
voice->position_fraction.store(DataPosFrac, std::memory_order_relaxed);
|
voice->position_fraction.store(DataPosFrac, std::memory_order_relaxed);
|
||||||
|
@ -252,7 +252,8 @@ struct ALvoice {
|
|||||||
|
|
||||||
ALuint Offset; /* Number of output samples mixed since starting. */
|
ALuint Offset; /* Number of output samples mixed since starting. */
|
||||||
|
|
||||||
alignas(16) std::array<std::array<ALfloat,MAX_RESAMPLE_PADDING>,MAX_INPUT_CHANNELS> PrevSamples;
|
using ResamplePaddingArray = std::array<ALfloat,MAX_RESAMPLE_PADDING*2>;
|
||||||
|
alignas(16) std::array<ResamplePaddingArray,MAX_INPUT_CHANNELS> PrevSamples;
|
||||||
|
|
||||||
InterpState ResampleState;
|
InterpState ResampleState;
|
||||||
|
|
||||||
|
@ -2855,7 +2855,7 @@ AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources)
|
|||||||
|
|
||||||
/* Clear previous samples. */
|
/* Clear previous samples. */
|
||||||
std::for_each(voice->PrevSamples.begin(), voice->PrevSamples.begin()+voice->NumChannels,
|
std::for_each(voice->PrevSamples.begin(), voice->PrevSamples.begin()+voice->NumChannels,
|
||||||
[](std::array<ALfloat,MAX_RESAMPLE_PADDING> &samples) -> void
|
[](std::array<ALfloat,MAX_RESAMPLE_PADDING*2> &samples) -> void
|
||||||
{ std::fill(std::begin(samples), std::end(samples), 0.0f); });
|
{ std::fill(std::begin(samples), std::end(samples), 0.0f); });
|
||||||
|
|
||||||
/* Clear the stepping value so the mixer knows not to mix this until
|
/* Clear the stepping value so the mixer knows not to mix this until
|
||||||
|
Loading…
x
Reference in New Issue
Block a user