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
, ALint
*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
)
201 evt
.EnumType
= enumtype
;
203 evt
.ObjectId
= objid
;
205 strcpy(evt
.Message
, msg
);
206 if(ll_ringbuffer_write(context
->AsyncEvents
, (const char*)&evt
, 1) == 1)
207 alsem_post(&context
->EventSem
);
211 static inline ALfloat
Sample_ALubyte(ALubyte val
)
212 { return (val
-128) * (1.0f
/128.0f
); }
214 static inline ALfloat
Sample_ALshort(ALshort val
)
215 { return val
* (1.0f
/32768.0f
); }
217 static inline ALfloat
Sample_ALfloat(ALfloat val
)
220 static inline ALfloat
Sample_ALdouble(ALdouble val
)
221 { return (ALfloat
)val
; }
223 typedef ALubyte ALmulaw
;
224 static inline ALfloat
Sample_ALmulaw(ALmulaw val
)
225 { return muLawDecompressionTable
[val
] * (1.0f
/32768.0f
); }
227 typedef ALubyte ALalaw
;
228 static inline ALfloat
Sample_ALalaw(ALalaw val
)
229 { return aLawDecompressionTable
[val
] * (1.0f
/32768.0f
); }
231 #define DECL_TEMPLATE(T) \
232 static inline void Load_##T(ALfloat *restrict dst, const T *restrict src, \
233 ALint srcstep, ALsizei samples) \
236 for(i = 0;i < samples;i++) \
237 dst[i] += Sample_##T(src[i*srcstep]); \
240 DECL_TEMPLATE(ALubyte
)
241 DECL_TEMPLATE(ALshort
)
242 DECL_TEMPLATE(ALfloat
)
243 DECL_TEMPLATE(ALdouble
)
244 DECL_TEMPLATE(ALmulaw
)
245 DECL_TEMPLATE(ALalaw
)
249 static void LoadSamples(ALfloat
*restrict dst
, const ALvoid
*restrict src
, ALint srcstep
,
250 enum FmtType srctype
, ALsizei samples
)
252 #define HANDLE_FMT(ET, ST) case ET: Load_##ST(dst, src, srcstep, samples); break
255 HANDLE_FMT(FmtUByte
, ALubyte
);
256 HANDLE_FMT(FmtShort
, ALshort
);
257 HANDLE_FMT(FmtFloat
, ALfloat
);
258 HANDLE_FMT(FmtDouble
, ALdouble
);
259 HANDLE_FMT(FmtMulaw
, ALmulaw
);
260 HANDLE_FMT(FmtAlaw
, ALalaw
);
266 static const ALfloat
*DoFilters(BiquadFilter
*lpfilter
, BiquadFilter
*hpfilter
,
267 ALfloat
*restrict dst
, const ALfloat
*restrict src
,
268 ALsizei numsamples
, enum ActiveFilters type
)
274 BiquadFilter_passthru(lpfilter
, numsamples
);
275 BiquadFilter_passthru(hpfilter
, numsamples
);
279 BiquadFilter_process(lpfilter
, dst
, src
, numsamples
);
280 BiquadFilter_passthru(hpfilter
, numsamples
);
283 BiquadFilter_passthru(lpfilter
, numsamples
);
284 BiquadFilter_process(hpfilter
, dst
, src
, numsamples
);
288 for(i
= 0;i
< numsamples
;)
291 ALsizei todo
= mini(256, numsamples
-i
);
293 BiquadFilter_process(lpfilter
, temp
, src
+i
, todo
);
294 BiquadFilter_process(hpfilter
, dst
+i
, temp
, todo
);
303 /* This function uses these device temp buffers. */
304 #define SOURCE_DATA_BUF 0
305 #define RESAMPLED_BUF 1
306 #define FILTERED_BUF 2
307 #define NFC_DATA_BUF 3
308 ALboolean
MixSource(ALvoice
*voice
, ALuint SourceID
, ALCcontext
*Context
, ALsizei SamplesToDo
)
310 ALCdevice
*Device
= Context
->Device
;
311 ALbufferlistitem
*BufferListItem
;
312 ALbufferlistitem
*BufferLoopItem
;
313 ALsizei NumChannels
, SampleSize
;
314 ALbitfieldSOFT enabledevt
;
315 ALsizei buffers_done
= 0;
316 ResamplerFunc Resample
;
330 /* Get source info */
331 isplaying
= true; /* Will only be called while playing. */
332 isstatic
= !!(voice
->Flags
&VOICE_IS_STATIC
);
333 DataPosInt
= ATOMIC_LOAD(&voice
->position
, almemory_order_acquire
);
334 DataPosFrac
= ATOMIC_LOAD(&voice
->position_fraction
, almemory_order_relaxed
);
335 BufferListItem
= ATOMIC_LOAD(&voice
->current_buffer
, almemory_order_relaxed
);
336 BufferLoopItem
= ATOMIC_LOAD(&voice
->loop_buffer
, almemory_order_relaxed
);
337 NumChannels
= voice
->NumChannels
;
338 SampleSize
= voice
->SampleSize
;
339 increment
= voice
->Step
;
341 IrSize
= (Device
->HrtfHandle
? Device
->HrtfHandle
->irSize
: 0);
343 Resample
= ((increment
== FRACTIONONE
&& DataPosFrac
== 0) ?
344 Resample_copy_C
: voice
->Resampler
);
346 Counter
= (voice
->Flags
&VOICE_IS_FADING
) ? SamplesToDo
: 0;
351 ALsizei SrcBufferSize
, DstBufferSize
;
353 /* Figure out how many buffer samples will be needed */
354 DataSize64
= SamplesToDo
-OutPos
;
355 DataSize64
*= increment
;
356 DataSize64
+= DataPosFrac
+FRACTIONMASK
;
357 DataSize64
>>= FRACTIONBITS
;
358 DataSize64
+= MAX_RESAMPLE_PADDING
*2;
359 SrcBufferSize
= (ALsizei
)mini64(DataSize64
, BUFFERSIZE
);
361 /* Figure out how many samples we can actually mix from this. */
362 DataSize64
= SrcBufferSize
;
363 DataSize64
-= MAX_RESAMPLE_PADDING
*2;
364 DataSize64
<<= FRACTIONBITS
;
365 DataSize64
-= DataPosFrac
;
366 DstBufferSize
= (ALsizei
)mini64((DataSize64
+(increment
-1)) / increment
,
367 SamplesToDo
- OutPos
);
369 /* Some mixers like having a multiple of 4, so try to give that unless
370 * this is the last update. */
371 if(DstBufferSize
< SamplesToDo
-OutPos
)
374 /* It's impossible to have a buffer list item with no entries. */
375 assert(BufferListItem
->num_buffers
> 0);
377 for(chan
= 0;chan
< NumChannels
;chan
++)
379 const ALfloat
*ResampledData
;
380 ALfloat
*SrcData
= Device
->TempBuffer
[SOURCE_DATA_BUF
];
383 /* Load the previous samples into the source data first, and clear the rest. */
384 memcpy(SrcData
, voice
->PrevSamples
[chan
], MAX_RESAMPLE_PADDING
*sizeof(ALfloat
));
385 memset(SrcData
+MAX_RESAMPLE_PADDING
, 0, (BUFFERSIZE
-MAX_RESAMPLE_PADDING
)*
387 FilledAmt
= MAX_RESAMPLE_PADDING
;
391 /* TODO: For static sources, loop points are taken from the
392 * first buffer (should be adjusted by any buffer offset, to
393 * possibly be added later).
395 const ALbuffer
*Buffer0
= BufferListItem
->buffers
[0];
396 const ALsizei LoopStart
= Buffer0
->LoopStart
;
397 const ALsizei LoopEnd
= Buffer0
->LoopEnd
;
398 const ALsizei LoopSize
= LoopEnd
- LoopStart
;
400 /* If current pos is beyond the loop range, do not loop */
401 if(!BufferLoopItem
|| DataPosInt
>= LoopEnd
)
403 ALsizei SizeToDo
= SrcBufferSize
- FilledAmt
;
407 BufferLoopItem
= NULL
;
409 for(i
= 0;i
< BufferListItem
->num_buffers
;i
++)
411 const ALbuffer
*buffer
= BufferListItem
->buffers
[i
];
412 const ALubyte
*Data
= buffer
->data
;
415 if(DataPosInt
>= buffer
->SampleLen
)
418 /* Load what's left to play from the buffer */
419 DataSize
= mini(SizeToDo
, buffer
->SampleLen
- DataPosInt
);
420 CompLen
= maxi(CompLen
, DataSize
);
422 LoadSamples(&SrcData
[FilledAmt
],
423 &Data
[(DataPosInt
*NumChannels
+ chan
)*SampleSize
],
424 NumChannels
, buffer
->FmtType
, DataSize
427 FilledAmt
+= CompLen
;
431 ALsizei SizeToDo
= mini(SrcBufferSize
- FilledAmt
, LoopEnd
- DataPosInt
);
435 for(i
= 0;i
< BufferListItem
->num_buffers
;i
++)
437 const ALbuffer
*buffer
= BufferListItem
->buffers
[i
];
438 const ALubyte
*Data
= buffer
->data
;
441 if(DataPosInt
>= buffer
->SampleLen
)
444 /* Load what's left of this loop iteration */
445 DataSize
= mini(SizeToDo
, buffer
->SampleLen
- DataPosInt
);
446 CompLen
= maxi(CompLen
, DataSize
);
448 LoadSamples(&SrcData
[FilledAmt
],
449 &Data
[(DataPosInt
*NumChannels
+ chan
)*SampleSize
],
450 NumChannels
, buffer
->FmtType
, DataSize
453 FilledAmt
+= CompLen
;
455 while(SrcBufferSize
> FilledAmt
)
457 const ALsizei SizeToDo
= mini(SrcBufferSize
- FilledAmt
, LoopSize
);
460 for(i
= 0;i
< BufferListItem
->num_buffers
;i
++)
462 const ALbuffer
*buffer
= BufferListItem
->buffers
[i
];
463 const ALubyte
*Data
= buffer
->data
;
466 if(LoopStart
>= buffer
->SampleLen
)
469 DataSize
= mini(SizeToDo
, buffer
->SampleLen
- LoopStart
);
470 CompLen
= maxi(CompLen
, DataSize
);
472 LoadSamples(&SrcData
[FilledAmt
],
473 &Data
[(LoopStart
*NumChannels
+ chan
)*SampleSize
],
474 NumChannels
, buffer
->FmtType
, DataSize
477 FilledAmt
+= CompLen
;
483 /* Crawl the buffer queue to fill in the temp buffer */
484 ALbufferlistitem
*tmpiter
= BufferListItem
;
485 ALsizei pos
= DataPosInt
;
487 while(tmpiter
&& SrcBufferSize
> FilledAmt
)
489 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
= minu(SizeToDo
, DataSize
- pos
);
503 LoadSamples(&SrcData
[FilledAmt
], Data
, NumChannels
,
504 ALBuffer
->FmtType
, DataSize
);
507 if(pos
> tmpiter
->max_samples
)
508 pos
-= tmpiter
->max_samples
;
511 FilledAmt
+= tmpiter
->max_samples
- pos
;
514 if(SrcBufferSize
> FilledAmt
)
516 tmpiter
= ATOMIC_LOAD(&tmpiter
->next
, almemory_order_acquire
);
517 if(!tmpiter
) tmpiter
= BufferLoopItem
;
522 /* Store the last source samples used for next time. */
523 memcpy(voice
->PrevSamples
[chan
],
524 &SrcData
[(increment
*DstBufferSize
+ DataPosFrac
)>>FRACTIONBITS
],
525 MAX_RESAMPLE_PADDING
*sizeof(ALfloat
)
528 /* Now resample, then filter and mix to the appropriate outputs. */
529 ResampledData
= Resample(&voice
->ResampleState
,
530 &SrcData
[MAX_RESAMPLE_PADDING
], DataPosFrac
, increment
,
531 Device
->TempBuffer
[RESAMPLED_BUF
], DstBufferSize
534 DirectParams
*parms
= &voice
->Direct
.Params
[chan
];
535 const ALfloat
*samples
;
538 &parms
->LowPass
, &parms
->HighPass
, Device
->TempBuffer
[FILTERED_BUF
],
539 ResampledData
, DstBufferSize
, voice
->Direct
.FilterType
541 if(!(voice
->Flags
&VOICE_HAS_HRTF
))
544 memcpy(parms
->Gains
.Current
, parms
->Gains
.Target
,
545 sizeof(parms
->Gains
.Current
));
546 if(!(voice
->Flags
&VOICE_HAS_NFC
))
547 MixSamples(samples
, voice
->Direct
.Channels
, voice
->Direct
.Buffer
,
548 parms
->Gains
.Current
, parms
->Gains
.Target
, Counter
, OutPos
,
553 ALfloat
*nfcsamples
= Device
->TempBuffer
[NFC_DATA_BUF
];
554 ALsizei chanoffset
= 0;
557 voice
->Direct
.ChannelsPerOrder
[0], voice
->Direct
.Buffer
,
558 parms
->Gains
.Current
, parms
->Gains
.Target
, Counter
, OutPos
,
561 chanoffset
+= voice
->Direct
.ChannelsPerOrder
[0];
562 #define APPLY_NFC_MIX(order) \
563 if(voice->Direct.ChannelsPerOrder[order] > 0) \
565 NfcFilterProcess##order(&parms->NFCtrlFilter, nfcsamples, samples, \
567 MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[order], \
568 voice->Direct.Buffer+chanoffset, parms->Gains.Current+chanoffset, \
569 parms->Gains.Target+chanoffset, Counter, OutPos, DstBufferSize \
571 chanoffset += voice->Direct.ChannelsPerOrder[order]; \
581 MixHrtfParams hrtfparams
;
585 lidx
= GetChannelIdxByName(&Device
->RealOut
, FrontLeft
);
586 ridx
= GetChannelIdxByName(&Device
->RealOut
, FrontRight
);
587 assert(lidx
!= -1 && ridx
!= -1);
591 /* No fading, just overwrite the old HRTF params. */
592 parms
->Hrtf
.Old
= parms
->Hrtf
.Target
;
594 else if(!(parms
->Hrtf
.Old
.Gain
> GAIN_SILENCE_THRESHOLD
))
596 /* The old HRTF params are silent, so overwrite the old
597 * coefficients with the new, and reset the old gain to
598 * 0. The future mix will then fade from silence.
600 parms
->Hrtf
.Old
= parms
->Hrtf
.Target
;
601 parms
->Hrtf
.Old
.Gain
= 0.0f
;
607 /* Fade between the coefficients over 128 samples. */
608 fademix
= mini(DstBufferSize
, 128);
610 /* The new coefficients need to fade in completely
611 * since they're replacing the old ones. To keep the
612 * gain fading consistent, interpolate between the old
613 * and new target gains given how much of the fade time
616 gain
= lerp(parms
->Hrtf
.Old
.Gain
, parms
->Hrtf
.Target
.Gain
,
617 minf(1.0f
, (ALfloat
)fademix
/Counter
));
618 hrtfparams
.Coeffs
= parms
->Hrtf
.Target
.Coeffs
;
619 hrtfparams
.Delay
[0] = parms
->Hrtf
.Target
.Delay
[0];
620 hrtfparams
.Delay
[1] = parms
->Hrtf
.Target
.Delay
[1];
621 hrtfparams
.Gain
= 0.0f
;
622 hrtfparams
.GainStep
= gain
/ (ALfloat
)fademix
;
625 voice
->Direct
.Buffer
[lidx
], voice
->Direct
.Buffer
[ridx
],
626 samples
, voice
->Offset
, OutPos
, IrSize
, &parms
->Hrtf
.Old
,
627 &hrtfparams
, &parms
->Hrtf
.State
, fademix
629 /* Update the old parameters with the result. */
630 parms
->Hrtf
.Old
= parms
->Hrtf
.Target
;
631 if(fademix
< Counter
)
632 parms
->Hrtf
.Old
.Gain
= hrtfparams
.Gain
;
635 if(fademix
< DstBufferSize
)
637 ALsizei todo
= DstBufferSize
- fademix
;
638 ALfloat gain
= parms
->Hrtf
.Target
.Gain
;
640 /* Interpolate the target gain if the gain fading lasts
641 * longer than this mix.
643 if(Counter
> DstBufferSize
)
644 gain
= lerp(parms
->Hrtf
.Old
.Gain
, gain
,
645 (ALfloat
)todo
/(Counter
-fademix
));
647 hrtfparams
.Coeffs
= parms
->Hrtf
.Target
.Coeffs
;
648 hrtfparams
.Delay
[0] = parms
->Hrtf
.Target
.Delay
[0];
649 hrtfparams
.Delay
[1] = parms
->Hrtf
.Target
.Delay
[1];
650 hrtfparams
.Gain
= parms
->Hrtf
.Old
.Gain
;
651 hrtfparams
.GainStep
= (gain
- parms
->Hrtf
.Old
.Gain
) / (ALfloat
)todo
;
653 voice
->Direct
.Buffer
[lidx
], voice
->Direct
.Buffer
[ridx
],
654 samples
+fademix
, voice
->Offset
+fademix
, OutPos
+fademix
, IrSize
,
655 &hrtfparams
, &parms
->Hrtf
.State
, todo
657 /* Store the interpolated gain or the final target gain
658 * depending if the fade is done.
660 if(DstBufferSize
< Counter
)
661 parms
->Hrtf
.Old
.Gain
= gain
;
663 parms
->Hrtf
.Old
.Gain
= parms
->Hrtf
.Target
.Gain
;
668 for(send
= 0;send
< Device
->NumAuxSends
;send
++)
670 SendParams
*parms
= &voice
->Send
[send
].Params
[chan
];
671 const ALfloat
*samples
;
673 if(!voice
->Send
[send
].Buffer
)
677 &parms
->LowPass
, &parms
->HighPass
, Device
->TempBuffer
[FILTERED_BUF
],
678 ResampledData
, DstBufferSize
, voice
->Send
[send
].FilterType
682 memcpy(parms
->Gains
.Current
, parms
->Gains
.Target
,
683 sizeof(parms
->Gains
.Current
));
684 MixSamples(samples
, voice
->Send
[send
].Channels
, voice
->Send
[send
].Buffer
,
685 parms
->Gains
.Current
, parms
->Gains
.Target
, Counter
, OutPos
, DstBufferSize
689 /* Update positions */
690 DataPosFrac
+= increment
*DstBufferSize
;
691 DataPosInt
+= DataPosFrac
>>FRACTIONBITS
;
692 DataPosFrac
&= FRACTIONMASK
;
694 OutPos
+= DstBufferSize
;
695 voice
->Offset
+= DstBufferSize
;
696 Counter
= maxi(DstBufferSize
, Counter
) - DstBufferSize
;
703 /* Handle looping static source */
704 const ALbuffer
*Buffer
= BufferListItem
->buffers
[0];
705 ALsizei LoopStart
= Buffer
->LoopStart
;
706 ALsizei LoopEnd
= Buffer
->LoopEnd
;
707 if(DataPosInt
>= LoopEnd
)
709 assert(LoopEnd
> LoopStart
);
710 DataPosInt
= ((DataPosInt
-LoopStart
)%(LoopEnd
-LoopStart
)) + LoopStart
;
715 /* Handle non-looping static source */
716 if(DataPosInt
>= BufferListItem
->max_samples
)
719 BufferListItem
= NULL
;
728 /* Handle streaming source */
729 if(BufferListItem
->max_samples
> DataPosInt
)
732 buffers_done
+= BufferListItem
->num_buffers
;
733 BufferListItem
= ATOMIC_LOAD(&BufferListItem
->next
, almemory_order_acquire
);
734 if(!BufferListItem
&& !(BufferListItem
=BufferLoopItem
))
742 DataPosInt
-= BufferListItem
->max_samples
;
744 } while(isplaying
&& OutPos
< SamplesToDo
);
746 voice
->Flags
|= VOICE_IS_FADING
;
748 /* Update source info */
749 ATOMIC_STORE(&voice
->position
, DataPosInt
, almemory_order_relaxed
);
750 ATOMIC_STORE(&voice
->position_fraction
, DataPosFrac
, almemory_order_relaxed
);
751 ATOMIC_STORE(&voice
->current_buffer
, BufferListItem
, almemory_order_release
);
753 /* Send any events now, after the position/buffer info was updated. */
754 enabledevt
= ATOMIC_LOAD(&Context
->EnabledEvts
, almemory_order_acquire
);
755 if(buffers_done
> 0 && (enabledevt
&EventType_BufferCompleted
))
756 SendAsyncEvent(Context
, EventType_BufferCompleted
,
757 AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT
, SourceID
, buffers_done
, "Buffer completed"