174 static_assert(std::is_floating_point_v<Real>,
175 "AnalogRandom::Generator requires a floating-point Real type.");
183 explicit Generator(std::uint64_t seed) noexcept : prng_(seed)
194 : isSafeToRun_(other.isSafeToRun_),
196 sampleRate_(other.sampleRate_),
197 phaseAccumulator_(other.phaseAccumulator_),
198 triggerNext_(other.triggerNext_.load(std::memory_order_relaxed)),
199 currentValue_(other.currentValue_),
200 targetValue_(other.targetValue_),
201 noiseType_(other.noiseType_.load(std::memory_order_relaxed)),
202 useBpmSync_(other.useBpmSync_.load(std::memory_order_relaxed)),
203 rateHz_(other.rateHz_.load(std::memory_order_relaxed)),
204 bpm_(other.bpm_.load(std::memory_order_relaxed)),
205 bpmDivision_(other.bpmDivision_.load(std::memory_order_relaxed)),
206 min_(other.min_.load(std::memory_order_relaxed)),
207 max_(other.max_.load(std::memory_order_relaxed)),
208 smoothingEnabled_(other.smoothingEnabled_.load(std::memory_order_relaxed)),
209 smoothingCoeff_(other.smoothingCoeff_.load(std::memory_order_relaxed)),
210 smoothingTimeMs_(other.smoothingTimeMs_.load(std::memory_order_relaxed)),
211 quantizationStep_(other.quantizationStep_.load(std::memory_order_relaxed)),
212 pendingSeed_(other.pendingSeed_.load(std::memory_order_relaxed)),
213 publishedValue_(other.publishedValue_.load(std::memory_order_relaxed)),
214 publishedPhase_(other.publishedPhase_.load(std::memory_order_relaxed)),
215 brownNoiseState_(other.brownNoiseState_),
216 pinkNoiseOctaves_(other.pinkNoiseOctaves_),
217 denormalFlip_(other.denormalFlip_)
223 if (
this == &other)
return *
this;
224 isSafeToRun_ = other.isSafeToRun_;
226 sampleRate_ = other.sampleRate_;
227 phaseAccumulator_ = other.phaseAccumulator_;
228 triggerNext_.store(other.triggerNext_.load(std::memory_order_relaxed), std::memory_order_relaxed);
229 currentValue_ = other.currentValue_;
230 targetValue_ = other.targetValue_;
231 noiseType_.store (other.noiseType_.load(std::memory_order_relaxed), std::memory_order_relaxed);
232 useBpmSync_.store (other.useBpmSync_.load(std::memory_order_relaxed), std::memory_order_relaxed);
233 rateHz_.store (other.rateHz_.load(std::memory_order_relaxed), std::memory_order_relaxed);
234 bpm_.store (other.bpm_.load(std::memory_order_relaxed), std::memory_order_relaxed);
235 bpmDivision_.store(other.bpmDivision_.load(std::memory_order_relaxed), std::memory_order_relaxed);
236 min_.store (other.min_.load(std::memory_order_relaxed), std::memory_order_relaxed);
237 max_.store (other.max_.load(std::memory_order_relaxed), std::memory_order_relaxed);
238 smoothingEnabled_.store(other.smoothingEnabled_.load(std::memory_order_relaxed), std::memory_order_relaxed);
239 smoothingCoeff_.store (other.smoothingCoeff_.load(std::memory_order_relaxed), std::memory_order_relaxed);
240 smoothingTimeMs_.store (other.smoothingTimeMs_.load(std::memory_order_relaxed), std::memory_order_relaxed);
241 quantizationStep_.store(other.quantizationStep_.load(std::memory_order_relaxed), std::memory_order_relaxed);
242 pendingSeed_.store(other.pendingSeed_.load(std::memory_order_relaxed), std::memory_order_relaxed);
243 publishedValue_.store(other.publishedValue_.load(std::memory_order_relaxed), std::memory_order_relaxed);
244 publishedPhase_.store(other.publishedPhase_.load(std::memory_order_relaxed), std::memory_order_relaxed);
245 brownNoiseState_ = other.brownNoiseState_;
246 pinkNoiseOctaves_ = other.pinkNoiseOctaves_;
247 denormalFlip_ = other.denormalFlip_;
263 if (sampleRate > 0.0 && std::isfinite(sampleRate)) sampleRate_ = sampleRate;
267 updateSmoothingCoeff();
276 phaseAccumulator_ = 0.0;
277 triggerNext_.store(
true, std::memory_order_relaxed);
278 currentValue_ =
static_cast<Real
>(0);
279 targetValue_ =
static_cast<Real
>(0);
280 brownNoiseState_ =
static_cast<Real
>(0);
281 pinkNoiseOctaves_.fill(
static_cast<Real
>(0));
282 publishedValue_.store(
static_cast<Real
>(0), std::memory_order_relaxed);
283 publishedPhase_.store(
static_cast<Real
>(0), std::memory_order_relaxed);
290 void reseed(std::uint64_t newSeed)
noexcept
292 if (newSeed == 0) newSeed = 1;
293 pendingSeed_.store(newSeed, std::memory_order_release);
307 if (!isSafeToRun_) [[unlikely]]
return static_cast<Real
>(0);
309 const auto pending = pendingSeed_.exchange(0, std::memory_order_acq_rel);
318 if (triggerNext_.exchange(
false, std::memory_order_acquire))
320 generateNewTarget(tickColoredNoise(noiseType_.load(std::memory_order_relaxed)));
323 if (smoothingEnabled_.load(std::memory_order_relaxed))
325 const Real coeff = smoothingCoeff_.load(std::memory_order_relaxed);
326 currentValue_ += coeff * (targetValue_ - currentValue_);
330 currentValue_ = targetValue_;
334 denormalFlip_ = -denormalFlip_;
335 currentValue_ += denormalFlip_;
341 publishedValue_.store(currentValue_, std::memory_order_relaxed);
342 publishedPhase_.store(
static_cast<Real
>(phaseAccumulator_),
343 std::memory_order_relaxed);
345 return currentValue_;
355 if (!isSafeToRun_ || outputBuffer.empty()) [[unlikely]]
return;
357 const auto pending = pendingSeed_.exchange(0, std::memory_order_acq_rel);
365 const bool smoothing = smoothingEnabled_.load(std::memory_order_relaxed);
366 const Real coeff = smoothingCoeff_.load(std::memory_order_relaxed);
367 const NoiseType noiseType = noiseType_.load(std::memory_order_relaxed);
368 const Real quantStep = quantizationStep_.load(std::memory_order_relaxed);
369 Real currentMin = min_.load(std::memory_order_relaxed);
370 Real currentMax = max_.load(std::memory_order_relaxed);
372 if (currentMin > currentMax) std::swap(currentMin, currentMax);
374 for (Real& sample : outputBuffer)
378 if (triggerNext_.exchange(
false, std::memory_order_acquire))
382 const Real cn = std::clamp(tickColoredNoise(noiseType),
383 static_cast<Real
>(-1),
static_cast<Real
>(1));
384 targetValue_ = currentMin + ((cn *
static_cast<Real
>(0.5)) +
static_cast<Real
>(0.5)) * (currentMax - currentMin);
385 if (quantStep >
static_cast<Real
>(0)) targetValue_ = std::round(targetValue_ / quantStep) * quantStep;
390 currentValue_ += coeff * (targetValue_ - currentValue_);
394 currentValue_ = targetValue_;
397 denormalFlip_ = -denormalFlip_;
398 currentValue_ += denormalFlip_;
400 sample = currentValue_;
406 publishedValue_.store(currentValue_, std::memory_order_relaxed);
407 publishedPhase_.store(
static_cast<Real
>(phaseAccumulator_),
408 std::memory_order_relaxed);
416 return publishedValue_.load(std::memory_order_relaxed);
425 return publishedPhase_.load(std::memory_order_relaxed);
434 noiseType_.store(type, std::memory_order_relaxed);
439 useBpmSync_.store(
false, std::memory_order_relaxed);
440 rateHz_.store(
static_cast<float>(rateInHz), std::memory_order_relaxed);
445 bpm_.store(
static_cast<float>(bpm), std::memory_order_relaxed);
446 bpmDivision_.store(division, std::memory_order_relaxed);
447 useBpmSync_.store(
true, std::memory_order_relaxed);
452 bpm_.store(
static_cast<float>(newBpm), std::memory_order_relaxed);
458 template <
typename T>
461 static_assert(std::is_floating_point_v<T>,
"setRange only accepts floating-point types.");
462 if (min > max) std::swap(min, max);
463 min_.store(
static_cast<Real
>(min), std::memory_order_relaxed);
464 max_.store(
static_cast<Real
>(max), std::memory_order_relaxed);
474 void setSmoothing(
bool shouldBeEnabled, Real timeInMs =
static_cast<Real
>(50.0)) noexcept
476 smoothingEnabled_.store(shouldBeEnabled, std::memory_order_relaxed);
477 smoothingTimeMs_.store(timeInMs >
static_cast<Real
>(0) ?
static_cast<double>(timeInMs) : 0.0,
478 std::memory_order_relaxed);
479 updateSmoothingCoeff();
484 if (std::isnan(step) || step <
static_cast<Real
>(0)) step =
static_cast<Real
>(0);
485 quantizationStep_.store(step, std::memory_order_relaxed);
525 template <
typename Int>
528 static_assert(std::is_integral_v<Int>,
"getNextDiscrete requires an integral type.");
529 if (imax <= imin)
return imin;
532 Real minVal = min_.load(std::memory_order_relaxed);
533 Real maxVal = max_.load(std::memory_order_relaxed);
534 if (minVal > maxVal) std::swap(minVal, maxVal);
535 if (maxVal == minVal)
return imin;
537 const double t =
static_cast<double>((value - minVal) / (maxVal - minVal));
538 const double mapped = t *
static_cast<double>(imax - imin) +
static_cast<double>(imin);
541 return static_cast<Int
>(std::clamp(std::llround(mapped),
542 static_cast<long long>(imin),
543 static_cast<long long>(imax)));
548 return static_cast<int>(getNextDiscrete<int>(imin, imax));
552 void checkLockFree() noexcept
558 isSafeToRun_ = std::atomic<Real>::is_always_lock_free
559 && std::atomic<float>::is_always_lock_free
560 && std::atomic<std::uint64_t>::is_always_lock_free
561 && std::atomic<double>::is_always_lock_free
562 && std::atomic<NoiseType>::is_always_lock_free
563 && std::atomic<BpmDivision>::is_always_lock_free
564 && std::atomic<bool>::is_always_lock_free;
568 void updateSmoothingCoeff() noexcept
570 const double timeMs = smoothingTimeMs_.load(std::memory_order_relaxed);
574 const double coeff = (timeMs > 0.0)
575 ? 1.0 - std::exp(-1.0 / (sampleRate_ * timeMs / 1000.0))
577 smoothingCoeff_.store(
static_cast<Real
>(coeff), std::memory_order_relaxed);
585 [[nodiscard]] Real tickColoredNoise(
NoiseType type)
noexcept
587 const Real white =
static_cast<Real
>(prng_.
next_double() * 2.0 - 1.0);
593 default:
return white;
600 void generateNewTarget(Real sampledNoise)
noexcept
602 sampledNoise = std::clamp(sampledNoise,
static_cast<Real
>(-1),
static_cast<Real
>(1));
604 Real currentMin = min_.load(std::memory_order_relaxed);
605 Real currentMax = max_.load(std::memory_order_relaxed);
606 if (currentMin > currentMax) std::swap(currentMin, currentMax);
608 targetValue_ = currentMin + ((sampledNoise *
static_cast<Real
>(0.5)) +
static_cast<Real
>(0.5)) * (currentMax - currentMin);
610 const Real quantStep = quantizationStep_.load(std::memory_order_relaxed);
611 if (quantStep >
static_cast<Real
>(0))
613 targetValue_ = std::round(targetValue_ / quantStep) * quantStep;
617 void updatePhase() noexcept
619 Real rate =
static_cast<Real
>(0);
620 if (useBpmSync_.load(std::memory_order_relaxed))
622 const float bpm = bpm_.load(std::memory_order_relaxed);
625 const double noteLengthInBeats = 4.0 / getBpmDivisionMultiplier(bpmDivision_.load(std::memory_order_relaxed));
626 const double periodInSeconds = (60.0 /
static_cast<double>(bpm)) * noteLengthInBeats;
627 if (periodInSeconds > 0.0) rate =
static_cast<Real
>(1.0 / periodInSeconds);
632 rate =
static_cast<Real
>(rateHz_.load(std::memory_order_relaxed));
635 if (rate <=
static_cast<Real
>(0))
return;
637 phaseAccumulator_ +=
static_cast<double>(rate) / sampleRate_;
638 if (phaseAccumulator_ >= 1.0)
643 phaseAccumulator_ -= std::floor(phaseAccumulator_);
644 triggerNext_.store(
true, std::memory_order_release);
648 [[nodiscard]] Real tickPinkNoise(Real white)
noexcept
655 Real b0 = pinkNoiseOctaves_[0];
656 Real b1 = pinkNoiseOctaves_[1];
657 Real b2 = pinkNoiseOctaves_[2];
658 Real b3 = pinkNoiseOctaves_[3];
659 Real b4 = pinkNoiseOctaves_[4];
660 Real b5 = pinkNoiseOctaves_[5];
661 Real b6 = pinkNoiseOctaves_[6];
663 b0 =
static_cast<Real
>(0.99886) * b0 + white *
static_cast<Real
>(0.0555179);
664 b1 =
static_cast<Real
>(0.99332) * b1 + white *
static_cast<Real
>(0.0750759);
665 b2 =
static_cast<Real
>(0.96900) * b2 + white *
static_cast<Real
>(0.1538520);
666 b3 =
static_cast<Real
>(0.86650) * b3 + white *
static_cast<Real
>(0.3104856);
667 b4 =
static_cast<Real
>(0.55000) * b4 + white *
static_cast<Real
>(0.5329522);
668 b5 =
static_cast<Real
>(-0.7616) * b5 - white *
static_cast<Real
>(0.0168980);
670 const Real pink = b0 + b1 + b2 + b3 + b4 + b5 + b6
671 + white *
static_cast<Real
>(0.5362);
673 pinkNoiseOctaves_[0] = b0;
674 pinkNoiseOctaves_[1] = b1;
675 pinkNoiseOctaves_[2] = b2;
676 pinkNoiseOctaves_[3] = b3;
677 pinkNoiseOctaves_[4] = b4;
678 pinkNoiseOctaves_[5] = b5;
679 pinkNoiseOctaves_[6] = white *
static_cast<Real
>(0.115926);
684 return pink *
static_cast<Real
>(0.11);
687 [[nodiscard]] Real tickBrownNoise(Real white)
noexcept
689 brownNoiseState_ += white *
static_cast<Real
>(0.02);
690 brownNoiseState_ *=
static_cast<Real
>(0.995);
691 brownNoiseState_ = std::clamp(brownNoiseState_,
static_cast<Real
>(-1.0),
static_cast<Real
>(1.0));
692 return brownNoiseState_;
695 [[nodiscard]]
static constexpr double getBpmDivisionMultiplier(
BpmDivision division)
noexcept
716 [[nodiscard]]
static std::uint64_t generateUniqueSeed() noexcept
718 static std::atomic_uint64_t instanceCounter{ 0 };
719 const auto instanceId = instanceCounter.fetch_add(1, std::memory_order_relaxed);
720 const auto thisPtrAddress =
reinterpret_cast<std::uintptr_t
>(&instanceCounter);
721 const auto now =
static_cast<std::uint64_t
>(std::chrono::steady_clock::now().time_since_epoch().count());
722 std::uint64_t seed = instanceId ^ (thisPtrAddress << 1) ^ (now + 0x9E3779B97F4A7C15ull);
723 return seed == 0 ? 1 : seed;
726 bool isSafeToRun_{
false };
727 Detail::Xoshiro256pp prng_;
728 double sampleRate_{ 44100.0 };
729 double phaseAccumulator_{ 0.0 };
730 std::atomic<bool> triggerNext_{
true };
731 Real currentValue_{
static_cast<Real
>(0) };
732 Real targetValue_{
static_cast<Real
>(0) };
734 std::atomic<bool> useBpmSync_{
false };
735 std::atomic<float> rateHz_{ 1.0f };
736 std::atomic<float> bpm_{ 120.0f };
741 std::atomic<Real> min_{
static_cast<Real
>(-1) };
742 std::atomic<Real> max_{
static_cast<Real
>(1) };
743 std::atomic<bool> smoothingEnabled_{
false };
744 std::atomic<Real> smoothingCoeff_{
static_cast<Real
>(1) };
745 std::atomic<double> smoothingTimeMs_{ 0.0 };
746 std::atomic<Real> quantizationStep_{
static_cast<Real
>(0) };
747 std::atomic<std::uint64_t> pendingSeed_{ 0 };
756 std::atomic<Real> publishedValue_{
static_cast<Real
>(0) };
757 std::atomic<Real> publishedPhase_{
static_cast<Real
>(0) };
758 Real brownNoiseState_{
static_cast<Real
>(0) };
759 std::array<Real, 7> pinkNoiseOctaves_{};
760 Real denormalFlip_{
static_cast<Real
>(1e-18) };