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
40 #include "al/buffer.h"
42 #include "al/source.h"
46 #include "alcontext.h"
47 #include "alnumeric.h"
48 #include "aloptional.h"
53 #include "devformat.h"
54 #include "filters/biquad.h"
55 #include "filters/nfc.h"
56 #include "filters/splitter.h"
58 #include "inprogext.h"
60 #include "mixer/defs.h"
61 #include "opthelpers.h"
62 #include "ringbuffer.h"
67 static_assert((INT_MAX
>>FRACTIONBITS
)/MAX_PITCH
> BUFFERSIZE
,
68 "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
71 Resampler ResamplerDefault
{Resampler::Linear
};
75 using HrtfMixerFunc
= void(*)(FloatBufferLine
&LeftOut
, FloatBufferLine
&RightOut
,
76 const ALfloat
*InSamples
, float2
*AccumSamples
, const size_t OutPos
, const ALuint IrSize
,
77 MixHrtfFilter
*hrtfparams
, const size_t BufferSize
);
78 using HrtfMixerBlendFunc
= void(*)(FloatBufferLine
&LeftOut
, FloatBufferLine
&RightOut
,
79 const ALfloat
*InSamples
, float2
*AccumSamples
, const size_t OutPos
, const ALuint IrSize
,
80 const HrtfFilter
*oldparams
, MixHrtfFilter
*newparams
, const size_t BufferSize
);
82 HrtfMixerFunc MixHrtfSamples
= MixHrtf_
<CTag
>;
83 HrtfMixerBlendFunc MixHrtfBlendSamples
= MixHrtfBlend_
<CTag
>;
85 inline HrtfMixerFunc
SelectHrtfMixer()
88 if((CPUCapFlags
&CPU_CAP_NEON
))
89 return MixHrtf_
<NEONTag
>;
92 if((CPUCapFlags
&CPU_CAP_SSE
))
93 return MixHrtf_
<SSETag
>;
95 return MixHrtf_
<CTag
>;
98 inline HrtfMixerBlendFunc
SelectHrtfBlendMixer()
101 if((CPUCapFlags
&CPU_CAP_NEON
))
102 return MixHrtfBlend_
<NEONTag
>;
105 if((CPUCapFlags
&CPU_CAP_SSE
))
106 return MixHrtfBlend_
<SSETag
>;
108 return MixHrtfBlend_
<CTag
>;
116 if(auto resopt
= ConfigValueStr(nullptr, nullptr, "resampler"))
118 struct ResamplerEntry
{
120 const Resampler resampler
;
122 constexpr ResamplerEntry ResamplerList
[]{
123 { "none", Resampler::Point
},
124 { "point", Resampler::Point
},
125 { "cubic", Resampler::Cubic
},
126 { "bsinc12", Resampler::BSinc12
},
127 { "fast_bsinc12", Resampler::FastBSinc12
},
128 { "bsinc24", Resampler::BSinc24
},
129 { "fast_bsinc24", Resampler::FastBSinc24
},
132 const char *str
{resopt
->c_str()};
133 if(al::strcasecmp(str
, "bsinc") == 0)
135 WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", str
);
138 else if(al::strcasecmp(str
, "sinc4") == 0 || al::strcasecmp(str
, "sinc8") == 0)
140 WARN("Resampler option \"%s\" is deprecated, using cubic\n", str
);
144 auto iter
= std::find_if(std::begin(ResamplerList
), std::end(ResamplerList
),
145 [str
](const ResamplerEntry
&entry
) -> bool
146 { return al::strcasecmp(str
, entry
.name
) == 0; });
147 if(iter
== std::end(ResamplerList
))
148 ERR("Invalid resampler: %s\n", str
);
150 ResamplerDefault
= iter
->resampler
;
153 MixHrtfBlendSamples
= SelectHrtfBlendMixer();
154 MixHrtfSamples
= SelectHrtfMixer();
160 /* A quick'n'dirty lookup table to decode a muLaw-encoded byte sample into a
161 * signed 16-bit sample */
162 constexpr ALshort muLawDecompressionTable
[256] = {
163 -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
164 -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
165 -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
166 -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
167 -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
168 -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
169 -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
170 -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
171 -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
172 -1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
173 -876, -844, -812, -780, -748, -716, -684, -652,
174 -620, -588, -556, -524, -492, -460, -428, -396,
175 -372, -356, -340, -324, -308, -292, -276, -260,
176 -244, -228, -212, -196, -180, -164, -148, -132,
177 -120, -112, -104, -96, -88, -80, -72, -64,
178 -56, -48, -40, -32, -24, -16, -8, 0,
179 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
180 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
181 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
182 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
183 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
184 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
185 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
186 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
187 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
188 1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
189 876, 844, 812, 780, 748, 716, 684, 652,
190 620, 588, 556, 524, 492, 460, 428, 396,
191 372, 356, 340, 324, 308, 292, 276, 260,
192 244, 228, 212, 196, 180, 164, 148, 132,
193 120, 112, 104, 96, 88, 80, 72, 64,
194 56, 48, 40, 32, 24, 16, 8, 0
197 /* A quick'n'dirty lookup table to decode an aLaw-encoded byte sample into a
198 * signed 16-bit sample */
199 constexpr ALshort aLawDecompressionTable
[256] = {
200 -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
201 -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
202 -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
203 -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
204 -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
205 -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
206 -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472,
207 -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
208 -344, -328, -376, -360, -280, -264, -312, -296,
209 -472, -456, -504, -488, -408, -392, -440, -424,
210 -88, -72, -120, -104, -24, -8, -56, -40,
211 -216, -200, -248, -232, -152, -136, -184, -168,
212 -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
213 -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
214 -688, -656, -752, -720, -560, -528, -624, -592,
215 -944, -912, -1008, -976, -816, -784, -880, -848,
216 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
217 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
218 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
219 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
220 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
221 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
222 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
223 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
224 344, 328, 376, 360, 280, 264, 312, 296,
225 472, 456, 504, 488, 408, 392, 440, 424,
226 88, 72, 120, 104, 24, 8, 56, 40,
227 216, 200, 248, 232, 152, 136, 184, 168,
228 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
229 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
230 688, 656, 752, 720, 560, 528, 624, 592,
231 944, 912, 1008, 976, 816, 784, 880, 848
235 struct FmtTypeTraits
{ };
238 struct FmtTypeTraits
<FmtUByte
> {
239 using Type
= ALubyte
;
240 static constexpr inline float to_float(const Type val
) noexcept
241 { return val
*(1.0f
/128.0f
) - 1.0f
; }
244 struct FmtTypeTraits
<FmtShort
> {
245 using Type
= ALshort
;
246 static constexpr inline float to_float(const Type val
) noexcept
{ return val
*(1.0f
/32768.0f
); }
249 struct FmtTypeTraits
<FmtFloat
> {
250 using Type
= ALfloat
;
251 static constexpr inline float to_float(const Type val
) noexcept
{ return val
; }
254 struct FmtTypeTraits
<FmtDouble
> {
255 using Type
= ALdouble
;
256 static constexpr inline float to_float(const Type val
) noexcept
257 { return static_cast<ALfloat
>(val
); }
260 struct FmtTypeTraits
<FmtMulaw
> {
261 using Type
= ALubyte
;
262 static constexpr inline float to_float(const Type val
) noexcept
263 { return muLawDecompressionTable
[val
] * (1.0f
/32768.0f
); }
266 struct FmtTypeTraits
<FmtAlaw
> {
267 using Type
= ALubyte
;
268 static constexpr inline float to_float(const Type val
) noexcept
269 { return aLawDecompressionTable
[val
] * (1.0f
/32768.0f
); }
273 void SendSourceStoppedEvent(ALCcontext
*context
, ALuint id
)
275 RingBuffer
*ring
{context
->mAsyncEvents
.get()};
276 auto evt_vec
= ring
->getWriteVector();
277 if(evt_vec
.first
.len
< 1) return;
279 AsyncEvent
*evt
{new (evt_vec
.first
.buf
) AsyncEvent
{EventType_SourceStateChange
}};
280 evt
->u
.srcstate
.id
= id
;
281 evt
->u
.srcstate
.state
= AL_STOPPED
;
283 ring
->writeAdvance(1);
284 context
->mEventSem
.post();
288 const ALfloat
*DoFilters(BiquadFilter
*lpfilter
, BiquadFilter
*hpfilter
, ALfloat
*dst
,
289 const ALfloat
*src
, const size_t numsamples
, int type
)
299 lpfilter
->process(dst
, src
, numsamples
);
304 hpfilter
->process(dst
, src
, numsamples
);
308 lpfilter
->process(dst
, src
, numsamples
);
309 hpfilter
->process(dst
, dst
, numsamples
);
317 inline void LoadSampleArray(ALfloat
*RESTRICT dst
, const al::byte
*src
, const size_t srcstep
,
318 const size_t samples
) noexcept
320 using SampleType
= typename FmtTypeTraits
<T
>::Type
;
322 const SampleType
*RESTRICT ssrc
{reinterpret_cast<const SampleType
*>(src
)};
323 for(size_t i
{0u};i
< samples
;i
++)
324 dst
[i
] = FmtTypeTraits
<T
>::to_float(ssrc
[i
*srcstep
]);
327 void LoadSamples(ALfloat
*RESTRICT dst
, const al::byte
*src
, const size_t srcstep
, FmtType srctype
,
328 const size_t samples
) noexcept
330 #define HANDLE_FMT(T) case T: LoadSampleArray<T>(dst, src, srcstep, samples); break
333 HANDLE_FMT(FmtUByte
);
334 HANDLE_FMT(FmtShort
);
335 HANDLE_FMT(FmtFloat
);
336 HANDLE_FMT(FmtDouble
);
337 HANDLE_FMT(FmtMulaw
);
343 ALfloat
*LoadBufferStatic(ALbufferlistitem
*BufferListItem
, ALbufferlistitem
*&BufferLoopItem
,
344 const size_t NumChannels
, const size_t SampleSize
, const size_t chan
, size_t DataPosInt
,
345 al::span
<ALfloat
> SrcBuffer
)
347 const ALbuffer
*Buffer
{BufferListItem
->mBuffer
};
348 const ALuint LoopStart
{Buffer
->LoopStart
};
349 const ALuint LoopEnd
{Buffer
->LoopEnd
};
350 ASSUME(LoopEnd
> LoopStart
);
352 /* If current pos is beyond the loop range, do not loop */
353 if(!BufferLoopItem
|| DataPosInt
>= LoopEnd
)
355 BufferLoopItem
= nullptr;
357 /* Load what's left to play from the buffer */
358 const size_t DataRem
{minz(SrcBuffer
.size(), Buffer
->SampleLen
-DataPosInt
)};
360 const al::byte
*Data
{Buffer
->mData
.data()};
361 Data
+= (DataPosInt
*NumChannels
+ chan
)*SampleSize
;
363 LoadSamples(SrcBuffer
.data(), Data
, NumChannels
, Buffer
->mFmtType
, DataRem
);
364 SrcBuffer
= SrcBuffer
.subspan(DataRem
);
368 /* Load what's left of this loop iteration */
369 const size_t DataRem
{minz(SrcBuffer
.size(), LoopEnd
-DataPosInt
)};
371 const al::byte
*Data
{Buffer
->mData
.data()};
372 Data
+= (DataPosInt
*NumChannels
+ chan
)*SampleSize
;
374 LoadSamples(SrcBuffer
.data(), Data
, NumChannels
, Buffer
->mFmtType
, DataRem
);
375 SrcBuffer
= SrcBuffer
.subspan(DataRem
);
377 /* Load any repeats of the loop we can to fill the buffer. */
378 const auto LoopSize
= static_cast<size_t>(LoopEnd
- LoopStart
);
379 while(!SrcBuffer
.empty())
381 const size_t DataSize
{minz(SrcBuffer
.size(), LoopSize
)};
383 Data
= Buffer
->mData
.data() + (LoopStart
*NumChannels
+ chan
)*SampleSize
;
385 LoadSamples(SrcBuffer
.data(), Data
, NumChannels
, Buffer
->mFmtType
, DataSize
);
386 SrcBuffer
= SrcBuffer
.subspan(DataSize
);
389 return SrcBuffer
.begin();
392 ALfloat
*LoadBufferQueue(ALbufferlistitem
*BufferListItem
, ALbufferlistitem
*BufferLoopItem
,
393 const size_t NumChannels
, const size_t SampleSize
, const size_t chan
, size_t DataPosInt
,
394 al::span
<ALfloat
> SrcBuffer
)
396 /* Crawl the buffer queue to fill in the temp buffer */
397 while(BufferListItem
&& !SrcBuffer
.empty())
399 ALbuffer
*Buffer
{BufferListItem
->mBuffer
};
400 if(!(Buffer
&& DataPosInt
< Buffer
->SampleLen
))
402 if(Buffer
) DataPosInt
-= Buffer
->SampleLen
;
403 BufferListItem
= BufferListItem
->mNext
.load(std::memory_order_acquire
);
404 if(!BufferListItem
) BufferListItem
= BufferLoopItem
;
408 const size_t DataSize
{minz(SrcBuffer
.size(), Buffer
->SampleLen
-DataPosInt
)};
410 const al::byte
*Data
{Buffer
->mData
.data()};
411 Data
+= (DataPosInt
*NumChannels
+ chan
)*SampleSize
;
413 LoadSamples(SrcBuffer
.data(), Data
, NumChannels
, Buffer
->mFmtType
, DataSize
);
414 SrcBuffer
= SrcBuffer
.subspan(DataSize
);
415 if(SrcBuffer
.empty()) break;
418 BufferListItem
= BufferListItem
->mNext
.load(std::memory_order_acquire
);
419 if(!BufferListItem
) BufferListItem
= BufferLoopItem
;
422 return SrcBuffer
.begin();
426 void DoHrtfMix(ALvoice::TargetData
&Direct
, const float TargetGain
, DirectParams
&parms
,
427 const float *samples
, const ALuint DstBufferSize
, const ALuint Counter
, const ALuint OutPos
,
428 const ALuint IrSize
, ALCdevice
*Device
)
430 const ALuint OutLIdx
{GetChannelIdxByName(Device
->RealOut
, FrontLeft
)};
431 const ALuint OutRIdx
{GetChannelIdxByName(Device
->RealOut
, FrontRight
)};
432 auto &HrtfSamples
= Device
->HrtfSourceData
;
433 auto &AccumSamples
= Device
->HrtfAccumData
;
435 /* Copy the HRTF history and new input samples into a temp buffer. */
436 auto src_iter
= std::copy(parms
.Hrtf
.State
.History
.begin(), parms
.Hrtf
.State
.History
.end(),
437 std::begin(HrtfSamples
));
438 std::copy_n(samples
, DstBufferSize
, src_iter
);
439 /* Copy the last used samples back into the history buffer for later. */
440 std::copy_n(std::begin(HrtfSamples
) + DstBufferSize
, parms
.Hrtf
.State
.History
.size(),
441 parms
.Hrtf
.State
.History
.begin());
443 /* Copy the current filtered values being accumulated into the temp buffer. */
444 auto accum_iter
= std::copy_n(parms
.Hrtf
.State
.Values
.begin(), parms
.Hrtf
.State
.Values
.size(),
445 std::begin(AccumSamples
));
446 /* Clear the accumulation buffer that will start getting filled in. */
447 std::fill_n(accum_iter
, DstBufferSize
, float2
{});
449 /* If fading, the old gain is not silence, and this is the first mixing
450 * pass, fade between the IRs.
453 if(Counter
&& parms
.Hrtf
.Old
.Gain
> GAIN_SILENCE_THRESHOLD
&& OutPos
== 0)
455 fademix
= minu(DstBufferSize
, 128);
457 float gain
{TargetGain
};
459 /* The new coefficients need to fade in completely since they're
460 * replacing the old ones. To keep the gain fading consistent,
461 * interpolate between the old and new target gains given how much of
462 * the fade time this mix handles.
464 if LIKELY(Counter
> fademix
)
466 const ALfloat a
{static_cast<float>(fademix
) / static_cast<float>(Counter
)};
467 gain
= lerp(parms
.Hrtf
.Old
.Gain
, TargetGain
, a
);
469 MixHrtfFilter hrtfparams
;
470 hrtfparams
.Coeffs
= &parms
.Hrtf
.Target
.Coeffs
;
471 hrtfparams
.Delay
[0] = parms
.Hrtf
.Target
.Delay
[0];
472 hrtfparams
.Delay
[1] = parms
.Hrtf
.Target
.Delay
[1];
473 hrtfparams
.Gain
= 0.0f
;
474 hrtfparams
.GainStep
= gain
/ static_cast<float>(fademix
);
476 MixHrtfBlendSamples(Direct
.Buffer
[OutLIdx
], Direct
.Buffer
[OutRIdx
], HrtfSamples
,
477 AccumSamples
, OutPos
, IrSize
, &parms
.Hrtf
.Old
, &hrtfparams
, fademix
);
478 /* Update the old parameters with the result. */
479 parms
.Hrtf
.Old
= parms
.Hrtf
.Target
;
480 if(fademix
< Counter
)
481 parms
.Hrtf
.Old
.Gain
= hrtfparams
.Gain
;
483 parms
.Hrtf
.Old
.Gain
= TargetGain
;
486 if LIKELY(fademix
< DstBufferSize
)
488 const ALuint todo
{DstBufferSize
- fademix
};
489 float gain
{TargetGain
};
491 /* Interpolate the target gain if the gain fading lasts longer than
494 if(Counter
> DstBufferSize
)
496 const float a
{static_cast<float>(todo
) / static_cast<float>(Counter
-fademix
)};
497 gain
= lerp(parms
.Hrtf
.Old
.Gain
, TargetGain
, a
);
500 MixHrtfFilter hrtfparams
;
501 hrtfparams
.Coeffs
= &parms
.Hrtf
.Target
.Coeffs
;
502 hrtfparams
.Delay
[0] = parms
.Hrtf
.Target
.Delay
[0];
503 hrtfparams
.Delay
[1] = parms
.Hrtf
.Target
.Delay
[1];
504 hrtfparams
.Gain
= parms
.Hrtf
.Old
.Gain
;
505 hrtfparams
.GainStep
= (gain
- parms
.Hrtf
.Old
.Gain
) / static_cast<float>(todo
);
506 MixHrtfSamples(Direct
.Buffer
[OutLIdx
], Direct
.Buffer
[OutRIdx
], HrtfSamples
+fademix
,
507 AccumSamples
+fademix
, OutPos
+fademix
, IrSize
, &hrtfparams
, todo
);
508 /* Store the interpolated gain or the final target gain depending if
511 if(DstBufferSize
< Counter
)
512 parms
.Hrtf
.Old
.Gain
= gain
;
514 parms
.Hrtf
.Old
.Gain
= TargetGain
;
517 /* Copy the new in-progress accumulation values back for the next mix. */
518 std::copy_n(std::begin(AccumSamples
) + DstBufferSize
, parms
.Hrtf
.State
.Values
.size(),
519 parms
.Hrtf
.State
.Values
.begin());
522 void DoNfcMix(ALvoice::TargetData
&Direct
, const float *TargetGains
, DirectParams
&parms
,
523 const float *samples
, const ALuint DstBufferSize
, const ALuint Counter
, const ALuint OutPos
,
526 const size_t outcount
{Device
->NumChannelsPerOrder
[0]};
527 MixSamples({samples
, DstBufferSize
}, Direct
.Buffer
.first(outcount
),
528 parms
.Gains
.Current
.data(), TargetGains
, Counter
, OutPos
);
530 const al::span
<float> nfcsamples
{Device
->NfcSampleData
, DstBufferSize
};
531 size_t chanoffset
{outcount
};
532 using FilterProc
= void (NfcFilter::*)(float*,const float*,const size_t);
533 auto apply_nfc
= [&Direct
,&parms
,samples
,TargetGains
,Counter
,OutPos
,&chanoffset
,nfcsamples
](
534 const FilterProc process
, const size_t chancount
) -> void
536 if(chancount
< 1) return;
537 (parms
.NFCtrlFilter
.*process
)(nfcsamples
.data(), samples
, nfcsamples
.size());
538 MixSamples(nfcsamples
, Direct
.Buffer
.subspan(chanoffset
, chancount
),
539 &parms
.Gains
.Current
[chanoffset
], &TargetGains
[chanoffset
], Counter
, OutPos
);
540 chanoffset
+= chancount
;
542 apply_nfc(&NfcFilter::process1
, Device
->NumChannelsPerOrder
[1]);
543 apply_nfc(&NfcFilter::process2
, Device
->NumChannelsPerOrder
[2]);
544 apply_nfc(&NfcFilter::process3
, Device
->NumChannelsPerOrder
[3]);
549 void ALvoice::mix(const State vstate
, ALCcontext
*Context
, const ALuint SamplesToDo
)
551 static constexpr std::array
<float,MAX_OUTPUT_CHANNELS
> SilentTarget
{};
553 ASSUME(SamplesToDo
> 0);
556 const bool isstatic
{(mFlags
&VOICE_IS_STATIC
) != 0};
557 ALuint DataPosInt
{mPosition
.load(std::memory_order_relaxed
)};
558 ALuint DataPosFrac
{mPositionFrac
.load(std::memory_order_relaxed
)};
559 ALbufferlistitem
*BufferListItem
{mCurrentBuffer
.load(std::memory_order_relaxed
)};
560 ALbufferlistitem
*BufferLoopItem
{mLoopBuffer
.load(std::memory_order_relaxed
)};
561 const ALuint NumChannels
{mNumChannels
};
562 const ALuint SampleSize
{mSampleSize
};
563 const ALuint increment
{mStep
};
564 if(increment
< 1) return;
566 ASSUME(NumChannels
> 0);
567 ASSUME(SampleSize
> 0);
568 ASSUME(increment
> 0);
570 ALCdevice
*Device
{Context
->mDevice
.get()};
571 const ALuint NumSends
{Device
->NumAuxSends
};
572 const ALuint IrSize
{Device
->mHrtf
? Device
->mHrtf
->irSize
: 0};
574 ResamplerFunc Resample
{(increment
== FRACTIONONE
&& DataPosFrac
== 0) ?
575 Resample_
<CopyTag
,CTag
> : mResampler
};
577 ALuint Counter
{(mFlags
&VOICE_IS_FADING
) ? SamplesToDo
: 0};
580 /* No fading, just overwrite the old/current params. */
581 for(ALuint chan
{0};chan
< NumChannels
;chan
++)
583 ChannelData
&chandata
= mChans
[chan
];
585 DirectParams
&parms
= chandata
.mDryParams
;
586 if(!(mFlags
&VOICE_HAS_HRTF
))
587 parms
.Gains
.Current
= parms
.Gains
.Target
;
589 parms
.Hrtf
.Old
= parms
.Hrtf
.Target
;
591 for(ALuint send
{0};send
< NumSends
;++send
)
593 if(mSend
[send
].Buffer
.empty())
596 SendParams
&parms
= chandata
.mWetParams
[send
];
597 parms
.Gains
.Current
= parms
.Gains
.Target
;
601 else if((mFlags
&VOICE_HAS_HRTF
))
603 for(ALuint chan
{0};chan
< NumChannels
;chan
++)
605 DirectParams
&parms
= mChans
[chan
].mDryParams
;
606 if(!(parms
.Hrtf
.Old
.Gain
> GAIN_SILENCE_THRESHOLD
))
608 /* The old HRTF params are silent, so overwrite the old
609 * coefficients with the new, and reset the old gain to 0. The
610 * future mix will then fade from silence.
612 parms
.Hrtf
.Old
= parms
.Hrtf
.Target
;
613 parms
.Hrtf
.Old
.Gain
= 0.0f
;
618 ALuint buffers_done
{0u};
621 /* Figure out how many buffer samples will be needed */
622 ALuint DstBufferSize
{SamplesToDo
- OutPos
};
624 /* Calculate the last written dst sample pos. */
625 uint64_t DataSize64
{DstBufferSize
- 1};
626 /* Calculate the last read src sample pos. */
627 DataSize64
= (DataSize64
*increment
+ DataPosFrac
) >> FRACTIONBITS
;
628 /* +1 to get the src sample count, include padding. */
629 DataSize64
+= 1 + MAX_RESAMPLER_PADDING
;
631 auto SrcBufferSize
= static_cast<ALuint
>(
632 minu64(DataSize64
, BUFFERSIZE
+ MAX_RESAMPLER_PADDING
+ 1));
633 if(SrcBufferSize
> BUFFERSIZE
+ MAX_RESAMPLER_PADDING
)
635 SrcBufferSize
= BUFFERSIZE
+ MAX_RESAMPLER_PADDING
;
636 /* If the source buffer got saturated, we can't fill the desired
637 * dst size. Figure out how many samples we can actually mix from
640 DataSize64
= SrcBufferSize
- MAX_RESAMPLER_PADDING
;
641 DataSize64
= ((DataSize64
<<FRACTIONBITS
) - DataPosFrac
+ increment
-1) / increment
;
642 DstBufferSize
= static_cast<ALuint
>(minu64(DataSize64
, DstBufferSize
));
644 /* Some mixers like having a multiple of 4, so try to give that
645 * unless this is the last update.
647 if(DstBufferSize
< SamplesToDo
-OutPos
)
648 DstBufferSize
&= ~3u;
651 ASSUME(DstBufferSize
> 0);
652 for(ALuint chan
{0};chan
< NumChannels
;chan
++)
654 ChannelData
&chandata
= mChans
[chan
];
655 const al::span
<ALfloat
> SrcData
{Device
->SourceData
, SrcBufferSize
};
657 /* Load the previous samples into the source data first, then load
658 * what we can from the buffer queue.
660 auto srciter
= std::copy_n(chandata
.mPrevSamples
.begin(), MAX_RESAMPLER_PADDING
>>1,
663 if UNLIKELY(!BufferListItem
)
664 srciter
= std::copy(chandata
.mPrevSamples
.begin()+(MAX_RESAMPLER_PADDING
>>1),
665 chandata
.mPrevSamples
.end(), srciter
);
667 srciter
= LoadBufferStatic(BufferListItem
, BufferLoopItem
, NumChannels
,
668 SampleSize
, chan
, DataPosInt
, {srciter
, SrcData
.end()});
670 srciter
= LoadBufferQueue(BufferListItem
, BufferLoopItem
, NumChannels
,
671 SampleSize
, chan
, DataPosInt
, {srciter
, SrcData
.end()});
673 if UNLIKELY(srciter
!= SrcData
.end())
675 /* If the source buffer wasn't filled, copy the last sample for
676 * the remaining buffer. Ideally it should have ended with
677 * silence, but if not the gain fading should help avoid clicks
678 * from sudden amplitude changes.
680 const ALfloat sample
{*(srciter
-1)};
681 std::fill(srciter
, SrcData
.end(), sample
);
684 /* Store the last source samples used for next time. */
685 std::copy_n(&SrcData
[(increment
*DstBufferSize
+ DataPosFrac
)>>FRACTIONBITS
],
686 chandata
.mPrevSamples
.size(), chandata
.mPrevSamples
.begin());
688 /* Resample, then apply ambisonic upsampling as needed. */
689 const ALfloat
*ResampledData
{Resample(&mResampleState
,
690 &SrcData
[MAX_RESAMPLER_PADDING
>>1], DataPosFrac
, increment
,
691 {Device
->ResampledData
, DstBufferSize
})};
692 if((mFlags
&VOICE_IS_AMBISONIC
))
694 const ALfloat hfscale
{chandata
.mAmbiScale
};
695 /* Beware the evil const_cast. It's safe since it's pointing to
696 * either SourceData or ResampledData (both non-const), but the
697 * resample method takes the source as const float* and may
698 * return it without copying to output, making it currently
701 chandata
.mAmbiSplitter
.applyHfScale(const_cast<ALfloat
*>(ResampledData
), hfscale
,
705 /* Now filter and mix to the appropriate outputs. */
706 ALfloat (&FilterBuf
)[BUFFERSIZE
] = Device
->FilteredData
;
708 DirectParams
&parms
= chandata
.mDryParams
;
709 const ALfloat
*samples
{DoFilters(&parms
.LowPass
, &parms
.HighPass
, FilterBuf
,
710 ResampledData
, DstBufferSize
, mDirect
.FilterType
)};
712 if((mFlags
&VOICE_HAS_HRTF
))
714 const ALfloat TargetGain
{UNLIKELY(vstate
== ALvoice::Stopping
) ? 0.0f
:
715 parms
.Hrtf
.Target
.Gain
};
716 DoHrtfMix(mDirect
, TargetGain
, parms
, samples
, DstBufferSize
, Counter
, OutPos
,
719 else if((mFlags
&VOICE_HAS_NFC
))
721 const float *TargetGains
{UNLIKELY(vstate
== ALvoice::Stopping
) ?
722 SilentTarget
.data() : parms
.Gains
.Target
.data()};
723 DoNfcMix(mDirect
, TargetGains
, parms
, samples
, DstBufferSize
, Counter
, OutPos
,
728 const float *TargetGains
{UNLIKELY(vstate
== ALvoice::Stopping
) ?
729 SilentTarget
.data() : parms
.Gains
.Target
.data()};
730 MixSamples({samples
, DstBufferSize
}, mDirect
.Buffer
,
731 parms
.Gains
.Current
.data(), TargetGains
, Counter
, OutPos
);
735 for(ALuint send
{0};send
< NumSends
;++send
)
737 if(mSend
[send
].Buffer
.empty())
740 SendParams
&parms
= chandata
.mWetParams
[send
];
741 const ALfloat
*samples
{DoFilters(&parms
.LowPass
, &parms
.HighPass
, FilterBuf
,
742 ResampledData
, DstBufferSize
, mSend
[send
].FilterType
)};
744 const float *TargetGains
{UNLIKELY(vstate
== ALvoice::Stopping
) ?
745 SilentTarget
.data() : parms
.Gains
.Target
.data()};
746 MixSamples({samples
, DstBufferSize
}, mSend
[send
].Buffer
,
747 parms
.Gains
.Current
.data(), TargetGains
, Counter
, OutPos
);
750 /* Update positions */
751 DataPosFrac
+= increment
*DstBufferSize
;
752 DataPosInt
+= DataPosFrac
>>FRACTIONBITS
;
753 DataPosFrac
&= FRACTIONMASK
;
755 OutPos
+= DstBufferSize
;
756 Counter
= maxu(DstBufferSize
, Counter
) - DstBufferSize
;
758 if UNLIKELY(!BufferListItem
)
760 /* Do nothing extra when there's no buffers. */
766 /* Handle looping static source */
767 const ALbuffer
*Buffer
{BufferListItem
->mBuffer
};
768 const ALuint LoopStart
{Buffer
->LoopStart
};
769 const ALuint LoopEnd
{Buffer
->LoopEnd
};
770 if(DataPosInt
>= LoopEnd
)
772 assert(LoopEnd
> LoopStart
);
773 DataPosInt
= ((DataPosInt
-LoopStart
)%(LoopEnd
-LoopStart
)) + LoopStart
;
778 /* Handle non-looping static source */
779 if(DataPosInt
>= BufferListItem
->mSampleLen
)
781 BufferListItem
= nullptr;
788 /* Handle streaming source */
790 if(BufferListItem
->mSampleLen
> DataPosInt
)
793 DataPosInt
-= BufferListItem
->mSampleLen
;
796 BufferListItem
= BufferListItem
->mNext
.load(std::memory_order_relaxed
);
797 if(!BufferListItem
) BufferListItem
= BufferLoopItem
;
798 } while(BufferListItem
);
800 } while(OutPos
< SamplesToDo
);
802 mFlags
|= VOICE_IS_FADING
;
804 /* Don't update positions and buffers if we were stopping. */
805 if UNLIKELY(vstate
== ALvoice::Stopping
)
807 mPlayState
.store(ALvoice::Stopped
, std::memory_order_release
);
811 /* Capture the source ID in case it's reset for stopping. */
812 const ALuint SourceID
{mSourceID
.load(std::memory_order_relaxed
)};
814 /* Update voice info */
815 mPosition
.store(DataPosInt
, std::memory_order_relaxed
);
816 mPositionFrac
.store(DataPosFrac
, std::memory_order_relaxed
);
817 mCurrentBuffer
.store(BufferListItem
, std::memory_order_relaxed
);
820 mLoopBuffer
.store(nullptr, std::memory_order_relaxed
);
821 mSourceID
.store(0u, std::memory_order_relaxed
);
823 std::atomic_thread_fence(std::memory_order_release
);
825 /* Send any events now, after the position/buffer info was updated. */
826 const ALbitfieldSOFT enabledevt
{Context
->mEnabledEvts
.load(std::memory_order_acquire
)};
827 if(buffers_done
> 0 && (enabledevt
&EventType_BufferCompleted
))
829 RingBuffer
*ring
{Context
->mAsyncEvents
.get()};
830 auto evt_vec
= ring
->getWriteVector();
831 if(evt_vec
.first
.len
> 0)
833 AsyncEvent
*evt
{new (evt_vec
.first
.buf
) AsyncEvent
{EventType_BufferCompleted
}};
834 evt
->u
.bufcomp
.id
= SourceID
;
835 evt
->u
.bufcomp
.count
= buffers_done
;
836 ring
->writeAdvance(1);
837 Context
->mEventSem
.post();
843 /* If the voice just ended, set it to Stopping so the next render
844 * ensures any residual noise fades to 0 amplitude.
846 mPlayState
.store(ALvoice::Stopping
, std::memory_order_release
);
847 if((enabledevt
&EventType_SourceStateChange
))
848 SendSourceStoppedEvent(Context
, SourceID
);