Use istream for makemhr input
[openal-soft.git] / alc / mixvoice.cpp
blob99e4dc48a3a8a3cda8f0f8c7ac680a3be035722c
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 <algorithm>
24 #include <array>
25 #include <atomic>
26 #include <cassert>
27 #include <climits>
28 #include <cstddef>
29 #include <cstdint>
30 #include <iterator>
31 #include <memory>
32 #include <new>
33 #include <string>
34 #include <utility>
36 #include "AL/al.h"
37 #include "AL/alc.h"
39 #include "al/buffer.h"
40 #include "al/event.h"
41 #include "al/source.h"
42 #include "alcmain.h"
43 #include "albyte.h"
44 #include "alconfig.h"
45 #include "alcontext.h"
46 #include "alnumeric.h"
47 #include "aloptional.h"
48 #include "alspan.h"
49 #include "alstring.h"
50 #include "alu.h"
51 #include "cpu_caps.h"
52 #include "devformat.h"
53 #include "filters/biquad.h"
54 #include "filters/nfc.h"
55 #include "filters/splitter.h"
56 #include "hrtf.h"
57 #include "inprogext.h"
58 #include "logging.h"
59 #include "mixer/defs.h"
60 #include "opthelpers.h"
61 #include "ringbuffer.h"
62 #include "threads.h"
63 #include "vector.h"
66 static_assert((INT_MAX>>FRACTIONBITS)/MAX_PITCH > BUFFERSIZE,
67 "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
69 /* BSinc24 requires up to 23 extra samples before the current position, and 24 after. */
70 static_assert(MAX_RESAMPLE_PADDING >= 24, "MAX_RESAMPLE_PADDING must be at least 24!");
73 Resampler ResamplerDefault{Resampler::Linear};
75 MixerFunc MixSamples = Mix_<CTag>;
76 RowMixerFunc MixRowSamples = MixRow_<CTag>;
78 namespace {
80 HrtfMixerFunc MixHrtfSamples = MixHrtf_<CTag>;
81 HrtfMixerBlendFunc MixHrtfBlendSamples = MixHrtfBlend_<CTag>;
83 inline MixerFunc SelectMixer()
85 #ifdef HAVE_NEON
86 if((CPUCapFlags&CPU_CAP_NEON))
87 return Mix_<NEONTag>;
88 #endif
89 #ifdef HAVE_SSE
90 if((CPUCapFlags&CPU_CAP_SSE))
91 return Mix_<SSETag>;
92 #endif
93 return Mix_<CTag>;
96 inline RowMixerFunc SelectRowMixer()
98 #ifdef HAVE_NEON
99 if((CPUCapFlags&CPU_CAP_NEON))
100 return MixRow_<NEONTag>;
101 #endif
102 #ifdef HAVE_SSE
103 if((CPUCapFlags&CPU_CAP_SSE))
104 return MixRow_<SSETag>;
105 #endif
106 return MixRow_<CTag>;
109 inline HrtfMixerFunc SelectHrtfMixer()
111 #ifdef HAVE_NEON
112 if((CPUCapFlags&CPU_CAP_NEON))
113 return MixHrtf_<NEONTag>;
114 #endif
115 #ifdef HAVE_SSE
116 if((CPUCapFlags&CPU_CAP_SSE))
117 return MixHrtf_<SSETag>;
118 #endif
119 return MixHrtf_<CTag>;
122 inline HrtfMixerBlendFunc SelectHrtfBlendMixer()
124 #ifdef HAVE_NEON
125 if((CPUCapFlags&CPU_CAP_NEON))
126 return MixHrtfBlend_<NEONTag>;
127 #endif
128 #ifdef HAVE_SSE
129 if((CPUCapFlags&CPU_CAP_SSE))
130 return MixHrtfBlend_<SSETag>;
131 #endif
132 return MixHrtfBlend_<CTag>;
135 } // namespace
138 ResamplerFunc SelectResampler(Resampler resampler)
140 switch(resampler)
142 case Resampler::Point:
143 return Resample_<PointTag,CTag>;
144 case Resampler::Linear:
145 #ifdef HAVE_NEON
146 if((CPUCapFlags&CPU_CAP_NEON))
147 return Resample_<LerpTag,NEONTag>;
148 #endif
149 #ifdef HAVE_SSE4_1
150 if((CPUCapFlags&CPU_CAP_SSE4_1))
151 return Resample_<LerpTag,SSE4Tag>;
152 #endif
153 #ifdef HAVE_SSE2
154 if((CPUCapFlags&CPU_CAP_SSE2))
155 return Resample_<LerpTag,SSE2Tag>;
156 #endif
157 return Resample_<LerpTag,CTag>;
158 case Resampler::Cubic:
159 return Resample_<CubicTag,CTag>;
160 case Resampler::BSinc12:
161 case Resampler::BSinc24:
162 #ifdef HAVE_NEON
163 if((CPUCapFlags&CPU_CAP_NEON))
164 return Resample_<BSincTag,NEONTag>;
165 #endif
166 #ifdef HAVE_SSE
167 if((CPUCapFlags&CPU_CAP_SSE))
168 return Resample_<BSincTag,SSETag>;
169 #endif
170 return Resample_<BSincTag,CTag>;
173 return Resample_<PointTag,CTag>;
177 void aluInitMixer()
179 if(auto resopt = ConfigValueStr(nullptr, nullptr, "resampler"))
181 struct ResamplerEntry {
182 const char name[12];
183 const Resampler resampler;
185 constexpr ResamplerEntry ResamplerList[]{
186 { "none", Resampler::Point },
187 { "point", Resampler::Point },
188 { "cubic", Resampler::Cubic },
189 { "bsinc12", Resampler::BSinc12 },
190 { "bsinc24", Resampler::BSinc24 },
193 const char *str{resopt->c_str()};
194 if(al::strcasecmp(str, "bsinc") == 0)
196 WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", str);
197 str = "bsinc12";
199 else if(al::strcasecmp(str, "sinc4") == 0 || al::strcasecmp(str, "sinc8") == 0)
201 WARN("Resampler option \"%s\" is deprecated, using cubic\n", str);
202 str = "cubic";
205 auto iter = std::find_if(std::begin(ResamplerList), std::end(ResamplerList),
206 [str](const ResamplerEntry &entry) -> bool
207 { return al::strcasecmp(str, entry.name) == 0; });
208 if(iter == std::end(ResamplerList))
209 ERR("Invalid resampler: %s\n", str);
210 else
211 ResamplerDefault = iter->resampler;
214 MixHrtfBlendSamples = SelectHrtfBlendMixer();
215 MixHrtfSamples = SelectHrtfMixer();
216 MixSamples = SelectMixer();
217 MixRowSamples = SelectRowMixer();
221 namespace {
223 /* A quick'n'dirty lookup table to decode a muLaw-encoded byte sample into a
224 * signed 16-bit sample */
225 constexpr ALshort muLawDecompressionTable[256] = {
226 -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
227 -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
228 -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
229 -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
230 -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
231 -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
232 -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
233 -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
234 -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
235 -1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
236 -876, -844, -812, -780, -748, -716, -684, -652,
237 -620, -588, -556, -524, -492, -460, -428, -396,
238 -372, -356, -340, -324, -308, -292, -276, -260,
239 -244, -228, -212, -196, -180, -164, -148, -132,
240 -120, -112, -104, -96, -88, -80, -72, -64,
241 -56, -48, -40, -32, -24, -16, -8, 0,
242 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
243 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
244 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
245 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
246 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
247 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
248 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
249 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
250 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
251 1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
252 876, 844, 812, 780, 748, 716, 684, 652,
253 620, 588, 556, 524, 492, 460, 428, 396,
254 372, 356, 340, 324, 308, 292, 276, 260,
255 244, 228, 212, 196, 180, 164, 148, 132,
256 120, 112, 104, 96, 88, 80, 72, 64,
257 56, 48, 40, 32, 24, 16, 8, 0
260 /* A quick'n'dirty lookup table to decode an aLaw-encoded byte sample into a
261 * signed 16-bit sample */
262 constexpr ALshort aLawDecompressionTable[256] = {
263 -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
264 -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
265 -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
266 -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
267 -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
268 -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
269 -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472,
270 -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
271 -344, -328, -376, -360, -280, -264, -312, -296,
272 -472, -456, -504, -488, -408, -392, -440, -424,
273 -88, -72, -120, -104, -24, -8, -56, -40,
274 -216, -200, -248, -232, -152, -136, -184, -168,
275 -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
276 -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
277 -688, -656, -752, -720, -560, -528, -624, -592,
278 -944, -912, -1008, -976, -816, -784, -880, -848,
279 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
280 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
281 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
282 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
283 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
284 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
285 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
286 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
287 344, 328, 376, 360, 280, 264, 312, 296,
288 472, 456, 504, 488, 408, 392, 440, 424,
289 88, 72, 120, 104, 24, 8, 56, 40,
290 216, 200, 248, 232, 152, 136, 184, 168,
291 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
292 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
293 688, 656, 752, 720, 560, 528, 624, 592,
294 944, 912, 1008, 976, 816, 784, 880, 848
297 template<FmtType T>
298 struct FmtTypeTraits { };
300 template<>
301 struct FmtTypeTraits<FmtUByte> {
302 using Type = ALubyte;
303 static constexpr inline float to_float(const Type val) noexcept
304 { return val*(1.0f/128.0f) - 128.0f; }
306 template<>
307 struct FmtTypeTraits<FmtShort> {
308 using Type = ALshort;
309 static constexpr inline float to_float(const Type val) noexcept { return val*(1.0f/32768.0f); }
311 template<>
312 struct FmtTypeTraits<FmtFloat> {
313 using Type = ALfloat;
314 static constexpr inline float to_float(const Type val) noexcept { return val; }
316 template<>
317 struct FmtTypeTraits<FmtDouble> {
318 using Type = ALdouble;
319 static constexpr inline float to_float(const Type val) noexcept
320 { return static_cast<ALfloat>(val); }
322 template<>
323 struct FmtTypeTraits<FmtMulaw> {
324 using Type = ALubyte;
325 static constexpr inline float to_float(const Type val) noexcept
326 { return muLawDecompressionTable[val] * (1.0f/32768.0f); }
328 template<>
329 struct FmtTypeTraits<FmtAlaw> {
330 using Type = ALubyte;
331 static constexpr inline float to_float(const Type val) noexcept
332 { return aLawDecompressionTable[val] * (1.0f/32768.0f); }
336 void SendSourceStoppedEvent(ALCcontext *context, ALuint id)
338 RingBuffer *ring{context->mAsyncEvents.get()};
339 auto evt_vec = ring->getWriteVector();
340 if(evt_vec.first.len < 1) return;
342 AsyncEvent *evt{new (evt_vec.first.buf) AsyncEvent{EventType_SourceStateChange}};
343 evt->u.srcstate.id = id;
344 evt->u.srcstate.state = AL_STOPPED;
346 ring->writeAdvance(1);
347 context->mEventSem.post();
351 const ALfloat *DoFilters(BiquadFilter *lpfilter, BiquadFilter *hpfilter, ALfloat *dst,
352 const ALfloat *src, const size_t numsamples, int type)
354 switch(type)
356 case AF_None:
357 lpfilter->clear();
358 hpfilter->clear();
359 break;
361 case AF_LowPass:
362 lpfilter->process(dst, src, numsamples);
363 hpfilter->clear();
364 return dst;
365 case AF_HighPass:
366 lpfilter->clear();
367 hpfilter->process(dst, src, numsamples);
368 return dst;
370 case AF_BandPass:
371 lpfilter->process(dst, src, numsamples);
372 hpfilter->process(dst, dst, numsamples);
373 return dst;
375 return src;
379 template<FmtType T>
380 inline void LoadSampleArray(ALfloat *RESTRICT dst, const al::byte *src, const size_t srcstep,
381 const size_t samples) noexcept
383 using SampleType = typename FmtTypeTraits<T>::Type;
385 const SampleType *RESTRICT ssrc{reinterpret_cast<const SampleType*>(src)};
386 for(size_t i{0u};i < samples;i++)
387 dst[i] = FmtTypeTraits<T>::to_float(ssrc[i*srcstep]);
390 void LoadSamples(ALfloat *RESTRICT dst, const al::byte *src, const size_t srcstep, FmtType srctype,
391 const size_t samples) noexcept
393 #define HANDLE_FMT(T) case T: LoadSampleArray<T>(dst, src, srcstep, samples); break
394 switch(srctype)
396 HANDLE_FMT(FmtUByte);
397 HANDLE_FMT(FmtShort);
398 HANDLE_FMT(FmtFloat);
399 HANDLE_FMT(FmtDouble);
400 HANDLE_FMT(FmtMulaw);
401 HANDLE_FMT(FmtAlaw);
403 #undef HANDLE_FMT
406 ALfloat *LoadBufferStatic(ALbufferlistitem *BufferListItem, ALbufferlistitem *&BufferLoopItem,
407 const size_t NumChannels, const size_t SampleSize, const size_t chan, size_t DataPosInt,
408 al::span<ALfloat> SrcBuffer)
410 const ALbuffer *Buffer{BufferListItem->mBuffer};
411 const ALuint LoopStart{Buffer->LoopStart};
412 const ALuint LoopEnd{Buffer->LoopEnd};
413 ASSUME(LoopEnd > LoopStart);
415 /* If current pos is beyond the loop range, do not loop */
416 if(!BufferLoopItem || DataPosInt >= LoopEnd)
418 BufferLoopItem = nullptr;
420 /* Load what's left to play from the buffer */
421 const size_t DataRem{minz(SrcBuffer.size(), Buffer->SampleLen-DataPosInt)};
423 const al::byte *Data{Buffer->mData.data()};
424 Data += (DataPosInt*NumChannels + chan)*SampleSize;
426 LoadSamples(SrcBuffer.data(), Data, NumChannels, Buffer->mFmtType, DataRem);
427 SrcBuffer = SrcBuffer.subspan(DataRem);
429 else
431 /* Load what's left of this loop iteration */
432 const size_t DataRem{minz(SrcBuffer.size(), LoopEnd-DataPosInt)};
434 const al::byte *Data{Buffer->mData.data()};
435 Data += (DataPosInt*NumChannels + chan)*SampleSize;
437 LoadSamples(SrcBuffer.data(), Data, NumChannels, Buffer->mFmtType, DataRem);
438 SrcBuffer = SrcBuffer.subspan(DataRem);
440 /* Load any repeats of the loop we can to fill the buffer. */
441 const auto LoopSize = static_cast<size_t>(LoopEnd - LoopStart);
442 while(!SrcBuffer.empty())
444 const size_t DataSize{minz(SrcBuffer.size(), LoopSize)};
446 Data = Buffer->mData.data() + (LoopStart*NumChannels + chan)*SampleSize;
448 LoadSamples(SrcBuffer.data(), Data, NumChannels, Buffer->mFmtType, DataSize);
449 SrcBuffer = SrcBuffer.subspan(DataSize);
452 return SrcBuffer.begin();
455 ALfloat *LoadBufferQueue(ALbufferlistitem *BufferListItem, ALbufferlistitem *BufferLoopItem,
456 const size_t NumChannels, const size_t SampleSize, const size_t chan, size_t DataPosInt,
457 al::span<ALfloat> SrcBuffer)
459 /* Crawl the buffer queue to fill in the temp buffer */
460 while(BufferListItem && !SrcBuffer.empty())
462 ALbuffer *Buffer{BufferListItem->mBuffer};
463 if(!(Buffer && DataPosInt < Buffer->SampleLen))
465 if(Buffer) DataPosInt -= Buffer->SampleLen;
466 BufferListItem = BufferListItem->mNext.load(std::memory_order_acquire);
467 if(!BufferListItem) BufferListItem = BufferLoopItem;
468 continue;
471 const size_t DataSize{minz(SrcBuffer.size(), Buffer->SampleLen-DataPosInt)};
473 const al::byte *Data{Buffer->mData.data()};
474 Data += (DataPosInt*NumChannels + chan)*SampleSize;
476 LoadSamples(SrcBuffer.data(), Data, NumChannels, Buffer->mFmtType, DataSize);
477 SrcBuffer = SrcBuffer.subspan(DataSize);
478 if(SrcBuffer.empty()) break;
480 DataPosInt = 0;
481 BufferListItem = BufferListItem->mNext.load(std::memory_order_acquire);
482 if(!BufferListItem) BufferListItem = BufferLoopItem;
485 return SrcBuffer.begin();
489 void DoHrtfMix(ALvoice::DirectData &Direct, const float TargetGain, DirectParams &parms,
490 const float *samples, const ALuint DstBufferSize, const ALuint Counter, const ALuint OutPos,
491 const ALuint IrSize, ALCdevice *Device)
493 const ALuint OutLIdx{GetChannelIdxByName(Device->RealOut, FrontLeft)};
494 const ALuint OutRIdx{GetChannelIdxByName(Device->RealOut, FrontRight)};
495 auto &HrtfSamples = Device->HrtfSourceData;
496 auto &AccumSamples = Device->HrtfAccumData;
498 /* Copy the HRTF history and new input samples into a temp buffer. */
499 auto src_iter = std::copy(parms.Hrtf.State.History.begin(), parms.Hrtf.State.History.end(),
500 std::begin(HrtfSamples));
501 std::copy_n(samples, DstBufferSize, src_iter);
502 /* Copy the last used samples back into the history buffer for later. */
503 std::copy_n(std::begin(HrtfSamples) + DstBufferSize, parms.Hrtf.State.History.size(),
504 parms.Hrtf.State.History.begin());
506 /* Copy the current filtered values being accumulated into the temp buffer. */
507 auto accum_iter = std::copy_n(parms.Hrtf.State.Values.begin(), parms.Hrtf.State.Values.size(),
508 std::begin(AccumSamples));
509 /* Clear the accumulation buffer that will start getting filled in. */
510 std::fill_n(accum_iter, DstBufferSize, float2{});
512 /* If fading, the old gain is not silence, and this is the first mixing
513 * pass, fade between the IRs.
515 ALuint fademix{0u};
516 if(Counter && parms.Hrtf.Old.Gain > GAIN_SILENCE_THRESHOLD && OutPos == 0)
518 fademix = minu(DstBufferSize, 128);
520 float gain{TargetGain};
522 /* The new coefficients need to fade in completely since they're
523 * replacing the old ones. To keep the gain fading consistent,
524 * interpolate between the old and new target gains given how much of
525 * the fade time this mix handles.
527 if LIKELY(Counter > fademix)
529 const ALfloat a{static_cast<float>(fademix) / static_cast<float>(Counter)};
530 gain = lerp(parms.Hrtf.Old.Gain, TargetGain, a);
532 MixHrtfFilter hrtfparams;
533 hrtfparams.Coeffs = &parms.Hrtf.Target.Coeffs;
534 hrtfparams.Delay[0] = parms.Hrtf.Target.Delay[0];
535 hrtfparams.Delay[1] = parms.Hrtf.Target.Delay[1];
536 hrtfparams.Gain = 0.0f;
537 hrtfparams.GainStep = gain / static_cast<float>(fademix);
539 MixHrtfBlendSamples(Direct.Buffer[OutLIdx], Direct.Buffer[OutRIdx], HrtfSamples,
540 AccumSamples, OutPos, IrSize, &parms.Hrtf.Old, &hrtfparams, fademix);
541 /* Update the old parameters with the result. */
542 parms.Hrtf.Old = parms.Hrtf.Target;
543 if(fademix < Counter)
544 parms.Hrtf.Old.Gain = hrtfparams.Gain;
545 else
546 parms.Hrtf.Old.Gain = TargetGain;
549 if LIKELY(fademix < DstBufferSize)
551 const ALuint todo{DstBufferSize - fademix};
552 float gain{TargetGain};
554 /* Interpolate the target gain if the gain fading lasts longer than
555 * this mix.
557 if(Counter > DstBufferSize)
559 const float a{static_cast<float>(todo) / static_cast<float>(Counter-fademix)};
560 gain = lerp(parms.Hrtf.Old.Gain, TargetGain, a);
563 MixHrtfFilter hrtfparams;
564 hrtfparams.Coeffs = &parms.Hrtf.Target.Coeffs;
565 hrtfparams.Delay[0] = parms.Hrtf.Target.Delay[0];
566 hrtfparams.Delay[1] = parms.Hrtf.Target.Delay[1];
567 hrtfparams.Gain = parms.Hrtf.Old.Gain;
568 hrtfparams.GainStep = (gain - parms.Hrtf.Old.Gain) / static_cast<float>(todo);
569 MixHrtfSamples(Direct.Buffer[OutLIdx], Direct.Buffer[OutRIdx], HrtfSamples+fademix,
570 AccumSamples+fademix, OutPos+fademix, IrSize, &hrtfparams, todo);
571 /* Store the interpolated gain or the final target gain depending if
572 * the fade is done.
574 if(DstBufferSize < Counter)
575 parms.Hrtf.Old.Gain = gain;
576 else
577 parms.Hrtf.Old.Gain = TargetGain;
580 /* Copy the new in-progress accumulation values back for the next mix. */
581 std::copy_n(std::begin(AccumSamples) + DstBufferSize, parms.Hrtf.State.Values.size(),
582 parms.Hrtf.State.Values.begin());
585 void DoNfcMix(ALvoice::DirectData &Direct, const float *TargetGains, DirectParams &parms,
586 const float *samples, const ALuint DstBufferSize, const ALuint Counter, const ALuint OutPos,
587 ALCdevice *Device)
589 const size_t outcount{Device->NumChannelsPerOrder[0]};
590 MixSamples({samples, DstBufferSize}, Direct.Buffer.first(outcount),
591 parms.Gains.Current, TargetGains, Counter, OutPos);
593 const al::span<float> nfcsamples{Device->NfcSampleData, DstBufferSize};
594 size_t chanoffset{outcount};
595 using FilterProc = void (NfcFilter::*)(float*,const float*,const size_t);
596 auto apply_nfc = [&Direct,&parms,samples,TargetGains,Counter,OutPos,&chanoffset,nfcsamples](
597 const FilterProc process, const size_t chancount) -> void
599 if(chancount < 1) return;
600 (parms.NFCtrlFilter.*process)(nfcsamples.data(), samples, nfcsamples.size());
601 MixSamples(nfcsamples, Direct.Buffer.subspan(chanoffset, chancount),
602 parms.Gains.Current+chanoffset, TargetGains+chanoffset, Counter, OutPos);
603 chanoffset += chancount;
605 apply_nfc(&NfcFilter::process1, Device->NumChannelsPerOrder[1]);
606 apply_nfc(&NfcFilter::process2, Device->NumChannelsPerOrder[2]);
607 apply_nfc(&NfcFilter::process3, Device->NumChannelsPerOrder[3]);
610 } // namespace
612 void ALvoice::mix(State vstate, ALCcontext *Context, const ALuint SamplesToDo)
614 static constexpr ALfloat SilentTarget[MAX_OUTPUT_CHANNELS]{};
616 ASSUME(SamplesToDo > 0);
618 /* Get voice info */
619 const bool isstatic{(mFlags&VOICE_IS_STATIC) != 0};
620 ALuint DataPosInt{mPosition.load(std::memory_order_relaxed)};
621 ALuint DataPosFrac{mPositionFrac.load(std::memory_order_relaxed)};
622 ALbufferlistitem *BufferListItem{mCurrentBuffer.load(std::memory_order_relaxed)};
623 ALbufferlistitem *BufferLoopItem{mLoopBuffer.load(std::memory_order_relaxed)};
624 const ALuint NumChannels{mNumChannels};
625 const ALuint SampleSize{mSampleSize};
626 const ALuint increment{mStep};
627 if(increment < 1) return;
629 ASSUME(NumChannels > 0);
630 ASSUME(SampleSize > 0);
631 ASSUME(increment > 0);
633 ALCdevice *Device{Context->mDevice.get()};
634 const ALuint NumSends{Device->NumAuxSends};
635 const ALuint IrSize{Device->mHrtf ? Device->mHrtf->irSize : 0};
637 ResamplerFunc Resample{(increment == FRACTIONONE && DataPosFrac == 0) ?
638 Resample_<CopyTag,CTag> : mResampler};
640 ALuint Counter{(mFlags&VOICE_IS_FADING) ? SamplesToDo : 0};
641 if(!Counter)
643 /* No fading, just overwrite the old/current params. */
644 for(ALuint chan{0};chan < NumChannels;chan++)
646 ChannelData &chandata = mChans[chan];
648 DirectParams &parms = chandata.mDryParams;
649 if(!(mFlags&VOICE_HAS_HRTF))
650 std::copy(std::begin(parms.Gains.Target), std::end(parms.Gains.Target),
651 std::begin(parms.Gains.Current));
652 else
653 parms.Hrtf.Old = parms.Hrtf.Target;
655 for(ALuint send{0};send < NumSends;++send)
657 if(mSend[send].Buffer.empty())
658 continue;
660 SendParams &parms = chandata.mWetParams[send];
661 std::copy(std::begin(parms.Gains.Target), std::end(parms.Gains.Target),
662 std::begin(parms.Gains.Current));
666 else if((mFlags&VOICE_HAS_HRTF))
668 for(ALuint chan{0};chan < NumChannels;chan++)
670 DirectParams &parms = mChans[chan].mDryParams;
671 if(!(parms.Hrtf.Old.Gain > GAIN_SILENCE_THRESHOLD))
673 /* The old HRTF params are silent, so overwrite the old
674 * coefficients with the new, and reset the old gain to 0. The
675 * future mix will then fade from silence.
677 parms.Hrtf.Old = parms.Hrtf.Target;
678 parms.Hrtf.Old.Gain = 0.0f;
683 ALuint buffers_done{0u};
684 ALuint OutPos{0u};
685 do {
686 /* Figure out how many buffer samples will be needed */
687 ALuint DstBufferSize{SamplesToDo - OutPos};
689 /* Calculate the last written dst sample pos. */
690 uint64_t DataSize64{DstBufferSize - 1};
691 /* Calculate the last read src sample pos. */
692 DataSize64 = (DataSize64*increment + DataPosFrac) >> FRACTIONBITS;
693 /* +1 to get the src sample count, include padding. */
694 DataSize64 += 1 + MAX_RESAMPLE_PADDING*2;
696 auto SrcBufferSize = static_cast<ALuint>(
697 minu64(DataSize64, BUFFERSIZE + MAX_RESAMPLE_PADDING*2 + 1));
698 if(SrcBufferSize > BUFFERSIZE + MAX_RESAMPLE_PADDING*2)
700 SrcBufferSize = BUFFERSIZE + MAX_RESAMPLE_PADDING*2;
701 /* If the source buffer got saturated, we can't fill the desired
702 * dst size. Figure out how many samples we can actually mix from
703 * this.
705 DataSize64 = SrcBufferSize - MAX_RESAMPLE_PADDING*2;
706 DataSize64 = ((DataSize64<<FRACTIONBITS) - DataPosFrac + increment-1) / increment;
707 DstBufferSize = static_cast<ALuint>(minu64(DataSize64, DstBufferSize));
709 /* Some mixers like having a multiple of 4, so try to give that
710 * unless this is the last update.
712 if(DstBufferSize < SamplesToDo-OutPos)
713 DstBufferSize &= ~3u;
716 ASSUME(DstBufferSize > 0);
717 for(ALuint chan{0};chan < NumChannels;chan++)
719 ChannelData &chandata = mChans[chan];
720 const al::span<ALfloat> SrcData{Device->SourceData, SrcBufferSize};
722 /* Load the previous samples into the source data first, then load
723 * what we can from the buffer queue.
725 auto srciter = std::copy_n(chandata.mPrevSamples.begin(), MAX_RESAMPLE_PADDING,
726 SrcData.begin());
728 if UNLIKELY(!BufferListItem)
729 srciter = std::copy(chandata.mPrevSamples.begin()+MAX_RESAMPLE_PADDING,
730 chandata.mPrevSamples.end(), srciter);
731 else if(isstatic)
732 srciter = LoadBufferStatic(BufferListItem, BufferLoopItem, NumChannels,
733 SampleSize, chan, DataPosInt, {srciter, SrcData.end()});
734 else
735 srciter = LoadBufferQueue(BufferListItem, BufferLoopItem, NumChannels,
736 SampleSize, chan, DataPosInt, {srciter, SrcData.end()});
738 if UNLIKELY(srciter != SrcData.end())
740 /* If the source buffer wasn't filled, copy the last sample for
741 * the remaining buffer. Ideally it should have ended with
742 * silence, but if not the gain fading should help avoid clicks
743 * from sudden amplitude changes.
745 const ALfloat sample{*(srciter-1)};
746 std::fill(srciter, SrcData.end(), sample);
749 /* Store the last source samples used for next time. */
750 std::copy_n(&SrcData[(increment*DstBufferSize + DataPosFrac)>>FRACTIONBITS],
751 chandata.mPrevSamples.size(), chandata.mPrevSamples.begin());
753 /* Resample, then apply ambisonic upsampling as needed. */
754 const ALfloat *ResampledData{Resample(&mResampleState, &SrcData[MAX_RESAMPLE_PADDING],
755 DataPosFrac, increment, {Device->ResampledData, DstBufferSize})};
756 if((mFlags&VOICE_IS_AMBISONIC))
758 const ALfloat hfscale{chandata.mAmbiScale};
759 /* Beware the evil const_cast. It's safe since it's pointing to
760 * either SourceData or ResampledData (both non-const), but the
761 * resample method takes the source as const float* and may
762 * return it without copying to output, making it currently
763 * unavoidable.
765 chandata.mAmbiSplitter.applyHfScale(const_cast<ALfloat*>(ResampledData), hfscale,
766 DstBufferSize);
769 /* Now filter and mix to the appropriate outputs. */
770 ALfloat (&FilterBuf)[BUFFERSIZE] = Device->FilteredData;
772 DirectParams &parms = chandata.mDryParams;
773 const ALfloat *samples{DoFilters(&parms.LowPass, &parms.HighPass, FilterBuf,
774 ResampledData, DstBufferSize, mDirect.FilterType)};
776 if((mFlags&VOICE_HAS_HRTF))
778 const ALfloat TargetGain{UNLIKELY(vstate == ALvoice::Stopping) ? 0.0f :
779 parms.Hrtf.Target.Gain};
780 DoHrtfMix(mDirect, TargetGain, parms, samples, DstBufferSize, Counter, OutPos,
781 IrSize, Device);
783 else if((mFlags&VOICE_HAS_NFC))
785 const ALfloat *TargetGains{UNLIKELY(vstate == ALvoice::Stopping) ?
786 SilentTarget : parms.Gains.Target};
787 DoNfcMix(mDirect, TargetGains, parms, samples, DstBufferSize, Counter, OutPos,
788 Device);
790 else
792 const ALfloat *TargetGains{UNLIKELY(vstate == ALvoice::Stopping) ?
793 SilentTarget : parms.Gains.Target};
794 MixSamples({samples, DstBufferSize}, mDirect.Buffer, parms.Gains.Current,
795 TargetGains, Counter, OutPos);
799 for(ALuint send{0};send < NumSends;++send)
801 if(mSend[send].Buffer.empty())
802 continue;
804 SendParams &parms = chandata.mWetParams[send];
805 const ALfloat *samples{DoFilters(&parms.LowPass, &parms.HighPass, FilterBuf,
806 ResampledData, DstBufferSize, mSend[send].FilterType)};
808 const ALfloat *TargetGains{UNLIKELY(vstate==ALvoice::Stopping) ? SilentTarget :
809 parms.Gains.Target};
810 MixSamples({samples, DstBufferSize}, mSend[send].Buffer, parms.Gains.Current,
811 TargetGains, Counter, OutPos);
814 /* Update positions */
815 DataPosFrac += increment*DstBufferSize;
816 DataPosInt += DataPosFrac>>FRACTIONBITS;
817 DataPosFrac &= FRACTIONMASK;
819 OutPos += DstBufferSize;
820 Counter = maxu(DstBufferSize, Counter) - DstBufferSize;
822 if UNLIKELY(!BufferListItem)
824 /* Do nothing extra when there's no buffers. */
826 else if(isstatic)
828 if(BufferLoopItem)
830 /* Handle looping static source */
831 const ALbuffer *Buffer{BufferListItem->mBuffer};
832 const ALuint LoopStart{Buffer->LoopStart};
833 const ALuint LoopEnd{Buffer->LoopEnd};
834 if(DataPosInt >= LoopEnd)
836 assert(LoopEnd > LoopStart);
837 DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart;
840 else
842 /* Handle non-looping static source */
843 if(DataPosInt >= BufferListItem->mSampleLen)
845 if LIKELY(vstate == ALvoice::Playing)
846 vstate = ALvoice::Stopped;
847 BufferListItem = nullptr;
848 break;
852 else while(1)
854 /* Handle streaming source */
855 if(BufferListItem->mSampleLen > DataPosInt)
856 break;
858 DataPosInt -= BufferListItem->mSampleLen;
860 ++buffers_done;
861 BufferListItem = BufferListItem->mNext.load(std::memory_order_relaxed);
862 if(!BufferListItem && !(BufferListItem=BufferLoopItem))
864 if LIKELY(vstate == ALvoice::Playing)
865 vstate = ALvoice::Stopped;
866 break;
869 } while(OutPos < SamplesToDo);
871 mFlags |= VOICE_IS_FADING;
873 /* Don't update positions and buffers if we were stopping. */
874 if UNLIKELY(vstate == ALvoice::Stopping)
876 mPlayState.store(ALvoice::Stopped, std::memory_order_release);
877 return;
880 /* Capture the source ID in case it's reset for stopping. */
881 const ALuint SourceID{mSourceID.load(std::memory_order_relaxed)};
883 /* Update voice info */
884 mPosition.store(DataPosInt, std::memory_order_relaxed);
885 mPositionFrac.store(DataPosFrac, std::memory_order_relaxed);
886 mCurrentBuffer.store(BufferListItem, std::memory_order_relaxed);
887 if(vstate == ALvoice::Stopped)
889 mLoopBuffer.store(nullptr, std::memory_order_relaxed);
890 mSourceID.store(0u, std::memory_order_relaxed);
892 std::atomic_thread_fence(std::memory_order_release);
894 /* Send any events now, after the position/buffer info was updated. */
895 const ALbitfieldSOFT enabledevt{Context->mEnabledEvts.load(std::memory_order_acquire)};
896 if(buffers_done > 0 && (enabledevt&EventType_BufferCompleted))
898 RingBuffer *ring{Context->mAsyncEvents.get()};
899 auto evt_vec = ring->getWriteVector();
900 if(evt_vec.first.len > 0)
902 AsyncEvent *evt{new (evt_vec.first.buf) AsyncEvent{EventType_BufferCompleted}};
903 evt->u.bufcomp.id = SourceID;
904 evt->u.bufcomp.count = buffers_done;
905 ring->writeAdvance(1);
906 Context->mEventSem.post();
910 if(vstate == ALvoice::Stopped)
912 /* If the voice just ended, set it to Stopping so the next render
913 * ensures any residual noise fades to 0 amplitude.
915 mPlayState.store(ALvoice::Stopping, std::memory_order_release);
916 if((enabledevt&EventType_SourceStateChange))
917 SendSourceStoppedEvent(Context, SourceID);