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"
57 #include "fmt_traits.h"
59 #include "inprogext.h"
61 #include "mixer/defs.h"
62 #include "opthelpers.h"
63 #include "ringbuffer.h"
77 static_assert((BUFFERSIZE
-1)/MAX_PITCH
> 0, "MAX_PITCH is too large for BUFFERSIZE!");
78 static_assert((INT_MAX
>>FRACTIONBITS
)/MAX_PITCH
> BUFFERSIZE
,
79 "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
82 Resampler ResamplerDefault
{Resampler::Linear
};
84 MixerFunc MixSamples
{Mix_
<CTag
>};
88 using HrtfMixerFunc
= void(*)(const float *InSamples
, float2
*AccumSamples
, const ALuint IrSize
,
89 const MixHrtfFilter
*hrtfparams
, const size_t BufferSize
);
90 using HrtfMixerBlendFunc
= void(*)(const float *InSamples
, float2
*AccumSamples
,
91 const ALuint IrSize
, const HrtfFilter
*oldparams
, const MixHrtfFilter
*newparams
,
92 const size_t BufferSize
);
94 HrtfMixerFunc MixHrtfSamples
{MixHrtf_
<CTag
>};
95 HrtfMixerBlendFunc MixHrtfBlendSamples
{MixHrtfBlend_
<CTag
>};
97 inline MixerFunc
SelectMixer()
100 if((CPUCapFlags
&CPU_CAP_NEON
))
101 return Mix_
<NEONTag
>;
104 if((CPUCapFlags
&CPU_CAP_SSE
))
110 inline HrtfMixerFunc
SelectHrtfMixer()
113 if((CPUCapFlags
&CPU_CAP_NEON
))
114 return MixHrtf_
<NEONTag
>;
117 if((CPUCapFlags
&CPU_CAP_SSE
))
118 return MixHrtf_
<SSETag
>;
120 return MixHrtf_
<CTag
>;
123 inline HrtfMixerBlendFunc
SelectHrtfBlendMixer()
126 if((CPUCapFlags
&CPU_CAP_NEON
))
127 return MixHrtfBlend_
<NEONTag
>;
130 if((CPUCapFlags
&CPU_CAP_SSE
))
131 return MixHrtfBlend_
<SSETag
>;
133 return MixHrtfBlend_
<CTag
>;
141 if(auto resopt
= ConfigValueStr(nullptr, nullptr, "resampler"))
143 struct ResamplerEntry
{
145 const Resampler resampler
;
147 constexpr ResamplerEntry ResamplerList
[]{
148 { "none", Resampler::Point
},
149 { "point", Resampler::Point
},
150 { "linear", Resampler::Linear
},
151 { "cubic", Resampler::Cubic
},
152 { "bsinc12", Resampler::BSinc12
},
153 { "fast_bsinc12", Resampler::FastBSinc12
},
154 { "bsinc24", Resampler::BSinc24
},
155 { "fast_bsinc24", Resampler::FastBSinc24
},
158 const char *str
{resopt
->c_str()};
159 if(al::strcasecmp(str
, "bsinc") == 0)
161 WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", str
);
164 else if(al::strcasecmp(str
, "sinc4") == 0 || al::strcasecmp(str
, "sinc8") == 0)
166 WARN("Resampler option \"%s\" is deprecated, using cubic\n", str
);
170 auto iter
= std::find_if(std::begin(ResamplerList
), std::end(ResamplerList
),
171 [str
](const ResamplerEntry
&entry
) -> bool
172 { return al::strcasecmp(str
, entry
.name
) == 0; });
173 if(iter
== std::end(ResamplerList
))
174 ERR("Invalid resampler: %s\n", str
);
176 ResamplerDefault
= iter
->resampler
;
179 MixSamples
= SelectMixer();
180 MixHrtfBlendSamples
= SelectHrtfBlendMixer();
181 MixHrtfSamples
= SelectHrtfMixer();
187 void SendSourceStoppedEvent(ALCcontext
*context
, ALuint id
)
189 RingBuffer
*ring
{context
->mAsyncEvents
.get()};
190 auto evt_vec
= ring
->getWriteVector();
191 if(evt_vec
.first
.len
< 1) return;
193 AsyncEvent
*evt
{::new(evt_vec
.first
.buf
) AsyncEvent
{EventType_SourceStateChange
}};
194 evt
->u
.srcstate
.id
= id
;
195 evt
->u
.srcstate
.state
= AL_STOPPED
;
197 ring
->writeAdvance(1);
201 const float *DoFilters(BiquadFilter
&lpfilter
, BiquadFilter
&hpfilter
, float *dst
,
202 const al::span
<const float> src
, int type
)
212 lpfilter
.process(src
, dst
);
217 hpfilter
.process(src
, dst
);
221 DualBiquad
{lpfilter
, hpfilter
}.process(src
, dst
);
228 void LoadSamples(float *RESTRICT dst
, const al::byte
*src
, const size_t srcstep
, FmtType srctype
,
229 const size_t samples
) noexcept
231 #define HANDLE_FMT(T) case T: al::LoadSampleArray<T>(dst, src, srcstep, samples); break
234 HANDLE_FMT(FmtUByte
);
235 HANDLE_FMT(FmtShort
);
236 HANDLE_FMT(FmtFloat
);
237 HANDLE_FMT(FmtDouble
);
238 HANDLE_FMT(FmtMulaw
);
244 float *LoadBufferStatic(ALbufferlistitem
*BufferListItem
, ALbufferlistitem
*&BufferLoopItem
,
245 const size_t NumChannels
, const size_t SampleSize
, const size_t chan
, size_t DataPosInt
,
246 al::span
<float> SrcBuffer
)
248 const ALbuffer
*Buffer
{BufferListItem
->mBuffer
};
249 const ALuint LoopStart
{Buffer
->LoopStart
};
250 const ALuint LoopEnd
{Buffer
->LoopEnd
};
251 ASSUME(LoopEnd
> LoopStart
);
253 /* If current pos is beyond the loop range, do not loop */
254 if(!BufferLoopItem
|| DataPosInt
>= LoopEnd
)
256 BufferLoopItem
= nullptr;
258 /* Load what's left to play from the buffer */
259 const size_t DataRem
{minz(SrcBuffer
.size(), Buffer
->mBuffer
.mSampleLen
-DataPosInt
)};
261 const al::byte
*Data
{Buffer
->mBuffer
.mData
.data()};
262 Data
+= (DataPosInt
*NumChannels
+ chan
)*SampleSize
;
264 LoadSamples(SrcBuffer
.data(), Data
, NumChannels
, Buffer
->mBuffer
.mType
, DataRem
);
265 SrcBuffer
= SrcBuffer
.subspan(DataRem
);
269 /* Load what's left of this loop iteration */
270 const size_t DataRem
{minz(SrcBuffer
.size(), LoopEnd
-DataPosInt
)};
272 const al::byte
*Data
{Buffer
->mBuffer
.mData
.data()};
273 Data
+= (DataPosInt
*NumChannels
+ chan
)*SampleSize
;
275 LoadSamples(SrcBuffer
.data(), Data
, NumChannels
, Buffer
->mBuffer
.mType
, DataRem
);
276 SrcBuffer
= SrcBuffer
.subspan(DataRem
);
278 /* Load any repeats of the loop we can to fill the buffer. */
279 const auto LoopSize
= static_cast<size_t>(LoopEnd
- LoopStart
);
280 while(!SrcBuffer
.empty())
282 const size_t DataSize
{minz(SrcBuffer
.size(), LoopSize
)};
284 Data
= Buffer
->mBuffer
.mData
.data() + (LoopStart
*NumChannels
+ chan
)*SampleSize
;
286 LoadSamples(SrcBuffer
.data(), Data
, NumChannels
, Buffer
->mBuffer
.mType
, DataSize
);
287 SrcBuffer
= SrcBuffer
.subspan(DataSize
);
290 return SrcBuffer
.begin();
293 float *LoadBufferCallback(ALbufferlistitem
*BufferListItem
, const size_t NumChannels
,
294 const size_t SampleSize
, const size_t chan
, size_t NumCallbackSamples
,
295 al::span
<float> SrcBuffer
)
297 const ALbuffer
*Buffer
{BufferListItem
->mBuffer
};
299 /* Load what's left to play from the buffer */
300 const size_t DataRem
{minz(SrcBuffer
.size(), NumCallbackSamples
)};
302 const al::byte
*Data
{Buffer
->mBuffer
.mData
.data() + chan
*SampleSize
};
304 LoadSamples(SrcBuffer
.data(), Data
, NumChannels
, Buffer
->mBuffer
.mType
, DataRem
);
305 SrcBuffer
= SrcBuffer
.subspan(DataRem
);
307 return SrcBuffer
.begin();
310 float *LoadBufferQueue(ALbufferlistitem
*BufferListItem
, ALbufferlistitem
*BufferLoopItem
,
311 const size_t NumChannels
, const size_t SampleSize
, const size_t chan
, size_t DataPosInt
,
312 al::span
<float> SrcBuffer
)
314 /* Crawl the buffer queue to fill in the temp buffer */
315 while(BufferListItem
&& !SrcBuffer
.empty())
317 ALbuffer
*Buffer
{BufferListItem
->mBuffer
};
318 if(!(Buffer
&& DataPosInt
< Buffer
->mBuffer
.mSampleLen
))
320 if(Buffer
) DataPosInt
-= Buffer
->mBuffer
.mSampleLen
;
321 BufferListItem
= BufferListItem
->mNext
.load(std::memory_order_acquire
);
322 if(!BufferListItem
) BufferListItem
= BufferLoopItem
;
326 const size_t DataSize
{minz(SrcBuffer
.size(), Buffer
->mBuffer
.mSampleLen
-DataPosInt
)};
328 const al::byte
*Data
{Buffer
->mBuffer
.mData
.data()};
329 Data
+= (DataPosInt
*NumChannels
+ chan
)*SampleSize
;
331 LoadSamples(SrcBuffer
.data(), Data
, NumChannels
, Buffer
->mBuffer
.mType
, DataSize
);
332 SrcBuffer
= SrcBuffer
.subspan(DataSize
);
333 if(SrcBuffer
.empty()) break;
336 BufferListItem
= BufferListItem
->mNext
.load(std::memory_order_acquire
);
337 if(!BufferListItem
) BufferListItem
= BufferLoopItem
;
340 return SrcBuffer
.begin();
344 void DoHrtfMix(const float *samples
, const ALuint DstBufferSize
, DirectParams
&parms
,
345 const float TargetGain
, const ALuint Counter
, ALuint OutPos
, const ALuint IrSize
,
348 auto &HrtfSamples
= Device
->HrtfSourceData
;
349 /* Source HRTF mixing needs to include the direct delay so it remains
350 * aligned with the direct mix's HRTF filtering.
352 float2
*AccumSamples
{Device
->HrtfAccumData
+ HRTF_DIRECT_DELAY
};
354 /* Copy the HRTF history and new input samples into a temp buffer. */
355 auto src_iter
= std::copy(parms
.Hrtf
.History
.begin(), parms
.Hrtf
.History
.end(),
356 std::begin(HrtfSamples
));
357 std::copy_n(samples
, DstBufferSize
, src_iter
);
358 /* Copy the last used samples back into the history buffer for later. */
359 std::copy_n(std::begin(HrtfSamples
) + DstBufferSize
, parms
.Hrtf
.History
.size(),
360 parms
.Hrtf
.History
.begin());
362 /* If fading and this is the first mixing pass, fade between the IRs. */
364 if(Counter
&& OutPos
== 0)
366 fademix
= minu(DstBufferSize
, Counter
);
368 float gain
{TargetGain
};
370 /* The new coefficients need to fade in completely since they're
371 * replacing the old ones. To keep the gain fading consistent,
372 * interpolate between the old and new target gains given how much of
373 * the fade time this mix handles.
375 if(Counter
> fademix
)
377 const float a
{static_cast<float>(fademix
) / static_cast<float>(Counter
)};
378 gain
= lerp(parms
.Hrtf
.Old
.Gain
, TargetGain
, a
);
380 MixHrtfFilter hrtfparams
;
381 hrtfparams
.Coeffs
= &parms
.Hrtf
.Target
.Coeffs
;
382 hrtfparams
.Delay
= parms
.Hrtf
.Target
.Delay
;
383 hrtfparams
.Gain
= 0.0f
;
384 hrtfparams
.GainStep
= gain
/ static_cast<float>(fademix
);
386 MixHrtfBlendSamples(HrtfSamples
, AccumSamples
+OutPos
, IrSize
, &parms
.Hrtf
.Old
, &hrtfparams
,
388 /* Update the old parameters with the result. */
389 parms
.Hrtf
.Old
= parms
.Hrtf
.Target
;
390 parms
.Hrtf
.Old
.Gain
= gain
;
394 if(fademix
< DstBufferSize
)
396 const ALuint todo
{DstBufferSize
- fademix
};
397 float gain
{TargetGain
};
399 /* Interpolate the target gain if the gain fading lasts longer than
402 if(Counter
> DstBufferSize
)
404 const float a
{static_cast<float>(todo
) / static_cast<float>(Counter
-fademix
)};
405 gain
= lerp(parms
.Hrtf
.Old
.Gain
, TargetGain
, a
);
408 MixHrtfFilter hrtfparams
;
409 hrtfparams
.Coeffs
= &parms
.Hrtf
.Target
.Coeffs
;
410 hrtfparams
.Delay
= parms
.Hrtf
.Target
.Delay
;
411 hrtfparams
.Gain
= parms
.Hrtf
.Old
.Gain
;
412 hrtfparams
.GainStep
= (gain
- parms
.Hrtf
.Old
.Gain
) / static_cast<float>(todo
);
413 MixHrtfSamples(HrtfSamples
+fademix
, AccumSamples
+OutPos
, IrSize
, &hrtfparams
, todo
);
414 /* Store the now-current gain for next time. */
415 parms
.Hrtf
.Old
.Gain
= gain
;
419 void DoNfcMix(const al::span
<const float> samples
, FloatBufferLine
*OutBuffer
, DirectParams
&parms
,
420 const float *TargetGains
, const ALuint Counter
, const ALuint OutPos
, ALCdevice
*Device
)
422 using FilterProc
= void (NfcFilter::*)(const al::span
<const float>, float*);
423 static constexpr FilterProc NfcProcess
[MAX_AMBI_ORDER
+1]{
424 nullptr, &NfcFilter::process1
, &NfcFilter::process2
, &NfcFilter::process3
};
426 float *CurrentGains
{parms
.Gains
.Current
.data()};
427 MixSamples(samples
, {OutBuffer
, 1u}, CurrentGains
, TargetGains
, Counter
, OutPos
);
432 const al::span
<float> nfcsamples
{Device
->NfcSampleData
, samples
.size()};
434 while(const size_t chancount
{Device
->NumChannelsPerOrder
[order
]})
436 (parms
.NFCtrlFilter
.*NfcProcess
[order
])(samples
, nfcsamples
.data());
437 MixSamples(nfcsamples
, {OutBuffer
, chancount
}, CurrentGains
, TargetGains
, Counter
, OutPos
);
438 OutBuffer
+= chancount
;
439 CurrentGains
+= chancount
;
440 TargetGains
+= chancount
;
441 if(++order
== MAX_AMBI_ORDER
+1)
448 void Voice::mix(const State vstate
, ALCcontext
*Context
, const ALuint SamplesToDo
)
450 static constexpr std::array
<float,MAX_OUTPUT_CHANNELS
> SilentTarget
{};
452 ASSUME(SamplesToDo
> 0);
455 ALuint DataPosInt
{mPosition
.load(std::memory_order_relaxed
)};
456 ALuint DataPosFrac
{mPositionFrac
.load(std::memory_order_relaxed
)};
457 ALbufferlistitem
*BufferListItem
{mCurrentBuffer
.load(std::memory_order_relaxed
)};
458 ALbufferlistitem
*BufferLoopItem
{mLoopBuffer
.load(std::memory_order_relaxed
)};
459 const ALuint SampleSize
{mSampleSize
};
460 const ALuint increment
{mStep
};
461 if UNLIKELY(increment
< 1)
463 /* If the voice is supposed to be stopping but can't be mixed, just
464 * stop it before bailing.
466 if(vstate
== Stopping
)
467 mPlayState
.store(Stopped
, std::memory_order_release
);
471 ASSUME(SampleSize
> 0);
473 const size_t FrameSize
{mChans
.size() * SampleSize
};
474 ASSUME(FrameSize
> 0);
476 ALCdevice
*Device
{Context
->mDevice
.get()};
477 const ALuint NumSends
{Device
->NumAuxSends
};
478 const ALuint IrSize
{Device
->mHrtf
? Device
->mHrtf
->irSize
: 0};
480 ResamplerFunc Resample
{(increment
== FRACTIONONE
&& DataPosFrac
== 0) ?
481 Resample_
<CopyTag
,CTag
> : mResampler
};
483 ALuint Counter
{(mFlags
&VOICE_IS_FADING
) ? SamplesToDo
: 0};
486 /* No fading, just overwrite the old/current params. */
487 for(auto &chandata
: mChans
)
490 DirectParams
&parms
= chandata
.mDryParams
;
491 if(!(mFlags
&VOICE_HAS_HRTF
))
492 parms
.Gains
.Current
= parms
.Gains
.Target
;
494 parms
.Hrtf
.Old
= parms
.Hrtf
.Target
;
496 for(ALuint send
{0};send
< NumSends
;++send
)
498 if(mSend
[send
].Buffer
.empty())
501 SendParams
&parms
= chandata
.mWetParams
[send
];
502 parms
.Gains
.Current
= parms
.Gains
.Target
;
507 ALuint buffers_done
{0u};
510 /* Figure out how many buffer samples will be needed */
511 ALuint DstBufferSize
{SamplesToDo
- OutPos
};
512 ALuint SrcBufferSize
;
514 if(increment
<= FRACTIONONE
)
516 /* Calculate the last written dst sample pos. */
517 uint64_t DataSize64
{DstBufferSize
- 1};
518 /* Calculate the last read src sample pos. */
519 DataSize64
= (DataSize64
*increment
+ DataPosFrac
) >> FRACTIONBITS
;
520 /* +1 to get the src sample count, include padding. */
521 DataSize64
+= 1 + MAX_RESAMPLER_PADDING
;
523 /* Result is guaranteed to be <= BUFFERSIZE+MAX_RESAMPLER_PADDING
524 * since we won't use more src samples than dst samples+padding.
526 SrcBufferSize
= static_cast<ALuint
>(DataSize64
);
530 uint64_t DataSize64
{DstBufferSize
};
531 /* Calculate the end src sample pos, include padding. */
532 DataSize64
= (DataSize64
*increment
+ DataPosFrac
) >> FRACTIONBITS
;
533 DataSize64
+= MAX_RESAMPLER_PADDING
;
535 if(DataSize64
<= BUFFERSIZE
+ MAX_RESAMPLER_PADDING
)
536 SrcBufferSize
= static_cast<ALuint
>(DataSize64
);
539 /* If the source size got saturated, we can't fill the desired
540 * dst size. Figure out how many samples we can actually mix.
542 SrcBufferSize
= BUFFERSIZE
+ MAX_RESAMPLER_PADDING
;
544 DataSize64
= SrcBufferSize
- MAX_RESAMPLER_PADDING
;
545 DataSize64
= ((DataSize64
<<FRACTIONBITS
) - DataPosFrac
) / increment
;
546 if(DataSize64
< DstBufferSize
)
548 /* Some mixers require being 16-byte aligned, so also limit
549 * to a multiple of 4 samples to maintain alignment.
551 DstBufferSize
= static_cast<ALuint
>(DataSize64
) & ~3u;
556 if((mFlags
&(VOICE_IS_CALLBACK
|VOICE_CALLBACK_STOPPED
)) == VOICE_IS_CALLBACK
559 ALbuffer
*buffer
{BufferListItem
->mBuffer
};
561 /* Exclude resampler pre-padding from the needed size. */
562 const ALuint toLoad
{SrcBufferSize
- (MAX_RESAMPLER_PADDING
>>1)};
563 if(toLoad
> mNumCallbackSamples
)
565 const size_t byteOffset
{mNumCallbackSamples
*FrameSize
};
566 const size_t needBytes
{toLoad
*FrameSize
- byteOffset
};
568 const ALsizei gotBytes
{buffer
->mBuffer
.mCallback(buffer
->mBuffer
.mUserData
,
569 &buffer
->mBuffer
.mData
[byteOffset
], static_cast<ALsizei
>(needBytes
))};
571 mFlags
|= VOICE_CALLBACK_STOPPED
;
572 else if(static_cast<ALuint
>(gotBytes
) < needBytes
)
574 mFlags
|= VOICE_CALLBACK_STOPPED
;
575 mNumCallbackSamples
+= static_cast<ALuint
>(static_cast<ALuint
>(gotBytes
) /
579 mNumCallbackSamples
= toLoad
;
583 ASSUME(DstBufferSize
> 0);
584 for(auto &chandata
: mChans
)
586 const size_t num_chans
{mChans
.size()};
587 const auto chan
= static_cast<size_t>(std::distance(mChans
.data(),
588 std::addressof(chandata
)));
589 const al::span
<float> SrcData
{Device
->SourceData
, SrcBufferSize
};
591 /* Load the previous samples into the source data first, then load
592 * what we can from the buffer queue.
594 auto srciter
= std::copy_n(chandata
.mPrevSamples
.begin(), MAX_RESAMPLER_PADDING
>>1,
597 if UNLIKELY(!BufferListItem
)
598 srciter
= std::copy(chandata
.mPrevSamples
.begin()+(MAX_RESAMPLER_PADDING
>>1),
599 chandata
.mPrevSamples
.end(), srciter
);
600 else if((mFlags
&VOICE_IS_STATIC
))
601 srciter
= LoadBufferStatic(BufferListItem
, BufferLoopItem
, num_chans
,
602 SampleSize
, chan
, DataPosInt
, {srciter
, SrcData
.end()});
603 else if((mFlags
&VOICE_IS_CALLBACK
))
604 srciter
= LoadBufferCallback(BufferListItem
, num_chans
, SampleSize
, chan
,
605 mNumCallbackSamples
, {srciter
, SrcData
.end()});
607 srciter
= LoadBufferQueue(BufferListItem
, BufferLoopItem
, num_chans
,
608 SampleSize
, chan
, DataPosInt
, {srciter
, SrcData
.end()});
610 if UNLIKELY(srciter
!= SrcData
.end())
612 /* If the source buffer wasn't filled, copy the last sample for
613 * the remaining buffer. Ideally it should have ended with
614 * silence, but if not the gain fading should help avoid clicks
615 * from sudden amplitude changes.
617 const float sample
{*(srciter
-1)};
618 std::fill(srciter
, SrcData
.end(), sample
);
621 /* Store the last source samples used for next time. */
622 std::copy_n(&SrcData
[(increment
*DstBufferSize
+ DataPosFrac
)>>FRACTIONBITS
],
623 chandata
.mPrevSamples
.size(), chandata
.mPrevSamples
.begin());
625 /* Resample, then apply ambisonic upsampling as needed. */
626 const float *ResampledData
{Resample(&mResampleState
,
627 &SrcData
[MAX_RESAMPLER_PADDING
>>1], DataPosFrac
, increment
,
628 {Device
->ResampledData
, DstBufferSize
})};
629 if((mFlags
&VOICE_IS_AMBISONIC
))
631 const float hfscale
{chandata
.mAmbiScale
};
632 /* Beware the evil const_cast. It's safe since it's pointing to
633 * either SourceData or ResampledData (both non-const), but the
634 * resample method takes the source as const float* and may
635 * return it without copying to output, making it currently
638 const al::span
<float> samples
{const_cast<float*>(ResampledData
), DstBufferSize
};
639 chandata
.mAmbiSplitter
.processHfScale(samples
, hfscale
);
642 /* Now filter and mix to the appropriate outputs. */
643 float (&FilterBuf
)[BUFFERSIZE
] = Device
->FilteredData
;
645 DirectParams
&parms
= chandata
.mDryParams
;
646 const float *samples
{DoFilters(parms
.LowPass
, parms
.HighPass
, FilterBuf
,
647 {ResampledData
, DstBufferSize
}, mDirect
.FilterType
)};
649 if((mFlags
&VOICE_HAS_HRTF
))
651 const float TargetGain
{UNLIKELY(vstate
== Stopping
) ? 0.0f
:
652 parms
.Hrtf
.Target
.Gain
};
653 DoHrtfMix(samples
, DstBufferSize
, parms
, TargetGain
, Counter
, OutPos
, IrSize
,
656 else if((mFlags
&VOICE_HAS_NFC
))
658 const float *TargetGains
{UNLIKELY(vstate
== Stopping
) ? SilentTarget
.data()
659 : parms
.Gains
.Target
.data()};
660 DoNfcMix({samples
, DstBufferSize
}, mDirect
.Buffer
.data(), parms
, TargetGains
,
661 Counter
, OutPos
, Device
);
665 const float *TargetGains
{UNLIKELY(vstate
== Stopping
) ? SilentTarget
.data()
666 : parms
.Gains
.Target
.data()};
667 MixSamples({samples
, DstBufferSize
}, mDirect
.Buffer
,
668 parms
.Gains
.Current
.data(), TargetGains
, Counter
, OutPos
);
672 for(ALuint send
{0};send
< NumSends
;++send
)
674 if(mSend
[send
].Buffer
.empty())
677 SendParams
&parms
= chandata
.mWetParams
[send
];
678 const float *samples
{DoFilters(parms
.LowPass
, parms
.HighPass
, FilterBuf
,
679 {ResampledData
, DstBufferSize
}, mSend
[send
].FilterType
)};
681 const float *TargetGains
{UNLIKELY(vstate
== Stopping
) ? SilentTarget
.data()
682 : parms
.Gains
.Target
.data()};
683 MixSamples({samples
, DstBufferSize
}, mSend
[send
].Buffer
,
684 parms
.Gains
.Current
.data(), TargetGains
, Counter
, OutPos
);
687 /* Update positions */
688 DataPosFrac
+= increment
*DstBufferSize
;
689 const ALuint SrcSamplesDone
{DataPosFrac
>>FRACTIONBITS
};
690 DataPosInt
+= SrcSamplesDone
;
691 DataPosFrac
&= FRACTIONMASK
;
693 OutPos
+= DstBufferSize
;
694 Counter
= maxu(DstBufferSize
, Counter
) - DstBufferSize
;
696 if UNLIKELY(!BufferListItem
)
698 /* Do nothing extra when there's no buffers. */
700 else if((mFlags
&VOICE_IS_STATIC
))
704 /* Handle looping static source */
705 const ALbuffer
*Buffer
{BufferListItem
->mBuffer
};
706 const ALuint LoopStart
{Buffer
->LoopStart
};
707 const ALuint LoopEnd
{Buffer
->LoopEnd
};
708 if(DataPosInt
>= LoopEnd
)
710 assert(LoopEnd
> LoopStart
);
711 DataPosInt
= ((DataPosInt
-LoopStart
)%(LoopEnd
-LoopStart
)) + LoopStart
;
716 /* Handle non-looping static source */
717 if(DataPosInt
>= BufferListItem
->mSampleLen
)
719 BufferListItem
= nullptr;
724 else if((mFlags
&VOICE_IS_CALLBACK
))
726 ALbuffer
*buffer
{BufferListItem
->mBuffer
};
727 if(SrcSamplesDone
< mNumCallbackSamples
)
729 const size_t byteOffset
{SrcSamplesDone
*FrameSize
};
730 const size_t byteEnd
{mNumCallbackSamples
*FrameSize
};
731 al::byte
*data
{buffer
->mBuffer
.mData
.data()};
732 std::copy(data
+byteOffset
, data
+byteEnd
, data
);
733 mNumCallbackSamples
-= SrcSamplesDone
;
737 BufferListItem
= nullptr;
738 mNumCallbackSamples
= 0;
743 /* Handle streaming source */
745 if(BufferListItem
->mSampleLen
> DataPosInt
)
748 DataPosInt
-= BufferListItem
->mSampleLen
;
751 BufferListItem
= BufferListItem
->mNext
.load(std::memory_order_relaxed
);
752 if(!BufferListItem
) BufferListItem
= BufferLoopItem
;
753 } while(BufferListItem
);
755 } while(OutPos
< SamplesToDo
);
757 mFlags
|= VOICE_IS_FADING
;
759 /* Don't update positions and buffers if we were stopping. */
760 if UNLIKELY(vstate
== Stopping
)
762 mPlayState
.store(Stopped
, std::memory_order_release
);
766 /* Capture the source ID in case it's reset for stopping. */
767 const ALuint SourceID
{mSourceID
.load(std::memory_order_relaxed
)};
769 /* Update voice info */
770 mPosition
.store(DataPosInt
, std::memory_order_relaxed
);
771 mPositionFrac
.store(DataPosFrac
, std::memory_order_relaxed
);
772 mCurrentBuffer
.store(BufferListItem
, std::memory_order_relaxed
);
775 mLoopBuffer
.store(nullptr, std::memory_order_relaxed
);
776 mSourceID
.store(0u, std::memory_order_relaxed
);
778 std::atomic_thread_fence(std::memory_order_release
);
780 /* Send any events now, after the position/buffer info was updated. */
781 const ALbitfieldSOFT enabledevt
{Context
->mEnabledEvts
.load(std::memory_order_acquire
)};
782 if(buffers_done
> 0 && (enabledevt
&EventType_BufferCompleted
))
784 RingBuffer
*ring
{Context
->mAsyncEvents
.get()};
785 auto evt_vec
= ring
->getWriteVector();
786 if(evt_vec
.first
.len
> 0)
788 AsyncEvent
*evt
{::new(evt_vec
.first
.buf
) AsyncEvent
{EventType_BufferCompleted
}};
789 evt
->u
.bufcomp
.id
= SourceID
;
790 evt
->u
.bufcomp
.count
= buffers_done
;
791 ring
->writeAdvance(1);
797 /* If the voice just ended, set it to Stopping so the next render
798 * ensures any residual noise fades to 0 amplitude.
800 mPlayState
.store(Stopping
, std::memory_order_release
);
801 if((enabledevt
&EventType_SourceStateChange
))
802 SendSourceStoppedEvent(Context
, SourceID
);