AuroraOpenALSoft/Alc/mixer.c

640 lines
23 KiB
C
Raw Normal View History

/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
#include "alSource.h"
#include "alBuffer.h"
#include "alListener.h"
#include "alAuxEffectSlot.h"
#include "alu.h"
#include "mixer_defs.h"
static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
"MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size);
/* BSinc requires up to 11 extra samples before the current position, and 12 after. */
static_assert(MAX_PRE_SAMPLES >= 11, "MAX_PRE_SAMPLES must be at least 11!");
static_assert(MAX_POST_SAMPLES >= 12, "MAX_POST_SAMPLES must be at least 12!");
2017-04-21 06:21:46 +00:00
enum Resampler ResamplerDefault = LinearResampler;
2015-09-28 03:55:39 +00:00
static MixerFunc MixSamples = Mix_C;
2016-09-06 20:21:11 +00:00
static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
2015-09-28 03:55:39 +00:00
static ResamplerFunc ResampleSamples = Resample_point32_C;
2016-09-06 20:21:11 +00:00
MixerFunc SelectMixer(void)
{
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
2016-09-06 20:21:11 +00:00
return Mix_Neon;
#endif
2017-04-21 03:58:32 +00:00
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return Mix_SSE;
#endif
2016-09-06 20:21:11 +00:00
return Mix_C;
}
RowMixerFunc SelectRowMixer(void)
{
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return MixRow_Neon;
2017-04-21 03:58:32 +00:00
#endif
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return MixRow_SSE;
#endif
return MixRow_C;
}
2016-09-06 20:21:11 +00:00
static inline HrtfMixerFunc SelectHrtfMixer(void)
{
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
2016-09-06 20:21:11 +00:00
return MixHrtf_Neon;
#endif
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return MixHrtf_SSE;
#endif
2016-09-06 20:21:11 +00:00
return MixHrtf_C;
}
ResamplerFunc SelectResampler(enum Resampler resampler)
{
switch(resampler)
{
case PointResampler:
return Resample_point32_C;
case LinearResampler:
2017-02-13 05:03:30 +00:00
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return Resample_lerp32_Neon;
#endif
#ifdef HAVE_SSE4_1
if((CPUCapFlags&CPU_CAP_SSE4_1))
return Resample_lerp32_SSE41;
#endif
#ifdef HAVE_SSE2
if((CPUCapFlags&CPU_CAP_SSE2))
return Resample_lerp32_SSE2;
#endif
return Resample_lerp32_C;
case FIR4Resampler:
2017-02-13 05:03:30 +00:00
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return Resample_fir4_32_Neon;
#endif
2014-12-15 21:58:41 +00:00
#ifdef HAVE_SSE4_1
if((CPUCapFlags&CPU_CAP_SSE4_1))
return Resample_fir4_32_SSE41;
2014-12-15 21:58:41 +00:00
#endif
#ifdef HAVE_SSE3
if((CPUCapFlags&CPU_CAP_SSE3))
return Resample_fir4_32_SSE3;
2014-12-15 21:58:41 +00:00
#endif
return Resample_fir4_32_C;
case BSincResampler:
2017-02-13 05:03:30 +00:00
#ifdef HAVE_NEON
if((CPUCapFlags&CPU_CAP_NEON))
return Resample_bsinc32_Neon;
#endif
#ifdef HAVE_SSE
if((CPUCapFlags&CPU_CAP_SSE))
return Resample_bsinc32_SSE;
#endif
return Resample_bsinc32_C;
}
return Resample_point32_C;
}
2015-09-28 03:55:39 +00:00
void aluInitMixer(void)
{
const char *str;
if(ConfigValueStr(NULL, NULL, "resampler", &str))
{
if(strcasecmp(str, "point") == 0 || strcasecmp(str, "none") == 0)
2017-04-21 06:21:46 +00:00
ResamplerDefault = PointResampler;
else if(strcasecmp(str, "linear") == 0)
2017-04-21 06:21:46 +00:00
ResamplerDefault = LinearResampler;
else if(strcasecmp(str, "sinc4") == 0)
2017-04-21 06:21:46 +00:00
ResamplerDefault = FIR4Resampler;
else if(strcasecmp(str, "bsinc") == 0)
2017-04-21 06:21:46 +00:00
ResamplerDefault = BSincResampler;
else if(strcasecmp(str, "cubic") == 0 || strcasecmp(str, "sinc8") == 0)
{
WARN("Resampler option \"%s\" is deprecated, using sinc4\n", str);
2017-04-21 06:21:46 +00:00
ResamplerDefault = FIR4Resampler;
}
else
{
char *end;
long n = strtol(str, &end, 0);
if(*end == '\0' && (n == PointResampler || n == LinearResampler || n == FIR4Resampler))
2017-04-21 06:21:46 +00:00
ResamplerDefault = n;
else
WARN("Invalid resampler: %s\n", str);
}
}
2015-09-28 03:55:39 +00:00
MixHrtfSamples = SelectHrtfMixer();
MixSamples = SelectMixer();
2017-04-21 06:21:46 +00:00
ResampleSamples = SelectResampler(ResamplerDefault);
2015-09-28 03:55:39 +00:00
}
2013-05-29 05:27:07 +00:00
static inline ALfloat Sample_ALbyte(ALbyte val)
{ return val * (1.0f/128.0f); }
2013-05-29 05:27:07 +00:00
static inline ALfloat Sample_ALshort(ALshort val)
{ return val * (1.0f/32768.0f); }
2013-05-29 05:27:07 +00:00
static inline ALfloat Sample_ALfloat(ALfloat val)
{ return val; }
#define DECL_TEMPLATE(T) \
2017-01-18 15:13:23 +00:00
static inline void Load_##T(ALfloat *dst, const T *src, ALint srcstep, ALsizei samples)\
{ \
2017-01-18 15:13:23 +00:00
ALsizei i; \
for(i = 0;i < samples;i++) \
dst[i] = Sample_##T(src[i*srcstep]); \
}
DECL_TEMPLATE(ALbyte)
DECL_TEMPLATE(ALshort)
DECL_TEMPLATE(ALfloat)
#undef DECL_TEMPLATE
2017-01-18 15:13:23 +00:00
static void LoadSamples(ALfloat *dst, const ALvoid *src, ALint srcstep, enum FmtType srctype, ALsizei samples)
{
switch(srctype)
{
case FmtByte:
Load_ALbyte(dst, src, srcstep, samples);
break;
case FmtShort:
Load_ALshort(dst, src, srcstep, samples);
break;
case FmtFloat:
Load_ALfloat(dst, src, srcstep, samples);
break;
}
}
2017-01-18 15:13:23 +00:00
static inline void SilenceSamples(ALfloat *dst, ALsizei samples)
{
2017-01-18 15:13:23 +00:00
ALsizei i;
for(i = 0;i < samples;i++)
dst[i] = 0.0f;
}
static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter,
ALfloat *restrict dst, const ALfloat *restrict src,
2017-01-18 15:13:23 +00:00
ALsizei numsamples, enum ActiveFilters type)
{
2017-01-18 15:13:23 +00:00
ALsizei i;
switch(type)
{
case AF_None:
ALfilterState_processPassthru(lpfilter, src, numsamples);
ALfilterState_processPassthru(hpfilter, src, numsamples);
break;
case AF_LowPass:
ALfilterState_process(lpfilter, dst, src, numsamples);
ALfilterState_processPassthru(hpfilter, dst, numsamples);
return dst;
case AF_HighPass:
ALfilterState_processPassthru(lpfilter, src, numsamples);
ALfilterState_process(hpfilter, dst, src, numsamples);
return dst;
case AF_BandPass:
for(i = 0;i < numsamples;)
{
ALfloat temp[256];
2017-01-18 15:13:23 +00:00
ALsizei todo = mini(256, numsamples-i);
ALfilterState_process(lpfilter, temp, src+i, todo);
ALfilterState_process(hpfilter, dst+i, temp, todo);
i += todo;
}
return dst;
}
return src;
}
ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo)
{
ALbufferlistitem *BufferListItem;
ALbufferlistitem *BufferLoopItem;
2017-03-21 02:22:41 +00:00
ALsizei NumChannels, SampleSize;
ResamplerFunc Resample;
ALsizei DataPosInt;
ALsizei DataPosFrac;
ALint64 DataSize64;
2017-03-21 02:22:41 +00:00
ALint increment;
2017-01-18 15:13:23 +00:00
ALsizei Counter;
2017-03-21 02:22:41 +00:00
ALsizei OutPos;
2017-01-18 15:13:23 +00:00
ALsizei IrSize;
2017-03-21 02:22:41 +00:00
bool isplaying;
ALsizei chan;
ALsizei send;
/* Get source info */
2017-03-21 02:22:41 +00:00
isplaying = true; /* Will only be called while playing. */
DataPosInt = ATOMIC_LOAD(&voice->position, almemory_order_acquire);
DataPosFrac = ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed);
BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
BufferLoopItem = ATOMIC_LOAD(&voice->loop_buffer, almemory_order_relaxed);
NumChannels = voice->NumChannels;
SampleSize = voice->SampleSize;
increment = voice->Step;
IrSize = (Device->HrtfHandle ? Device->HrtfHandle->irSize : 0);
2014-11-29 11:32:25 +00:00
Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ?
2015-09-28 03:55:39 +00:00
Resample_copy32_C : ResampleSamples);
Counter = (voice->Flags&VOICE_IS_MOVING) ? SamplesToDo : 0;
2010-11-26 20:59:45 +00:00
OutPos = 0;
do {
2017-01-18 15:13:23 +00:00
ALsizei SrcBufferSize, DstBufferSize;
2013-10-26 15:49:37 +00:00
/* Figure out how many buffer samples will be needed */
DataSize64 = SamplesToDo-OutPos;
DataSize64 *= increment;
DataSize64 += DataPosFrac+FRACTIONMASK;
DataSize64 >>= FRACTIONBITS;
DataSize64 += MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
2017-01-18 15:13:23 +00:00
SrcBufferSize = (ALsizei)mini64(DataSize64, BUFFERSIZE);
/* Figure out how many samples we can actually mix from this. */
DataSize64 = SrcBufferSize;
DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
DataSize64 <<= FRACTIONBITS;
DataSize64 -= DataPosFrac;
2017-01-18 15:13:23 +00:00
DstBufferSize = (ALsizei)((DataSize64+(increment-1)) / increment);
DstBufferSize = mini(DstBufferSize, (SamplesToDo-OutPos));
/* Some mixers like having a multiple of 4, so try to give that unless
* this is the last update. */
if(OutPos+DstBufferSize < SamplesToDo)
DstBufferSize &= ~3;
for(chan = 0;chan < NumChannels;chan++)
{
const ALfloat *ResampledData;
ALfloat *SrcData = Device->SourceData;
2017-01-18 15:13:23 +00:00
ALsizei SrcDataSize;
/* Load the previous samples into the source data first. */
memcpy(SrcData, voice->PrevSamples[chan], MAX_PRE_SAMPLES*sizeof(ALfloat));
SrcDataSize = MAX_PRE_SAMPLES;
if(Source->SourceType == AL_STATIC)
{
const ALbuffer *ALBuffer = BufferListItem->buffer;
const ALubyte *Data = ALBuffer->data;
2017-01-18 15:13:23 +00:00
ALsizei DataSize;
/* Offset buffer data to current channel */
Data += chan*SampleSize;
/* If current pos is beyond the loop range, do not loop */
if(!BufferLoopItem || DataPosInt >= ALBuffer->LoopEnd)
{
BufferLoopItem = NULL;
/* Load what's left to play from the source buffer, and
* clear the rest of the temp buffer */
DataSize = minu(SrcBufferSize - SrcDataSize,
ALBuffer->SampleLen - DataPosInt);
LoadSamples(&SrcData[SrcDataSize], &Data[DataPosInt * NumChannels*SampleSize],
NumChannels, ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
SrcDataSize += SrcBufferSize - SrcDataSize;
}
else
{
2017-01-18 15:13:23 +00:00
ALsizei LoopStart = ALBuffer->LoopStart;
ALsizei LoopEnd = ALBuffer->LoopEnd;
/* Load what's left of this loop iteration, then load
* repeats of the loop section */
2017-01-18 15:13:23 +00:00
DataSize = minu(SrcBufferSize - SrcDataSize, LoopEnd - DataPosInt);
LoadSamples(&SrcData[SrcDataSize], &Data[DataPosInt * NumChannels*SampleSize],
NumChannels, ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
DataSize = LoopEnd-LoopStart;
while(SrcBufferSize > SrcDataSize)
{
2017-01-18 15:13:23 +00:00
DataSize = mini(SrcBufferSize - SrcDataSize, DataSize);
LoadSamples(&SrcData[SrcDataSize], &Data[LoopStart * NumChannels*SampleSize],
NumChannels, ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
}
}
}
else
{
/* Crawl the buffer queue to fill in the temp buffer */
ALbufferlistitem *tmpiter = BufferListItem;
2017-03-21 02:22:41 +00:00
ALsizei pos = DataPosInt;
while(tmpiter && SrcBufferSize > SrcDataSize)
{
const ALbuffer *ALBuffer;
if((ALBuffer=tmpiter->buffer) != NULL)
{
const ALubyte *Data = ALBuffer->data;
2017-03-21 02:22:41 +00:00
ALsizei DataSize = ALBuffer->SampleLen;
/* Skip the data already played */
if(DataSize <= pos)
pos -= DataSize;
else
{
Data += (pos*NumChannels + chan)*SampleSize;
DataSize -= pos;
pos -= pos;
DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
LoadSamples(&SrcData[SrcDataSize], Data, NumChannels,
ALBuffer->FmtType, DataSize);
SrcDataSize += DataSize;
}
}
tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
if(!tmpiter && BufferLoopItem)
tmpiter = BufferLoopItem;
else if(!tmpiter)
{
SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
SrcDataSize += SrcBufferSize - SrcDataSize;
}
}
}
/* Store the last source samples used for next time. */
memcpy(voice->PrevSamples[chan],
&SrcData[(increment*DstBufferSize + DataPosFrac)>>FRACTIONBITS],
MAX_PRE_SAMPLES*sizeof(ALfloat)
);
/* Now resample, then filter and mix to the appropriate outputs. */
2017-02-13 19:29:32 +00:00
ResampledData = Resample(&voice->ResampleState,
&SrcData[MAX_PRE_SAMPLES], DataPosFrac, increment,
Device->ResampledData, DstBufferSize
);
{
DirectParams *parms = &voice->Direct.Params[chan];
const ALfloat *samples;
samples = DoFilters(
&parms->LowPass, &parms->HighPass, Device->FilteredData,
ResampledData, DstBufferSize, parms->FilterType
);
if(!(voice->Flags&VOICE_IS_HRTF))
{
if(!Counter)
memcpy(parms->Gains.Current, parms->Gains.Target,
sizeof(parms->Gains.Current));
if(!(voice->Flags&VOICE_HAS_NFC))
MixSamples(samples, voice->Direct.Channels, voice->Direct.Buffer,
parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
DstBufferSize
);
else
{
2017-03-26 06:48:57 +00:00
static void (*const NfcUpdate[MAX_AMBI_ORDER])(
NfcFilter*,float*,const float*,const int
) = {
NfcFilterUpdate1, NfcFilterUpdate2, NfcFilterUpdate3
};
ALfloat *nfcsamples = Device->NFCtrlData;
2017-03-26 06:48:57 +00:00
ALsizei ord, chanoffset = 0;
MixSamples(samples,
voice->Direct.ChannelsPerOrder[0], voice->Direct.Buffer,
parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
DstBufferSize
);
chanoffset += voice->Direct.ChannelsPerOrder[0];
2017-03-26 06:48:57 +00:00
for(ord = 1;ord < MAX_AMBI_ORDER+1;ord++)
{
2017-03-26 06:48:57 +00:00
if(voice->Direct.ChannelsPerOrder[ord] <= 0)
break;
NfcUpdate[ord-1](&parms->NFCtrlFilter[ord-1], nfcsamples, samples,
DstBufferSize);
2017-03-26 06:48:57 +00:00
MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[ord],
voice->Direct.Buffer+chanoffset, parms->Gains.Current+chanoffset,
parms->Gains.Target+chanoffset, Counter, OutPos, DstBufferSize
);
2017-03-26 06:48:57 +00:00
chanoffset += voice->Direct.ChannelsPerOrder[ord];
}
}
}
else
{
MixHrtfParams hrtfparams;
int lidx, ridx;
lidx = GetChannelIdxByName(Device->RealOut, FrontLeft);
ridx = GetChannelIdxByName(Device->RealOut, FrontRight);
assert(lidx != -1 && ridx != -1);
if(!Counter)
{
parms->Hrtf.Old = parms->Hrtf.Target;
hrtfparams.Coeffs = SAFE_CONST(ALfloat2*,parms->Hrtf.Target.Coeffs);
hrtfparams.Delay[0] = parms->Hrtf.Target.Delay[0];
hrtfparams.Delay[1] = parms->Hrtf.Target.Delay[1];
hrtfparams.Gain = parms->Hrtf.Target.Gain;
hrtfparams.GainStep = 0.0f;
MixHrtfSamples(
voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx],
samples, voice->Offset, OutPos, IrSize, &hrtfparams,
&parms->Hrtf.State, DstBufferSize
);
}
else
{
HrtfState backupstate = parms->Hrtf.State;
ALfloat gain;
/* The old coefficients need to fade to silence
* completely since they'll be replaced after the mix.
* So it needs to fade out over DstBufferSize instead
* of Counter.
*/
hrtfparams.Coeffs = SAFE_CONST(ALfloat2*,parms->Hrtf.Old.Coeffs);
hrtfparams.Delay[0] = parms->Hrtf.Old.Delay[0];
hrtfparams.Delay[1] = parms->Hrtf.Old.Delay[1];
hrtfparams.Gain = parms->Hrtf.Old.Gain;
hrtfparams.GainStep = -hrtfparams.Gain /
(ALfloat)DstBufferSize;
MixHrtfSamples(
voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx],
samples, voice->Offset, OutPos, IrSize, &hrtfparams,
&backupstate, DstBufferSize
);
/* The new coefficients need to fade in completely
* since they're replacing the old ones. To keep the
* source gain fading consistent, interpolate between
* the old and new target gain given how much of the
* fade time this mix handles.
*/
gain = lerp(parms->Hrtf.Old.Gain, parms->Hrtf.Target.Gain,
2017-03-26 09:44:34 +00:00
(ALfloat)DstBufferSize/Counter);
hrtfparams.Coeffs = SAFE_CONST(ALfloat2*,parms->Hrtf.Target.Coeffs);
hrtfparams.Delay[0] = parms->Hrtf.Target.Delay[0];
hrtfparams.Delay[1] = parms->Hrtf.Target.Delay[1];
hrtfparams.Gain = 0.0f;
hrtfparams.GainStep = gain / (ALfloat)DstBufferSize;
MixHrtfSamples(
voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx],
samples, voice->Offset, OutPos, IrSize, &hrtfparams,
&parms->Hrtf.State, DstBufferSize
);
/* Update the old parameters with the result. */
parms->Hrtf.Old = parms->Hrtf.Target;
2017-03-26 09:44:34 +00:00
if(DstBufferSize < Counter)
parms->Hrtf.Old.Gain = hrtfparams.Gain;
}
}
}
2016-07-26 05:20:47 +00:00
for(send = 0;send < Device->NumAuxSends;send++)
{
SendParams *parms = &voice->Send[send].Params[chan];
const ALfloat *samples;
if(!voice->Send[send].Buffer)
continue;
samples = DoFilters(
&parms->LowPass, &parms->HighPass, Device->FilteredData,
ResampledData, DstBufferSize, parms->FilterType
);
if(!Counter)
memcpy(parms->Gains.Current, parms->Gains.Target,
sizeof(parms->Gains.Current));
MixSamples(samples, voice->Send[send].Channels, voice->Send[send].Buffer,
parms->Gains.Current, parms->Gains.Target, Counter, OutPos, DstBufferSize
2016-07-26 05:20:47 +00:00
);
}
}
/* Update positions */
DataPosFrac += increment*DstBufferSize;
DataPosInt += DataPosFrac>>FRACTIONBITS;
DataPosFrac &= FRACTIONMASK;
OutPos += DstBufferSize;
2014-08-21 10:24:48 +00:00
voice->Offset += DstBufferSize;
2017-01-18 15:13:23 +00:00
Counter = maxi(DstBufferSize, Counter) - DstBufferSize;
/* Handle looping sources */
while(1)
{
const ALbuffer *ALBuffer;
2017-01-18 15:13:23 +00:00
ALsizei DataSize = 0;
ALsizei LoopStart = 0;
ALsizei LoopEnd = 0;
if((ALBuffer=BufferListItem->buffer) != NULL)
{
DataSize = ALBuffer->SampleLen;
LoopStart = ALBuffer->LoopStart;
LoopEnd = ALBuffer->LoopEnd;
2017-03-21 02:22:41 +00:00
if(LoopEnd > DataPosInt)
2011-02-06 09:07:37 +00:00
break;
}
if(BufferLoopItem && Source->SourceType == AL_STATIC)
2011-02-06 09:07:37 +00:00
{
assert(LoopEnd > LoopStart);
2011-02-06 09:07:37 +00:00
DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
break;
}
2017-03-21 02:22:41 +00:00
if(DataSize > DataPosInt)
2011-02-06 09:07:37 +00:00
break;
BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
if(!BufferListItem)
{
BufferListItem = BufferLoopItem;
if(!BufferListItem)
{
2017-03-20 23:53:41 +00:00
isplaying = false;
DataPosInt = 0;
DataPosFrac = 0;
break;
}
}
DataPosInt -= DataSize;
}
2017-03-20 23:53:41 +00:00
} while(isplaying && OutPos < SamplesToDo);
voice->Flags |= VOICE_IS_MOVING;
/* Update source info */
ATOMIC_STORE(&voice->position, DataPosInt, almemory_order_relaxed);
ATOMIC_STORE(&voice->position_fraction, DataPosFrac, almemory_order_relaxed);
ATOMIC_STORE(&voice->current_buffer, BufferListItem, almemory_order_release);
2017-03-20 23:53:41 +00:00
return isplaying;
}