Remove the SAFE_CONST macro
[openal-soft.git] / Alc / mixer.c
blob76e5f759ad19df4275279794aa2400240e57d32d
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <math.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <assert.h>
29 #include "alMain.h"
30 #include "AL/al.h"
31 #include "AL/alc.h"
32 #include "alSource.h"
33 #include "alBuffer.h"
34 #include "alListener.h"
35 #include "alAuxEffectSlot.h"
36 #include "alu.h"
37 #include "alconfig.h"
39 #include "cpu_caps.h"
40 #include "mixer_defs.h"
43 static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
44 "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
46 extern inline void InitiatePositionArrays(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size);
49 /* BSinc24 requires up to 23 extra samples before the current position, and 24 after. */
50 static_assert(MAX_RESAMPLE_PADDING >= 24, "MAX_RESAMPLE_PADDING must be at least 24!");
53 enum Resampler ResamplerDefault = LinearResampler;
55 MixerFunc MixSamples = Mix_C;
56 static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
57 static HrtfMixerBlendFunc MixHrtfBlendSamples = MixHrtfBlend_C;
59 static MixerFunc SelectMixer(void)
61 #ifdef HAVE_NEON
62 if((CPUCapFlags&CPU_CAP_NEON))
63 return Mix_Neon;
64 #endif
65 #ifdef HAVE_SSE
66 if((CPUCapFlags&CPU_CAP_SSE))
67 return Mix_SSE;
68 #endif
69 return Mix_C;
72 RowMixerFunc SelectRowMixer(void)
74 #ifdef HAVE_NEON
75 if((CPUCapFlags&CPU_CAP_NEON))
76 return MixRow_Neon;
77 #endif
78 #ifdef HAVE_SSE
79 if((CPUCapFlags&CPU_CAP_SSE))
80 return MixRow_SSE;
81 #endif
82 return MixRow_C;
85 static inline HrtfMixerFunc SelectHrtfMixer(void)
87 #ifdef HAVE_NEON
88 if((CPUCapFlags&CPU_CAP_NEON))
89 return MixHrtf_Neon;
90 #endif
91 #ifdef HAVE_SSE
92 if((CPUCapFlags&CPU_CAP_SSE))
93 return MixHrtf_SSE;
94 #endif
95 return MixHrtf_C;
98 static inline HrtfMixerBlendFunc SelectHrtfBlendMixer(void)
100 #ifdef HAVE_NEON
101 if((CPUCapFlags&CPU_CAP_NEON))
102 return MixHrtfBlend_Neon;
103 #endif
104 #ifdef HAVE_SSE
105 if((CPUCapFlags&CPU_CAP_SSE))
106 return MixHrtfBlend_SSE;
107 #endif
108 return MixHrtfBlend_C;
111 ResamplerFunc SelectResampler(enum Resampler resampler)
113 switch(resampler)
115 case PointResampler:
116 return Resample_point_C;
117 case LinearResampler:
118 #ifdef HAVE_NEON
119 if((CPUCapFlags&CPU_CAP_NEON))
120 return Resample_lerp_Neon;
121 #endif
122 #ifdef HAVE_SSE4_1
123 if((CPUCapFlags&CPU_CAP_SSE4_1))
124 return Resample_lerp_SSE41;
125 #endif
126 #ifdef HAVE_SSE2
127 if((CPUCapFlags&CPU_CAP_SSE2))
128 return Resample_lerp_SSE2;
129 #endif
130 return Resample_lerp_C;
131 case FIR4Resampler:
132 return Resample_cubic_C;
133 case BSinc12Resampler:
134 case BSinc24Resampler:
135 #ifdef HAVE_NEON
136 if((CPUCapFlags&CPU_CAP_NEON))
137 return Resample_bsinc_Neon;
138 #endif
139 #ifdef HAVE_SSE
140 if((CPUCapFlags&CPU_CAP_SSE))
141 return Resample_bsinc_SSE;
142 #endif
143 return Resample_bsinc_C;
146 return Resample_point_C;
150 void aluInitMixer(void)
152 const char *str;
154 if(ConfigValueStr(NULL, NULL, "resampler", &str))
156 if(strcasecmp(str, "point") == 0 || strcasecmp(str, "none") == 0)
157 ResamplerDefault = PointResampler;
158 else if(strcasecmp(str, "linear") == 0)
159 ResamplerDefault = LinearResampler;
160 else if(strcasecmp(str, "cubic") == 0)
161 ResamplerDefault = FIR4Resampler;
162 else if(strcasecmp(str, "bsinc12") == 0)
163 ResamplerDefault = BSinc12Resampler;
164 else if(strcasecmp(str, "bsinc24") == 0)
165 ResamplerDefault = BSinc24Resampler;
166 else if(strcasecmp(str, "bsinc") == 0)
168 WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", str);
169 ResamplerDefault = BSinc12Resampler;
171 else if(strcasecmp(str, "sinc4") == 0 || strcasecmp(str, "sinc8") == 0)
173 WARN("Resampler option \"%s\" is deprecated, using cubic\n", str);
174 ResamplerDefault = FIR4Resampler;
176 else
178 char *end;
179 long n = strtol(str, &end, 0);
180 if(*end == '\0' && (n == PointResampler || n == LinearResampler || n == FIR4Resampler))
181 ResamplerDefault = n;
182 else
183 WARN("Invalid resampler: %s\n", str);
187 MixHrtfBlendSamples = SelectHrtfBlendMixer();
188 MixHrtfSamples = SelectHrtfMixer();
189 MixSamples = SelectMixer();
193 static inline ALfloat Sample_ALbyte(ALbyte val)
194 { return val * (1.0f/128.0f); }
196 static inline ALfloat Sample_ALshort(ALshort val)
197 { return val * (1.0f/32768.0f); }
199 static inline ALfloat Sample_ALfloat(ALfloat val)
200 { return val; }
202 #define DECL_TEMPLATE(T) \
203 static inline void Load_##T(ALfloat *restrict dst, const T *restrict src, \
204 ALint srcstep, ALsizei samples) \
206 ALsizei i; \
207 for(i = 0;i < samples;i++) \
208 dst[i] += Sample_##T(src[i*srcstep]); \
211 DECL_TEMPLATE(ALbyte)
212 DECL_TEMPLATE(ALshort)
213 DECL_TEMPLATE(ALfloat)
215 #undef DECL_TEMPLATE
217 static void LoadSamples(ALfloat *restrict dst, const ALvoid *restrict src, ALint srcstep,
218 enum FmtType srctype, ALsizei samples)
220 switch(srctype)
222 case FmtByte:
223 Load_ALbyte(dst, src, srcstep, samples);
224 break;
225 case FmtShort:
226 Load_ALshort(dst, src, srcstep, samples);
227 break;
228 case FmtFloat:
229 Load_ALfloat(dst, src, srcstep, samples);
230 break;
235 static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter,
236 ALfloat *restrict dst, const ALfloat *restrict src,
237 ALsizei numsamples, enum ActiveFilters type)
239 ALsizei i;
240 switch(type)
242 case AF_None:
243 ALfilterState_processPassthru(lpfilter, src, numsamples);
244 ALfilterState_processPassthru(hpfilter, src, numsamples);
245 break;
247 case AF_LowPass:
248 ALfilterState_process(lpfilter, dst, src, numsamples);
249 ALfilterState_processPassthru(hpfilter, dst, numsamples);
250 return dst;
251 case AF_HighPass:
252 ALfilterState_processPassthru(lpfilter, src, numsamples);
253 ALfilterState_process(hpfilter, dst, src, numsamples);
254 return dst;
256 case AF_BandPass:
257 for(i = 0;i < numsamples;)
259 ALfloat temp[256];
260 ALsizei todo = mini(256, numsamples-i);
262 ALfilterState_process(lpfilter, temp, src+i, todo);
263 ALfilterState_process(hpfilter, dst+i, temp, todo);
264 i += todo;
266 return dst;
268 return src;
272 /* This function uses these device temp buffers. */
273 #define SOURCE_DATA_BUF 0
274 #define RESAMPLED_BUF 1
275 #define FILTERED_BUF 2
276 #define NFC_DATA_BUF 3
277 ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo)
279 ALbufferlistitem *BufferListItem;
280 ALbufferlistitem *BufferLoopItem;
281 ALsizei NumChannels, SampleSize;
282 ResamplerFunc Resample;
283 ALsizei DataPosInt;
284 ALsizei DataPosFrac;
285 ALint64 DataSize64;
286 ALint increment;
287 ALsizei Counter;
288 ALsizei OutPos;
289 ALsizei IrSize;
290 bool isplaying;
291 bool firstpass;
292 bool isstatic;
293 ALsizei chan;
294 ALsizei send;
296 /* Get source info */
297 isplaying = true; /* Will only be called while playing. */
298 isstatic = Source->SourceType == AL_STATIC;
299 DataPosInt = ATOMIC_LOAD(&voice->position, almemory_order_acquire);
300 DataPosFrac = ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed);
301 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
302 BufferLoopItem = ATOMIC_LOAD(&voice->loop_buffer, almemory_order_relaxed);
303 NumChannels = voice->NumChannels;
304 SampleSize = voice->SampleSize;
305 increment = voice->Step;
307 IrSize = (Device->HrtfHandle ? Device->HrtfHandle->irSize : 0);
309 Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ?
310 Resample_copy_C : voice->Resampler);
312 Counter = (voice->Flags&VOICE_IS_FADING) ? SamplesToDo : 0;
313 firstpass = true;
314 OutPos = 0;
316 do {
317 ALsizei SrcBufferSize, DstBufferSize;
319 /* Figure out how many buffer samples will be needed */
320 DataSize64 = SamplesToDo-OutPos;
321 DataSize64 *= increment;
322 DataSize64 += DataPosFrac+FRACTIONMASK;
323 DataSize64 >>= FRACTIONBITS;
324 DataSize64 += MAX_RESAMPLE_PADDING*2;
325 SrcBufferSize = (ALsizei)mini64(DataSize64, BUFFERSIZE);
327 /* Figure out how many samples we can actually mix from this. */
328 DataSize64 = SrcBufferSize;
329 DataSize64 -= MAX_RESAMPLE_PADDING*2;
330 DataSize64 <<= FRACTIONBITS;
331 DataSize64 -= DataPosFrac;
332 DstBufferSize = (ALsizei)mini64((DataSize64+(increment-1)) / increment,
333 SamplesToDo - OutPos);
335 /* Some mixers like having a multiple of 4, so try to give that unless
336 * this is the last update. */
337 if(DstBufferSize < SamplesToDo-OutPos)
338 DstBufferSize &= ~3;
340 /* It's impossible to have a buffer list item with no entries. */
341 assert(BufferListItem->num_buffers > 0);
343 for(chan = 0;chan < NumChannels;chan++)
345 const ALfloat *ResampledData;
346 ALfloat *SrcData = Device->TempBuffer[SOURCE_DATA_BUF];
347 ALsizei FilledAmt;
349 /* Load the previous samples into the source data first, and clear the rest. */
350 memcpy(SrcData, voice->PrevSamples[chan], MAX_RESAMPLE_PADDING*sizeof(ALfloat));
351 memset(SrcData+MAX_RESAMPLE_PADDING, 0, (BUFFERSIZE-MAX_RESAMPLE_PADDING)*
352 sizeof(ALfloat));
353 FilledAmt = MAX_RESAMPLE_PADDING;
355 if(isstatic)
357 /* TODO: For static sources, loop points are taken from the
358 * first buffer (should be adjusted by any buffer offset, to
359 * possibly be added later).
361 const ALbuffer *Buffer0 = BufferListItem->buffers[0];
362 const ALsizei LoopStart = Buffer0->LoopStart;
363 const ALsizei LoopEnd = Buffer0->LoopEnd;
364 const ALsizei LoopSize = LoopEnd - LoopStart;
366 /* If current pos is beyond the loop range, do not loop */
367 if(!BufferLoopItem || DataPosInt >= LoopEnd)
369 ALsizei SizeToDo = SrcBufferSize - FilledAmt;
370 ALsizei CompLen = 0;
371 ALsizei i;
373 BufferLoopItem = NULL;
375 for(i = 0;i < BufferListItem->num_buffers;i++)
377 const ALbuffer *buffer = BufferListItem->buffers[i];
378 const ALubyte *Data = buffer->data;
379 ALsizei DataSize;
381 if(DataPosInt >= buffer->SampleLen)
382 continue;
384 /* Load what's left to play from the buffer */
385 DataSize = mini(SizeToDo, buffer->SampleLen - DataPosInt);
386 CompLen = maxi(CompLen, DataSize);
388 LoadSamples(&SrcData[FilledAmt],
389 &Data[(DataPosInt*NumChannels + chan)*SampleSize],
390 NumChannels, buffer->FmtType, DataSize
393 FilledAmt += CompLen;
395 else
397 ALsizei SizeToDo = mini(SrcBufferSize - FilledAmt, LoopEnd - DataPosInt);
398 ALsizei CompLen = 0;
399 ALsizei i;
401 for(i = 0;i < BufferListItem->num_buffers;i++)
403 const ALbuffer *buffer = BufferListItem->buffers[i];
404 const ALubyte *Data = buffer->data;
405 ALsizei DataSize;
407 if(DataPosInt >= buffer->SampleLen)
408 continue;
410 /* Load what's left of this loop iteration */
411 DataSize = mini(SizeToDo, buffer->SampleLen - DataPosInt);
412 CompLen = maxi(CompLen, DataSize);
414 LoadSamples(&SrcData[FilledAmt],
415 &Data[(DataPosInt*NumChannels + chan)*SampleSize],
416 NumChannels, buffer->FmtType, DataSize
419 FilledAmt += CompLen;
421 while(SrcBufferSize > FilledAmt)
423 const ALsizei SizeToDo = mini(SrcBufferSize - FilledAmt, LoopSize);
425 CompLen = 0;
426 for(i = 0;i < BufferListItem->num_buffers;i++)
428 const ALbuffer *buffer = BufferListItem->buffers[i];
429 const ALubyte *Data = buffer->data;
430 ALsizei DataSize;
432 if(LoopStart >= buffer->SampleLen)
433 continue;
435 DataSize = mini(SizeToDo, buffer->SampleLen - LoopStart);
436 CompLen = maxi(CompLen, DataSize);
438 LoadSamples(&SrcData[FilledAmt],
439 &Data[(LoopStart*NumChannels + chan)*SampleSize],
440 NumChannels, buffer->FmtType, DataSize
443 FilledAmt += CompLen;
447 else
449 /* Crawl the buffer queue to fill in the temp buffer */
450 ALbufferlistitem *tmpiter = BufferListItem;
451 ALsizei pos = DataPosInt;
453 while(tmpiter && SrcBufferSize > FilledAmt)
455 ALsizei SizeToDo = SrcBufferSize - FilledAmt;
456 ALsizei CompLen = 0;
457 ALsizei i;
459 for(i = 0;i < tmpiter->num_buffers;i++)
461 const ALbuffer *ALBuffer = tmpiter->buffers[i];
462 ALsizei DataSize = ALBuffer ? ALBuffer->SampleLen : 0;
463 CompLen = maxi(CompLen, DataSize);
465 if(DataSize > pos)
467 const ALubyte *Data = ALBuffer->data;
468 Data += (pos*NumChannels + chan)*SampleSize;
470 DataSize = minu(SizeToDo, DataSize - pos);
471 LoadSamples(&SrcData[FilledAmt], Data, NumChannels,
472 ALBuffer->FmtType, DataSize);
475 if(pos > CompLen)
476 pos -= CompLen;
477 else
479 FilledAmt += CompLen - pos;
480 pos = 0;
482 if(SrcBufferSize > FilledAmt)
484 tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
485 if(!tmpiter) tmpiter = BufferLoopItem;
490 /* Store the last source samples used for next time. */
491 memcpy(voice->PrevSamples[chan],
492 &SrcData[(increment*DstBufferSize + DataPosFrac)>>FRACTIONBITS],
493 MAX_RESAMPLE_PADDING*sizeof(ALfloat)
496 /* Now resample, then filter and mix to the appropriate outputs. */
497 ResampledData = Resample(&voice->ResampleState,
498 &SrcData[MAX_RESAMPLE_PADDING], DataPosFrac, increment,
499 Device->TempBuffer[RESAMPLED_BUF], DstBufferSize
502 DirectParams *parms = &voice->Direct.Params[chan];
503 const ALfloat *samples;
505 samples = DoFilters(
506 &parms->LowPass, &parms->HighPass, Device->TempBuffer[FILTERED_BUF],
507 ResampledData, DstBufferSize, voice->Direct.FilterType
509 if(!(voice->Flags&VOICE_HAS_HRTF))
511 if(!Counter)
512 memcpy(parms->Gains.Current, parms->Gains.Target,
513 sizeof(parms->Gains.Current));
514 if(!(voice->Flags&VOICE_HAS_NFC))
515 MixSamples(samples, voice->Direct.Channels, voice->Direct.Buffer,
516 parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
517 DstBufferSize
519 else
521 ALfloat *nfcsamples = Device->TempBuffer[NFC_DATA_BUF];
522 ALsizei chanoffset = 0;
524 MixSamples(samples,
525 voice->Direct.ChannelsPerOrder[0], voice->Direct.Buffer,
526 parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
527 DstBufferSize
529 chanoffset += voice->Direct.ChannelsPerOrder[0];
530 #define APPLY_NFC_MIX(order) \
531 if(voice->Direct.ChannelsPerOrder[order] > 0) \
533 NfcFilterUpdate##order(&parms->NFCtrlFilter[order-1], nfcsamples, \
534 samples, DstBufferSize); \
535 MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[order], \
536 voice->Direct.Buffer+chanoffset, parms->Gains.Current+chanoffset, \
537 parms->Gains.Target+chanoffset, Counter, OutPos, DstBufferSize \
538 ); \
539 chanoffset += voice->Direct.ChannelsPerOrder[order]; \
541 APPLY_NFC_MIX(1)
542 APPLY_NFC_MIX(2)
543 APPLY_NFC_MIX(3)
544 #undef APPLY_NFC_MIX
547 else
549 MixHrtfParams hrtfparams;
550 ALsizei fademix = 0;
551 int lidx, ridx;
553 lidx = GetChannelIdxByName(&Device->RealOut, FrontLeft);
554 ridx = GetChannelIdxByName(&Device->RealOut, FrontRight);
555 assert(lidx != -1 && ridx != -1);
557 if(!Counter)
559 /* No fading, just overwrite the old HRTF params. */
560 parms->Hrtf.Old = parms->Hrtf.Target;
562 else if(!(parms->Hrtf.Old.Gain > GAIN_SILENCE_THRESHOLD))
564 /* The old HRTF params are silent, so overwrite the old
565 * coefficients with the new, and reset the old gain to
566 * 0. The future mix will then fade from silence.
568 parms->Hrtf.Old = parms->Hrtf.Target;
569 parms->Hrtf.Old.Gain = 0.0f;
571 else if(firstpass)
573 ALfloat gain;
575 /* Fade between the coefficients over 128 samples. */
576 fademix = mini(DstBufferSize, 128);
578 /* The new coefficients need to fade in completely
579 * since they're replacing the old ones. To keep the
580 * gain fading consistent, interpolate between the old
581 * and new target gains given how much of the fade time
582 * this mix handles.
584 gain = lerp(parms->Hrtf.Old.Gain, parms->Hrtf.Target.Gain,
585 minf(1.0f, (ALfloat)fademix/Counter));
586 hrtfparams.Coeffs = parms->Hrtf.Target.Coeffs;
587 hrtfparams.Delay[0] = parms->Hrtf.Target.Delay[0];
588 hrtfparams.Delay[1] = parms->Hrtf.Target.Delay[1];
589 hrtfparams.Gain = 0.0f;
590 hrtfparams.GainStep = gain / (ALfloat)fademix;
592 MixHrtfBlendSamples(
593 voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx],
594 samples, voice->Offset, OutPos, IrSize, &parms->Hrtf.Old,
595 &hrtfparams, &parms->Hrtf.State, fademix
597 /* Update the old parameters with the result. */
598 parms->Hrtf.Old = parms->Hrtf.Target;
599 if(fademix < Counter)
600 parms->Hrtf.Old.Gain = hrtfparams.Gain;
603 if(fademix < DstBufferSize)
605 ALsizei todo = DstBufferSize - fademix;
606 ALfloat gain = parms->Hrtf.Target.Gain;
608 /* Interpolate the target gain if the gain fading lasts
609 * longer than this mix.
611 if(Counter > DstBufferSize)
612 gain = lerp(parms->Hrtf.Old.Gain, gain,
613 (ALfloat)todo/(Counter-fademix));
615 hrtfparams.Coeffs = parms->Hrtf.Target.Coeffs;
616 hrtfparams.Delay[0] = parms->Hrtf.Target.Delay[0];
617 hrtfparams.Delay[1] = parms->Hrtf.Target.Delay[1];
618 hrtfparams.Gain = parms->Hrtf.Old.Gain;
619 hrtfparams.GainStep = (gain - parms->Hrtf.Old.Gain) / (ALfloat)todo;
620 MixHrtfSamples(
621 voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx],
622 samples+fademix, voice->Offset+fademix, OutPos+fademix, IrSize,
623 &hrtfparams, &parms->Hrtf.State, todo
625 /* Store the interpolated gain or the final target gain
626 * depending if the fade is done.
628 if(DstBufferSize < Counter)
629 parms->Hrtf.Old.Gain = gain;
630 else
631 parms->Hrtf.Old.Gain = parms->Hrtf.Target.Gain;
636 for(send = 0;send < Device->NumAuxSends;send++)
638 SendParams *parms = &voice->Send[send].Params[chan];
639 const ALfloat *samples;
641 if(!voice->Send[send].Buffer)
642 continue;
644 samples = DoFilters(
645 &parms->LowPass, &parms->HighPass, Device->TempBuffer[FILTERED_BUF],
646 ResampledData, DstBufferSize, voice->Send[send].FilterType
649 if(!Counter)
650 memcpy(parms->Gains.Current, parms->Gains.Target,
651 sizeof(parms->Gains.Current));
652 MixSamples(samples, voice->Send[send].Channels, voice->Send[send].Buffer,
653 parms->Gains.Current, parms->Gains.Target, Counter, OutPos, DstBufferSize
657 /* Update positions */
658 DataPosFrac += increment*DstBufferSize;
659 DataPosInt += DataPosFrac>>FRACTIONBITS;
660 DataPosFrac &= FRACTIONMASK;
662 OutPos += DstBufferSize;
663 voice->Offset += DstBufferSize;
664 Counter = maxi(DstBufferSize, Counter) - DstBufferSize;
665 firstpass = false;
667 /* Handle looping source position */
668 if(isstatic && BufferLoopItem)
670 const ALbuffer *Buffer = BufferListItem->buffers[0];
671 ALsizei LoopStart = Buffer->LoopStart;
672 ALsizei LoopEnd = Buffer->LoopEnd;
673 if(DataPosInt >= LoopEnd)
675 assert(LoopEnd > LoopStart);
676 DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
679 else while(1)
681 /* Handle non-looping or buffer queue source position */
682 ALsizei CompLen = 0;
683 ALsizei i;
685 for(i = 0;i < BufferListItem->num_buffers;i++)
687 const ALbuffer *buffer = BufferListItem->buffers[i];
688 if(buffer) CompLen = maxi(CompLen, buffer->SampleLen);
691 if(CompLen > DataPosInt)
692 break;
694 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
695 if(!BufferListItem)
697 BufferListItem = BufferLoopItem;
698 if(!BufferListItem)
700 isplaying = false;
701 DataPosInt = 0;
702 DataPosFrac = 0;
703 break;
707 DataPosInt -= CompLen;
709 } while(isplaying && OutPos < SamplesToDo);
711 voice->Flags |= VOICE_IS_FADING;
713 /* Update source info */
714 ATOMIC_STORE(&voice->position, DataPosInt, almemory_order_relaxed);
715 ATOMIC_STORE(&voice->position_fraction, DataPosFrac, almemory_order_relaxed);
716 ATOMIC_STORE(&voice->current_buffer, BufferListItem, almemory_order_release);
717 return isplaying;