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"
36 #include "sample_cvt.h"
39 #include "ringbuffer.h"
42 #include "mixer/defs.h"
45 static_assert((INT_MAX
>>FRACTIONBITS
)/MAX_PITCH
> BUFFERSIZE
,
46 "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
48 extern inline void InitiatePositionArrays(ALsizei frac
, ALint increment
, ALsizei
*restrict frac_arr
, ALsizei
*restrict pos_arr
, ALsizei size
);
51 /* BSinc24 requires up to 23 extra samples before the current position, and 24 after. */
52 static_assert(MAX_RESAMPLE_PADDING
>= 24, "MAX_RESAMPLE_PADDING must be at least 24!");
55 enum Resampler ResamplerDefault
= LinearResampler
;
57 MixerFunc MixSamples
= Mix_C
;
58 RowMixerFunc MixRowSamples
= MixRow_C
;
59 static HrtfMixerFunc MixHrtfSamples
= MixHrtf_C
;
60 static HrtfMixerBlendFunc MixHrtfBlendSamples
= MixHrtfBlend_C
;
62 static MixerFunc
SelectMixer(void)
65 if((CPUCapFlags
&CPU_CAP_NEON
))
69 if((CPUCapFlags
&CPU_CAP_SSE
))
75 static RowMixerFunc
SelectRowMixer(void)
78 if((CPUCapFlags
&CPU_CAP_NEON
))
82 if((CPUCapFlags
&CPU_CAP_SSE
))
88 static inline HrtfMixerFunc
SelectHrtfMixer(void)
91 if((CPUCapFlags
&CPU_CAP_NEON
))
95 if((CPUCapFlags
&CPU_CAP_SSE
))
101 static inline HrtfMixerBlendFunc
SelectHrtfBlendMixer(void)
104 if((CPUCapFlags
&CPU_CAP_NEON
))
105 return MixHrtfBlend_Neon
;
108 if((CPUCapFlags
&CPU_CAP_SSE
))
109 return MixHrtfBlend_SSE
;
111 return MixHrtfBlend_C
;
114 ResamplerFunc
SelectResampler(enum Resampler resampler
)
119 return Resample_point_C
;
120 case LinearResampler
:
122 if((CPUCapFlags
&CPU_CAP_NEON
))
123 return Resample_lerp_Neon
;
126 if((CPUCapFlags
&CPU_CAP_SSE4_1
))
127 return Resample_lerp_SSE41
;
130 if((CPUCapFlags
&CPU_CAP_SSE2
))
131 return Resample_lerp_SSE2
;
133 return Resample_lerp_C
;
135 return Resample_cubic_C
;
136 case BSinc12Resampler
:
137 case BSinc24Resampler
:
139 if((CPUCapFlags
&CPU_CAP_NEON
))
140 return Resample_bsinc_Neon
;
143 if((CPUCapFlags
&CPU_CAP_SSE
))
144 return Resample_bsinc_SSE
;
146 return Resample_bsinc_C
;
149 return Resample_point_C
;
153 void aluInitMixer(void)
157 if(ConfigValueStr(NULL
, NULL
, "resampler", &str
))
159 if(strcasecmp(str
, "point") == 0 || strcasecmp(str
, "none") == 0)
160 ResamplerDefault
= PointResampler
;
161 else if(strcasecmp(str
, "linear") == 0)
162 ResamplerDefault
= LinearResampler
;
163 else if(strcasecmp(str
, "cubic") == 0)
164 ResamplerDefault
= FIR4Resampler
;
165 else if(strcasecmp(str
, "bsinc12") == 0)
166 ResamplerDefault
= BSinc12Resampler
;
167 else if(strcasecmp(str
, "bsinc24") == 0)
168 ResamplerDefault
= BSinc24Resampler
;
169 else if(strcasecmp(str
, "bsinc") == 0)
171 WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", str
);
172 ResamplerDefault
= BSinc12Resampler
;
174 else if(strcasecmp(str
, "sinc4") == 0 || strcasecmp(str
, "sinc8") == 0)
176 WARN("Resampler option \"%s\" is deprecated, using cubic\n", str
);
177 ResamplerDefault
= FIR4Resampler
;
182 long n
= strtol(str
, &end
, 0);
183 if(*end
== '\0' && (n
== PointResampler
|| n
== LinearResampler
|| n
== FIR4Resampler
))
184 ResamplerDefault
= n
;
186 WARN("Invalid resampler: %s\n", str
);
190 MixHrtfBlendSamples
= SelectHrtfBlendMixer();
191 MixHrtfSamples
= SelectHrtfMixer();
192 MixSamples
= SelectMixer();
193 MixRowSamples
= SelectRowMixer();
197 static void SendAsyncEvent(ALCcontext
*context
, ALuint enumtype
, ALenum type
,
198 ALuint objid
, ALuint param
, const char *msg
)
200 AsyncEvent evt
= ASYNC_EVENT(enumtype
);
201 evt
.u
.user
.type
= type
;
202 evt
.u
.user
.id
= objid
;
203 evt
.u
.user
.param
= param
;
204 strcpy(evt
.u
.user
.msg
, msg
);
205 if(ll_ringbuffer_write(context
->AsyncEvents
, (const char*)&evt
, 1) == 1)
206 alsem_post(&context
->EventSem
);
210 static inline ALfloat
Sample_ALubyte(ALubyte val
)
211 { return (val
-128) * (1.0f
/128.0f
); }
213 static inline ALfloat
Sample_ALshort(ALshort val
)
214 { return val
* (1.0f
/32768.0f
); }
216 static inline ALfloat
Sample_ALfloat(ALfloat val
)
219 static inline ALfloat
Sample_ALdouble(ALdouble val
)
220 { return (ALfloat
)val
; }
222 typedef ALubyte ALmulaw
;
223 static inline ALfloat
Sample_ALmulaw(ALmulaw val
)
224 { return muLawDecompressionTable
[val
] * (1.0f
/32768.0f
); }
226 typedef ALubyte ALalaw
;
227 static inline ALfloat
Sample_ALalaw(ALalaw val
)
228 { return aLawDecompressionTable
[val
] * (1.0f
/32768.0f
); }
230 #define DECL_TEMPLATE(T) \
231 static inline void Load_##T(ALfloat *restrict dst, const T *restrict src, \
232 ALint srcstep, ALsizei samples) \
235 for(i = 0;i < samples;i++) \
236 dst[i] += Sample_##T(src[i*srcstep]); \
239 DECL_TEMPLATE(ALubyte
)
240 DECL_TEMPLATE(ALshort
)
241 DECL_TEMPLATE(ALfloat
)
242 DECL_TEMPLATE(ALdouble
)
243 DECL_TEMPLATE(ALmulaw
)
244 DECL_TEMPLATE(ALalaw
)
248 static void LoadSamples(ALfloat
*restrict dst
, const ALvoid
*restrict src
, ALint srcstep
,
249 enum FmtType srctype
, ALsizei samples
)
251 #define HANDLE_FMT(ET, ST) case ET: Load_##ST(dst, src, srcstep, samples); break
254 HANDLE_FMT(FmtUByte
, ALubyte
);
255 HANDLE_FMT(FmtShort
, ALshort
);
256 HANDLE_FMT(FmtFloat
, ALfloat
);
257 HANDLE_FMT(FmtDouble
, ALdouble
);
258 HANDLE_FMT(FmtMulaw
, ALmulaw
);
259 HANDLE_FMT(FmtAlaw
, ALalaw
);
265 static const ALfloat
*DoFilters(BiquadFilter
*lpfilter
, BiquadFilter
*hpfilter
,
266 ALfloat
*restrict dst
, const ALfloat
*restrict src
,
267 ALsizei numsamples
, enum ActiveFilters type
)
273 BiquadFilter_passthru(lpfilter
, numsamples
);
274 BiquadFilter_passthru(hpfilter
, numsamples
);
278 BiquadFilter_process(lpfilter
, dst
, src
, numsamples
);
279 BiquadFilter_passthru(hpfilter
, numsamples
);
282 BiquadFilter_passthru(lpfilter
, numsamples
);
283 BiquadFilter_process(hpfilter
, dst
, src
, numsamples
);
287 for(i
= 0;i
< numsamples
;)
290 ALsizei todo
= mini(256, numsamples
-i
);
292 BiquadFilter_process(lpfilter
, temp
, src
+i
, todo
);
293 BiquadFilter_process(hpfilter
, dst
+i
, temp
, todo
);
302 /* This function uses these device temp buffers. */
303 #define SOURCE_DATA_BUF 0
304 #define RESAMPLED_BUF 1
305 #define FILTERED_BUF 2
306 #define NFC_DATA_BUF 3
307 ALboolean
MixSource(ALvoice
*voice
, ALuint SourceID
, ALCcontext
*Context
, ALsizei SamplesToDo
)
309 ALCdevice
*Device
= Context
->Device
;
310 ALbufferlistitem
*BufferListItem
;
311 ALbufferlistitem
*BufferLoopItem
;
312 ALsizei NumChannels
, SampleSize
;
313 ALbitfieldSOFT enabledevt
;
314 ALsizei buffers_done
= 0;
315 ResamplerFunc Resample
;
329 /* Get source info */
330 isplaying
= true; /* Will only be called while playing. */
331 isstatic
= !!(voice
->Flags
&VOICE_IS_STATIC
);
332 DataPosInt
= ATOMIC_LOAD(&voice
->position
, almemory_order_acquire
);
333 DataPosFrac
= ATOMIC_LOAD(&voice
->position_fraction
, almemory_order_relaxed
);
334 BufferListItem
= ATOMIC_LOAD(&voice
->current_buffer
, almemory_order_relaxed
);
335 BufferLoopItem
= ATOMIC_LOAD(&voice
->loop_buffer
, almemory_order_relaxed
);
336 NumChannels
= voice
->NumChannels
;
337 SampleSize
= voice
->SampleSize
;
338 increment
= voice
->Step
;
340 IrSize
= (Device
->HrtfHandle
? Device
->HrtfHandle
->irSize
: 0);
342 Resample
= ((increment
== FRACTIONONE
&& DataPosFrac
== 0) ?
343 Resample_copy_C
: voice
->Resampler
);
345 Counter
= (voice
->Flags
&VOICE_IS_FADING
) ? SamplesToDo
: 0;
350 ALsizei SrcBufferSize
, DstBufferSize
;
352 /* Figure out how many buffer samples will be needed */
353 DataSize64
= SamplesToDo
-OutPos
;
354 DataSize64
*= increment
;
355 DataSize64
+= DataPosFrac
+FRACTIONMASK
;
356 DataSize64
>>= FRACTIONBITS
;
357 DataSize64
+= MAX_RESAMPLE_PADDING
*2;
358 SrcBufferSize
= (ALsizei
)mini64(DataSize64
, BUFFERSIZE
);
360 /* Figure out how many samples we can actually mix from this. */
361 DataSize64
= SrcBufferSize
;
362 DataSize64
-= MAX_RESAMPLE_PADDING
*2;
363 DataSize64
<<= FRACTIONBITS
;
364 DataSize64
-= DataPosFrac
;
365 DstBufferSize
= (ALsizei
)mini64((DataSize64
+(increment
-1)) / increment
,
366 SamplesToDo
- OutPos
);
368 /* Some mixers like having a multiple of 4, so try to give that unless
369 * this is the last update. */
370 if(DstBufferSize
< SamplesToDo
-OutPos
)
373 /* It's impossible to have a buffer list item with no entries. */
374 assert(BufferListItem
->num_buffers
> 0);
376 for(chan
= 0;chan
< NumChannels
;chan
++)
378 const ALfloat
*ResampledData
;
379 ALfloat
*SrcData
= Device
->TempBuffer
[SOURCE_DATA_BUF
];
382 /* Load the previous samples into the source data first, and clear the rest. */
383 memcpy(SrcData
, voice
->PrevSamples
[chan
], MAX_RESAMPLE_PADDING
*sizeof(ALfloat
));
384 memset(SrcData
+MAX_RESAMPLE_PADDING
, 0, (BUFFERSIZE
-MAX_RESAMPLE_PADDING
)*
386 FilledAmt
= MAX_RESAMPLE_PADDING
;
390 /* TODO: For static sources, loop points are taken from the
391 * first buffer (should be adjusted by any buffer offset, to
392 * possibly be added later).
394 const ALbuffer
*Buffer0
= BufferListItem
->buffers
[0];
395 const ALsizei LoopStart
= Buffer0
->LoopStart
;
396 const ALsizei LoopEnd
= Buffer0
->LoopEnd
;
397 const ALsizei LoopSize
= LoopEnd
- LoopStart
;
399 /* If current pos is beyond the loop range, do not loop */
400 if(!BufferLoopItem
|| DataPosInt
>= LoopEnd
)
402 ALsizei SizeToDo
= SrcBufferSize
- FilledAmt
;
406 BufferLoopItem
= NULL
;
408 for(i
= 0;i
< BufferListItem
->num_buffers
;i
++)
410 const ALbuffer
*buffer
= BufferListItem
->buffers
[i
];
411 const ALubyte
*Data
= buffer
->data
;
414 if(DataPosInt
>= buffer
->SampleLen
)
417 /* Load what's left to play from the buffer */
418 DataSize
= mini(SizeToDo
, buffer
->SampleLen
- DataPosInt
);
419 CompLen
= maxi(CompLen
, DataSize
);
421 LoadSamples(&SrcData
[FilledAmt
],
422 &Data
[(DataPosInt
*NumChannels
+ chan
)*SampleSize
],
423 NumChannels
, buffer
->FmtType
, DataSize
426 FilledAmt
+= CompLen
;
430 ALsizei SizeToDo
= mini(SrcBufferSize
- FilledAmt
, LoopEnd
- DataPosInt
);
434 for(i
= 0;i
< BufferListItem
->num_buffers
;i
++)
436 const ALbuffer
*buffer
= BufferListItem
->buffers
[i
];
437 const ALubyte
*Data
= buffer
->data
;
440 if(DataPosInt
>= buffer
->SampleLen
)
443 /* Load what's left of this loop iteration */
444 DataSize
= mini(SizeToDo
, buffer
->SampleLen
- DataPosInt
);
445 CompLen
= maxi(CompLen
, DataSize
);
447 LoadSamples(&SrcData
[FilledAmt
],
448 &Data
[(DataPosInt
*NumChannels
+ chan
)*SampleSize
],
449 NumChannels
, buffer
->FmtType
, DataSize
452 FilledAmt
+= CompLen
;
454 while(SrcBufferSize
> FilledAmt
)
456 const ALsizei SizeToDo
= mini(SrcBufferSize
- FilledAmt
, LoopSize
);
459 for(i
= 0;i
< BufferListItem
->num_buffers
;i
++)
461 const ALbuffer
*buffer
= BufferListItem
->buffers
[i
];
462 const ALubyte
*Data
= buffer
->data
;
465 if(LoopStart
>= buffer
->SampleLen
)
468 DataSize
= mini(SizeToDo
, buffer
->SampleLen
- LoopStart
);
469 CompLen
= maxi(CompLen
, DataSize
);
471 LoadSamples(&SrcData
[FilledAmt
],
472 &Data
[(LoopStart
*NumChannels
+ chan
)*SampleSize
],
473 NumChannels
, buffer
->FmtType
, DataSize
476 FilledAmt
+= CompLen
;
482 /* Crawl the buffer queue to fill in the temp buffer */
483 ALbufferlistitem
*tmpiter
= BufferListItem
;
484 ALsizei pos
= DataPosInt
;
486 while(tmpiter
&& SrcBufferSize
> FilledAmt
)
488 ALsizei SizeToDo
= SrcBufferSize
- FilledAmt
;
492 for(i
= 0;i
< tmpiter
->num_buffers
;i
++)
494 const ALbuffer
*ALBuffer
= tmpiter
->buffers
[i
];
495 ALsizei DataSize
= ALBuffer
? ALBuffer
->SampleLen
: 0;
499 const ALubyte
*Data
= ALBuffer
->data
;
500 Data
+= (pos
*NumChannels
+ chan
)*SampleSize
;
502 DataSize
= mini(SizeToDo
, DataSize
- pos
);
503 CompLen
= maxi(CompLen
, DataSize
);
505 LoadSamples(&SrcData
[FilledAmt
], Data
, NumChannels
,
506 ALBuffer
->FmtType
, DataSize
);
509 if(UNLIKELY(!CompLen
))
510 pos
-= tmpiter
->max_samples
;
513 FilledAmt
+= CompLen
;
514 if(SrcBufferSize
<= FilledAmt
)
518 tmpiter
= ATOMIC_LOAD(&tmpiter
->next
, almemory_order_acquire
);
519 if(!tmpiter
) tmpiter
= BufferLoopItem
;
523 /* Store the last source samples used for next time. */
524 memcpy(voice
->PrevSamples
[chan
],
525 &SrcData
[(increment
*DstBufferSize
+ DataPosFrac
)>>FRACTIONBITS
],
526 MAX_RESAMPLE_PADDING
*sizeof(ALfloat
)
529 /* Now resample, then filter and mix to the appropriate outputs. */
530 ResampledData
= Resample(&voice
->ResampleState
,
531 &SrcData
[MAX_RESAMPLE_PADDING
], DataPosFrac
, increment
,
532 Device
->TempBuffer
[RESAMPLED_BUF
], DstBufferSize
535 DirectParams
*parms
= &voice
->Direct
.Params
[chan
];
536 const ALfloat
*samples
;
539 &parms
->LowPass
, &parms
->HighPass
, Device
->TempBuffer
[FILTERED_BUF
],
540 ResampledData
, DstBufferSize
, voice
->Direct
.FilterType
542 if(!(voice
->Flags
&VOICE_HAS_HRTF
))
545 memcpy(parms
->Gains
.Current
, parms
->Gains
.Target
,
546 sizeof(parms
->Gains
.Current
));
547 if(!(voice
->Flags
&VOICE_HAS_NFC
))
548 MixSamples(samples
, voice
->Direct
.Channels
, voice
->Direct
.Buffer
,
549 parms
->Gains
.Current
, parms
->Gains
.Target
, Counter
, OutPos
,
554 ALfloat
*nfcsamples
= Device
->TempBuffer
[NFC_DATA_BUF
];
555 ALsizei chanoffset
= 0;
558 voice
->Direct
.ChannelsPerOrder
[0], voice
->Direct
.Buffer
,
559 parms
->Gains
.Current
, parms
->Gains
.Target
, Counter
, OutPos
,
562 chanoffset
+= voice
->Direct
.ChannelsPerOrder
[0];
563 #define APPLY_NFC_MIX(order) \
564 if(voice->Direct.ChannelsPerOrder[order] > 0) \
566 NfcFilterProcess##order(&parms->NFCtrlFilter, nfcsamples, samples, \
568 MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[order], \
569 voice->Direct.Buffer+chanoffset, parms->Gains.Current+chanoffset, \
570 parms->Gains.Target+chanoffset, Counter, OutPos, DstBufferSize \
572 chanoffset += voice->Direct.ChannelsPerOrder[order]; \
582 MixHrtfParams hrtfparams
;
586 lidx
= GetChannelIdxByName(&Device
->RealOut
, FrontLeft
);
587 ridx
= GetChannelIdxByName(&Device
->RealOut
, FrontRight
);
588 assert(lidx
!= -1 && ridx
!= -1);
592 /* No fading, just overwrite the old HRTF params. */
593 parms
->Hrtf
.Old
= parms
->Hrtf
.Target
;
595 else if(!(parms
->Hrtf
.Old
.Gain
> GAIN_SILENCE_THRESHOLD
))
597 /* The old HRTF params are silent, so overwrite the old
598 * coefficients with the new, and reset the old gain to
599 * 0. The future mix will then fade from silence.
601 parms
->Hrtf
.Old
= parms
->Hrtf
.Target
;
602 parms
->Hrtf
.Old
.Gain
= 0.0f
;
608 /* Fade between the coefficients over 128 samples. */
609 fademix
= mini(DstBufferSize
, 128);
611 /* The new coefficients need to fade in completely
612 * since they're replacing the old ones. To keep the
613 * gain fading consistent, interpolate between the old
614 * and new target gains given how much of the fade time
617 gain
= lerp(parms
->Hrtf
.Old
.Gain
, parms
->Hrtf
.Target
.Gain
,
618 minf(1.0f
, (ALfloat
)fademix
/Counter
));
619 hrtfparams
.Coeffs
= parms
->Hrtf
.Target
.Coeffs
;
620 hrtfparams
.Delay
[0] = parms
->Hrtf
.Target
.Delay
[0];
621 hrtfparams
.Delay
[1] = parms
->Hrtf
.Target
.Delay
[1];
622 hrtfparams
.Gain
= 0.0f
;
623 hrtfparams
.GainStep
= gain
/ (ALfloat
)fademix
;
626 voice
->Direct
.Buffer
[lidx
], voice
->Direct
.Buffer
[ridx
],
627 samples
, voice
->Offset
, OutPos
, IrSize
, &parms
->Hrtf
.Old
,
628 &hrtfparams
, &parms
->Hrtf
.State
, fademix
630 /* Update the old parameters with the result. */
631 parms
->Hrtf
.Old
= parms
->Hrtf
.Target
;
632 if(fademix
< Counter
)
633 parms
->Hrtf
.Old
.Gain
= hrtfparams
.Gain
;
636 if(fademix
< DstBufferSize
)
638 ALsizei todo
= DstBufferSize
- fademix
;
639 ALfloat gain
= parms
->Hrtf
.Target
.Gain
;
641 /* Interpolate the target gain if the gain fading lasts
642 * longer than this mix.
644 if(Counter
> DstBufferSize
)
645 gain
= lerp(parms
->Hrtf
.Old
.Gain
, gain
,
646 (ALfloat
)todo
/(Counter
-fademix
));
648 hrtfparams
.Coeffs
= parms
->Hrtf
.Target
.Coeffs
;
649 hrtfparams
.Delay
[0] = parms
->Hrtf
.Target
.Delay
[0];
650 hrtfparams
.Delay
[1] = parms
->Hrtf
.Target
.Delay
[1];
651 hrtfparams
.Gain
= parms
->Hrtf
.Old
.Gain
;
652 hrtfparams
.GainStep
= (gain
- parms
->Hrtf
.Old
.Gain
) / (ALfloat
)todo
;
654 voice
->Direct
.Buffer
[lidx
], voice
->Direct
.Buffer
[ridx
],
655 samples
+fademix
, voice
->Offset
+fademix
, OutPos
+fademix
, IrSize
,
656 &hrtfparams
, &parms
->Hrtf
.State
, todo
658 /* Store the interpolated gain or the final target gain
659 * depending if the fade is done.
661 if(DstBufferSize
< Counter
)
662 parms
->Hrtf
.Old
.Gain
= gain
;
664 parms
->Hrtf
.Old
.Gain
= parms
->Hrtf
.Target
.Gain
;
669 for(send
= 0;send
< Device
->NumAuxSends
;send
++)
671 SendParams
*parms
= &voice
->Send
[send
].Params
[chan
];
672 const ALfloat
*samples
;
674 if(!voice
->Send
[send
].Buffer
)
678 &parms
->LowPass
, &parms
->HighPass
, Device
->TempBuffer
[FILTERED_BUF
],
679 ResampledData
, DstBufferSize
, voice
->Send
[send
].FilterType
683 memcpy(parms
->Gains
.Current
, parms
->Gains
.Target
,
684 sizeof(parms
->Gains
.Current
));
685 MixSamples(samples
, voice
->Send
[send
].Channels
, voice
->Send
[send
].Buffer
,
686 parms
->Gains
.Current
, parms
->Gains
.Target
, Counter
, OutPos
, DstBufferSize
690 /* Update positions */
691 DataPosFrac
+= increment
*DstBufferSize
;
692 DataPosInt
+= DataPosFrac
>>FRACTIONBITS
;
693 DataPosFrac
&= FRACTIONMASK
;
695 OutPos
+= DstBufferSize
;
696 voice
->Offset
+= DstBufferSize
;
697 Counter
= maxi(DstBufferSize
, Counter
) - DstBufferSize
;
704 /* Handle looping static source */
705 const ALbuffer
*Buffer
= BufferListItem
->buffers
[0];
706 ALsizei LoopStart
= Buffer
->LoopStart
;
707 ALsizei LoopEnd
= Buffer
->LoopEnd
;
708 if(DataPosInt
>= LoopEnd
)
710 assert(LoopEnd
> LoopStart
);
711 DataPosInt
= ((DataPosInt
-LoopStart
)%(LoopEnd
-LoopStart
)) + LoopStart
;
716 /* Handle non-looping static source */
717 if(DataPosInt
>= BufferListItem
->max_samples
)
720 BufferListItem
= NULL
;
729 /* Handle streaming source */
730 if(BufferListItem
->max_samples
> DataPosInt
)
733 DataPosInt
-= BufferListItem
->max_samples
;
735 buffers_done
+= BufferListItem
->num_buffers
;
736 BufferListItem
= ATOMIC_LOAD(&BufferListItem
->next
, almemory_order_relaxed
);
737 if(!BufferListItem
&& !(BufferListItem
=BufferLoopItem
))
745 } while(isplaying
&& OutPos
< SamplesToDo
);
747 voice
->Flags
|= VOICE_IS_FADING
;
749 /* Update source info */
750 ATOMIC_STORE(&voice
->position
, DataPosInt
, almemory_order_relaxed
);
751 ATOMIC_STORE(&voice
->position_fraction
, DataPosFrac
, almemory_order_relaxed
);
752 ATOMIC_STORE(&voice
->current_buffer
, BufferListItem
, almemory_order_release
);
754 /* Send any events now, after the position/buffer info was updated. */
755 enabledevt
= ATOMIC_LOAD(&Context
->EnabledEvts
, almemory_order_acquire
);
756 if(buffers_done
> 0 && (enabledevt
&EventType_BufferCompleted
))
757 SendAsyncEvent(Context
, EventType_BufferCompleted
,
758 AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT
, SourceID
, buffers_done
, "Buffer completed"