49 constexpr uint32 N = 2'000;
51 auto isPrime = [](
uint32 num)
53 if (num <= 1)
return false;
54 if (num == 2 || num == 3)
return true;
55 if (num % 2 == 0 || num % 3 == 0)
return false;
56 for (
uint32 i = 5; i * i <= num; i += 6)
57 if (num % i == 0 || num % (i + 2) == 0)
return false;
61 std::array<uint32, N> primes{};
62 std::size_t count = 0;
67 if (isPrime(candidate))
69 primes[count] = candidate;
79 auto found = std::ranges::lower_bound(
cPrimes, number);
87 template<u
int32 FDNOrder>
91 template<
class Sample>
95 uint32 x = fdnChannel + 0x9E3779B9u * (outChannel + 1);
96 x ^= x >> 16; x *= 0x7FEB352Du;
97 x ^= x >> 15; x *= 0x846CA68Bu;
99 return (x & 1u) ? Sample(1) : Sample(-1);
103 static constexpr std::array<Sample, NumFDNChannels> MakeSignMatrix()
105 std::array<Sample, NumFDNChannels> signs;
106 for (
uint32 i = 0; i < NumFDNChannels; ++i)
111 template<
class Sample, std::
size_t NumFDNChannels, u
int32 MaxOutputChannels>
113 static constexpr std::array<std::array<Sample, NumFDNChannels>, MaxOutputChannels> MakeSignMatrix()
115 std::array<std::array<Sample, NumFDNChannels>, MaxOutputChannels> signs;
116 for (
uint32 o = 0; o < MaxOutputChannels; ++o)
118 for (
uint32 i = 0; i < NumFDNChannels; ++i)
129 template<
class Sample, std::
size_t Size>
130 JPL_INLINE
void Multiply(
const std::array<Sample, Size>& dataA, std::array<Sample, Size>& dataBOut)
132 if constexpr (std::same_as<Sample, float> and Size >=
simd::size())
138 (
simd(&dataBOut[i]) *
simd(dataA[i])).store(&dataBOut[i]);
141 if constexpr (Size > simdFloor)
143 for (
uint32 i = simdFloor; i < Size; ++i)
144 dataBOut[i] *= dataA[i];
149 for (
uint32 i = 0; i < Size; ++i)
150 dataBOut[i] *= dataA[i];
154 template<
class Sample>
155 JPL_INLINE
void Multiply(std::span<Sample> data, Sample value)
157 if constexpr (std::same_as<Sample, float>)
165 (
simd(&data[i]) * v).store(&data[i]);
168 for (; i < data.size(); ++i)
173 for (
uint32 i = 0; i < data.size(); ++i)
178 template<
class Sample, std::
size_t Size>
179 JPL_INLINE
void Multiply(std::array<Sample, Size>& data, Sample value)
181 if constexpr (std::same_as<Sample, float> and Size >=
simd::size())
189 (
simd(&data[i]) * v).store(&data[i]);
192 if constexpr (Size > simdFloor)
194 for (
uint32 i = simdFloor; i < Size; ++i)
200 for (
uint32 i = 0; i < Size; ++i)
205 template<
class Sample, std::
size_t Size>
206 JPL_INLINE
void Add(std::array<Sample, Size>& data, Sample value)
208 if constexpr (std::same_as<Sample, float> and Size >=
simd::size())
216 (
simd(&data[i]) + v).store(&data[i]);
219 if constexpr (Size > simdFloor)
221 for (
uint32 i = simdFloor; i < Size; ++i)
227 for (
uint32 i = 0; i < Size; ++i)
232 template<
class Sample, std::
size_t Size>
233 JPL_INLINE
void Add(
const std::array<Sample, Size>& dataA, std::array<Sample, Size>& dataBOut)
235 if constexpr (std::same_as<Sample, float> and Size >=
simd::size())
241 (
simd(&dataBOut[i]) +
simd(dataA[i])).store(&dataBOut[i]);
244 if constexpr (Size > simdFloor)
246 for (
uint32 i = simdFloor; i < Size; ++i)
247 dataBOut[i] += dataA[i];
252 for (
uint32 i = 0; i < Size; ++i)
253 dataBOut[i] += dataA[i];
257 template<
class Sample, std::
size_t Size>
258 [[nodiscard]] JPL_INLINE Sample
ReduceSum(
const std::array<Sample, Size>& data)
261 if constexpr (std::same_as<Sample, float> and Size >=
simd::size())
270 if constexpr (Size > simdFloor)
272 for (
uint32 i = simdFloor; i < Size; ++i)
278 for (Sample s : data)
284 template<
class Sample, std::
size_t Size>
287 static constexpr auto signs = Detail::MakeSignMatrix<Sample, Size>();
291 template<std::
size_t MaxOutSize,
class Sample, std::
size_t Size>
294 static constexpr auto signs = Detail::MakeSignMatrix<Sample, Size, MaxOutSize>();
305 static constexpr Sample cMultiplier{ -2.0 / Size };
306 const Sample sum =
ReduceSum(arr) * cMultiplier;
311 template<
class Sample>
314 if constexpr (std::same_as<Sample, float>)
316 auto mix4 = [](
float* v)
319 const float sum = vec.
reduce();
328 for (
uint32 g = 0; g < 16; g += 4)
332 for (
uint32 lane = 0; lane < 4; ++lane)
344 arr[lane + 0 * 4] = v[0];
345 arr[lane + 1 * 4] = v[1];
346 arr[lane + 2 * 4] = v[2];
347 arr[lane + 3 * 4] = v[3];
352 auto mix4 = [](Sample* v)
354 const Sample sum = v[0] + v[1] + v[2] + v[3];
358 for (
uint32 i = 0; i < 4; ++i)
363 for (
uint32 g = 0; g < 16; g += 4)
367 for (
uint32 lane = 0; lane < 4; ++lane)
378 arr[lane + 0 * 4] = v[0] * Sample(0.25);
379 arr[lane + 1 * 4] = v[1] * Sample(0.25);
380 arr[lane + 2 * 4] = v[2] * Sample(0.25);
381 arr[lane + 3 * 4] = v[3] * Sample(0.25);
391 template<
class Sample, std::
size_t Size>
392 static void RecursiveUnscaled(std::span<Sample, Size> data)
397 static constexpr uint32 hSize = Size / 2;
400 Hadamard::RecursiveUnscaled(data.template subspan<0, hSize>());
401 Hadamard::RecursiveUnscaled(data.template subspan<hSize, hSize>());
403 if constexpr (std::same_as<Sample, float> and hSize >=
simd::size())
408 const simd a(&data[i]);
409 const simd b(&data[i + hSize]);
410 (a + b).store(&data[i]);
411 (a - b).store(&data[i + hSize]);
415 if constexpr (hSize > simdFloor)
417 for (
uint32 i = simdFloor; i < hSize; ++i)
419 const Sample a = data[i];
420 const Sample b = data[i + hSize];
422 data[i + hSize] = (a - b);
430 for (
uint32 i = 0; i < hSize; ++i)
432 const Sample a = data[i];
433 const Sample b = data[i + hSize];
435 data[i + hSize] = (a - b);
442 static JPL_INLINE
void MixInPlace(std::array<Sample, Size>& data)
444 RecursiveUnscaled(std::span<Sample, Size>(data));
445 static constexpr Sample scalingFactor =
Math::Sqrt(1.0 / Size);
455 static JPL_INLINE
void InjectNormalized(Sample monoInput, std::array<Sample, NumFDNChannels>& outFDNInput)
457 static constexpr Sample norm =
Math::Sqrt(Sample(1) / Sample(NumFDNChannels));
458 outFDNInput.fill(monoInput * norm);
464 static constexpr Sample norm =
Math::Sqrt(Sample(1) / Sample(NumFDNChannels));
465 outFDNInput.fill(monoInput * norm);
478 template<
class Sample, std::
size_t NumFDNChannels>
480 static void Mix(std::array<Sample, NumFDNChannels> fdn, std::span<Sample> out)
485 const uint32 numOutputChannels = out.size();
487 for (
uint32 i = 0; i < NumFDNChannels; ++i)
489 const uint32 outChannel = i % numOutputChannels;
490 out[outChannel] += fdn[i];
498 template<
class Sample, std::
size_t NumFDNChannels>
500 static void MixNormalize(std::array<Sample, NumFDNChannels> fdn, std::span<Sample> out)
504 static constexpr Sample invFDNChannels = Sample(1.0) / NumFDNChannels;
508 const Sample foldNorm =
509 Math::Sqrt(
static_cast<Sample
>(out.size()) * invFDNChannels);
516 template<u
int32 MaxOutputChannels = 16u>
requires(MaxOutputChannels > 0)
520 static void Mix(
const std::array<Sample, NumFDNChannels>& fdn, std::span<Sample> out)
524 for (
uint32 outIdx = 0; outIdx < out.size(); ++outIdx)
526 std::array<Sample, NumFDNChannels> fdnWithRandSign = fdn;
529 out[outIdx] =
ReduceSum(fdnWithRandSign);
535 static void MixNormalize(
const std::array<Sample, NumFDNChannels>& fdn, std::span<Sample> out)
539 static constexpr Sample norm =
Math::Sqrt(Sample(1) / Sample(NumFDNChannels));
562 [[nodiscard]] JPL_INLINE
float Process(
float sample)
578 static constexpr uint32 cPrimeDelayLengths[] ={
579 2927, 2593, 2273, 3697, 1877, 3877, 2477, 3461,
580 1609, 3779, 3541, 4259, 1669, 3539, 3637, 4013,
581 3121, 4003, 1627, 3733, 3511, 4093, 2411, 3011,
582 2801, 2017, 4013, 1621, 3023, 2161, 4201, 3079,
583 1783, 4027, 1613, 1907, 2129, 3797, 2543, 1579,
584 3967, 2551, 3833, 4111, 3059, 4159, 1753, 1601,
585 2423, 1993, 4493, 2707, 3719, 3547, 3769, 4219,
586 2389, 2087, 1709, 3229, 3083, 3391, 3259, 2791
592 uint32 DelaySamplesMin = 1500;
593 uint32 DelaySamplesMax = 4500;
596 template<
class ...FilterArgs>
597 void Prepare(
float sampleRate, FilterArgs&&...filterPrepareArgs)
599 mInvSampleRate = 1.0f / sampleRate;
601 std::ranges::copy_n(cPrimeDelayLengths, NumChannels, mDelaySamples.begin());
604 auto first = std::ranges::lower_bound(cPrime, DelaySamplesMin);
605 auto last = std::ranges::lower_bound(cPrime, DelaySamplesMax);
606 JPL_ASSERT(std::distance(first, last) >= NumChannels);
608 std::mt19937 rng{ std::random_device{}() };
609 std::ranges::sample(std::ranges::subrange(first, last), mDelaySamples.begin(), NumChannels, rng);
610 std::ranges::shuffle(mDelaySamples, rng);
613 for (
uint32 c = 0; c < NumChannels; ++c)
615 mDelays[c].Resize(mDelaySamples[c] + 1);
618 mAttenuationFilters[c].Prepare(sampleRate, std::forward<FilterArgs>(filterPrepareArgs)...);
622 template<
class ...FilterArgs>
625 for (
uint32 c = 0; c < NumChannels; ++c)
628 const float delaySeconds = mDelaySamples[c] * mInvSampleRate;
629 mAttenuationFilters[c].UpdateParameters(delaySeconds, std::forward<FilterArgs>(filterUpdateArgs)...);
636 return mAttenuationFilters[fdnIndex].Process(sample);
641 std::array<DelayLine<1>, NumChannels>
mDelays;
643 float mInvSampleRate = 1.0f / 48'000.0f;
659 for (
uint32 c = 0; c < NumChannels; ++c)
661 delayed[c] = Base::mDelays[c].template GetReadWindow<1>(Base::mDelaySamples[c]);
664 for (
uint32 c = 0; c < NumChannels; ++c)
666 const float sum = Base::ApplyAttenuationFilter(delayed[c] + input[c], c);
667 Base::mDelays[c].Push(sum);
687 for (
uint32 c = 0; c < NumChannels; ++c)
689 delayed[c] = Base::mDelays[c].template GetReadWindow<1>(Base::mDelaySamples[c]);
696 for (
uint32 c = 0; c < NumChannels; ++c)
698 const float sum = Base::ApplyAttenuationFilter(mixed[c] + input[c], c);
699 Base::mDelays[c].Push(sum);
712 float DelayMsRange = 50.0f;
716 static constexpr float invNumChannels = 1.0f / NumChannels;
717 const float delaySamplesRange = DelayMsRange * 0.001f * sampleRate;
719 auto randomInRange = [](
float min,
float max)
721 static std::mt19937 mt(std::random_device{}());
722 return std::uniform_real_distribution<float>{
min,
max }(mt);
725 for (
uint32 c = 0; c < NumChannels; ++c)
727 const float rangeLow = delaySamplesRange * c * invNumChannels;
728 const float rangeHigh = delaySamplesRange * (c + 1) * invNumChannels;
730 mDelays[c].Resize(mDelaySamples[c] + 1);
739 for (
uint32 c = 0; c < NumChannels; ++c)
741 mDelays[c].Push(input[c]);
742 delayed[c] = mDelays[c].template GetReadWindow<1>(mDelaySamples[c]);
756 std::array<uint32, NumChannels> mDelaySamples;
757 std::array<DelayLine<1>, NumChannels> mDelays;
775 for (
Step& step : Steps)
776 step.Prepare(sampleRate);
781 for (
Step& step : Steps)
782 samples = step.Process(samples);
797 const float delayMsRange = totalDiffusionMs / StepCount;
798 for (
Step& step : Steps)
799 step.DelayMsRange = delayMsRange;
813 for (
Step& step : Steps)
816 step.DelayMsRange = diffusionMs;
828 using Channels = std::array<float, NumChannels>;
830 FDNReverb(
float roomSizeMs,
const simd& rt60,
float dry = 0.0f,
float wet = 1.0f)
831 : mDiffuser(roomSizeMs)
834 , mDiffuserAmount(wet * 0.5f)
837 mFeedback.RT60 = rt60;
840 inline void Prepare(
float sampleRate)
842 mSampleRate = sampleRate;
843 mFeedback.Prepare(sampleRate);
844 mDiffuser.Prepare(sampleRate);
847 [[nodiscard]]
inline Channels Process(Channels input)
852 Channels diffuse = mDiffuser.Process(input);
853 Channels longLasting = mFeedback.Process(diffuse);
864 output = longLasting;
868 Channels longLasting = mFeedback.Process(input);
873 output = longLasting;
878 JPL_INLINE
void SetDryLevel(
float newDryLevel) { mDryAmount = newDryLevel; }
879 JPL_INLINE
void SetWetLevel(
float newWetLevel) { mWetAmount = newWetLevel; }
880 [[nodiscard]] JPL_INLINE
float GetCurrentRT60()
const {
return mFeedback.RT60; }
881 JPL_INLINE
void SetDiffuserTapLevel(
float newLevel) { mDiffuserAmount = newLevel; }
882 JPL_INLINE
void SetUseDiffuser(
bool shouldUse) { mUseDiffuser = shouldUse; }
883 JPL_INLINE
void SetRT60(
const simd& newRT60) { mFeedback.UpdateDecayGains(
clamp(newRT60, 0.03f, 10.0f), mSampleRate); }
886 MultiChannelMixedFeedback<NumChannels> mFeedback;
887 DiffuserEqualLengths<NumChannels, NumDiffusionSteps> mDiffuser;
888 float mDryAmount, mWetAmount, mDiffuserAmount;
890 float mSampleRate = 48'000.0f;
#define JPL_ASSERT(inExpression,...)
Main assert macro, usage: JPL_ASSERT(condition, message) or JPL_ASSERT(condition)
Definition ErrorReporting.h:80
Forward declaration.
Definition DelayLine.h:131
void Resize(uint32_t newSize)
Definition DelayLine.h:150
std::span< const float, WindowLength > GetReadWindow(uint32_t intDelay) const
Definition DelayLine.h:202
void Clear()
Definition DelayLine.h:176
void Push(float sample)
Definition DelayLine.h:183
Definition ReverbDesign.h:389
static JPL_INLINE void MixInPlace(std::array< Sample, Size > &data)
Definition ReverbDesign.h:442
Definition ReverbDesign.h:300
static void MixInPlace(std::array< Sample, Size > &arr)
Definition ReverbDesign.h:303
static void MixInPlace(std::array< Sample, 16 > &arr)
Definition ReverbDesign.h:312
Definition ReverbDesign.h:649
Channels Process(Channels input)
Definition ReverbDesign.h:656
typename Base::Channels Channels
Definition ReverbDesign.h:652
Definition ReverbDesign.h:677
Channels Process(const Channels &input)
Definition ReverbDesign.h:684
typename Base::Channels Channels
Definition ReverbDesign.h:680
constexpr auto cPrimes
Definition ReverbDesign.h:47
constexpr bool cValidFDNOrder
Definition ReverbDesign.h:88
constexpr Sample GenRandomSign(uint32 outChannel, uint32 fdnChannel)
Definition ReverbDesign.h:92
constexpr uint32 cMaxDelayInSamples
Definition ReverbDesign.h:85
JPL_INLINE uint32 CeilToPrime(uint32 number)
Definition ReverbDesign.h:77
constexpr uint32 cMaxFDNOrder
Definition ReverbDesign.h:84
JPL_INLINE constexpr T FMA(T a, T b, T c) noexcept
Inlined fuse multiply-add. Compiler in some circumstances is more eager to optimize this than std::fm...
Definition Math.h:186
JPL_INLINE constexpr T Sqrt(T x) noexcept
Definition Math.h:269
JPL_INLINE void Add(std::array< Sample, Size > &data, Sample value)
Definition ReverbDesign.h:206
JPL_INLINE void Multiply(const std::array< Sample, Size > &dataA, std::array< Sample, Size > &dataBOut)
Definition ReverbDesign.h:130
JPL_INLINE void AssignRandomSign(std::array< Sample, Size > &data)
Definition ReverbDesign.h:285
JPL_INLINE Sample ReduceSum(const std::array< Sample, Size > &data)
Definition ReverbDesign.h:258
Definition AcousticMaterial.h:36
JPL_INLINE simd clamp(const simd &value, const simd &minV, const simd &maxV) noexcept
Element-wise clamp.
Definition SIMD.h:1838
std::uint32_t uint32
Definition Core.h:311
JPL_INLINE constexpr auto FloorToSIMDSize(std::unsigned_integral auto count) noexcept
Floor count to 4-wide simd vector.
Definition SIMDMath.h:33
JPL_INLINE simd max(const simd &a, const simd &b) noexcept
Element-wise max.
Definition SIMD.h:1799
JPL_INLINE simd min(const simd &a, const simd &b) noexcept
Element-wise min.
Definition SIMD.h:1813
Definition ReverbDesign.h:763
void Prepare(float sampleRate)
Definition ReverbDesign.h:773
Channels Process(Channels samples)
Definition ReverbDesign.h:779
std::array< float, NumChannels > Channels
Definition ReverbDesign.h:768
std::array< Step, StepCount > Steps
Definition ReverbDesign.h:771
Definition ReverbDesign.h:790
std::array< Step, StepCount > Steps
Definition ReverbDesign.h:793
DiffuserEqualLengths(float totalDiffusionMs)
Definition ReverbDesign.h:795
Definition ReverbDesign.h:806
DiffuserHalfLengths(float diffusionMs)
Definition ReverbDesign.h:811
std::array< Step, StepCount > Steps
Definition ReverbDesign.h:809
Definition ReverbDesign.h:709
void Prepare(float sampleRate)
Definition ReverbDesign.h:714
std::array< float, NumChannels > Channels
Definition ReverbDesign.h:710
Channels Process(Channels input)
Definition ReverbDesign.h:735
Definition ReverbDesign.h:549
uint32 DelaySamples
Definition ReverbDesign.h:552
DelayLine< 1 > Delay
Definition ReverbDesign.h:553
float DelayMs
Definition ReverbDesign.h:551
JPL_INLINE float Process(float sample)
Definition ReverbDesign.h:562
float DecayGain
Definition ReverbDesign.h:550
JPL_INLINE void Prepare(float sampleRate)
Definition ReverbDesign.h:555
Definition ReverbDesign.h:477
static void Mix(std::array< Sample, NumFDNChannels > fdn, std::span< Sample > out)
Definition ReverbDesign.h:480
static void MixNormalize(std::array< Sample, NumFDNChannels > fdn, std::span< Sample > out)
Definition ReverbDesign.h:500
Definition ReverbDesign.h:518
static void MixNormalize(const std::array< Sample, NumFDNChannels > &fdn, std::span< Sample > out)
Definition ReverbDesign.h:535
static void Mix(const std::array< Sample, NumFDNChannels > &fdn, std::span< Sample > out)
Definition ReverbDesign.h:520
Definition ReverbDesign.h:575
std::array< AttenuationFilter, NumChannels > mAttenuationFilters
Definition ReverbDesign.h:642
std::array< uint32, NumChannels > mDelaySamples
Definition ReverbDesign.h:640
std::array< DelayLine< 1 >, NumChannels > mDelays
Definition ReverbDesign.h:641
void UpdateFilterParameters(FilterArgs &&...filterUpdateArgs)
Definition ReverbDesign.h:623
JPL_INLINE float ApplyAttenuationFilter(float sample, uint32 fdnIndex)
Definition ReverbDesign.h:634
void Prepare(float sampleRate, FilterArgs &&...filterPrepareArgs)
Definition ReverbDesign.h:597
std::array< float, NumChannels > Channels
Definition ReverbDesign.h:589
Minimal 4-wide 32-bit float vector implementation for SIMD.
Definition SIMD.h:60
JPL_INLINE float reduce() const noexcept
Returns sum of all components.
Definition SIMD.h:938
JPL_INLINE void store(float *mem) const
Store values from simd to provided memory location.
Definition SIMD.h:573
static constexpr std::size_t size() noexcept
Get number of element of the vector.
Definition SIMD.h:97