37template <std::
size_t MaximumVoices = 16>
40 static_assert(MaximumVoices > 0);
43 std::uint64_t note_instance_id = 0) noexcept {
44 if (
spec.voice_count == 0 ||
spec.voice_count > MaximumVoices ||
45 !finite_nonnegative(
spec.detune_cents) || !unit(
spec.stereo_spread) ||
46 !unit(
spec.phase_randomization) || !finite_nonnegative(
spec.drift_cents) ||
47 (
spec.drift_cents > 0.0 &&
spec.drift_period_frames == 0) ||
48 !valid_gain_law(
spec.gain_law)) {
51 std::array<UnisonVoiceParameters, MaximumVoices> next{};
53 ? 1.0 /
static_cast<double>(
spec.voice_count)
54 : 1.0 / std::sqrt(
static_cast<double>(
spec.voice_count));
55 for (std::size_t
i = 0;
i <
spec.voice_count; ++
i) {
56 const double position =
spec.voice_count == 1
58 : -1.0 + 2.0 *
static_cast<double>(
i) /
59 static_cast<double>(
spec.voice_count - 1);
60 const auto key =
mix64(seed, note_instance_id,
i + 0x554e49534f4eull);
61 next[
i] = {position *
spec.detune_cents,
62 position *
spec.stereo_spread,
64 spec.voice_count == 1 ? 0.0
70 note_instance_id_ = note_instance_id;
76 std::size_t
size() const noexcept {
return configured_ ? spec_.voice_count : 0; }
78 return voices_[index];
84 double drift_cents(std::size_t voice, std::uint64_t absolute_frame)
const noexcept {
85 if (!configured_ || voice >= spec_.voice_count || spec_.drift_cents == 0.0)
87 const auto period = spec_.drift_period_frames;
88 const auto knot = absolute_frame / period;
89 double t =
static_cast<double>(absolute_frame % period) /
static_cast<double>(period);
90 t = t * t * (3.0 - 2.0 * t);
91 const auto base =
mix64(seed_, note_instance_id_, voice + 0x4452494654ull);
94 return ((1.0 - t) *
a + t * b) * spec_.drift_cents;
101 static bool finite_nonnegative(
double value)
noexcept {
102 return std::isfinite(value) && value >= 0.0;
104 static bool unit(
double value)
noexcept {
105 return std::isfinite(value) && value >= 0.0 && value <= 1.0;
108 std::array<UnisonVoiceParameters, MaximumVoices> voices_{};
110 std::uint64_t seed_ = 0;
111 std::uint64_t note_instance_id_ = 0;
112 bool configured_ =
false;
std::size_t size() const noexcept
Definition unison.hpp:76
const UnisonVoiceParameters & operator[](std::size_t index) const noexcept
Definition unison.hpp:77
double drift_cents(std::size_t voice, std::uint64_t absolute_frame) const noexcept
Definition unison.hpp:84
bool configured() const noexcept
Definition unison.hpp:75
bool configure(const UnisonSpec &spec, std::uint64_t seed=0, std::uint64_t note_instance_id=0) noexcept
Definition unison.hpp:42
const UnisonSpec & spec() const noexcept
Definition unison.hpp:80
Definition additive_bank.hpp:218
UnisonGainLaw
Definition unison.hpp:12
@ EqualPower
Definition unison.hpp:12
@ PeakSafe
Definition unison.hpp:12
constexpr std::uint64_t mix64(std::uint64_t z)
splitmix64 finalizer (Steele, Lea & Flood, OOPSLA 2014).
Definition rng.hpp:140
@ EqualPower
(cos(u·π/2), sin(u·π/2)) — power sum == 1.
Definition crossfade.hpp:43
@ a
Definition formant_filter_bank.hpp:20
@ i
Definition formant_filter_bank.hpp:20
T bipolar_from(std::uint64_t key)
Definition rng.hpp:175
float unit_from(std::uint64_t key)
Stateless purpose-keyed draw in [0, 1).
Definition rng.hpp:164
Deterministic randomness for modulation: a counter-free PRNG, a Gaussian draw, a stateless purpose-ke...
Control-side configuration for a fixed unison layout.
Definition unison.hpp:20
std::uint64_t drift_period_frames
Definition unison.hpp:26
UnisonGainLaw gain_law
Definition unison.hpp:27
double phase_randomization
Definition unison.hpp:24
std::size_t voice_count
Definition unison.hpp:21
double drift_cents
Definition unison.hpp:25
double stereo_spread
Definition unison.hpp:23
double detune_cents
Definition unison.hpp:22
double gain
Definition unison.hpp:33
double pan
Definition unison.hpp:32
double phase
Definition unison.hpp:34
double detune_cents
Definition unison.hpp:31