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
39 #include "al/buffer.h"
41 #include "al/source.h"
45 #include "alcontext.h"
46 #include "alnumeric.h"
47 #include "aloptional.h"
52 #include "devformat.h"
53 #include "filters/biquad.h"
54 #include "filters/nfc.h"
55 #include "filters/splitter.h"
57 #include "inprogext.h"
59 #include "mixer/defs.h"
60 #include "opthelpers.h"
61 #include "ringbuffer.h"
66 static_assert((INT_MAX
>>FRACTIONBITS
)/MAX_PITCH
> BUFFERSIZE
,
67 "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
70 Resampler ResamplerDefault
{Resampler::Linear
};
72 MixerFunc MixSamples
= Mix_
<CTag
>;
73 RowMixerFunc MixRowSamples
= MixRow_
<CTag
>;
77 HrtfMixerFunc MixHrtfSamples
= MixHrtf_
<CTag
>;
78 HrtfMixerBlendFunc MixHrtfBlendSamples
= MixHrtfBlend_
<CTag
>;
80 inline MixerFunc
SelectMixer()
83 if((CPUCapFlags
&CPU_CAP_NEON
))
87 if((CPUCapFlags
&CPU_CAP_SSE
))
93 inline RowMixerFunc
SelectRowMixer()
96 if((CPUCapFlags
&CPU_CAP_NEON
))
97 return MixRow_
<NEONTag
>;
100 if((CPUCapFlags
&CPU_CAP_SSE
))
101 return MixRow_
<SSETag
>;
103 return MixRow_
<CTag
>;
106 inline HrtfMixerFunc
SelectHrtfMixer()
109 if((CPUCapFlags
&CPU_CAP_NEON
))
110 return MixHrtf_
<NEONTag
>;
113 if((CPUCapFlags
&CPU_CAP_SSE
))
114 return MixHrtf_
<SSETag
>;
116 return MixHrtf_
<CTag
>;
119 inline HrtfMixerBlendFunc
SelectHrtfBlendMixer()
122 if((CPUCapFlags
&CPU_CAP_NEON
))
123 return MixHrtfBlend_
<NEONTag
>;
126 if((CPUCapFlags
&CPU_CAP_SSE
))
127 return MixHrtfBlend_
<SSETag
>;
129 return MixHrtfBlend_
<CTag
>;
137 if(auto resopt
= ConfigValueStr(nullptr, nullptr, "resampler"))
139 struct ResamplerEntry
{
141 const Resampler resampler
;
143 constexpr ResamplerEntry ResamplerList
[]{
144 { "none", Resampler::Point
},
145 { "point", Resampler::Point
},
146 { "cubic", Resampler::Cubic
},
147 { "bsinc12", Resampler::BSinc12
},
148 { "fast_bsinc12", Resampler::FastBSinc12
},
149 { "bsinc24", Resampler::BSinc24
},
150 { "fast_bsinc24", Resampler::FastBSinc24
},
153 const char *str
{resopt
->c_str()};
154 if(al::strcasecmp(str
, "bsinc") == 0)
156 WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", str
);
159 else if(al::strcasecmp(str
, "sinc4") == 0 || al::strcasecmp(str
, "sinc8") == 0)
161 WARN("Resampler option \"%s\" is deprecated, using cubic\n", str
);
165 auto iter
= std::find_if(std::begin(ResamplerList
), std::end(ResamplerList
),
166 [str
](const ResamplerEntry
&entry
) -> bool
167 { return al::strcasecmp(str
, entry
.name
) == 0; });
168 if(iter
== std::end(ResamplerList
))
169 ERR("Invalid resampler: %s\n", str
);
171 ResamplerDefault
= iter
->resampler
;
174 MixHrtfBlendSamples
= SelectHrtfBlendMixer();
175 MixHrtfSamples
= SelectHrtfMixer();
176 MixSamples
= SelectMixer();
177 MixRowSamples
= SelectRowMixer();
183 /* A quick'n'dirty lookup table to decode a muLaw-encoded byte sample into a
184 * signed 16-bit sample */
185 constexpr ALshort muLawDecompressionTable
[256] = {
186 -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
187 -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
188 -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
189 -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
190 -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
191 -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
192 -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
193 -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
194 -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
195 -1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
196 -876, -844, -812, -780, -748, -716, -684, -652,
197 -620, -588, -556, -524, -492, -460, -428, -396,
198 -372, -356, -340, -324, -308, -292, -276, -260,
199 -244, -228, -212, -196, -180, -164, -148, -132,
200 -120, -112, -104, -96, -88, -80, -72, -64,
201 -56, -48, -40, -32, -24, -16, -8, 0,
202 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
203 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
204 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
205 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
206 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
207 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
208 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
209 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
210 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
211 1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
212 876, 844, 812, 780, 748, 716, 684, 652,
213 620, 588, 556, 524, 492, 460, 428, 396,
214 372, 356, 340, 324, 308, 292, 276, 260,
215 244, 228, 212, 196, 180, 164, 148, 132,
216 120, 112, 104, 96, 88, 80, 72, 64,
217 56, 48, 40, 32, 24, 16, 8, 0
220 /* A quick'n'dirty lookup table to decode an aLaw-encoded byte sample into a
221 * signed 16-bit sample */
222 constexpr ALshort aLawDecompressionTable
[256] = {
223 -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
224 -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
225 -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
226 -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
227 -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
228 -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
229 -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472,
230 -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
231 -344, -328, -376, -360, -280, -264, -312, -296,
232 -472, -456, -504, -488, -408, -392, -440, -424,
233 -88, -72, -120, -104, -24, -8, -56, -40,
234 -216, -200, -248, -232, -152, -136, -184, -168,
235 -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
236 -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
237 -688, -656, -752, -720, -560, -528, -624, -592,
238 -944, -912, -1008, -976, -816, -784, -880, -848,
239 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
240 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
241 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
242 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
243 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
244 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
245 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
246 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
247 344, 328, 376, 360, 280, 264, 312, 296,
248 472, 456, 504, 488, 408, 392, 440, 424,
249 88, 72, 120, 104, 24, 8, 56, 40,
250 216, 200, 248, 232, 152, 136, 184, 168,
251 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
252 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
253 688, 656, 752, 720, 560, 528, 624, 592,
254 944, 912, 1008, 976, 816, 784, 880, 848
258 struct FmtTypeTraits
{ };
261 struct FmtTypeTraits
<FmtUByte
> {
262 using Type
= ALubyte
;
263 static constexpr inline float to_float(const Type val
) noexcept
264 { return val
*(1.0f
/128.0f
) - 1.0f
; }
267 struct FmtTypeTraits
<FmtShort
> {
268 using Type
= ALshort
;
269 static constexpr inline float to_float(const Type val
) noexcept
{ return val
*(1.0f
/32768.0f
); }
272 struct FmtTypeTraits
<FmtFloat
> {
273 using Type
= ALfloat
;
274 static constexpr inline float to_float(const Type val
) noexcept
{ return val
; }
277 struct FmtTypeTraits
<FmtDouble
> {
278 using Type
= ALdouble
;
279 static constexpr inline float to_float(const Type val
) noexcept
280 { return static_cast<ALfloat
>(val
); }
283 struct FmtTypeTraits
<FmtMulaw
> {
284 using Type
= ALubyte
;
285 static constexpr inline float to_float(const Type val
) noexcept
286 { return muLawDecompressionTable
[val
] * (1.0f
/32768.0f
); }
289 struct FmtTypeTraits
<FmtAlaw
> {
290 using Type
= ALubyte
;
291 static constexpr inline float to_float(const Type val
) noexcept
292 { return aLawDecompressionTable
[val
] * (1.0f
/32768.0f
); }
296 void SendSourceStoppedEvent(ALCcontext
*context
, ALuint id
)
298 RingBuffer
*ring
{context
->mAsyncEvents
.get()};
299 auto evt_vec
= ring
->getWriteVector();
300 if(evt_vec
.first
.len
< 1) return;
302 AsyncEvent
*evt
{new (evt_vec
.first
.buf
) AsyncEvent
{EventType_SourceStateChange
}};
303 evt
->u
.srcstate
.id
= id
;
304 evt
->u
.srcstate
.state
= AL_STOPPED
;
306 ring
->writeAdvance(1);
307 context
->mEventSem
.post();
311 const ALfloat
*DoFilters(BiquadFilter
*lpfilter
, BiquadFilter
*hpfilter
, ALfloat
*dst
,
312 const ALfloat
*src
, const size_t numsamples
, int type
)
322 lpfilter
->process(dst
, src
, numsamples
);
327 hpfilter
->process(dst
, src
, numsamples
);
331 lpfilter
->process(dst
, src
, numsamples
);
332 hpfilter
->process(dst
, dst
, numsamples
);
340 inline void LoadSampleArray(ALfloat
*RESTRICT dst
, const al::byte
*src
, const size_t srcstep
,
341 const size_t samples
) noexcept
343 using SampleType
= typename FmtTypeTraits
<T
>::Type
;
345 const SampleType
*RESTRICT ssrc
{reinterpret_cast<const SampleType
*>(src
)};
346 for(size_t i
{0u};i
< samples
;i
++)
347 dst
[i
] = FmtTypeTraits
<T
>::to_float(ssrc
[i
*srcstep
]);
350 void LoadSamples(ALfloat
*RESTRICT dst
, const al::byte
*src
, const size_t srcstep
, FmtType srctype
,
351 const size_t samples
) noexcept
353 #define HANDLE_FMT(T) case T: LoadSampleArray<T>(dst, src, srcstep, samples); break
356 HANDLE_FMT(FmtUByte
);
357 HANDLE_FMT(FmtShort
);
358 HANDLE_FMT(FmtFloat
);
359 HANDLE_FMT(FmtDouble
);
360 HANDLE_FMT(FmtMulaw
);
366 ALfloat
*LoadBufferStatic(ALbufferlistitem
*BufferListItem
, ALbufferlistitem
*&BufferLoopItem
,
367 const size_t NumChannels
, const size_t SampleSize
, const size_t chan
, size_t DataPosInt
,
368 al::span
<ALfloat
> SrcBuffer
)
370 const ALbuffer
*Buffer
{BufferListItem
->mBuffer
};
371 const ALuint LoopStart
{Buffer
->LoopStart
};
372 const ALuint LoopEnd
{Buffer
->LoopEnd
};
373 ASSUME(LoopEnd
> LoopStart
);
375 /* If current pos is beyond the loop range, do not loop */
376 if(!BufferLoopItem
|| DataPosInt
>= LoopEnd
)
378 BufferLoopItem
= nullptr;
380 /* Load what's left to play from the buffer */
381 const size_t DataRem
{minz(SrcBuffer
.size(), Buffer
->SampleLen
-DataPosInt
)};
383 const al::byte
*Data
{Buffer
->mData
.data()};
384 Data
+= (DataPosInt
*NumChannels
+ chan
)*SampleSize
;
386 LoadSamples(SrcBuffer
.data(), Data
, NumChannels
, Buffer
->mFmtType
, DataRem
);
387 SrcBuffer
= SrcBuffer
.subspan(DataRem
);
391 /* Load what's left of this loop iteration */
392 const size_t DataRem
{minz(SrcBuffer
.size(), LoopEnd
-DataPosInt
)};
394 const al::byte
*Data
{Buffer
->mData
.data()};
395 Data
+= (DataPosInt
*NumChannels
+ chan
)*SampleSize
;
397 LoadSamples(SrcBuffer
.data(), Data
, NumChannels
, Buffer
->mFmtType
, DataRem
);
398 SrcBuffer
= SrcBuffer
.subspan(DataRem
);
400 /* Load any repeats of the loop we can to fill the buffer. */
401 const auto LoopSize
= static_cast<size_t>(LoopEnd
- LoopStart
);
402 while(!SrcBuffer
.empty())
404 const size_t DataSize
{minz(SrcBuffer
.size(), LoopSize
)};
406 Data
= Buffer
->mData
.data() + (LoopStart
*NumChannels
+ chan
)*SampleSize
;
408 LoadSamples(SrcBuffer
.data(), Data
, NumChannels
, Buffer
->mFmtType
, DataSize
);
409 SrcBuffer
= SrcBuffer
.subspan(DataSize
);
412 return SrcBuffer
.begin();
415 ALfloat
*LoadBufferQueue(ALbufferlistitem
*BufferListItem
, ALbufferlistitem
*BufferLoopItem
,
416 const size_t NumChannels
, const size_t SampleSize
, const size_t chan
, size_t DataPosInt
,
417 al::span
<ALfloat
> SrcBuffer
)
419 /* Crawl the buffer queue to fill in the temp buffer */
420 while(BufferListItem
&& !SrcBuffer
.empty())
422 ALbuffer
*Buffer
{BufferListItem
->mBuffer
};
423 if(!(Buffer
&& DataPosInt
< Buffer
->SampleLen
))
425 if(Buffer
) DataPosInt
-= Buffer
->SampleLen
;
426 BufferListItem
= BufferListItem
->mNext
.load(std::memory_order_acquire
);
427 if(!BufferListItem
) BufferListItem
= BufferLoopItem
;
431 const size_t DataSize
{minz(SrcBuffer
.size(), Buffer
->SampleLen
-DataPosInt
)};
433 const al::byte
*Data
{Buffer
->mData
.data()};
434 Data
+= (DataPosInt
*NumChannels
+ chan
)*SampleSize
;
436 LoadSamples(SrcBuffer
.data(), Data
, NumChannels
, Buffer
->mFmtType
, DataSize
);
437 SrcBuffer
= SrcBuffer
.subspan(DataSize
);
438 if(SrcBuffer
.empty()) break;
441 BufferListItem
= BufferListItem
->mNext
.load(std::memory_order_acquire
);
442 if(!BufferListItem
) BufferListItem
= BufferLoopItem
;
445 return SrcBuffer
.begin();
449 void DoHrtfMix(ALvoice::DirectData
&Direct
, const float TargetGain
, DirectParams
&parms
,
450 const float *samples
, const ALuint DstBufferSize
, const ALuint Counter
, const ALuint OutPos
,
451 const ALuint IrSize
, ALCdevice
*Device
)
453 const ALuint OutLIdx
{GetChannelIdxByName(Device
->RealOut
, FrontLeft
)};
454 const ALuint OutRIdx
{GetChannelIdxByName(Device
->RealOut
, FrontRight
)};
455 auto &HrtfSamples
= Device
->HrtfSourceData
;
456 auto &AccumSamples
= Device
->HrtfAccumData
;
458 /* Copy the HRTF history and new input samples into a temp buffer. */
459 auto src_iter
= std::copy(parms
.Hrtf
.State
.History
.begin(), parms
.Hrtf
.State
.History
.end(),
460 std::begin(HrtfSamples
));
461 std::copy_n(samples
, DstBufferSize
, src_iter
);
462 /* Copy the last used samples back into the history buffer for later. */
463 std::copy_n(std::begin(HrtfSamples
) + DstBufferSize
, parms
.Hrtf
.State
.History
.size(),
464 parms
.Hrtf
.State
.History
.begin());
466 /* Copy the current filtered values being accumulated into the temp buffer. */
467 auto accum_iter
= std::copy_n(parms
.Hrtf
.State
.Values
.begin(), parms
.Hrtf
.State
.Values
.size(),
468 std::begin(AccumSamples
));
469 /* Clear the accumulation buffer that will start getting filled in. */
470 std::fill_n(accum_iter
, DstBufferSize
, float2
{});
472 /* If fading, the old gain is not silence, and this is the first mixing
473 * pass, fade between the IRs.
476 if(Counter
&& parms
.Hrtf
.Old
.Gain
> GAIN_SILENCE_THRESHOLD
&& OutPos
== 0)
478 fademix
= minu(DstBufferSize
, 128);
480 float gain
{TargetGain
};
482 /* The new coefficients need to fade in completely since they're
483 * replacing the old ones. To keep the gain fading consistent,
484 * interpolate between the old and new target gains given how much of
485 * the fade time this mix handles.
487 if LIKELY(Counter
> fademix
)
489 const ALfloat a
{static_cast<float>(fademix
) / static_cast<float>(Counter
)};
490 gain
= lerp(parms
.Hrtf
.Old
.Gain
, TargetGain
, a
);
492 MixHrtfFilter hrtfparams
;
493 hrtfparams
.Coeffs
= &parms
.Hrtf
.Target
.Coeffs
;
494 hrtfparams
.Delay
[0] = parms
.Hrtf
.Target
.Delay
[0];
495 hrtfparams
.Delay
[1] = parms
.Hrtf
.Target
.Delay
[1];
496 hrtfparams
.Gain
= 0.0f
;
497 hrtfparams
.GainStep
= gain
/ static_cast<float>(fademix
);
499 MixHrtfBlendSamples(Direct
.Buffer
[OutLIdx
], Direct
.Buffer
[OutRIdx
], HrtfSamples
,
500 AccumSamples
, OutPos
, IrSize
, &parms
.Hrtf
.Old
, &hrtfparams
, fademix
);
501 /* Update the old parameters with the result. */
502 parms
.Hrtf
.Old
= parms
.Hrtf
.Target
;
503 if(fademix
< Counter
)
504 parms
.Hrtf
.Old
.Gain
= hrtfparams
.Gain
;
506 parms
.Hrtf
.Old
.Gain
= TargetGain
;
509 if LIKELY(fademix
< DstBufferSize
)
511 const ALuint todo
{DstBufferSize
- fademix
};
512 float gain
{TargetGain
};
514 /* Interpolate the target gain if the gain fading lasts longer than
517 if(Counter
> DstBufferSize
)
519 const float a
{static_cast<float>(todo
) / static_cast<float>(Counter
-fademix
)};
520 gain
= lerp(parms
.Hrtf
.Old
.Gain
, TargetGain
, a
);
523 MixHrtfFilter hrtfparams
;
524 hrtfparams
.Coeffs
= &parms
.Hrtf
.Target
.Coeffs
;
525 hrtfparams
.Delay
[0] = parms
.Hrtf
.Target
.Delay
[0];
526 hrtfparams
.Delay
[1] = parms
.Hrtf
.Target
.Delay
[1];
527 hrtfparams
.Gain
= parms
.Hrtf
.Old
.Gain
;
528 hrtfparams
.GainStep
= (gain
- parms
.Hrtf
.Old
.Gain
) / static_cast<float>(todo
);
529 MixHrtfSamples(Direct
.Buffer
[OutLIdx
], Direct
.Buffer
[OutRIdx
], HrtfSamples
+fademix
,
530 AccumSamples
+fademix
, OutPos
+fademix
, IrSize
, &hrtfparams
, todo
);
531 /* Store the interpolated gain or the final target gain depending if
534 if(DstBufferSize
< Counter
)
535 parms
.Hrtf
.Old
.Gain
= gain
;
537 parms
.Hrtf
.Old
.Gain
= TargetGain
;
540 /* Copy the new in-progress accumulation values back for the next mix. */
541 std::copy_n(std::begin(AccumSamples
) + DstBufferSize
, parms
.Hrtf
.State
.Values
.size(),
542 parms
.Hrtf
.State
.Values
.begin());
545 void DoNfcMix(ALvoice::DirectData
&Direct
, const float *TargetGains
, DirectParams
&parms
,
546 const float *samples
, const ALuint DstBufferSize
, const ALuint Counter
, const ALuint OutPos
,
549 const size_t outcount
{Device
->NumChannelsPerOrder
[0]};
550 MixSamples({samples
, DstBufferSize
}, Direct
.Buffer
.first(outcount
),
551 parms
.Gains
.Current
, TargetGains
, Counter
, OutPos
);
553 const al::span
<float> nfcsamples
{Device
->NfcSampleData
, DstBufferSize
};
554 size_t chanoffset
{outcount
};
555 using FilterProc
= void (NfcFilter::*)(float*,const float*,const size_t);
556 auto apply_nfc
= [&Direct
,&parms
,samples
,TargetGains
,Counter
,OutPos
,&chanoffset
,nfcsamples
](
557 const FilterProc process
, const size_t chancount
) -> void
559 if(chancount
< 1) return;
560 (parms
.NFCtrlFilter
.*process
)(nfcsamples
.data(), samples
, nfcsamples
.size());
561 MixSamples(nfcsamples
, Direct
.Buffer
.subspan(chanoffset
, chancount
),
562 parms
.Gains
.Current
+chanoffset
, TargetGains
+chanoffset
, Counter
, OutPos
);
563 chanoffset
+= chancount
;
565 apply_nfc(&NfcFilter::process1
, Device
->NumChannelsPerOrder
[1]);
566 apply_nfc(&NfcFilter::process2
, Device
->NumChannelsPerOrder
[2]);
567 apply_nfc(&NfcFilter::process3
, Device
->NumChannelsPerOrder
[3]);
572 void ALvoice::mix(State vstate
, ALCcontext
*Context
, const ALuint SamplesToDo
)
574 static constexpr ALfloat SilentTarget
[MAX_OUTPUT_CHANNELS
]{};
576 ASSUME(SamplesToDo
> 0);
579 const bool isstatic
{(mFlags
&VOICE_IS_STATIC
) != 0};
580 ALuint DataPosInt
{mPosition
.load(std::memory_order_relaxed
)};
581 ALuint DataPosFrac
{mPositionFrac
.load(std::memory_order_relaxed
)};
582 ALbufferlistitem
*BufferListItem
{mCurrentBuffer
.load(std::memory_order_relaxed
)};
583 ALbufferlistitem
*BufferLoopItem
{mLoopBuffer
.load(std::memory_order_relaxed
)};
584 const ALuint NumChannels
{mNumChannels
};
585 const ALuint SampleSize
{mSampleSize
};
586 const ALuint increment
{mStep
};
587 if(increment
< 1) return;
589 ASSUME(NumChannels
> 0);
590 ASSUME(SampleSize
> 0);
591 ASSUME(increment
> 0);
593 ALCdevice
*Device
{Context
->mDevice
.get()};
594 const ALuint NumSends
{Device
->NumAuxSends
};
595 const ALuint IrSize
{Device
->mHrtf
? Device
->mHrtf
->irSize
: 0};
597 ResamplerFunc Resample
{(increment
== FRACTIONONE
&& DataPosFrac
== 0) ?
598 Resample_
<CopyTag
,CTag
> : mResampler
};
600 ALuint Counter
{(mFlags
&VOICE_IS_FADING
) ? SamplesToDo
: 0};
603 /* No fading, just overwrite the old/current params. */
604 for(ALuint chan
{0};chan
< NumChannels
;chan
++)
606 ChannelData
&chandata
= mChans
[chan
];
608 DirectParams
&parms
= chandata
.mDryParams
;
609 if(!(mFlags
&VOICE_HAS_HRTF
))
610 std::copy(std::begin(parms
.Gains
.Target
), std::end(parms
.Gains
.Target
),
611 std::begin(parms
.Gains
.Current
));
613 parms
.Hrtf
.Old
= parms
.Hrtf
.Target
;
615 for(ALuint send
{0};send
< NumSends
;++send
)
617 if(mSend
[send
].Buffer
.empty())
620 SendParams
&parms
= chandata
.mWetParams
[send
];
621 std::copy(std::begin(parms
.Gains
.Target
), std::end(parms
.Gains
.Target
),
622 std::begin(parms
.Gains
.Current
));
626 else if((mFlags
&VOICE_HAS_HRTF
))
628 for(ALuint chan
{0};chan
< NumChannels
;chan
++)
630 DirectParams
&parms
= mChans
[chan
].mDryParams
;
631 if(!(parms
.Hrtf
.Old
.Gain
> GAIN_SILENCE_THRESHOLD
))
633 /* The old HRTF params are silent, so overwrite the old
634 * coefficients with the new, and reset the old gain to 0. The
635 * future mix will then fade from silence.
637 parms
.Hrtf
.Old
= parms
.Hrtf
.Target
;
638 parms
.Hrtf
.Old
.Gain
= 0.0f
;
643 ALuint buffers_done
{0u};
646 /* Figure out how many buffer samples will be needed */
647 ALuint DstBufferSize
{SamplesToDo
- OutPos
};
649 /* Calculate the last written dst sample pos. */
650 uint64_t DataSize64
{DstBufferSize
- 1};
651 /* Calculate the last read src sample pos. */
652 DataSize64
= (DataSize64
*increment
+ DataPosFrac
) >> FRACTIONBITS
;
653 /* +1 to get the src sample count, include padding. */
654 DataSize64
+= 1 + MAX_RESAMPLER_PADDING
;
656 auto SrcBufferSize
= static_cast<ALuint
>(
657 minu64(DataSize64
, BUFFERSIZE
+ MAX_RESAMPLER_PADDING
+ 1));
658 if(SrcBufferSize
> BUFFERSIZE
+ MAX_RESAMPLER_PADDING
)
660 SrcBufferSize
= BUFFERSIZE
+ MAX_RESAMPLER_PADDING
;
661 /* If the source buffer got saturated, we can't fill the desired
662 * dst size. Figure out how many samples we can actually mix from
665 DataSize64
= SrcBufferSize
- MAX_RESAMPLER_PADDING
;
666 DataSize64
= ((DataSize64
<<FRACTIONBITS
) - DataPosFrac
+ increment
-1) / increment
;
667 DstBufferSize
= static_cast<ALuint
>(minu64(DataSize64
, DstBufferSize
));
669 /* Some mixers like having a multiple of 4, so try to give that
670 * unless this is the last update.
672 if(DstBufferSize
< SamplesToDo
-OutPos
)
673 DstBufferSize
&= ~3u;
676 ASSUME(DstBufferSize
> 0);
677 for(ALuint chan
{0};chan
< NumChannels
;chan
++)
679 ChannelData
&chandata
= mChans
[chan
];
680 const al::span
<ALfloat
> SrcData
{Device
->SourceData
, SrcBufferSize
};
682 /* Load the previous samples into the source data first, then load
683 * what we can from the buffer queue.
685 auto srciter
= std::copy_n(chandata
.mPrevSamples
.begin(), MAX_RESAMPLER_PADDING
>>1,
688 if UNLIKELY(!BufferListItem
)
689 srciter
= std::copy(chandata
.mPrevSamples
.begin()+(MAX_RESAMPLER_PADDING
>>1),
690 chandata
.mPrevSamples
.end(), srciter
);
692 srciter
= LoadBufferStatic(BufferListItem
, BufferLoopItem
, NumChannels
,
693 SampleSize
, chan
, DataPosInt
, {srciter
, SrcData
.end()});
695 srciter
= LoadBufferQueue(BufferListItem
, BufferLoopItem
, NumChannels
,
696 SampleSize
, chan
, DataPosInt
, {srciter
, SrcData
.end()});
698 if UNLIKELY(srciter
!= SrcData
.end())
700 /* If the source buffer wasn't filled, copy the last sample for
701 * the remaining buffer. Ideally it should have ended with
702 * silence, but if not the gain fading should help avoid clicks
703 * from sudden amplitude changes.
705 const ALfloat sample
{*(srciter
-1)};
706 std::fill(srciter
, SrcData
.end(), sample
);
709 /* Store the last source samples used for next time. */
710 std::copy_n(&SrcData
[(increment
*DstBufferSize
+ DataPosFrac
)>>FRACTIONBITS
],
711 chandata
.mPrevSamples
.size(), chandata
.mPrevSamples
.begin());
713 /* Resample, then apply ambisonic upsampling as needed. */
714 const ALfloat
*ResampledData
{Resample(&mResampleState
,
715 &SrcData
[MAX_RESAMPLER_PADDING
>>1], DataPosFrac
, increment
,
716 {Device
->ResampledData
, DstBufferSize
})};
717 if((mFlags
&VOICE_IS_AMBISONIC
))
719 const ALfloat hfscale
{chandata
.mAmbiScale
};
720 /* Beware the evil const_cast. It's safe since it's pointing to
721 * either SourceData or ResampledData (both non-const), but the
722 * resample method takes the source as const float* and may
723 * return it without copying to output, making it currently
726 chandata
.mAmbiSplitter
.applyHfScale(const_cast<ALfloat
*>(ResampledData
), hfscale
,
730 /* Now filter and mix to the appropriate outputs. */
731 ALfloat (&FilterBuf
)[BUFFERSIZE
] = Device
->FilteredData
;
733 DirectParams
&parms
= chandata
.mDryParams
;
734 const ALfloat
*samples
{DoFilters(&parms
.LowPass
, &parms
.HighPass
, FilterBuf
,
735 ResampledData
, DstBufferSize
, mDirect
.FilterType
)};
737 if((mFlags
&VOICE_HAS_HRTF
))
739 const ALfloat TargetGain
{UNLIKELY(vstate
== ALvoice::Stopping
) ? 0.0f
:
740 parms
.Hrtf
.Target
.Gain
};
741 DoHrtfMix(mDirect
, TargetGain
, parms
, samples
, DstBufferSize
, Counter
, OutPos
,
744 else if((mFlags
&VOICE_HAS_NFC
))
746 const ALfloat
*TargetGains
{UNLIKELY(vstate
== ALvoice::Stopping
) ?
747 SilentTarget
: parms
.Gains
.Target
};
748 DoNfcMix(mDirect
, TargetGains
, parms
, samples
, DstBufferSize
, Counter
, OutPos
,
753 const ALfloat
*TargetGains
{UNLIKELY(vstate
== ALvoice::Stopping
) ?
754 SilentTarget
: parms
.Gains
.Target
};
755 MixSamples({samples
, DstBufferSize
}, mDirect
.Buffer
, parms
.Gains
.Current
,
756 TargetGains
, Counter
, OutPos
);
760 for(ALuint send
{0};send
< NumSends
;++send
)
762 if(mSend
[send
].Buffer
.empty())
765 SendParams
&parms
= chandata
.mWetParams
[send
];
766 const ALfloat
*samples
{DoFilters(&parms
.LowPass
, &parms
.HighPass
, FilterBuf
,
767 ResampledData
, DstBufferSize
, mSend
[send
].FilterType
)};
769 const ALfloat
*TargetGains
{UNLIKELY(vstate
==ALvoice::Stopping
) ? SilentTarget
:
771 MixSamples({samples
, DstBufferSize
}, mSend
[send
].Buffer
, parms
.Gains
.Current
,
772 TargetGains
, Counter
, OutPos
);
775 /* Update positions */
776 DataPosFrac
+= increment
*DstBufferSize
;
777 DataPosInt
+= DataPosFrac
>>FRACTIONBITS
;
778 DataPosFrac
&= FRACTIONMASK
;
780 OutPos
+= DstBufferSize
;
781 Counter
= maxu(DstBufferSize
, Counter
) - DstBufferSize
;
783 if UNLIKELY(!BufferListItem
)
785 /* Do nothing extra when there's no buffers. */
791 /* Handle looping static source */
792 const ALbuffer
*Buffer
{BufferListItem
->mBuffer
};
793 const ALuint LoopStart
{Buffer
->LoopStart
};
794 const ALuint LoopEnd
{Buffer
->LoopEnd
};
795 if(DataPosInt
>= LoopEnd
)
797 assert(LoopEnd
> LoopStart
);
798 DataPosInt
= ((DataPosInt
-LoopStart
)%(LoopEnd
-LoopStart
)) + LoopStart
;
803 /* Handle non-looping static source */
804 if(DataPosInt
>= BufferListItem
->mSampleLen
)
806 if LIKELY(vstate
== ALvoice::Playing
)
807 vstate
= ALvoice::Stopped
;
808 BufferListItem
= nullptr;
815 /* Handle streaming source */
816 if(BufferListItem
->mSampleLen
> DataPosInt
)
819 DataPosInt
-= BufferListItem
->mSampleLen
;
822 BufferListItem
= BufferListItem
->mNext
.load(std::memory_order_relaxed
);
823 if(!BufferListItem
) BufferListItem
= BufferLoopItem
;
826 if LIKELY(vstate
== ALvoice::Playing
)
827 vstate
= ALvoice::Stopped
;
831 } while(OutPos
< SamplesToDo
);
833 mFlags
|= VOICE_IS_FADING
;
835 /* Don't update positions and buffers if we were stopping. */
836 if UNLIKELY(vstate
== ALvoice::Stopping
)
838 mPlayState
.store(ALvoice::Stopped
, std::memory_order_release
);
842 /* Capture the source ID in case it's reset for stopping. */
843 const ALuint SourceID
{mSourceID
.load(std::memory_order_relaxed
)};
845 /* Update voice info */
846 mPosition
.store(DataPosInt
, std::memory_order_relaxed
);
847 mPositionFrac
.store(DataPosFrac
, std::memory_order_relaxed
);
848 mCurrentBuffer
.store(BufferListItem
, std::memory_order_relaxed
);
849 if(vstate
== ALvoice::Stopped
)
851 mLoopBuffer
.store(nullptr, std::memory_order_relaxed
);
852 mSourceID
.store(0u, std::memory_order_relaxed
);
854 std::atomic_thread_fence(std::memory_order_release
);
856 /* Send any events now, after the position/buffer info was updated. */
857 const ALbitfieldSOFT enabledevt
{Context
->mEnabledEvts
.load(std::memory_order_acquire
)};
858 if(buffers_done
> 0 && (enabledevt
&EventType_BufferCompleted
))
860 RingBuffer
*ring
{Context
->mAsyncEvents
.get()};
861 auto evt_vec
= ring
->getWriteVector();
862 if(evt_vec
.first
.len
> 0)
864 AsyncEvent
*evt
{new (evt_vec
.first
.buf
) AsyncEvent
{EventType_BufferCompleted
}};
865 evt
->u
.bufcomp
.id
= SourceID
;
866 evt
->u
.bufcomp
.count
= buffers_done
;
867 ring
->writeAdvance(1);
868 Context
->mEventSem
.post();
872 if(vstate
== ALvoice::Stopped
)
874 /* If the voice just ended, set it to Stopping so the next render
875 * ensures any residual noise fades to 0 amplitude.
877 mPlayState
.store(ALvoice::Stopping
, std::memory_order_release
);
878 if((enabledevt
&EventType_SourceStateChange
))
879 SendSourceStoppedEvent(Context
, SourceID
);