151 static_assert(std::is_floating_point_v<Real>,
152 "AnalogRandom::Generator requires a floating-point Real type.");
160 explicit Generator(std::uint64_t seed) noexcept : prng_(seed)
171 : isSafeToRun_(other.isSafeToRun_),
173 sampleRate_(other.sampleRate_),
174 phaseAccumulator_(other.phaseAccumulator_),
175 triggerNext_(other.triggerNext_.load(std::memory_order_relaxed)),
176 currentValue_(other.currentValue_),
177 targetValue_(other.targetValue_),
178 noiseType_(other.noiseType_.load(std::memory_order_relaxed)),
179 useBpmSync_(other.useBpmSync_.load(std::memory_order_relaxed)),
180 rateHz_(other.rateHz_.load(std::memory_order_relaxed)),
181 bpm_(other.bpm_.load(std::memory_order_relaxed)),
182 bpmDivision_(other.bpmDivision_.load(std::memory_order_relaxed)),
183 min_(other.min_.load(std::memory_order_relaxed)),
184 max_(other.max_.load(std::memory_order_relaxed)),
185 smoothingEnabled_(other.smoothingEnabled_.load(std::memory_order_relaxed)),
186 smoothingCoeff_(other.smoothingCoeff_.load(std::memory_order_relaxed)),
187 quantizationStep_(other.quantizationStep_.load(std::memory_order_relaxed)),
188 pendingSeed_(other.pendingSeed_.load(std::memory_order_relaxed)),
189 brownNoiseState_(other.brownNoiseState_),
190 pinkNoiseOctaves_(other.pinkNoiseOctaves_),
191 denormalFlip_(other.denormalFlip_)
197 if (
this == &other)
return *
this;
198 isSafeToRun_ = other.isSafeToRun_;
200 sampleRate_ = other.sampleRate_;
201 phaseAccumulator_ = other.phaseAccumulator_;
202 triggerNext_.store(other.triggerNext_.load(std::memory_order_relaxed), std::memory_order_relaxed);
203 currentValue_ = other.currentValue_;
204 targetValue_ = other.targetValue_;
205 noiseType_.store (other.noiseType_.load(std::memory_order_relaxed), std::memory_order_relaxed);
206 useBpmSync_.store (other.useBpmSync_.load(std::memory_order_relaxed), std::memory_order_relaxed);
207 rateHz_.store (other.rateHz_.load(std::memory_order_relaxed), std::memory_order_relaxed);
208 bpm_.store (other.bpm_.load(std::memory_order_relaxed), std::memory_order_relaxed);
209 bpmDivision_.store(other.bpmDivision_.load(std::memory_order_relaxed), std::memory_order_relaxed);
210 min_.store (other.min_.load(std::memory_order_relaxed), std::memory_order_relaxed);
211 max_.store (other.max_.load(std::memory_order_relaxed), std::memory_order_relaxed);
212 smoothingEnabled_.store(other.smoothingEnabled_.load(std::memory_order_relaxed), std::memory_order_relaxed);
213 smoothingCoeff_.store (other.smoothingCoeff_.load(std::memory_order_relaxed), std::memory_order_relaxed);
214 quantizationStep_.store(other.quantizationStep_.load(std::memory_order_relaxed), std::memory_order_relaxed);
215 pendingSeed_.store(other.pendingSeed_.load(std::memory_order_relaxed), std::memory_order_relaxed);
216 brownNoiseState_ = other.brownNoiseState_;
217 pinkNoiseOctaves_ = other.pinkNoiseOctaves_;
218 denormalFlip_ = other.denormalFlip_;
234 if (sampleRate > 0.0) sampleRate_ = sampleRate;
243 phaseAccumulator_ = 0.0;
244 triggerNext_.store(
true, std::memory_order_relaxed);
245 currentValue_ =
static_cast<Real
>(0);
246 targetValue_ =
static_cast<Real
>(0);
247 brownNoiseState_ =
static_cast<Real
>(0);
248 pinkNoiseOctaves_.fill(
static_cast<Real
>(0));
255 void reseed(std::uint64_t newSeed)
noexcept
257 if (newSeed == 0) newSeed = 1;
258 pendingSeed_.store(newSeed, std::memory_order_release);
272 if (!isSafeToRun_) [[unlikely]]
return static_cast<Real
>(0);
274 const auto pending = pendingSeed_.exchange(0, std::memory_order_acq_rel);
281 const Real continuousNoise = tickContinuousNoise();
284 if (triggerNext_.exchange(
false, std::memory_order_acquire))
286 generateNewTarget(continuousNoise);
289 if (smoothingEnabled_.load(std::memory_order_relaxed))
291 const Real coeff = smoothingCoeff_.load(std::memory_order_relaxed);
292 currentValue_ += coeff * (targetValue_ - currentValue_);
296 currentValue_ = targetValue_;
300 denormalFlip_ = -denormalFlip_;
301 currentValue_ += denormalFlip_;
303 return currentValue_;
313 if (!isSafeToRun_ || outputBuffer.empty()) [[unlikely]]
return;
315 const auto pending = pendingSeed_.exchange(0, std::memory_order_acq_rel);
323 const bool smoothing = smoothingEnabled_.load(std::memory_order_relaxed);
324 const Real coeff = smoothingCoeff_.load(std::memory_order_relaxed);
325 const NoiseType noiseType = noiseType_.load(std::memory_order_relaxed);
326 const Real quantStep = quantizationStep_.load(std::memory_order_relaxed);
327 Real currentMin = min_.load(std::memory_order_relaxed);
328 Real currentMax = max_.load(std::memory_order_relaxed);
330 if (currentMin > currentMax) std::swap(currentMin, currentMax);
332 for (Real& sample : outputBuffer)
334 const Real white =
static_cast<Real
>(prng_.
next_double() * 2.0 - 1.0);
335 Real continuousNoise = white;
337 if (noiseType ==
NoiseType::Pink) continuousNoise = tickPinkNoise(white);
338 else if (noiseType ==
NoiseType::Brown) continuousNoise = tickBrownNoise(white);
342 if (triggerNext_.exchange(
false, std::memory_order_acquire))
346 const Real cn = std::clamp(continuousNoise,
static_cast<Real
>(-1),
static_cast<Real
>(1));
347 targetValue_ = currentMin + ((cn *
static_cast<Real
>(0.5)) +
static_cast<Real
>(0.5)) * (currentMax - currentMin);
348 if (quantStep >
static_cast<Real
>(0)) targetValue_ = std::round(targetValue_ / quantStep) * quantStep;
353 currentValue_ += coeff * (targetValue_ - currentValue_);
357 currentValue_ = targetValue_;
360 denormalFlip_ = -denormalFlip_;
361 currentValue_ += denormalFlip_;
363 sample = currentValue_;
368 [[nodiscard]] Real
getPhase() const noexcept {
return static_cast<Real
>(phaseAccumulator_); }
376 noiseType_.store(type, std::memory_order_relaxed);
381 useBpmSync_.store(
false, std::memory_order_relaxed);
382 rateHz_.store(
static_cast<float>(rateInHz), std::memory_order_relaxed);
387 bpm_.store(
static_cast<float>(bpm), std::memory_order_relaxed);
388 bpmDivision_.store(division, std::memory_order_relaxed);
389 useBpmSync_.store(
true, std::memory_order_relaxed);
394 bpm_.store(
static_cast<float>(newBpm), std::memory_order_relaxed);
400 template <
typename T>
403 static_assert(std::is_floating_point_v<T>,
"setRange only accepts floating-point types.");
404 if (min > max) std::swap(min, max);
405 min_.store(
static_cast<Real
>(min), std::memory_order_relaxed);
406 max_.store(
static_cast<Real
>(max), std::memory_order_relaxed);
409 void setSmoothing(
bool shouldBeEnabled, Real timeInMs =
static_cast<Real
>(50.0)) noexcept
411 smoothingEnabled_.store(shouldBeEnabled, std::memory_order_relaxed);
412 if (sampleRate_ > 0 && timeInMs >
static_cast<Real
>(0))
414 const double coeff = std::exp(-1.0 / (sampleRate_ * (
static_cast<double>(timeInMs) / 1000.0)));
415 smoothingCoeff_.store(
static_cast<Real
>(1.0 - coeff), std::memory_order_relaxed);
423 smoothingCoeff_.store(
static_cast<Real
>(1), std::memory_order_relaxed);
429 if (std::isnan(step) || step <
static_cast<Real
>(0)) step =
static_cast<Real
>(0);
430 quantizationStep_.store(step, std::memory_order_relaxed);
470 template <
typename Int>
473 static_assert(std::is_integral_v<Int>,
"getNextDiscrete requires an integral type.");
474 if (imax <= imin)
return imin;
477 Real minVal = min_.load(std::memory_order_relaxed);
478 Real maxVal = max_.load(std::memory_order_relaxed);
479 if (minVal > maxVal) std::swap(minVal, maxVal);
480 if (maxVal == minVal)
return imin;
482 const double t =
static_cast<double>((value - minVal) / (maxVal - minVal));
483 const double mapped = t *
static_cast<double>(imax - imin) +
static_cast<double>(imin);
486 return static_cast<Int
>(std::clamp(std::llround(mapped),
487 static_cast<long long>(imin),
488 static_cast<long long>(imax)));
493 return static_cast<int>(getNextDiscrete<int>(imin, imax));
497 void checkLockFree() noexcept
503 isSafeToRun_ = std::atomic<Real>::is_always_lock_free
504 && std::atomic<float>::is_always_lock_free
505 && std::atomic<std::uint64_t>::is_always_lock_free
506 && std::atomic<NoiseType>::is_always_lock_free
507 && std::atomic<BpmDivision>::is_always_lock_free
508 && std::atomic<bool>::is_always_lock_free;
514 [[nodiscard]] Real tickContinuousNoise() noexcept
516 const Real white =
static_cast<Real
>(prng_.
next_double() * 2.0 - 1.0);
517 switch (noiseType_.load(std::memory_order_relaxed))
522 default:
return white;
529 void generateNewTarget(Real sampledNoise)
noexcept
531 sampledNoise = std::clamp(sampledNoise,
static_cast<Real
>(-1),
static_cast<Real
>(1));
533 Real currentMin = min_.load(std::memory_order_relaxed);
534 Real currentMax = max_.load(std::memory_order_relaxed);
535 if (currentMin > currentMax) std::swap(currentMin, currentMax);
537 targetValue_ = currentMin + ((sampledNoise *
static_cast<Real
>(0.5)) +
static_cast<Real
>(0.5)) * (currentMax - currentMin);
539 const Real quantStep = quantizationStep_.load(std::memory_order_relaxed);
540 if (quantStep >
static_cast<Real
>(0))
542 targetValue_ = std::round(targetValue_ / quantStep) * quantStep;
546 void updatePhase() noexcept
548 Real rate =
static_cast<Real
>(0);
549 if (useBpmSync_.load(std::memory_order_relaxed))
551 const float bpm = bpm_.load(std::memory_order_relaxed);
554 const double noteLengthInBeats = 4.0 / getBpmDivisionMultiplier(bpmDivision_.load(std::memory_order_relaxed));
555 const double periodInSeconds = (60.0 /
static_cast<double>(bpm)) * noteLengthInBeats;
556 if (periodInSeconds > 0.0) rate =
static_cast<Real
>(1.0 / periodInSeconds);
561 rate =
static_cast<Real
>(rateHz_.load(std::memory_order_relaxed));
564 if (rate <=
static_cast<Real
>(0))
return;
566 phaseAccumulator_ +=
static_cast<double>(rate) / sampleRate_;
567 if (phaseAccumulator_ >= 1.0)
572 phaseAccumulator_ -= std::floor(phaseAccumulator_);
573 triggerNext_.store(
true, std::memory_order_release);
577 [[nodiscard]] Real tickPinkNoise(Real white)
noexcept
584 Real b0 = pinkNoiseOctaves_[0];
585 Real b1 = pinkNoiseOctaves_[1];
586 Real b2 = pinkNoiseOctaves_[2];
587 Real b3 = pinkNoiseOctaves_[3];
588 Real b4 = pinkNoiseOctaves_[4];
589 Real b5 = pinkNoiseOctaves_[5];
590 Real b6 = pinkNoiseOctaves_[6];
592 b0 =
static_cast<Real
>(0.99886) * b0 + white *
static_cast<Real
>(0.0555179);
593 b1 =
static_cast<Real
>(0.99332) * b1 + white *
static_cast<Real
>(0.0750759);
594 b2 =
static_cast<Real
>(0.96900) * b2 + white *
static_cast<Real
>(0.1538520);
595 b3 =
static_cast<Real
>(0.86650) * b3 + white *
static_cast<Real
>(0.3104856);
596 b4 =
static_cast<Real
>(0.55000) * b4 + white *
static_cast<Real
>(0.5329522);
597 b5 =
static_cast<Real
>(-0.7616) * b5 - white *
static_cast<Real
>(0.0168980);
599 const Real pink = b0 + b1 + b2 + b3 + b4 + b5 + b6
600 + white *
static_cast<Real
>(0.5362);
602 pinkNoiseOctaves_[0] = b0;
603 pinkNoiseOctaves_[1] = b1;
604 pinkNoiseOctaves_[2] = b2;
605 pinkNoiseOctaves_[3] = b3;
606 pinkNoiseOctaves_[4] = b4;
607 pinkNoiseOctaves_[5] = b5;
608 pinkNoiseOctaves_[6] = white *
static_cast<Real
>(0.115926);
613 return pink *
static_cast<Real
>(0.11);
616 [[nodiscard]] Real tickBrownNoise(Real white)
noexcept
618 brownNoiseState_ += white *
static_cast<Real
>(0.02);
619 brownNoiseState_ *=
static_cast<Real
>(0.995);
620 brownNoiseState_ = std::clamp(brownNoiseState_,
static_cast<Real
>(-1.0),
static_cast<Real
>(1.0));
621 return brownNoiseState_;
624 [[nodiscard]]
static constexpr double getBpmDivisionMultiplier(
BpmDivision division)
noexcept
645 [[nodiscard]]
static std::uint64_t generateUniqueSeed() noexcept
647 static std::atomic_uint64_t instanceCounter{ 0 };
648 const auto instanceId = instanceCounter.fetch_add(1, std::memory_order_relaxed);
649 const auto thisPtrAddress =
reinterpret_cast<std::uintptr_t
>(&instanceCounter);
650 const auto now =
static_cast<std::uint64_t
>(std::chrono::steady_clock::now().time_since_epoch().count());
651 std::uint64_t seed = instanceId ^ (thisPtrAddress << 1) ^ (now + 0x9E3779B97F4A7C15ull);
652 return seed == 0 ? 1 : seed;
655 bool isSafeToRun_{
false };
656 Detail::Xoshiro256pp prng_;
657 double sampleRate_{ 44100.0 };
658 double phaseAccumulator_{ 0.0 };
659 std::atomic<bool> triggerNext_{
true };
660 Real currentValue_{
static_cast<Real
>(0) };
661 Real targetValue_{
static_cast<Real
>(0) };
663 std::atomic<bool> useBpmSync_{
false };
664 std::atomic<float> rateHz_{ 1.0f };
665 std::atomic<float> bpm_{ 120.0f };
670 std::atomic<Real> min_{
static_cast<Real
>(-1) };
671 std::atomic<Real> max_{
static_cast<Real
>(1) };
672 std::atomic<bool> smoothingEnabled_{
false };
673 std::atomic<Real> smoothingCoeff_{
static_cast<Real
>(0) };
674 std::atomic<Real> quantizationStep_{
static_cast<Real
>(0) };
675 std::atomic<std::uint64_t> pendingSeed_{ 0 };
676 Real brownNoiseState_{
static_cast<Real
>(0) };
677 std::array<Real, 7> pinkNoiseOctaves_{};
678 Real denormalFlip_{
static_cast<Real
>(1e-18) };