Store the source's previous samples with the voice

This helps avoid different results when looping is toggled within a couple
samples of the loop point, or when a processed buffer is removed while the
source is only a couple samples into the next buffer.
This commit is contained in:
Chris Robinson 2015-10-15 07:29:25 -07:00
parent 3c54ba3901
commit 97f53d941c
4 changed files with 49 additions and 92 deletions

View File

@ -57,16 +57,17 @@ enum Resampler {
static enum Resampler DefaultResampler = LinearResampler;
/* Each entry is a pair, where the first is the number of samples needed before
* the current position, and the second is the number of samples needed after
* (not including) the current position, for the given resampler.
/* Specifies the number of samples needed after (not including) the current
* position, for the given resampler.
*/
static const ALsizei ResamplerPadding[ResamplerMax][2] = {
{0, 0}, /* Point */
{0, 1}, /* Linear */
{1, 2}, /* FIR4 */
{3, 4}, /* FIR8 */
static const ALsizei ResamplerPadding[ResamplerMax] = {
0, /* Point */
1, /* Linear */
2, /* FIR4 */
4, /* FIR8 */
};
/* FIR8 requires 3 extra samples before the current position. */
static_assert(MAX_PREVIOUS_SAMPLES >= 3, "MAX_PREVIOUS_SAMPLES must be at least 3!");
static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
@ -342,8 +343,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
OutPos = 0;
do {
const ALuint BufferPrePadding = ResamplerPadding[DefaultResampler][0];
const ALuint BufferPadding = ResamplerPadding[DefaultResampler][1];
const ALuint BufferPadding = ResamplerPadding[DefaultResampler];
ALuint SrcBufferSize, DstBufferSize;
/* Figure out how many buffer samples will be needed */
@ -351,13 +351,13 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
DataSize64 *= increment;
DataSize64 += DataPosFrac+FRACTIONMASK;
DataSize64 >>= FRACTIONBITS;
DataSize64 += BufferPadding+BufferPrePadding;
DataSize64 += BufferPadding+MAX_PREVIOUS_SAMPLES;
SrcBufferSize = (ALuint)mini64(DataSize64, BUFFERSIZE);
/* Figure out how many samples we can actually mix from this. */
DataSize64 = SrcBufferSize;
DataSize64 -= BufferPadding+BufferPrePadding;
DataSize64 -= BufferPadding+MAX_PREVIOUS_SAMPLES;
DataSize64 <<= FRACTIONBITS;
DataSize64 -= DataPosFrac;
@ -373,7 +373,11 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
{
const ALfloat *ResampledData;
ALfloat *SrcData = Device->SourceData;
ALuint SrcDataSize = 0;
ALuint SrcDataSize;
/* Load the previous samples into the source data first. */
memcpy(SrcData, voice->PrevSamples[chan], MAX_PREVIOUS_SAMPLES*sizeof(ALfloat));
SrcDataSize = MAX_PREVIOUS_SAMPLES;
if(Source->SourceType == AL_STATIC)
{
@ -382,7 +386,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
ALuint DataSize;
ALuint pos;
/* Offset to current channel */
/* Offset buffer data to current channel */
Data += chan*SampleSize;
/* If current pos is beyond the loop range, do not loop */
@ -390,21 +394,9 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
{
Looping = AL_FALSE;
if(DataPosInt >= BufferPrePadding)
pos = DataPosInt - BufferPrePadding;
else
{
DataSize = BufferPrePadding - DataPosInt;
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
SilenceSamples(&SrcData[SrcDataSize], DataSize);
SrcDataSize += DataSize;
pos = 0;
}
/* Copy what's left to play in the source buffer, and clear the
* rest of the temp buffer */
/* Load what's left to play from the source buffer, and
* clear the rest of the temp buffer */
pos = DataPosInt;
DataSize = minu(SrcBufferSize - SrcDataSize, ALBuffer->SampleLen - pos);
LoadSamples(&SrcData[SrcDataSize], &Data[pos * NumChannels*SampleSize],
@ -419,29 +411,9 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
ALuint LoopStart = ALBuffer->LoopStart;
ALuint LoopEnd = ALBuffer->LoopEnd;
if(DataPosInt >= LoopStart)
{
pos = DataPosInt-LoopStart;
while(pos < BufferPrePadding)
pos += LoopEnd-LoopStart;
pos -= BufferPrePadding;
pos += LoopStart;
}
else if(DataPosInt >= BufferPrePadding)
pos = DataPosInt - BufferPrePadding;
else
{
DataSize = BufferPrePadding - DataPosInt;
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
SilenceSamples(&SrcData[SrcDataSize], DataSize);
SrcDataSize += DataSize;
pos = 0;
}
/* Copy what's left of this loop iteration, then copy repeats
* of the loop section */
/* Load what's left of this loop iteration, then load
* repeats of the loop section */
pos = DataPosInt;
DataSize = LoopEnd - pos;
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
@ -464,45 +436,7 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
{
/* Crawl the buffer queue to fill in the temp buffer */
ALbufferlistitem *tmpiter = BufferListItem;
ALuint pos;
if(DataPosInt >= BufferPrePadding)
pos = DataPosInt - BufferPrePadding;
else
{
pos = BufferPrePadding - DataPosInt;
while(pos > 0)
{
ALbufferlistitem *prev;
if((prev=tmpiter->prev) != NULL)
tmpiter = prev;
else if(Looping)
{
while(tmpiter->next)
tmpiter = tmpiter->next;
}
else
{
ALuint DataSize = minu(SrcBufferSize - SrcDataSize, pos);
SilenceSamples(&SrcData[SrcDataSize], DataSize);
SrcDataSize += DataSize;
pos = 0;
break;
}
if(tmpiter->buffer)
{
if((ALuint)tmpiter->buffer->SampleLen > pos)
{
pos = tmpiter->buffer->SampleLen - pos;
break;
}
pos -= tmpiter->buffer->SampleLen;
}
}
}
ALuint pos = DataPosInt;
while(tmpiter && SrcBufferSize > SrcDataSize)
{
@ -538,9 +472,15 @@ ALvoid MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALuint Sam
}
}
/* Store the last source samples used for next time. */
memcpy(voice->PrevSamples[chan],
&SrcData[(increment*DstBufferSize + DataPosFrac)>>FRACTIONBITS],
MAX_PREVIOUS_SAMPLES*sizeof(ALfloat)
);
/* Now resample, then filter and mix to the appropriate outputs. */
ResampledData = Resample(
&SrcData[BufferPrePadding], DataPosFrac, increment,
&SrcData[MAX_PREVIOUS_SAMPLES], DataPosFrac, increment,
Device->ResampledData, DstBufferSize
);
{

View File

@ -35,6 +35,8 @@ typedef struct ALvoice {
ALuint Offset; /* Number of output samples mixed since starting. */
alignas(16) ALfloat PrevSamples[MAX_INPUT_CHANNELS][MAX_PREVIOUS_SAMPLES];
DirectParams Direct;
SendParams Send[MAX_SENDS];
} ALvoice;

View File

@ -32,6 +32,9 @@
#define MAX_PITCH (255)
/* Maximum number of previous buffer samples needed for resampling. */
#define MAX_PREVIOUS_SAMPLES 4
#ifdef __cplusplus
extern "C" {

View File

@ -2574,6 +2574,7 @@ ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state)
{
ALCdevice *device = Context->Device;
ALbufferlistitem *BufferList;
ALboolean discontinuity;
ALvoice *voice = NULL;
ALsizei i;
@ -2594,13 +2595,20 @@ ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state)
Source->position = 0;
Source->position_fraction = 0;
ATOMIC_STORE(&Source->current_buffer, BufferList);
discontinuity = AL_TRUE;
}
else
{
Source->state = AL_PLAYING;
discontinuity = AL_FALSE;
}
// Check if an Offset has been set
if(Source->Offset >= 0.0)
{
ApplyOffset(Source);
/* discontinuity = AL_TRUE;??? */
}
/* If there's nothing to play, or device is disconnected, go right to
* stopped */
@ -2631,6 +2639,10 @@ ALvoid SetSourceState(ALsource *Source, ALCcontext *Context, ALenum state)
voice->Source = Source;
}
/* Clear previous samples if playback is discontinuous. */
if(discontinuity)
memset(voice->PrevSamples, 0, sizeof(voice->PrevSamples));
voice->Direct.Moving = AL_FALSE;
voice->Direct.Counter = 0;
for(i = 0;i < MAX_INPUT_CHANNELS;i++)