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
34 #include "alListener.h"
35 #include "alAuxEffectSlot.h"
38 #include "mixer_defs.h"
41 static_assert((INT_MAX
>>FRACTIONBITS
)/MAX_PITCH
> BUFFERSIZE
,
42 "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
44 extern inline void InitiatePositionArrays(ALuint frac
, ALint increment
, ALuint
*restrict frac_arr
, ALint
*restrict pos_arr
, ALsizei size
);
46 alignas(16) ALfloat ResampleCoeffs_FIR4
[FRACTIONONE
][4];
55 ResamplerDefault
= LinearResampler
58 /* BSinc requires up to 11 extra samples before the current position, and 12 after. */
59 static_assert(MAX_PRE_SAMPLES
>= 11, "MAX_PRE_SAMPLES must be at least 11!");
60 static_assert(MAX_POST_SAMPLES
>= 12, "MAX_POST_SAMPLES must be at least 12!");
63 static MixerFunc MixSamples
= Mix_C
;
64 static HrtfMixerFunc MixHrtfSamples
= MixHrtf_C
;
65 static ResamplerFunc ResampleSamples
= Resample_point32_C
;
67 MixerFunc
SelectMixer(void)
70 if((CPUCapFlags
&CPU_CAP_SSE
))
74 if((CPUCapFlags
&CPU_CAP_NEON
))
81 RowMixerFunc
SelectRowMixer(void)
84 if((CPUCapFlags
&CPU_CAP_SSE
))
88 if((CPUCapFlags
&CPU_CAP_NEON
))
94 static inline HrtfMixerFunc
SelectHrtfMixer(void)
97 if((CPUCapFlags
&CPU_CAP_NEON
))
101 if((CPUCapFlags
&CPU_CAP_SSE
))
108 static inline ResamplerFunc
SelectResampler(enum Resampler resampler
)
113 return Resample_point32_C
;
114 case LinearResampler
:
116 if((CPUCapFlags
&CPU_CAP_NEON
))
117 return Resample_lerp32_Neon
;
120 if((CPUCapFlags
&CPU_CAP_SSE4_1
))
121 return Resample_lerp32_SSE41
;
124 if((CPUCapFlags
&CPU_CAP_SSE2
))
125 return Resample_lerp32_SSE2
;
127 return Resample_lerp32_C
;
130 if((CPUCapFlags
&CPU_CAP_NEON
))
131 return Resample_fir4_32_Neon
;
134 if((CPUCapFlags
&CPU_CAP_SSE4_1
))
135 return Resample_fir4_32_SSE41
;
138 if((CPUCapFlags
&CPU_CAP_SSE3
))
139 return Resample_fir4_32_SSE3
;
141 return Resample_fir4_32_C
;
144 if((CPUCapFlags
&CPU_CAP_NEON
))
145 return Resample_bsinc32_Neon
;
148 if((CPUCapFlags
&CPU_CAP_SSE
))
149 return Resample_bsinc32_SSE
;
151 return Resample_bsinc32_C
;
154 return Resample_point32_C
;
158 /* The sinc resampler makes use of a Kaiser window to limit the needed sample
159 * points to 4 and 8, respectively.
163 #define M_PI (3.14159265358979323846)
165 static inline double Sinc(double x
)
167 if(x
== 0.0) return 1.0;
168 return sin(x
*M_PI
) / (x
*M_PI
);
171 /* The zero-order modified Bessel function of the first kind, used for the
174 * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k)
175 * = sum_{k=0}^inf ((x / 2)^k / k!)^2
177 static double BesselI_0(double x
)
179 double term
, sum
, x2
, y
, last_sum
;
182 /* Start at k=1 since k=0 is trivial. */
188 /* Let the integration converge until the term of the sum is no longer
197 } while(sum
!= last_sum
);
201 /* Calculate a Kaiser window from the given beta value and a normalized k
204 * w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1
207 * Where k can be calculated as:
209 * k = i / l, where -l <= i <= l.
213 * k = 2 i / M - 1, where 0 <= i <= M.
215 static inline double Kaiser(double b
, double k
)
217 if(k
<= -1.0 || k
>= 1.0) return 0.0;
218 return BesselI_0(b
* sqrt(1.0 - (k
*k
))) / BesselI_0(b
);
221 static inline double CalcKaiserBeta(double rejection
)
224 return 0.1102 * (rejection
- 8.7);
225 if(rejection
>= 21.0)
226 return (0.5842 * pow(rejection
- 21.0, 0.4)) +
227 (0.07886 * (rejection
- 21.0));
231 static float SincKaiser(double r
, double x
)
233 /* Limit rippling to -60dB. */
234 return (float)(Kaiser(CalcKaiserBeta(60.0), x
/ r
) * Sinc(x
));
238 void aluInitMixer(void)
240 enum Resampler resampler
= ResamplerDefault
;
244 if(ConfigValueStr(NULL
, NULL
, "resampler", &str
))
246 if(strcasecmp(str
, "point") == 0 || strcasecmp(str
, "none") == 0)
247 resampler
= PointResampler
;
248 else if(strcasecmp(str
, "linear") == 0)
249 resampler
= LinearResampler
;
250 else if(strcasecmp(str
, "sinc4") == 0)
251 resampler
= FIR4Resampler
;
252 else if(strcasecmp(str
, "bsinc") == 0)
253 resampler
= BSincResampler
;
254 else if(strcasecmp(str
, "cubic") == 0 || strcasecmp(str
, "sinc8") == 0)
256 WARN("Resampler option \"%s\" is deprecated, using sinc4\n", str
);
257 resampler
= FIR4Resampler
;
262 long n
= strtol(str
, &end
, 0);
263 if(*end
== '\0' && (n
== PointResampler
|| n
== LinearResampler
|| n
== FIR4Resampler
))
266 WARN("Invalid resampler: %s\n", str
);
270 for(i
= 0;i
< FRACTIONONE
;i
++)
272 ALdouble mu
= (ALdouble
)i
/ FRACTIONONE
;
273 ResampleCoeffs_FIR4
[i
][0] = SincKaiser(2.0, mu
- -1.0);
274 ResampleCoeffs_FIR4
[i
][1] = SincKaiser(2.0, mu
- 0.0);
275 ResampleCoeffs_FIR4
[i
][2] = SincKaiser(2.0, mu
- 1.0);
276 ResampleCoeffs_FIR4
[i
][3] = SincKaiser(2.0, mu
- 2.0);
279 MixHrtfSamples
= SelectHrtfMixer();
280 MixSamples
= SelectMixer();
281 ResampleSamples
= SelectResampler(resampler
);
285 static inline ALfloat
Sample_ALbyte(ALbyte val
)
286 { return val
* (1.0f
/127.0f
); }
288 static inline ALfloat
Sample_ALshort(ALshort val
)
289 { return val
* (1.0f
/32767.0f
); }
291 static inline ALfloat
Sample_ALfloat(ALfloat val
)
294 #define DECL_TEMPLATE(T) \
295 static inline void Load_##T(ALfloat *dst, const T *src, ALint srcstep, ALsizei samples)\
298 for(i = 0;i < samples;i++) \
299 dst[i] = Sample_##T(src[i*srcstep]); \
302 DECL_TEMPLATE(ALbyte
)
303 DECL_TEMPLATE(ALshort
)
304 DECL_TEMPLATE(ALfloat
)
308 static void LoadSamples(ALfloat
*dst
, const ALvoid
*src
, ALint srcstep
, enum FmtType srctype
, ALsizei samples
)
313 Load_ALbyte(dst
, src
, srcstep
, samples
);
316 Load_ALshort(dst
, src
, srcstep
, samples
);
319 Load_ALfloat(dst
, src
, srcstep
, samples
);
324 static inline void SilenceSamples(ALfloat
*dst
, ALsizei samples
)
327 for(i
= 0;i
< samples
;i
++)
332 static const ALfloat
*DoFilters(ALfilterState
*lpfilter
, ALfilterState
*hpfilter
,
333 ALfloat
*restrict dst
, const ALfloat
*restrict src
,
334 ALsizei numsamples
, enum ActiveFilters type
)
340 ALfilterState_processPassthru(lpfilter
, src
, numsamples
);
341 ALfilterState_processPassthru(hpfilter
, src
, numsamples
);
345 ALfilterState_process(lpfilter
, dst
, src
, numsamples
);
346 ALfilterState_processPassthru(hpfilter
, dst
, numsamples
);
349 ALfilterState_processPassthru(lpfilter
, src
, numsamples
);
350 ALfilterState_process(hpfilter
, dst
, src
, numsamples
);
354 for(i
= 0;i
< numsamples
;)
357 ALsizei todo
= mini(256, numsamples
-i
);
359 ALfilterState_process(lpfilter
, temp
, src
+i
, todo
);
360 ALfilterState_process(hpfilter
, dst
+i
, temp
, todo
);
369 ALboolean
MixSource(ALvoice
*voice
, ALsource
*Source
, ALCdevice
*Device
, ALsizei SamplesToDo
)
371 ResamplerFunc Resample
;
372 ALbufferlistitem
*BufferListItem
;
373 ALuint DataPosInt
, DataPosFrac
;
374 bool isplaying
= true;
386 /* Get source info */
387 DataPosInt
= ATOMIC_LOAD(&voice
->position
, almemory_order_acquire
);
388 DataPosFrac
= ATOMIC_LOAD(&voice
->position_fraction
, almemory_order_relaxed
);
389 BufferListItem
= ATOMIC_LOAD(&voice
->current_buffer
, almemory_order_relaxed
);
390 islooping
= ATOMIC_LOAD(&Source
->looping
, almemory_order_relaxed
);
391 NumChannels
= voice
->NumChannels
;
392 SampleSize
= voice
->SampleSize
;
393 increment
= voice
->Step
;
395 IrSize
= (Device
->HrtfHandle
? Device
->HrtfHandle
->irSize
: 0);
397 Resample
= ((increment
== FRACTIONONE
&& DataPosFrac
== 0) ?
398 Resample_copy32_C
: ResampleSamples
);
400 Counter
= (voice
->Flags
&VOICE_IS_MOVING
) ? SamplesToDo
: 0;
403 ALsizei SrcBufferSize
, DstBufferSize
;
405 /* Figure out how many buffer samples will be needed */
406 DataSize64
= SamplesToDo
-OutPos
;
407 DataSize64
*= increment
;
408 DataSize64
+= DataPosFrac
+FRACTIONMASK
;
409 DataSize64
>>= FRACTIONBITS
;
410 DataSize64
+= MAX_POST_SAMPLES
+MAX_PRE_SAMPLES
;
412 SrcBufferSize
= (ALsizei
)mini64(DataSize64
, BUFFERSIZE
);
414 /* Figure out how many samples we can actually mix from this. */
415 DataSize64
= SrcBufferSize
;
416 DataSize64
-= MAX_POST_SAMPLES
+MAX_PRE_SAMPLES
;
417 DataSize64
<<= FRACTIONBITS
;
418 DataSize64
-= DataPosFrac
;
420 DstBufferSize
= (ALsizei
)((DataSize64
+(increment
-1)) / increment
);
421 DstBufferSize
= mini(DstBufferSize
, (SamplesToDo
-OutPos
));
423 /* Some mixers like having a multiple of 4, so try to give that unless
424 * this is the last update. */
425 if(OutPos
+DstBufferSize
< SamplesToDo
)
428 for(chan
= 0;chan
< NumChannels
;chan
++)
430 const ALfloat
*ResampledData
;
431 ALfloat
*SrcData
= Device
->SourceData
;
434 /* Load the previous samples into the source data first. */
435 memcpy(SrcData
, voice
->PrevSamples
[chan
], MAX_PRE_SAMPLES
*sizeof(ALfloat
));
436 SrcDataSize
= MAX_PRE_SAMPLES
;
438 if(Source
->SourceType
== AL_STATIC
)
440 const ALbuffer
*ALBuffer
= BufferListItem
->buffer
;
441 const ALubyte
*Data
= ALBuffer
->data
;
444 /* Offset buffer data to current channel */
445 Data
+= chan
*SampleSize
;
447 /* If current pos is beyond the loop range, do not loop */
448 if(!islooping
|| DataPosInt
>= (ALuint
)ALBuffer
->LoopEnd
)
452 /* Load what's left to play from the source buffer, and
453 * clear the rest of the temp buffer */
454 DataSize
= minu(SrcBufferSize
- SrcDataSize
,
455 ALBuffer
->SampleLen
- DataPosInt
);
457 LoadSamples(&SrcData
[SrcDataSize
], &Data
[DataPosInt
* NumChannels
*SampleSize
],
458 NumChannels
, ALBuffer
->FmtType
, DataSize
);
459 SrcDataSize
+= DataSize
;
461 SilenceSamples(&SrcData
[SrcDataSize
], SrcBufferSize
- SrcDataSize
);
462 SrcDataSize
+= SrcBufferSize
- SrcDataSize
;
466 ALsizei LoopStart
= ALBuffer
->LoopStart
;
467 ALsizei LoopEnd
= ALBuffer
->LoopEnd
;
469 /* Load what's left of this loop iteration, then load
470 * repeats of the loop section */
471 DataSize
= minu(SrcBufferSize
- SrcDataSize
, LoopEnd
- DataPosInt
);
473 LoadSamples(&SrcData
[SrcDataSize
], &Data
[DataPosInt
* NumChannels
*SampleSize
],
474 NumChannels
, ALBuffer
->FmtType
, DataSize
);
475 SrcDataSize
+= DataSize
;
477 DataSize
= LoopEnd
-LoopStart
;
478 while(SrcBufferSize
> SrcDataSize
)
480 DataSize
= mini(SrcBufferSize
- SrcDataSize
, DataSize
);
482 LoadSamples(&SrcData
[SrcDataSize
], &Data
[LoopStart
* NumChannels
*SampleSize
],
483 NumChannels
, ALBuffer
->FmtType
, DataSize
);
484 SrcDataSize
+= DataSize
;
490 /* Crawl the buffer queue to fill in the temp buffer */
491 ALbufferlistitem
*tmpiter
= BufferListItem
;
492 ALuint pos
= DataPosInt
;
494 while(tmpiter
&& SrcBufferSize
> SrcDataSize
)
496 const ALbuffer
*ALBuffer
;
497 if((ALBuffer
=tmpiter
->buffer
) != NULL
)
499 const ALubyte
*Data
= ALBuffer
->data
;
500 ALuint DataSize
= ALBuffer
->SampleLen
;
502 /* Skip the data already played */
507 Data
+= (pos
*NumChannels
+ chan
)*SampleSize
;
511 DataSize
= minu(SrcBufferSize
- SrcDataSize
, DataSize
);
512 LoadSamples(&SrcData
[SrcDataSize
], Data
, NumChannels
,
513 ALBuffer
->FmtType
, DataSize
);
514 SrcDataSize
+= DataSize
;
517 tmpiter
= tmpiter
->next
;
518 if(!tmpiter
&& islooping
)
519 tmpiter
= ATOMIC_LOAD(&Source
->queue
, almemory_order_acquire
);
522 SilenceSamples(&SrcData
[SrcDataSize
], SrcBufferSize
- SrcDataSize
);
523 SrcDataSize
+= SrcBufferSize
- SrcDataSize
;
528 /* Store the last source samples used for next time. */
529 memcpy(voice
->PrevSamples
[chan
],
530 &SrcData
[(increment
*DstBufferSize
+ DataPosFrac
)>>FRACTIONBITS
],
531 MAX_PRE_SAMPLES
*sizeof(ALfloat
)
534 /* Now resample, then filter and mix to the appropriate outputs. */
535 ResampledData
= Resample(&voice
->ResampleState
,
536 &SrcData
[MAX_PRE_SAMPLES
], DataPosFrac
, increment
,
537 Device
->ResampledData
, DstBufferSize
540 DirectParams
*parms
= &voice
->Direct
.Params
[chan
];
541 const ALfloat
*samples
;
544 &parms
->LowPass
, &parms
->HighPass
, Device
->FilteredData
,
545 ResampledData
, DstBufferSize
, parms
->FilterType
547 if(!(voice
->Flags
&VOICE_IS_HRTF
))
550 memcpy(parms
->Gains
.Current
, parms
->Gains
.Target
,
551 sizeof(parms
->Gains
.Current
));
552 if(!(voice
->Flags
&VOICE_HAS_NFC
))
553 MixSamples(samples
, voice
->Direct
.Channels
, voice
->Direct
.Buffer
,
554 parms
->Gains
.Current
, parms
->Gains
.Target
, Counter
, OutPos
,
559 ALfloat
*nfcsamples
= Device
->NFCtrlData
;
560 ALsizei chanoffset
= 0;
562 voice
->Direct
.ChannelsPerOrder
[0], voice
->Direct
.Buffer
,
563 parms
->Gains
.Current
, parms
->Gains
.Target
, Counter
, OutPos
,
566 chanoffset
+= voice
->Direct
.ChannelsPerOrder
[0];
567 if(voice
->Direct
.ChannelsPerOrder
[1] > 0)
569 NfcFilterUpdate1(&parms
->NFCtrlFilter
[0], nfcsamples
, samples
,
571 MixSamples(nfcsamples
,
572 voice
->Direct
.ChannelsPerOrder
[1], voice
->Direct
.Buffer
+chanoffset
,
573 parms
->Gains
.Current
+chanoffset
, parms
->Gains
.Target
+chanoffset
,
574 Counter
, OutPos
, DstBufferSize
576 chanoffset
+= voice
->Direct
.ChannelsPerOrder
[1];
578 if(voice
->Direct
.ChannelsPerOrder
[2] > 0)
580 NfcFilterUpdate2(&parms
->NFCtrlFilter
[1], nfcsamples
, samples
,
582 MixSamples(nfcsamples
,
583 voice
->Direct
.ChannelsPerOrder
[2], voice
->Direct
.Buffer
+chanoffset
,
584 parms
->Gains
.Current
+chanoffset
, parms
->Gains
.Target
+chanoffset
,
585 Counter
, OutPos
, DstBufferSize
587 chanoffset
+= voice
->Direct
.ChannelsPerOrder
[2];
589 if(voice
->Direct
.ChannelsPerOrder
[3] > 0)
591 NfcFilterUpdate3(&parms
->NFCtrlFilter
[2], nfcsamples
, samples
,
593 MixSamples(nfcsamples
,
594 voice
->Direct
.ChannelsPerOrder
[3], voice
->Direct
.Buffer
+chanoffset
,
595 parms
->Gains
.Current
+chanoffset
, parms
->Gains
.Target
+chanoffset
,
596 Counter
, OutPos
, DstBufferSize
598 chanoffset
+= voice
->Direct
.ChannelsPerOrder
[3];
604 MixHrtfParams hrtfparams
;
607 lidx
= GetChannelIdxByName(Device
->RealOut
, FrontLeft
);
608 ridx
= GetChannelIdxByName(Device
->RealOut
, FrontRight
);
609 assert(lidx
!= -1 && ridx
!= -1);
613 parms
->Hrtf
.Old
= parms
->Hrtf
.Target
;
614 hrtfparams
.Coeffs
= SAFE_CONST(ALfloat2
*,parms
->Hrtf
.Target
.Coeffs
);
615 hrtfparams
.Delay
[0] = parms
->Hrtf
.Target
.Delay
[0];
616 hrtfparams
.Delay
[1] = parms
->Hrtf
.Target
.Delay
[1];
617 hrtfparams
.Gain
= parms
->Hrtf
.Target
.Gain
;
618 hrtfparams
.GainStep
= 0.0f
;
620 voice
->Direct
.Buffer
[lidx
], voice
->Direct
.Buffer
[ridx
],
621 samples
, voice
->Offset
, OutPos
, IrSize
, &hrtfparams
,
622 &parms
->Hrtf
.State
, DstBufferSize
627 HrtfState backupstate
= parms
->Hrtf
.State
;
630 /* The old coefficients need to fade to silence
631 * completely since they'll be replaced after the mix.
632 * So it needs to fade out over DstBufferSize instead
635 hrtfparams
.Coeffs
= SAFE_CONST(ALfloat2
*,parms
->Hrtf
.Old
.Coeffs
);
636 hrtfparams
.Delay
[0] = parms
->Hrtf
.Old
.Delay
[0];
637 hrtfparams
.Delay
[1] = parms
->Hrtf
.Old
.Delay
[1];
638 hrtfparams
.Gain
= parms
->Hrtf
.Old
.Gain
;
639 hrtfparams
.GainStep
= -hrtfparams
.Gain
/
640 (ALfloat
)DstBufferSize
;
642 voice
->Direct
.Buffer
[lidx
], voice
->Direct
.Buffer
[ridx
],
643 samples
, voice
->Offset
, OutPos
, IrSize
, &hrtfparams
,
644 &backupstate
, DstBufferSize
647 /* The new coefficients need to fade in completely
648 * since they're replacing the old ones. To keep the
649 * source gain fading consistent, interpolate between
650 * the old and new target gain given how much of the
651 * fade time this mix handles.
653 gain
= lerp(parms
->Hrtf
.Old
.Gain
, parms
->Hrtf
.Target
.Gain
,
654 minf(1.0f
, (ALfloat
)Counter
/DstBufferSize
));
655 hrtfparams
.Coeffs
= SAFE_CONST(ALfloat2
*,parms
->Hrtf
.Target
.Coeffs
);
656 hrtfparams
.Delay
[0] = parms
->Hrtf
.Target
.Delay
[0];
657 hrtfparams
.Delay
[1] = parms
->Hrtf
.Target
.Delay
[1];
658 hrtfparams
.Gain
= 0.0f
;
659 hrtfparams
.GainStep
= gain
/ (ALfloat
)DstBufferSize
;
661 voice
->Direct
.Buffer
[lidx
], voice
->Direct
.Buffer
[ridx
],
662 samples
, voice
->Offset
, OutPos
, IrSize
, &hrtfparams
,
663 &parms
->Hrtf
.State
, DstBufferSize
665 /* Update the old parameters with the result. */
666 parms
->Hrtf
.Old
= parms
->Hrtf
.Target
;
667 if(Counter
> DstBufferSize
)
668 parms
->Hrtf
.Old
.Gain
= hrtfparams
.Gain
;
673 for(send
= 0;send
< Device
->NumAuxSends
;send
++)
675 SendParams
*parms
= &voice
->Send
[send
].Params
[chan
];
676 const ALfloat
*samples
;
678 if(!voice
->Send
[send
].Buffer
)
682 &parms
->LowPass
, &parms
->HighPass
, Device
->FilteredData
,
683 ResampledData
, DstBufferSize
, parms
->FilterType
687 memcpy(parms
->Gains
.Current
, parms
->Gains
.Target
,
688 sizeof(parms
->Gains
.Current
));
689 MixSamples(samples
, voice
->Send
[send
].Channels
, voice
->Send
[send
].Buffer
,
690 parms
->Gains
.Current
, parms
->Gains
.Target
, Counter
, OutPos
, DstBufferSize
694 /* Update positions */
695 DataPosFrac
+= increment
*DstBufferSize
;
696 DataPosInt
+= DataPosFrac
>>FRACTIONBITS
;
697 DataPosFrac
&= FRACTIONMASK
;
699 OutPos
+= DstBufferSize
;
700 voice
->Offset
+= DstBufferSize
;
701 Counter
= maxi(DstBufferSize
, Counter
) - DstBufferSize
;
703 /* Handle looping sources */
706 const ALbuffer
*ALBuffer
;
707 ALsizei DataSize
= 0;
708 ALsizei LoopStart
= 0;
711 if((ALBuffer
=BufferListItem
->buffer
) != NULL
)
713 DataSize
= ALBuffer
->SampleLen
;
714 LoopStart
= ALBuffer
->LoopStart
;
715 LoopEnd
= ALBuffer
->LoopEnd
;
716 if((ALuint
)LoopEnd
> DataPosInt
)
720 if(islooping
&& Source
->SourceType
== AL_STATIC
)
722 assert(LoopEnd
> LoopStart
);
723 DataPosInt
= ((DataPosInt
-LoopStart
)%(LoopEnd
-LoopStart
)) + LoopStart
;
727 if((ALuint
)DataSize
> DataPosInt
)
730 if(!(BufferListItem
=BufferListItem
->next
))
733 BufferListItem
= ATOMIC_LOAD(&Source
->queue
, almemory_order_acquire
);
737 BufferListItem
= NULL
;
744 DataPosInt
-= DataSize
;
746 } while(isplaying
&& OutPos
< SamplesToDo
);
748 voice
->Flags
|= VOICE_IS_MOVING
;
750 /* Update source info */
751 ATOMIC_STORE(&voice
->position
, DataPosInt
, almemory_order_relaxed
);
752 ATOMIC_STORE(&voice
->position_fraction
, DataPosFrac
, almemory_order_relaxed
);
753 ATOMIC_STORE(&voice
->current_buffer
, BufferListItem
, almemory_order_release
);