Update ChangeLog
[openal-soft.git] / Alc / mixer.c
blobb5c4605be27268007a44784a247607fc52ae42dd
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"
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(ALsizei frac, ALint increment, ALsizei *restrict frac_arr, ALint *restrict pos_arr, ALsizei size);
47 /* BSinc requires up to 11 extra samples before the current position, and 12 after. */
48 static_assert(MAX_PRE_SAMPLES >= 11, "MAX_PRE_SAMPLES must be at least 11!");
49 static_assert(MAX_POST_SAMPLES >= 12, "MAX_POST_SAMPLES must be at least 12!");
52 enum Resampler ResamplerDefault = LinearResampler;
54 static MixerFunc MixSamples = Mix_C;
55 static HrtfMixerFunc MixHrtfSamples = MixHrtf_C;
56 HrtfMixerBlendFunc MixHrtfBlendSamples = MixHrtfBlend_C;
58 MixerFunc SelectMixer(void)
60 #ifdef HAVE_NEON
61 if((CPUCapFlags&CPU_CAP_NEON))
62 return Mix_Neon;
63 #endif
64 #ifdef HAVE_SSE
65 if((CPUCapFlags&CPU_CAP_SSE))
66 return Mix_SSE;
67 #endif
68 return Mix_C;
71 RowMixerFunc SelectRowMixer(void)
73 #ifdef HAVE_NEON
74 if((CPUCapFlags&CPU_CAP_NEON))
75 return MixRow_Neon;
76 #endif
77 #ifdef HAVE_SSE
78 if((CPUCapFlags&CPU_CAP_SSE))
79 return MixRow_SSE;
80 #endif
81 return MixRow_C;
84 static inline HrtfMixerFunc SelectHrtfMixer(void)
86 #ifdef HAVE_NEON
87 if((CPUCapFlags&CPU_CAP_NEON))
88 return MixHrtf_Neon;
89 #endif
90 #ifdef HAVE_SSE
91 if((CPUCapFlags&CPU_CAP_SSE))
92 return MixHrtf_SSE;
93 #endif
94 return MixHrtf_C;
97 static inline HrtfMixerBlendFunc SelectHrtfBlendMixer(void)
99 #ifdef HAVE_NEON
100 if((CPUCapFlags&CPU_CAP_NEON))
101 return MixHrtfBlend_Neon;
102 #endif
103 #ifdef HAVE_SSE
104 if((CPUCapFlags&CPU_CAP_SSE))
105 return MixHrtfBlend_SSE;
106 #endif
107 return MixHrtfBlend_C;
110 ResamplerFunc SelectResampler(enum Resampler resampler)
112 switch(resampler)
114 case PointResampler:
115 return Resample_point32_C;
116 case LinearResampler:
117 #ifdef HAVE_NEON
118 if((CPUCapFlags&CPU_CAP_NEON))
119 return Resample_lerp32_Neon;
120 #endif
121 #ifdef HAVE_SSE4_1
122 if((CPUCapFlags&CPU_CAP_SSE4_1))
123 return Resample_lerp32_SSE41;
124 #endif
125 #ifdef HAVE_SSE2
126 if((CPUCapFlags&CPU_CAP_SSE2))
127 return Resample_lerp32_SSE2;
128 #endif
129 return Resample_lerp32_C;
130 case FIR4Resampler:
131 #ifdef HAVE_NEON
132 if((CPUCapFlags&CPU_CAP_NEON))
133 return Resample_fir4_32_Neon;
134 #endif
135 #ifdef HAVE_SSE4_1
136 if((CPUCapFlags&CPU_CAP_SSE4_1))
137 return Resample_fir4_32_SSE41;
138 #endif
139 #ifdef HAVE_SSE3
140 if((CPUCapFlags&CPU_CAP_SSE3))
141 return Resample_fir4_32_SSE3;
142 #endif
143 return Resample_fir4_32_C;
144 case BSincResampler:
145 #ifdef HAVE_NEON
146 if((CPUCapFlags&CPU_CAP_NEON))
147 return Resample_bsinc32_Neon;
148 #endif
149 #ifdef HAVE_SSE
150 if((CPUCapFlags&CPU_CAP_SSE))
151 return Resample_bsinc32_SSE;
152 #endif
153 return Resample_bsinc32_C;
156 return Resample_point32_C;
160 void aluInitMixer(void)
162 const char *str;
164 if(ConfigValueStr(NULL, NULL, "resampler", &str))
166 if(strcasecmp(str, "point") == 0 || strcasecmp(str, "none") == 0)
167 ResamplerDefault = PointResampler;
168 else if(strcasecmp(str, "linear") == 0)
169 ResamplerDefault = LinearResampler;
170 else if(strcasecmp(str, "sinc4") == 0)
171 ResamplerDefault = FIR4Resampler;
172 else if(strcasecmp(str, "bsinc") == 0)
173 ResamplerDefault = BSincResampler;
174 else if(strcasecmp(str, "cubic") == 0 || strcasecmp(str, "sinc8") == 0)
176 WARN("Resampler option \"%s\" is deprecated, using sinc4\n", str);
177 ResamplerDefault = FIR4Resampler;
179 else
181 char *end;
182 long n = strtol(str, &end, 0);
183 if(*end == '\0' && (n == PointResampler || n == LinearResampler || n == FIR4Resampler))
184 ResamplerDefault = n;
185 else
186 WARN("Invalid resampler: %s\n", str);
190 MixHrtfBlendSamples = SelectHrtfBlendMixer();
191 MixHrtfSamples = SelectHrtfMixer();
192 MixSamples = SelectMixer();
196 static inline ALfloat Sample_ALbyte(ALbyte val)
197 { return val * (1.0f/128.0f); }
199 static inline ALfloat Sample_ALshort(ALshort val)
200 { return val * (1.0f/32768.0f); }
202 static inline ALfloat Sample_ALfloat(ALfloat val)
203 { return val; }
205 #define DECL_TEMPLATE(T) \
206 static inline void Load_##T(ALfloat *dst, const T *src, ALint srcstep, ALsizei samples)\
208 ALsizei i; \
209 for(i = 0;i < samples;i++) \
210 dst[i] = Sample_##T(src[i*srcstep]); \
213 DECL_TEMPLATE(ALbyte)
214 DECL_TEMPLATE(ALshort)
215 DECL_TEMPLATE(ALfloat)
217 #undef DECL_TEMPLATE
219 static void LoadSamples(ALfloat *dst, const ALvoid *src, ALint srcstep, enum FmtType srctype, ALsizei samples)
221 switch(srctype)
223 case FmtByte:
224 Load_ALbyte(dst, src, srcstep, samples);
225 break;
226 case FmtShort:
227 Load_ALshort(dst, src, srcstep, samples);
228 break;
229 case FmtFloat:
230 Load_ALfloat(dst, src, srcstep, samples);
231 break;
235 static inline void SilenceSamples(ALfloat *dst, ALsizei samples)
237 ALsizei i;
238 for(i = 0;i < samples;i++)
239 dst[i] = 0.0f;
243 static const ALfloat *DoFilters(ALfilterState *lpfilter, ALfilterState *hpfilter,
244 ALfloat *restrict dst, const ALfloat *restrict src,
245 ALsizei numsamples, enum ActiveFilters type)
247 ALsizei i;
248 switch(type)
250 case AF_None:
251 ALfilterState_processPassthru(lpfilter, src, numsamples);
252 ALfilterState_processPassthru(hpfilter, src, numsamples);
253 break;
255 case AF_LowPass:
256 ALfilterState_process(lpfilter, dst, src, numsamples);
257 ALfilterState_processPassthru(hpfilter, dst, numsamples);
258 return dst;
259 case AF_HighPass:
260 ALfilterState_processPassthru(lpfilter, src, numsamples);
261 ALfilterState_process(hpfilter, dst, src, numsamples);
262 return dst;
264 case AF_BandPass:
265 for(i = 0;i < numsamples;)
267 ALfloat temp[256];
268 ALsizei todo = mini(256, numsamples-i);
270 ALfilterState_process(lpfilter, temp, src+i, todo);
271 ALfilterState_process(hpfilter, dst+i, temp, todo);
272 i += todo;
274 return dst;
276 return src;
280 ALboolean MixSource(ALvoice *voice, ALsource *Source, ALCdevice *Device, ALsizei SamplesToDo)
282 ALbufferlistitem *BufferListItem;
283 ALbufferlistitem *BufferLoopItem;
284 ALsizei NumChannels, SampleSize;
285 ResamplerFunc Resample;
286 ALsizei DataPosInt;
287 ALsizei DataPosFrac;
288 ALint64 DataSize64;
289 ALint increment;
290 ALsizei Counter;
291 ALsizei OutPos;
292 ALsizei IrSize;
293 bool isplaying;
294 bool firstpass;
295 ALsizei chan;
296 ALsizei send;
298 /* Get source info */
299 isplaying = true; /* Will only be called while playing. */
300 DataPosInt = ATOMIC_LOAD(&voice->position, almemory_order_acquire);
301 DataPosFrac = ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed);
302 BufferListItem = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
303 BufferLoopItem = ATOMIC_LOAD(&voice->loop_buffer, almemory_order_relaxed);
304 NumChannels = voice->NumChannels;
305 SampleSize = voice->SampleSize;
306 increment = voice->Step;
308 IrSize = (Device->HrtfHandle ? Device->HrtfHandle->irSize : 0);
310 Resample = ((increment == FRACTIONONE && DataPosFrac == 0) ?
311 Resample_copy32_C : voice->Resampler);
313 Counter = (voice->Flags&VOICE_IS_FADING) ? SamplesToDo : 0;
314 firstpass = true;
315 OutPos = 0;
317 do {
318 ALsizei SrcBufferSize, DstBufferSize;
320 /* Figure out how many buffer samples will be needed */
321 DataSize64 = SamplesToDo-OutPos;
322 DataSize64 *= increment;
323 DataSize64 += DataPosFrac+FRACTIONMASK;
324 DataSize64 >>= FRACTIONBITS;
325 DataSize64 += MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
327 SrcBufferSize = (ALsizei)mini64(DataSize64, BUFFERSIZE);
329 /* Figure out how many samples we can actually mix from this. */
330 DataSize64 = SrcBufferSize;
331 DataSize64 -= MAX_POST_SAMPLES+MAX_PRE_SAMPLES;
332 DataSize64 <<= FRACTIONBITS;
333 DataSize64 -= DataPosFrac;
335 DstBufferSize = (ALsizei)((DataSize64+(increment-1)) / increment);
336 DstBufferSize = mini(DstBufferSize, (SamplesToDo-OutPos));
338 /* Some mixers like having a multiple of 4, so try to give that unless
339 * this is the last update. */
340 if(OutPos+DstBufferSize < SamplesToDo)
341 DstBufferSize &= ~3;
343 for(chan = 0;chan < NumChannels;chan++)
345 const ALfloat *ResampledData;
346 ALfloat *SrcData = Device->SourceData;
347 ALsizei SrcDataSize;
349 /* Load the previous samples into the source data first. */
350 memcpy(SrcData, voice->PrevSamples[chan], MAX_PRE_SAMPLES*sizeof(ALfloat));
351 SrcDataSize = MAX_PRE_SAMPLES;
353 if(Source->SourceType == AL_STATIC)
355 const ALbuffer *ALBuffer = BufferListItem->buffer;
356 const ALubyte *Data = ALBuffer->data;
357 ALsizei DataSize;
359 /* Offset buffer data to current channel */
360 Data += chan*SampleSize;
362 /* If current pos is beyond the loop range, do not loop */
363 if(!BufferLoopItem || DataPosInt >= ALBuffer->LoopEnd)
365 BufferLoopItem = NULL;
367 /* Load what's left to play from the source buffer, and
368 * clear the rest of the temp buffer */
369 DataSize = minu(SrcBufferSize - SrcDataSize,
370 ALBuffer->SampleLen - DataPosInt);
372 LoadSamples(&SrcData[SrcDataSize], &Data[DataPosInt * NumChannels*SampleSize],
373 NumChannels, ALBuffer->FmtType, DataSize);
374 SrcDataSize += DataSize;
376 SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
377 SrcDataSize += SrcBufferSize - SrcDataSize;
379 else
381 ALsizei LoopStart = ALBuffer->LoopStart;
382 ALsizei LoopEnd = ALBuffer->LoopEnd;
384 /* Load what's left of this loop iteration, then load
385 * repeats of the loop section */
386 DataSize = minu(SrcBufferSize - SrcDataSize, LoopEnd - DataPosInt);
388 LoadSamples(&SrcData[SrcDataSize], &Data[DataPosInt * NumChannels*SampleSize],
389 NumChannels, ALBuffer->FmtType, DataSize);
390 SrcDataSize += DataSize;
392 DataSize = LoopEnd-LoopStart;
393 while(SrcBufferSize > SrcDataSize)
395 DataSize = mini(SrcBufferSize - SrcDataSize, DataSize);
397 LoadSamples(&SrcData[SrcDataSize], &Data[LoopStart * NumChannels*SampleSize],
398 NumChannels, ALBuffer->FmtType, DataSize);
399 SrcDataSize += DataSize;
403 else
405 /* Crawl the buffer queue to fill in the temp buffer */
406 ALbufferlistitem *tmpiter = BufferListItem;
407 ALsizei pos = DataPosInt;
409 while(tmpiter && SrcBufferSize > SrcDataSize)
411 const ALbuffer *ALBuffer;
412 if((ALBuffer=tmpiter->buffer) != NULL)
414 const ALubyte *Data = ALBuffer->data;
415 ALsizei DataSize = ALBuffer->SampleLen;
417 /* Skip the data already played */
418 if(DataSize <= pos)
419 pos -= DataSize;
420 else
422 Data += (pos*NumChannels + chan)*SampleSize;
423 DataSize -= pos;
424 pos -= pos;
426 DataSize = minu(SrcBufferSize - SrcDataSize, DataSize);
427 LoadSamples(&SrcData[SrcDataSize], Data, NumChannels,
428 ALBuffer->FmtType, DataSize);
429 SrcDataSize += DataSize;
432 tmpiter = ATOMIC_LOAD(&tmpiter->next, almemory_order_acquire);
433 if(!tmpiter && BufferLoopItem)
434 tmpiter = BufferLoopItem;
435 else if(!tmpiter)
437 SilenceSamples(&SrcData[SrcDataSize], SrcBufferSize - SrcDataSize);
438 SrcDataSize += SrcBufferSize - SrcDataSize;
443 /* Store the last source samples used for next time. */
444 memcpy(voice->PrevSamples[chan],
445 &SrcData[(increment*DstBufferSize + DataPosFrac)>>FRACTIONBITS],
446 MAX_PRE_SAMPLES*sizeof(ALfloat)
449 /* Now resample, then filter and mix to the appropriate outputs. */
450 ResampledData = Resample(&voice->ResampleState,
451 &SrcData[MAX_PRE_SAMPLES], DataPosFrac, increment,
452 Device->ResampledData, DstBufferSize
455 DirectParams *parms = &voice->Direct.Params[chan];
456 const ALfloat *samples;
458 samples = DoFilters(
459 &parms->LowPass, &parms->HighPass, Device->FilteredData,
460 ResampledData, DstBufferSize, voice->Direct.FilterType
462 if(!(voice->Flags&VOICE_HAS_HRTF))
464 if(!Counter)
465 memcpy(parms->Gains.Current, parms->Gains.Target,
466 sizeof(parms->Gains.Current));
467 if(!(voice->Flags&VOICE_HAS_NFC))
468 MixSamples(samples, voice->Direct.Channels, voice->Direct.Buffer,
469 parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
470 DstBufferSize
472 else
474 ALfloat *nfcsamples = Device->NFCtrlData;
475 ALsizei chanoffset = 0;
477 MixSamples(samples,
478 voice->Direct.ChannelsPerOrder[0], voice->Direct.Buffer,
479 parms->Gains.Current, parms->Gains.Target, Counter, OutPos,
480 DstBufferSize
482 chanoffset += voice->Direct.ChannelsPerOrder[0];
483 #define APPLY_NFC_MIX(order) \
484 if(voice->Direct.ChannelsPerOrder[order] > 0) \
486 NfcFilterUpdate##order(&parms->NFCtrlFilter[order-1], nfcsamples, \
487 samples, DstBufferSize); \
488 MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[order], \
489 voice->Direct.Buffer+chanoffset, parms->Gains.Current+chanoffset, \
490 parms->Gains.Target+chanoffset, Counter, OutPos, DstBufferSize \
491 ); \
492 chanoffset += voice->Direct.ChannelsPerOrder[order]; \
494 APPLY_NFC_MIX(1)
495 APPLY_NFC_MIX(2)
496 APPLY_NFC_MIX(3)
497 #undef APPLY_NFC_MIX
500 else
502 MixHrtfParams hrtfparams;
503 ALsizei fademix = 0;
504 int lidx, ridx;
506 lidx = GetChannelIdxByName(Device->RealOut, FrontLeft);
507 ridx = GetChannelIdxByName(Device->RealOut, FrontRight);
508 assert(lidx != -1 && ridx != -1);
510 if(!Counter)
512 /* No fading, just overwrite the old HRTF params. */
513 parms->Hrtf.Old = parms->Hrtf.Target;
515 else if(!(parms->Hrtf.Old.Gain > GAIN_SILENCE_THRESHOLD))
517 /* The old HRTF params are silent, so overwrite the old
518 * coefficients with the new, and reset the old gain to
519 * 0. The future mix will then fade from silence.
521 parms->Hrtf.Old = parms->Hrtf.Target;
522 parms->Hrtf.Old.Gain = 0.0f;
524 else if(firstpass)
526 ALfloat gain;
528 /* Fade between the coefficients over 128 samples. */
529 fademix = mini(DstBufferSize, 128);
531 /* The new coefficients need to fade in completely
532 * since they're replacing the old ones. To keep the
533 * gain fading consistent, interpolate between the old
534 * and new target gains given how much of the fade time
535 * this mix handles.
537 gain = lerp(parms->Hrtf.Old.Gain, parms->Hrtf.Target.Gain,
538 minf(1.0f, (ALfloat)fademix/Counter));
539 hrtfparams.Coeffs = SAFE_CONST(ALfloat2*,parms->Hrtf.Target.Coeffs);
540 hrtfparams.Delay[0] = parms->Hrtf.Target.Delay[0];
541 hrtfparams.Delay[1] = parms->Hrtf.Target.Delay[1];
542 hrtfparams.Gain = 0.0f;
543 hrtfparams.GainStep = gain / (ALfloat)fademix;
545 MixHrtfBlendSamples(
546 voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx],
547 samples, voice->Offset, OutPos, IrSize, &parms->Hrtf.Old,
548 &hrtfparams, &parms->Hrtf.State, fademix
550 /* Update the old parameters with the result. */
551 parms->Hrtf.Old = parms->Hrtf.Target;
552 if(fademix < Counter)
553 parms->Hrtf.Old.Gain = hrtfparams.Gain;
556 if(fademix < DstBufferSize)
558 ALsizei todo = DstBufferSize - fademix;
559 ALfloat gain = parms->Hrtf.Target.Gain;
561 /* Interpolate the target gain if the gain fading lasts
562 * longer than this mix.
564 if(Counter > DstBufferSize)
565 gain = lerp(parms->Hrtf.Old.Gain, gain,
566 (ALfloat)todo/(Counter-fademix));
568 hrtfparams.Coeffs = SAFE_CONST(ALfloat2*,parms->Hrtf.Target.Coeffs);
569 hrtfparams.Delay[0] = parms->Hrtf.Target.Delay[0];
570 hrtfparams.Delay[1] = parms->Hrtf.Target.Delay[1];
571 hrtfparams.Gain = parms->Hrtf.Old.Gain;
572 hrtfparams.GainStep = (gain - parms->Hrtf.Old.Gain) / (ALfloat)todo;
573 MixHrtfSamples(
574 voice->Direct.Buffer[lidx], voice->Direct.Buffer[ridx],
575 samples+fademix, voice->Offset+fademix, OutPos+fademix, IrSize,
576 &hrtfparams, &parms->Hrtf.State, todo
578 /* Store the interpolated gain or the final target gain
579 * depending if the fade is done.
581 if(DstBufferSize < Counter)
582 parms->Hrtf.Old.Gain = gain;
583 else
584 parms->Hrtf.Old.Gain = parms->Hrtf.Target.Gain;
589 for(send = 0;send < Device->NumAuxSends;send++)
591 SendParams *parms = &voice->Send[send].Params[chan];
592 const ALfloat *samples;
594 if(!voice->Send[send].Buffer)
595 continue;
597 samples = DoFilters(
598 &parms->LowPass, &parms->HighPass, Device->FilteredData,
599 ResampledData, DstBufferSize, voice->Send[send].FilterType
602 if(!Counter)
603 memcpy(parms->Gains.Current, parms->Gains.Target,
604 sizeof(parms->Gains.Current));
605 MixSamples(samples, voice->Send[send].Channels, voice->Send[send].Buffer,
606 parms->Gains.Current, parms->Gains.Target, Counter, OutPos, DstBufferSize
610 /* Update positions */
611 DataPosFrac += increment*DstBufferSize;
612 DataPosInt += DataPosFrac>>FRACTIONBITS;
613 DataPosFrac &= FRACTIONMASK;
615 OutPos += DstBufferSize;
616 voice->Offset += DstBufferSize;
617 Counter = maxi(DstBufferSize, Counter) - DstBufferSize;
618 firstpass = false;
620 /* Handle looping sources */
621 while(1)
623 const ALbuffer *ALBuffer;
624 ALsizei DataSize = 0;
625 ALsizei LoopStart = 0;
626 ALsizei LoopEnd = 0;
628 if((ALBuffer=BufferListItem->buffer) != NULL)
630 DataSize = ALBuffer->SampleLen;
631 LoopStart = ALBuffer->LoopStart;
632 LoopEnd = ALBuffer->LoopEnd;
633 if(LoopEnd > DataPosInt)
634 break;
637 if(BufferLoopItem && Source->SourceType == AL_STATIC)
639 assert(LoopEnd > LoopStart);
640 DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
641 break;
644 if(DataSize > DataPosInt)
645 break;
647 BufferListItem = ATOMIC_LOAD(&BufferListItem->next, almemory_order_acquire);
648 if(!BufferListItem)
650 BufferListItem = BufferLoopItem;
651 if(!BufferListItem)
653 isplaying = false;
654 DataPosInt = 0;
655 DataPosFrac = 0;
656 break;
660 DataPosInt -= DataSize;
662 } while(isplaying && OutPos < SamplesToDo);
664 voice->Flags |= VOICE_IS_FADING;
666 /* Update source info */
667 ATOMIC_STORE(&voice->position, DataPosInt, almemory_order_relaxed);
668 ATOMIC_STORE(&voice->position_fraction, DataPosFrac, almemory_order_relaxed);
669 ATOMIC_STORE(&voice->current_buffer, BufferListItem, almemory_order_release);
670 return isplaying;