Skip to main content

auracle_grammar/
compile.rs

1//! Compile a [`PatchTree`] term into a playable quiver [`Patch`].
2//!
3//! Every compiled voice gets the mandatory output chain
4//! `<audio> → DC blocker → VCA (amp ADSR) → Limiter → StereoOutput` and two
5//! external controls (`pitch` in V/Oct, `gate` in volts) fanned out to every
6//! pitched source and every envelope — no evolved patch can bypass the
7//! limiter or end up unplayable.
8//!
9//! The tail is built once per channel: a subtree that produces true stereo
10//! (reverb, chorus) keeps its two tanks all the way to the output rather than
11//! having the right one discarded.
12//!
13//! ## Validation mode
14//!
15//! Patches are wired under [`ValidationMode::Warn`], not `Strict`: quiver's
16//! `Strict` rejects *warning-class* pairs, which includes blessed idioms this
17//! compiler leans on (a unipolar mod envelope driving a bipolar FM input, the
18//! bipolar pitch [`Offset`] driving V/Oct inputs). The type discipline
19//! that `Strict` would enforce is already guaranteed by construction: the
20//! term's Audio/Mod sorts are Rust types, and this compiler only emits
21//! known-good connection shapes. Compile errors (invalid ports, cycles) are
22//! still hard failures; accumulated warnings are returned for inspection and
23//! property tests assert they stay within the expected classes.
24//!
25//! ## Parameter mapping
26//!
27//! Genome parameters are normalized `[0, 1]`; this module owns their musical
28//! mapping. Ranges are deliberately **bounded away from pathology** (max
29//! resonance 0.85, max delay feedback 0.7) — the grammar cannot express the
30//! most degenerate settings, which is safety layer 3 of the vetting design.
31
32use std::collections::HashMap;
33use std::sync::Arc;
34
35use quiver::modules::{
36    Attenuverter, Bitcrusher, Chorus, Clock, Compressor, DelayLine, Distortion, Ducker,
37    EnvelopeFollower, Euclidean, Flanger, FormantOsc, Granular, KarplusStrong, Limiter, LogicAnd,
38    LogicOr, LogicXor, Max, Min, NoiseGate, ParametricEq, Phaser, Rectifier, Reverb, SampleAndHold,
39    ScaleQuantizer, Supersaw, Tremolo, VcSwitch, Vibrato,
40};
41use quiver::prelude::*;
42use quiver::{AtomicF64, ExternalInput};
43
44use crate::term::{
45    rect_mode_index, AudioNode, DriveMode, FilterKind, ModNode, ModOp, PairOp, PatchTree,
46};
47
48/// quiver reads `Adsr.shape`, `Vca.response` and `Limiter.soft` as *gates* at
49/// the 2.5 V threshold, not as continuous curve amounts — 5 V and 10 V do the
50/// same thing. These two names say which side of the threshold we mean.
51const GATE_TRUE: f64 = 5.0;
52const GATE_FALSE: f64 = 0.0;
53/// Filter keytracking amount (`Svf`/`DiodeLadderFilter` port 5). quiver applies
54/// `2^(voct · amt)`, so 0.5 moves the corner half an octave per octave played:
55/// enough that a patch still speaks two octaves above where it was dialled in,
56/// not so much that a bass patch turns thin in the upper register. Fixed rather
57/// than a knob because a `keytrack` genome field is a grammar-shape change.
58const KEYTRACK_AMT: f64 = 0.5;
59/// Attack time of every [`ModNode::Follow`] detector, normalized on quiver's
60/// `0.1 + 99.9·x` ms map — so 0.05 is ≈5 ms. Fixed rather than a knob because
61/// the faceplate is already at its four-knob budget, and because a follower
62/// that is slow on the *attack* stops being an envelope follower: it misses
63/// the transient, which is the only part of a note whose dynamics carry
64/// timbral information the rest of the patch does not already have. Release
65/// is the musical choice, so release gets the knob.
66const FOLLOW_ATTACK: f64 = 0.05;
67/// Number of allpass stages in the phaser, as quiver's `stages` CV (< 0.33 →
68/// 2, < 0.66 → 4, else 6). Pinned to 6: fewer stages give fewer notches, and
69/// a two-notch phaser on a bright source is hard to tell from a chorus.
70const PHASER_STAGES: f64 = 1.0;
71/// Phaser stereo spread (0 = mono, 1 = the two sweeps 180° apart). A little
72/// under half keeps the notches audibly decorrelated without the swimming,
73/// phase-cancelling collapse a full 180° gives on a mono playback system.
74const PHASER_SPREAD: f64 = 0.35;
75/// Phaser wet/dry. A phaser *is* the interference between wet and dry, so an
76/// even blend is the only setting at which the notches reach full depth;
77/// `#pdepth` and `#pfb` are the expressive controls and this is not one.
78const PHASER_MIX: f64 = 0.5;
79/// The formant oscillator's own vibrato depth (quiver's port 3, a fixed
80/// 5.5 Hz LFO on pitch). Pinned off: a pre-baked vibrato at a rate the patch
81/// cannot name is exactly what the modulation slot exists to replace, and now
82/// that [`Offset`]-based pitch modulation exists the grammar can express the
83/// same gesture with a rate, a waveform and a depth of its own.
84const FORMANT_VIBRATO: f64 = 0.0;
85/// Flanger wet/dry. A flanger *is* the interference between the swept comb and
86/// the dry signal, so an even blend is where the notches reach full depth —
87/// the same argument as [`PHASER_MIX`], and `#fdepth`/`#ffb` are the
88/// expressive controls.
89const FLANGER_MIX: f64 = 0.5;
90/// Flanger stereo spread (0 = mono, 1 = the two sweeps 180° apart). Matched to
91/// [`PHASER_SPREAD`] for the same reason and, incidentally, because a non-zero
92/// spread is what makes quiver's ports 11/12 differ at all — at 0 they are
93/// bit-identical and the stereo pair would be a lie.
94const FLANGER_SPREAD: f64 = 0.35;
95/// EQ low-shelf corner, on quiver's `50·10^cv` Hz map — 0.2 is ≈79 Hz, under
96/// the fundamental of most of what this instrument plays, so the shelf lifts
97/// or cuts *weight* rather than re-voicing the note.
98const EQ_LOW_FREQ: f64 = 0.2;
99/// EQ mid-bell centre, on quiver's `200·40^cv` Hz map — 0.5 is ≈1.26 kHz, the
100/// presence region where a synth patch reads as forward or recessed.
101const EQ_MID_FREQ: f64 = 0.5;
102/// EQ mid-bell Q, on quiver's `0.5 + 9.5·cv` map — 0.35 is Q ≈ 3.8, so the
103/// bell is about a third of an octave wide. Narrow enough that the band is a
104/// *place* rather than a broad tilt the two shelves already cover, wide enough
105/// that a full cut is a scoop and not a notch.
106const EQ_MID_Q: f64 = 0.35;
107/// EQ high-shelf corner, on quiver's `2000 + 10000·cv` Hz map — 0.5 is 7 kHz,
108/// above the highest fundamental the keyboard reaches, so the shelf is
109/// unambiguously air and never a second mid control.
110const EQ_HIGH_FREQ: f64 = 0.5;
111/// Granular pitch shift. Pinned to no transposition: quiver reads this port as
112/// ±24 semitones, and a granulator that also transposes is a second pitch
113/// source fighting the keyboard for the same note.
114const GRANULAR_PITCH: f64 = 0.0;
115/// Granular position randomization. A little spray decorrelates the grain
116/// starts so overlapping grains stop phase-summing into a single tone; at 0
117/// the module is an odd stutter rather than a texture.
118const GRANULAR_SPRAY: f64 = 0.15;
119/// Granular buffer freeze (a quiver `Gate` port). Pinned open: freeze is a
120/// performance gesture, not a genome parameter, and a frozen buffer in an
121/// evolved patch is a patch that ignores the keyboard — every note after the
122/// first would replay the first one's audio.
123const GRANULAR_FREEZE: f64 = 0.0;
124/// Compressor attack, on quiver's `0.1 + 99.9·x` ms map — 0.15 is ≈15 ms.
125/// Slow enough to let a transient through before the gain moves, which is what
126/// makes a compressed patch still sound plucked. Fixed rather than a knob
127/// because ratio and threshold are the character and the faceplate is at its
128/// four-knob budget; the ballistics are where that budget spends least.
129const COMP_ATTACK: f64 = 0.15;
130/// Compressor release, on quiver's `10 + 990·x` ms map — 0.35 is ≈357 ms.
131/// Long enough that the gain does not chatter on a decaying note, short enough
132/// that a sidechain pump recovers inside one beat at any tempo the phrase
133/// implies. Fixed for the same reason as [`COMP_ATTACK`].
134const COMP_RELEASE: f64 = 0.35;
135/// Ducker attack, on quiver's `0.1 + 99.9·x` ms map — 0.05 is ≈5 ms. A ducker
136/// that opens slowly is a ducker you cannot hear working: the whole gesture is
137/// the *edge* of the key's transient, and anything past ~10 ms puts the duck
138/// behind the hit that caused it.
139const DUCK_ATTACK: f64 = 0.05;
140/// Gate attack, on quiver's `0.1 + 49.9·x` ms map — 0.02 is ≈1.1 ms. Same
141/// argument as [`DUCK_ATTACK`], and more so: a gate that opens slowly eats the
142/// transient it was opened by, which is the one part of the note that carried
143/// the information.
144const GATE_ATTACK: f64 = 0.02;
145/// [`quiver::modules::Euclidean`]'s pattern rotation. Pinned to no rotation:
146/// with `steps` and `pulses` both live, rotation only chooses *which* of the
147/// pattern's rests the cycle starts on, which is a phase and not a timbre —
148/// and a phase is inaudible in a five-second phrase that fires the pattern
149/// once or twice.
150const EUCLID_ROTATION: f64 = 0.0;
151/// Attenuverter level (gain = `level/5`) on the **input** of a
152/// [`ModOp::Quantize`], and its inverse on the output.
153///
154/// quiver's `ScaleQuantizer` reads and writes **V/Oct**: it snaps to the
155/// nearest scale degree on a fixed 1/12 V grid. Handed a modulator at its
156/// native ±5 V that is ±60 semitones, so the port emits 121 steps — finer than
157/// the destination can show and, after the mod cable's own attenuation, finer
158/// than the ear can hear. It would have been a quantizer that reviews as
159/// correct and sounds continuous.
160///
161/// So the input is scaled *into* a musical window and the output scaled back
162/// out, leaving the cable's gain unchanged and only the **grid** resized. At
163/// level 0.5 (gain 0.1) a ±5 V source arrives as ±0.5 V = ±6 semitones, so the
164/// scale gets 13 chromatic degrees to choose from — and the effective grid
165/// referred to the mod cable is `(1/12)/0.1 = 0.833 V`.
166///
167/// That number is chosen so the headline case lands exactly: on the pitch
168/// [`Offset`] at full `mod_depth` the cable's gain is 0.1, so a grid step is
169/// `0.833 · 0.1 = 1/12 V` — **one semitone**, over the ±6 semitones
170/// [`map::mod_depth_pitch`] allows. A quantized random melody is in tune at
171/// depth 1.0 and in a stretched tuning below it, which is the honest
172/// consequence of putting one attenuverter between every modulator and its
173/// destination: depth scales interval size.
174const QUANTIZE_IN_LEVEL: f64 = 0.5;
175/// The output side of [`QUANTIZE_IN_LEVEL`] — `25 / QUANTIZE_IN_LEVEL`, so the
176/// two gains multiply to exactly 1 and the op is transparent in scale.
177/// `Attenuverter` is `in · level/5` with no clamp, and a pinned port default
178/// is not range-checked, so a level above 5 V is a real gain of 10.
179const QUANTIZE_OUT_LEVEL: f64 = 25.0 / QUANTIZE_IN_LEVEL;
180/// Normalized cutoff of the voice's DC blocker. quiver maps `cutoff` as
181/// `20·1000^x` and then hard-clamps to 20 Hz, so 0.0 is the lowest corner the
182/// engine can produce — measured at −17 dB at 8 Hz, −2.7 dB at C1 and −0.8 dB
183/// at C2, which blocks offset without auditing as a bass cut.
184const DC_BLOCK_CUTOFF: f64 = 0.0;
185
186/// How a normalized knob value maps to the volts written to its handle.
187#[derive(Clone, Copy, Debug)]
188pub enum ParamMap {
189    /// Pass through (0..1 knob CV).
190    Unit,
191    /// Bounded resonance (`0.85·x`).
192    Resonance,
193    /// Bounded delay feedback (`0.7·x`).
194    Feedback,
195    /// Bounded feedback on a bipolar port (`(2x−1)·0.7`), where the sign of
196    /// the feedback is itself a timbre.
197    FeedbackBipolar,
198    /// Crossfader position (`(2x−1)·5 V`).
199    XfadePos,
200    /// Wavefolder threshold (`0.1 + 0.9·x`).
201    FoldThreshold,
202    /// Shelf/bell gain on a ±5 V port (`(2x−1)·5`), where knob centre must be
203    /// 0 dB.
204    GainBipolar,
205    /// Formant shift on a ±5 V port (`(2x−1)·5`), where knob centre is no
206    /// shift.
207    FormantShift,
208    /// A [`quiver::modules::Clock`] tempo (`10·x`), i.e. the port's whole
209    /// 0–10 V range.
210    ClockRate,
211    /// Euclidean step count (`0.14 + 0.86·x`), i.e. 4..16 rather than 2..16.
212    EuclidSteps,
213    /// Euclidean pulse density (`0.25 + 0.74·x`), bounded off both degenerate
214    /// ends at every step count.
215    EuclidPulses,
216    /// A [`quiver::modules::SlewLimiter`] time (`0.4·x`), i.e. the bottom of a
217    /// port whose own map is already square-law.
218    SlewTime,
219    /// Transposition on the pitch shifter's ±5 V `shift` port
220    /// (`(2x−1)·2.5`), i.e. ∓12 semitones with unison at knob centre.
221    Semitones,
222    /// The ducker's `amount`, a bipolar CV summed onto a knob base of 1.0
223    /// (`(x−1)·5`).
224    DuckAmount,
225    /// A dynamics detector threshold, geometric over 0.05–5 V, on a port
226    /// quiver reads as `cv · 5` volts.
227    DetectorThreshold,
228    /// The same threshold on the ducker's `ModulatedParam` port, which reads
229    /// as `(0.2 + cv/5)·5` volts.
230    DuckThreshold,
231    /// Mod depth for a ±5 V source into a **normalized** 0..1 port.
232    ModDepthBipolar,
233    /// Mod depth for a 0–10 V source into a **normalized** 0..1 port.
234    ModDepthUnipolar,
235    /// Mod depth for a ±5 V source into the **pitch** [`Offset`] (V/Oct).
236    ModDepthPitch,
237    /// Mod depth for a 0–10 V source into the **pitch** [`Offset`] (V/Oct).
238    ModDepthPitchUnipolar,
239    /// Mod depth for a ±5 V source into a **±5 V gain** port (the EQ bands).
240    ModDepthGain,
241    /// Mod depth for a 0–10 V source into a **±5 V gain** port.
242    ModDepthGainUnipolar,
243    /// Mod depth for a ±5 V source into the pitch shifter's **semitone** port.
244    ModDepthShift,
245    /// Mod depth for a 0–10 V source into the **semitone** port.
246    ModDepthShiftUnipolar,
247    /// Mod depth for a ±5 V source into a [`quiver::prelude::ModulatedParam`]
248    /// knob+CV port, where ±5 V spans the whole normalized parameter.
249    ModDepthParamCv,
250    /// Mod depth for a 0–10 V source into a `ModulatedParam` knob+CV port.
251    ModDepthParamCvUnipolar,
252    /// Mod depth for a ±5 V source into a **dynamics threshold** port.
253    ModDepthDetector,
254    /// Mod depth for a 0–10 V source into a dynamics threshold port.
255    ModDepthDetectorUnipolar,
256    /// **Categorical.** Wavetable select, as a table *index* rather than a
257    /// 0..1 knob: `i ↦ i/7`, the same [`map::table_cv`] the port used to be
258    /// pinned to. See [`Self::clamp_input`] for why the domain is not 0..1.
259    TableIndex,
260    /// **Categorical.** Octave select, as an index `0..=4` (`i ↦ i−2`
261    /// octaves), *minus the octave already baked into this voice's pitch
262    /// [`Offset`]* — which is the `i8` payload.
263    ///
264    /// The relative form is the whole trick, and it is what keeps this change
265    /// from moving a single sample of an existing patch. quiver **sums** every
266    /// cable into a patched input and **writes** the default into an unpatched
267    /// one, so a cable carrying the absolute offset would have to be added in
268    /// a different place in the sum than the [`Offset`]'s own constant is:
269    /// `(pitch + oct) + detune` instead of `pitch + (oct + detune)`, which
270    /// disagree in the last bit, and again with a pitch-mod cable in the sum.
271    /// A trim is `+0.0` at compile time, and `x + 0.0` is exactly `x`.
272    ///
273    /// It also means the panel and the compiler can never double-count: a
274    /// recompile re-bakes whatever octave the tree now holds and re-zeroes the
275    /// trim in the same breath.
276    OctaveTrim(i8),
277}
278
279impl ParamMap {
280    /// Clamp a value arriving from the panel to this map's input domain.
281    ///
282    /// Continuous knobs are 0..1 and always were. The two categorical sites
283    /// that became live send a *category index*, so the blanket
284    /// `value.clamp(0.0, 1.0)` the live path used to apply would have folded
285    /// all eight wavetables onto the first two.
286    pub fn clamp_input(self, x: f64) -> f64 {
287        match self {
288            ParamMap::TableIndex => x.round().clamp(0.0, 7.0),
289            ParamMap::OctaveTrim(_) => x.round().clamp(0.0, 4.0),
290            _ => x.clamp(0.0, 1.0),
291        }
292    }
293
294    /// Map a normalized value to the wire value.
295    pub fn apply(self, x: f64) -> f64 {
296        match self {
297            ParamMap::Unit => x,
298            ParamMap::Resonance => map::resonance(x),
299            ParamMap::Feedback => map::feedback(x),
300            ParamMap::FeedbackBipolar => map::feedback_bipolar(x),
301            ParamMap::XfadePos => map::xfade_pos(x),
302            ParamMap::FoldThreshold => map::fold_threshold(x),
303            ParamMap::GainBipolar => map::gain_bipolar(x),
304            ParamMap::FormantShift => map::formant_shift(x),
305            ParamMap::ClockRate => map::clock_rate(x),
306            ParamMap::EuclidSteps => map::euclid_steps(x),
307            ParamMap::EuclidPulses => map::euclid_pulses(x),
308            ParamMap::SlewTime => map::slew_time(x),
309            ParamMap::Semitones => map::semitones(x),
310            ParamMap::DuckAmount => map::duck_amount(x),
311            ParamMap::DetectorThreshold => map::detector_threshold(x),
312            ParamMap::DuckThreshold => map::duck_threshold(x),
313            ParamMap::ModDepthBipolar => map::mod_depth_bipolar(x),
314            ParamMap::ModDepthUnipolar => map::mod_depth_unipolar(x),
315            ParamMap::ModDepthPitch => map::mod_depth_pitch(x),
316            ParamMap::ModDepthPitchUnipolar => map::mod_depth_pitch_unipolar(x),
317            ParamMap::ModDepthGain => map::mod_depth_gain(x),
318            ParamMap::ModDepthGainUnipolar => map::mod_depth_gain_unipolar(x),
319            ParamMap::ModDepthShift => map::mod_depth_shift(x),
320            ParamMap::ModDepthShiftUnipolar => map::mod_depth_shift_unipolar(x),
321            ParamMap::ModDepthParamCv => map::mod_depth_param_cv(x),
322            ParamMap::ModDepthParamCvUnipolar => map::mod_depth_param_cv_unipolar(x),
323            ParamMap::ModDepthDetector => map::mod_depth_detector(x),
324            ParamMap::ModDepthDetectorUnipolar => map::mod_depth_detector_unipolar(x),
325            ParamMap::TableIndex => map::table_cv(x),
326            ParamMap::OctaveTrim(baked) => (x - 2.0) - baked as f64,
327        }
328    }
329}
330
331/// Which *destination* a modulation cable is headed for.
332///
333/// The attenuverter level that means "full depth" is a property of the
334/// destination's volt scale, not of the knob: a normalized 0..1 CV port, the
335/// V/Oct pitch [`Offset`] and a ±5 V gain port all want different levels for
336/// the same musical amount. [`Compiler::wire_mod`] picks the source polarity;
337/// this picks the scale, and the two together choose the taper.
338#[derive(Clone, Copy, Debug)]
339enum DepthScale {
340    /// A 0..1 CV port (cutoff, morph, depth, drive, position, …) — where
341    /// almost every mod slot in this grammar lands.
342    Normalized,
343    /// The pitch [`Offset`]'s summing input, in V/Oct.
344    Pitch,
345    /// A ±5 V gain port, read by quiver as `cv/5 · 12` dB.
346    Gain,
347    /// The pitch shifter's `shift` port, read by quiver as `cv/5 · 24`
348    /// semitones.
349    Shift,
350    /// A [`quiver::prelude::ModulatedParam`] knob+CV port — the ducker's
351    /// `amount` and `threshold`. The CV is summed onto the module's own knob
352    /// base after `cv / 5`, so ±5 V spans the parameter's *whole* normalized
353    /// range rather than the 0..1 the plain CV ports carry.
354    ParamCv,
355    /// A dynamics detector threshold: a 0..1 CV port whose *knob* is
356    /// geometric, so the useful settings crowd the bottom of it.
357    Detector,
358}
359
360impl DepthScale {
361    /// The taper for this destination, given the source's polarity.
362    fn taper(self, unipolar: bool) -> ParamMap {
363        match (self, unipolar) {
364            (DepthScale::Normalized, false) => ParamMap::ModDepthBipolar,
365            (DepthScale::Normalized, true) => ParamMap::ModDepthUnipolar,
366            (DepthScale::Pitch, false) => ParamMap::ModDepthPitch,
367            (DepthScale::Pitch, true) => ParamMap::ModDepthPitchUnipolar,
368            (DepthScale::Gain, false) => ParamMap::ModDepthGain,
369            (DepthScale::Gain, true) => ParamMap::ModDepthGainUnipolar,
370            (DepthScale::Shift, false) => ParamMap::ModDepthShift,
371            (DepthScale::Shift, true) => ParamMap::ModDepthShiftUnipolar,
372            (DepthScale::ParamCv, false) => ParamMap::ModDepthParamCv,
373            (DepthScale::ParamCv, true) => ParamMap::ModDepthParamCvUnipolar,
374            (DepthScale::Detector, false) => ParamMap::ModDepthDetector,
375            (DepthScale::Detector, true) => ParamMap::ModDepthDetectorUnipolar,
376        }
377    }
378}
379
380/// A live control: the atomic the audio thread reads, plus the knob mapping.
381#[derive(Clone)]
382pub struct ParamHandle {
383    /// Shared with the running patch — writing it changes the sound on the
384    /// next sample, no recompilation.
385    pub value: Arc<AtomicF64>,
386    /// Normalized-to-volts mapping.
387    pub map: ParamMap,
388}
389
390impl ParamHandle {
391    /// Write a knob value in the site's own units — 0..1 for a continuous
392    /// knob, a category index for the two live categorical sites (see
393    /// [`ParamMap::clamp_input`], which is why this is no longer a bare
394    /// `clamp(0.0, 1.0)`).
395    pub fn set_normalized(&self, x: f64) {
396        self.value.set(self.map.apply(self.map.clamp_input(x)));
397    }
398}
399
400/// The node name of the mandatory amp envelope. Every compiled voice has
401/// exactly one, and [`CompiledVoice::seed_env_phase`] is the only thing that
402/// reaches for it by name.
403const AMP_ADSR: &str = "voice:adsr";
404/// `Adsr`'s `env` output port id (0–10 V unipolar).
405const ADSR_ENV_PORT: PortId = 10;
406/// quiver's `Adsr` runs its exponential segments until it is within this of
407/// the target, then snaps. Mirrored here so the seeder knows when a segment
408/// has finished rather than guessing at a settling time.
409const ADSR_EXP_DONE: f64 = 1.0e-3;
410/// Ticks [`CompiledVoice::seed_env_phase`] will spend per segment. The
411/// envelope is running its fastest possible segment (1 ms) while it seeds, so
412/// ~7 time constants is a few hundred ticks at any sane rate; this is the
413/// guard rail, not the expected cost.
414const SEED_MAX_TICKS: usize = 4096;
415
416/// A compiled, playable voice: the patch plus its external control handles.
417pub struct CompiledVoice {
418    /// The compiled quiver patch (output already selected and compiled).
419    pub patch: Patch,
420    /// Pitch control, V/Oct (0 V = C4). Shared with the patch.
421    pub pitch: Arc<AtomicF64>,
422    /// Gate control (≥ 2.5 V = on). Shared with the patch.
423    pub gate: Arc<AtomicF64>,
424    /// Live parameter handles, keyed by the knob's trace address
425    /// (`node/0#cut`, `amp#attack`, `node/0#table`, `node/0#oct`, …).
426    /// Everything the panel can move without a recompile.
427    pub params: HashMap<String, ParamHandle>,
428    /// Signal-kind warnings accumulated while wiring (Warn mode).
429    pub warnings: Vec<String>,
430    /// Where each term node's audio leaves it: trace key → the **name** of the
431    /// quiver node carrying its output, and the port id on that node.
432    ///
433    /// Named rather than `NodeId`-keyed because that is what
434    /// `StateObserver::add_subscriptions` takes, and because a name survives
435    /// the patch being rebuilt while a `NodeId` does not — the rack asks for a
436    /// tap by the key of the module the player is looking at, and the answer
437    /// has to still mean something after the next swap.
438    ///
439    /// **Every tap here already has a consumer**, which is why nothing needs
440    /// `StateObserver::sync_output_keepalive`: the genome is a typed tree, so
441    /// each module's output feeds exactly one parent and quiver is already
442    /// producing it. That call exists for ports nothing reads, and it dirties
443    /// the patch — a recompile the audio thread would have to be staged around.
444    /// Metering here costs no recompile at all.
445    pub taps: HashMap<String, (String, PortId)>,
446}
447
448impl CompiledVoice {
449    /// Where the amp envelope is right now, 0..1 — quiver's 0–10 V `env`
450    /// output scaled back down.
451    ///
452    /// Reads the routing's last computed value, so it is meaningful after the
453    /// voice has ticked at least once and zero before that.
454    pub fn env_phase(&self) -> f64 {
455        self.patch
456            .get_node_id_by_name(AMP_ADSR)
457            .and_then(|n| self.patch.get_output_value(n, ADSR_ENV_PORT))
458            .map(|v| (v * 0.1).clamp(0.0, 1.0))
459            .unwrap_or(0.0)
460    }
461
462    /// Fast-forward this voice's amp envelope to `level` (0..1), with the gate
463    /// **already high**, so a note carried across a patch swap resumes where it
464    /// was instead of re-attacking from silence.
465    ///
466    /// ## Why this is a pre-roll and not a setter
467    ///
468    /// The plan asked for an envelope-phase getter *and setter* on this type.
469    /// The getter is above and is honest. The setter cannot be: quiver's
470    /// `Adsr` keeps `stage` and `level` private and exposes no parameter,
471    /// state-serialization or introspection surface for them (`GraphModule`
472    /// gives it `port_spec`/`tick`/`reset`/`set_sample_rate`/`type_id` and
473    /// nothing else), so there is no way to write a level into it from
474    /// outside. The alternative was to fork the ADSR into this crate to gain
475    /// two accessors, which would put a hand-copy of quiver's envelope
476    /// arithmetic — `Libm` transcendentals and all — on the critical path of
477    /// every patch the instrument has ever rendered. That is a rendered-audio
478    /// change dressed as a refactor.
479    ///
480    /// So the envelope is driven to the level the same way the player would:
481    /// the attack and decay CVs are pinned to their fastest (1 ms) settings,
482    /// the patch is ticked in silence until `env` arrives, and the CVs are put
483    /// back. It costs a few hundred ticks — well inside the swap's silent
484    /// window, which already budgets a whole voice compile per quantum — and
485    /// it leaves the envelope in the *stage* the level implies, which is the
486    /// part that actually matters:
487    ///
488    /// - below sustain, only Attack can be there, so the rise stops on arrival
489    ///   and the envelope goes on attacking at its real rate;
490    /// - at or above sustain, the note is in Decay or Sustain, so the rise runs
491    ///   to the peak (which is what puts quiver's stage machine into Decay) and
492    ///   then falls to the level.
493    ///
494    /// A side effect worth naming: the pre-roll is real audio, so it also
495    /// primes the new voice's filters and delay lines with ~15 ms of its own
496    /// signal rather than handing the fade-in an empty reverb. Tails still do
497    /// not transfer across a rewire — that is [R3] and is accepted.
498    ///
499    /// Returns whether anything was seeded.
500    ///
501    /// [R3]: the panel plan's §4 risk register.
502    pub fn seed_env_phase(&mut self, level: f64) -> bool {
503        let level = level.clamp(0.0, 1.0);
504        let Some(adsr) = self.patch.get_node_id_by_name(AMP_ADSR) else {
505            return false;
506        };
507        // A percussive patch (sustain 0) whose note has already decayed has no
508        // phase to carry, and neither has a note that never sounded.
509        if level <= 0.0 {
510            return false;
511        }
512        let (Some(attack), Some(decay), Some(sustain)) = (
513            self.params.get("amp#attack").cloned(),
514            self.params.get("amp#decay").cloned(),
515            self.params.get("amp#sustain").cloned(),
516        ) else {
517            return false;
518        };
519        let sustain = sustain.value.get();
520        let (a0, d0) = (attack.value.get(), decay.value.get());
521        // `ParamMap::Unit` on both, and quiver maps 0 V to its 1 ms floor.
522        attack.value.set(0.0);
523        decay.value.set(0.0);
524
525        let peak = if level < sustain { level } else { 1.0 };
526        for _ in 0..SEED_MAX_TICKS {
527            let now = self
528                .patch
529                .get_output_value(adsr, ADSR_ENV_PORT)
530                .unwrap_or(0.0)
531                * 0.1;
532            // The exponential attack snaps to exactly 1.0 (and hands the stage
533            // machine over to Decay) once it is within `ADSR_EXP_DONE`.
534            if now >= peak - ADSR_EXP_DONE {
535                break;
536            }
537            self.patch.tick();
538        }
539        if level >= sustain {
540            for _ in 0..SEED_MAX_TICKS {
541                let now = self
542                    .patch
543                    .get_output_value(adsr, ADSR_ENV_PORT)
544                    .unwrap_or(0.0)
545                    * 0.1;
546                if now <= level {
547                    break;
548                }
549                self.patch.tick();
550            }
551        }
552
553        attack.value.set(a0);
554        decay.value.set(d0);
555        true
556    }
557}
558
559/// Bounded musical mappings from normalized genome parameters.
560mod map {
561    /// Resonance: cap below self-oscillation screech.
562    pub fn resonance(x: f64) -> f64 {
563        0.85 * x
564    }
565    /// Delay feedback: cap below runaway.
566    pub fn feedback(x: f64) -> f64 {
567        0.7 * x
568    }
569    /// Feedback on a port whose *sign* is musical (the phaser's resonance:
570    /// negative feedback notches, positive peaks). Knob centre is no
571    /// feedback, and the ends stop short of quiver's own ±0.95 clamp so the
572    /// allpass chain never sits on the edge of ringing.
573    pub fn feedback_bipolar(x: f64) -> f64 {
574        (2.0 * x - 1.0) * 0.7
575    }
576    /// Wavetable select: the CV that lands table `i` of eight.
577    ///
578    /// The port is a **crossfade position**, not a quantizer, and getting that
579    /// wrong is inaudible in a code review and unmissable at the keyboard.
580    /// quiver computes `table_pos = cv·7`, takes `idx = floor(table_pos)` and
581    /// then blends table `idx` into table `idx+1` by `frac + morph`
582    /// (`quiver::modules::Wavetable`, oscillators.rs). So the cell-centre
583    /// convention that is right for the *quantized* `mode` port below is
584    /// exactly wrong here: `(i + 0.5)/8` put every table at a fractional
585    /// position, which meant picking `sine` gave 56% sine and 44% triangle —
586    /// and left `morph`, the knob this module exists for, with only the top
587    /// half of its travel doing anything before `frac + morph` clamped at 1.
588    ///
589    /// `i/7` lands `frac` on exactly 0 for every table, so the plate names what
590    /// you hear and morph sweeps the whole way to the next shape. (`i = 7`
591    /// gives `table_pos = 7`, which quiver clamps to `idx = 6, frac = 1.0` —
592    /// i.e. table 7 at full blend, still exact.)
593    ///
594    /// The index is an `f64` because the site is live: a smoothed write ramps
595    /// *through* the fractional positions between two tables, which is the
596    /// morph this port was always capable of and the panel could never ask
597    /// for. Integral inputs are the shapes the grammar can name.
598    pub fn table_cv(index: f64) -> f64 {
599        index / 7.0
600    }
601    /// Distortion mode select. quiver quantizes this port as `cv·3.99`, and
602    /// its slot 2 is foldback, which this grammar deliberately does not
603    /// expose (that module is [`crate::term::AudioNode::Fold`]), so the three
604    /// values step *over* it: 0.125 → soft, 0.375 → hard, 0.875 → tube.
605    pub fn drive_mode_cv(index: usize) -> f64 {
606        match index {
607            0 => 0.125,
608            1 => 0.375,
609            _ => 0.875,
610        }
611    }
612    /// Wavefolder threshold: keep off the hard-zero fold-everything corner.
613    pub fn fold_threshold(x: f64) -> f64 {
614        0.1 + 0.9 * x
615    }
616    /// Shelf/bell gain on a bipolar ±5 V port. quiver's `ParametricEq` reads
617    /// each band as `cv/5 · 12` dB, so this spans ±12 dB with **unity at knob
618    /// centre** — the only sane home position for a tone control, and the
619    /// reason a freshly placed eq is audibly a no-op until you move it.
620    pub fn gain_bipolar(x: f64) -> f64 {
621        (2.0 * x - 1.0) * 5.0
622    }
623    /// Formant shift on a bipolar ±5 V port. quiver's `FormantOsc` applies
624    /// `2^(cv/5)` to every formant frequency, so the full sweep is 0.5×–2×
625    /// (an octave either way) with **no shift at knob centre**. Both ends stay
626    /// vocal: at 2× the /i/ formants land where a child's do, and at 0.5×
627    /// where a very large chest does. Passing the raw 0..1 knob instead would
628    /// have given 1.0×–1.15× and no downward shift at all.
629    pub fn formant_shift(x: f64) -> f64 {
630        (2.0 * x - 1.0) * 5.0
631    }
632    /// Transposition on the pitch shifter's bipolar `shift` port. quiver reads
633    /// it as `cv/5 · 24` semitones and hard-clamps at ±24 (`PitchShifter`,
634    /// nonlinear.rs), so a volt is 4.8 semitones and the port's full swing is
635    /// two octaves each way.
636    ///
637    /// Half of it is the knob: **±12 semitones with unison at centre**. Two
638    /// reasons for stopping there rather than at the rail. Musically, an
639    /// octave either way is the whole harmony vocabulary this module has —
640    /// the module aliases by design (no oversampling) and two octaves up is a
641    /// 4× resample of a buffer that is already grainy. Structurally, the knob
642    /// and the modulation cable **sum on this one port** (as on the wavefolder
643    /// threshold), so leaving half the port free means a fully modulated,
644    /// fully transposed shifter lands exactly on quiver's ±24 clamp instead of
645    /// pinning against it for most of the sweep.
646    pub fn semitones(x: f64) -> f64 {
647        (2.0 * x - 1.0) * SHIFT_PEAK_V
648    }
649    /// The ducker's `amount` knob, in volts on its bipolar CV port.
650    ///
651    /// This port is **not** a plain CV: quiver reads it through a
652    /// [`ModulatedParam`](quiver::prelude::ModulatedParam) whose value is
653    /// `base + cv/5` over a `Linear{0, 1}` range, and `Ducker::new` sets
654    /// `base = 1.0`. The base is only reachable through `set_amount` on the
655    /// Rust struct — there is no port for it — so the *knob* has to arrive as
656    /// the CV, and it arrives as a **negative offset from full depth**: knob
657    /// 1.0 is 0 V (duck all the way), knob 0.0 is −5 V (do not duck at all).
658    ///
659    /// Passing the raw 0..1 knob instead would have run the parameter from
660    /// 1.0 to 1.2 and clamped — a control that is at full depth across its
661    /// entire travel and reviews as correct because the cable is there.
662    pub fn duck_amount(x: f64) -> f64 {
663        (x - DUCK_AMOUNT_BASE) * PARAM_CV_FULL_SCALE_V
664    }
665    /// Detector level, in volts, for a dynamics threshold knob — **geometric**
666    /// over 0.05–5 V rather than linear over 0–5 V.
667    ///
668    /// Every one of quiver's three dynamics modules reads its threshold as a
669    /// straight `cv · 5` volts against a smoothed `|x|` detector, and passing
670    /// the raw knob through would have been the wave-2A eq bug in reverse: not
671    /// a control that is too small to hear, but one whose entire useful range
672    /// is squeezed into the bottom tenth of its travel.
673    ///
674    /// The reason is that this instrument's sources are nowhere near a common
675    /// level. Measured as mean `|x|` on a held note through the voice tail: a
676    /// sine vco is 3.18 V, a supersaw ≈0.6 V, and a **plucked string 0.14 V** —
677    /// 27 dB below the vco, and the pluck is precisely what a gate or a ducker
678    /// is most often keyed from. A linear 0–5 V knob puts every source but the
679    /// oscillators under knob position 0.1; the default gate threshold of 0.35
680    /// measured as 1.75 V, which no key in the palette ever reaches, so the
681    /// gate sat shut for the whole note and read as a fixed −10 dB pad.
682    ///
683    /// 0.05–5 V is 40 dB, which covers that spread with the midpoint (0.5 V)
684    /// between a supersaw and a pluck. Geometric, because level is.
685    fn detector_volts(x: f64) -> f64 {
686        DETECT_MIN_V * (DETECT_MAX_V / DETECT_MIN_V).powf(x.clamp(0.0, 1.0))
687    }
688    /// [`detector_volts`] on the compressor's and gate's plain CV ports, which
689    /// quiver reads as `clamp(cv, 0, 1) · 5` volts.
690    pub fn detector_threshold(x: f64) -> f64 {
691        detector_volts(x) / PARAM_CV_FULL_SCALE_V
692    }
693    /// [`detector_volts`] on the ducker's `ModulatedParam` port.
694    ///
695    /// Same shape as [`duck_amount`] — the knob arrives as an offset from
696    /// quiver's own base — but the range is `Linear{0, 5}` **volts** of key
697    /// level, so the port resolves to `(0.2 + cv/5)·5 = 1 + cv` volts and the
698    /// knob is the wanted level minus one.
699    pub fn duck_threshold(x: f64) -> f64 {
700        detector_volts(x) - DUCK_THRESHOLD_BASE * PARAM_CV_FULL_SCALE_V
701    }
702    /// Attenuverter level, in volts, for a destination whose full modulation
703    /// excursion is `peak` volts, driven by a **±5 V** source.
704    ///
705    /// The attenuverter's gain is `level / 5`, so a ±5 V source arrives at
706    /// `±level` volts: the level *is* the peak excursion.
707    fn mod_level_bipolar(peak: f64, x: f64) -> f64 {
708        peak * x
709    }
710    /// The same, driven by a **0–10 V** source (the mod envelope and the
711    /// follower). Half the level for the same peak excursion, so both source
712    /// families reach the same depth at the same knob position.
713    fn mod_level_unipolar(peak: f64, x: f64) -> f64 {
714        peak * x * 0.5
715    }
716    /// Peak excursion for a **normalized 0..1** destination port: half of full
717    /// scale at knob 1.0.
718    const PEAK_NORMALIZED: f64 = 0.5;
719    /// Peak excursion for the **pitch [`Offset`]**, in volts — which on a
720    /// V/Oct summing input is numerically octaves, so knob 1.0 is ±0.5 octave.
721    const PEAK_PITCH: f64 = 0.5;
722    /// Peak excursion for a **±5 V gain** port: the whole port, i.e. ±12 dB at
723    /// knob 1.0.
724    const PEAK_GAIN: f64 = 5.0;
725    /// Half of the pitch shifter's `shift` port, in volts — the same half the
726    /// knob gets (see [`semitones`]), so knob and cable each own one octave
727    /// and their sum lands on quiver's ±24-semitone clamp rather than through
728    /// it.
729    pub(super) const SHIFT_PEAK_V: f64 = 2.5;
730    /// Volts of CV that move a [`ModulatedParam`](quiver::prelude::ModulatedParam)
731    /// across its whole normalized range — quiver's
732    /// `ModulatedParam::CV_FULL_SCALE_VOLTS`.
733    pub(super) const PARAM_CV_FULL_SCALE_V: f64 = 5.0;
734    /// `Ducker::new`'s `amount` knob base. The CV port offsets *this*.
735    pub(super) const DUCK_AMOUNT_BASE: f64 = 1.0;
736    /// `Ducker::new`'s `threshold` knob base, on its 0–5 V range.
737    pub(super) const DUCK_THRESHOLD_BASE: f64 = 0.2;
738    /// Quietest detector level a dynamics threshold knob can ask for, in
739    /// volts. Under a plucked string's own envelope, so knob 0 is "trigger on
740    /// anything" for every source in the palette.
741    pub(super) const DETECT_MIN_V: f64 = 0.05;
742    /// Loudest — the nominal full scale of quiver audio, so knob 1 is
743    /// "trigger on nothing short of a bare oscillator".
744    pub(super) const DETECT_MAX_V: f64 = 5.0;
745    /// Peak excursion for a `ModulatedParam` port: half of the parameter's
746    /// full normalized range, matching [`PEAK_NORMALIZED`]'s convention — but
747    /// **ten times its volts**, because on this port a normalized unit costs
748    /// 5 V rather than 1. Getting that wrong is the eq's ±1.2 dB bug again,
749    /// with the ducker's depth knob doing nothing across its whole travel.
750    const PEAK_PARAM_CV: f64 = 2.5;
751    /// Peak excursion for a **dynamics threshold** port, in the port's own
752    /// 0..1 CV units — 0.1, i.e. ±0.5 V of detector level. A tenth of full
753    /// scale rather than a half, for the reason on [`mod_depth_detector`].
754    const PEAK_DETECTOR: f64 = 0.1;
755    /// Modulation depth for a ±5 V source (LFO, S&H), expressed as an
756    /// [`quiver::modules::Attenuverter`] level in volts (its gain is
757    /// `level / 5`), so knob 1.0 = ±5 octaves of cutoff.
758    ///
759    /// Every destination this taper reaches is *normalized*, not volt-scaled,
760    /// which is what makes one curve serve all of them: `Svf.fm` sums straight
761    /// into a 0..1 cutoff CV whose full span is 20 Hz–20 kHz (~10 octaves),
762    /// `Wavefolder.threshold` lives in 0.1..1, and the palette's other slots —
763    /// `Wavetable.morph`, `KarplusStrong.damping`, `Distortion.drive`,
764    /// `Bitcrusher.bits`, `Chorus.depth`, `Reverb.size`, `Phaser.depth`,
765    /// `Flanger.depth`, `Tremolo.depth`, `Vibrato.depth`, `FormantOsc.vowel`,
766    /// `Granular.position` — are all 0..1 CVs on the same convention. A raw
767    /// ±5 V cable is ~5× full scale, so every knob position above ~0.2 only
768    /// clipped the modulator harder into a square wave — 97% of the travel did
769    /// nothing.
770    ///
771    /// The two destinations that are *not* normalized get their own tapers
772    /// ([`mod_depth_pitch`], [`mod_depth_gain`]) rather than borrowing this
773    /// one, because "half of full scale" means a different number of volts on
774    /// each of them.
775    ///
776    /// `DelayLine.time` is the one destination whose port is 0..1 but whose
777    /// *musical* range is exponential (1 ms · 2000^cv), so a given depth buys
778    /// far more motion there than anywhere else. That is the classic tape-wow
779    /// gesture rather than a defect, but it is the slot to look at first if a
780    /// dedicated taper is ever wanted.
781    ///
782    /// The taper is deliberately **linear, not square-law**. A square taper
783    /// gives a nicer knob feel, but this function is not only a knob mapping:
784    /// the grammar draws `mod_depth ~ U(0,1)` and the compiler applies the same
785    /// curve, so squaring also reshapes the *evolutionary prior* toward weak
786    /// modulation. Measured over eight seeds of the closed-loop synthetic-taste
787    /// gate, the square taper cost the posterior 0.11 of its correlation with
788    /// ground truth (0.59 vs 0.70) — the pool simply stopped moving enough for
789    /// timbral-movement preferences to be learnable. Linear costs nothing and
790    /// is still perfectly dialable once the 10× scale error is gone: the
791    /// musically useful first ±2 octaves occupy the bottom 40% of the sweep.
792    pub fn mod_depth_bipolar(x: f64) -> f64 {
793        mod_level_bipolar(PEAK_NORMALIZED, x)
794    }
795    /// Modulation depth for a 0–10 V source (the mod envelope, the follower).
796    /// Half the bipolar scale, so both source families reach the same depth at
797    /// the same knob position.
798    pub fn mod_depth_unipolar(x: f64) -> f64 {
799        mod_level_unipolar(PEAK_NORMALIZED, x)
800    }
801    /// Modulation depth for a ±5 V source landing on the **pitch**
802    /// [`Offset`]'s summing input.
803    ///
804    /// That input is V/Oct and the attenuverter's gain is `level / 5`, so a
805    /// ±5 V source arrives at `±level` **volts** — and on a V/Oct wire a volt
806    /// is an octave. The level is therefore numerically the octave depth, and
807    /// knob 1.0 gives **±0.5 octave** (±6 semitones).
808    ///
809    /// That ceiling is a deliberate compromise and reads as one on the knob: a
810    /// musical vibrato is ±50 cents, which sits at ~8% of the sweep — a small
811    /// corner to dial in. Capping tighter would make that corner usable and
812    /// put the other pitch-mod idiom, a mod envelope dropping a note in from
813    /// several semitones above, out of reach entirely. Six semitones covers
814    /// both; the vibrato end is fiddly and the alternative was not having it.
815    ///
816    /// Linear rather than square-law for the reason documented at length on
817    /// [`mod_depth_bipolar`] — the grammar draws `mod_depth ~ U(0,1)`, so the
818    /// curve here is an evolutionary prior and not only a knob feel.
819    ///
820    /// It currently evaluates to the same number as [`mod_depth_bipolar`],
821    /// which is a coincidence of two peaks both being 0.5 and not a shared
822    /// derivation: one is half of a normalized port's full scale, the other is
823    /// half an octave. They are two names so that retuning either cannot
824    /// silently move the other.
825    pub fn mod_depth_pitch(x: f64) -> f64 {
826        mod_level_bipolar(PEAK_PITCH, x)
827    }
828    /// [`mod_depth_pitch`] for a 0–10 V source: an envelope reaching the same
829    /// ±0.5 octave at the same knob position, as one-sided motion.
830    pub fn mod_depth_pitch_unipolar(x: f64) -> f64 {
831        mod_level_unipolar(PEAK_PITCH, x)
832    }
833    /// Modulation depth for a ±5 V source landing on a **±5 V gain** port
834    /// (the EQ's three bands).
835    ///
836    /// Ten times [`mod_depth_bipolar`], and it has to be: the destination is
837    /// volt-scaled, not normalized. quiver reads the band as `cv/5 · 12` dB,
838    /// so the normalized taper's ±0.5 V would be ±1.2 dB at *full* depth —
839    /// around the level JND, i.e. a mod slot that does nothing across its
840    /// whole travel. This reaches the port's own ±5 V, so knob 1.0 is a
841    /// ±12 dB pump on the mid band and the bottom of the sweep is where the
842    /// subtle settings live.
843    pub fn mod_depth_gain(x: f64) -> f64 {
844        mod_level_bipolar(PEAK_GAIN, x)
845    }
846    /// [`mod_depth_gain`] for a 0–10 V source.
847    pub fn mod_depth_gain_unipolar(x: f64) -> f64 {
848        mod_level_unipolar(PEAK_GAIN, x)
849    }
850    /// Modulation depth for a ±5 V source landing on the pitch shifter's
851    /// **semitone** port.
852    ///
853    /// Five times [`mod_depth_bipolar`], because the destination is
854    /// volt-scaled: quiver reads the port as `cv/5 · 24` semitones, so the
855    /// normalized taper's ±0.5 V would be ±2.4 semitones at *full* depth —
856    /// dialable, but it would put the module's whole reason for existing (a
857    /// modulated harmony line, a warble that crosses a semitone) in the top
858    /// fifth of the knob. This reaches [`SHIFT_PEAK_V`], the same half of the
859    /// port the knob owns, so full depth is ±12 semitones and the classic
860    /// slow detune-warble sits around 0.05 rather than under 0.01.
861    pub fn mod_depth_shift(x: f64) -> f64 {
862        mod_level_bipolar(SHIFT_PEAK_V, x)
863    }
864    /// [`mod_depth_shift`] for a 0–10 V source (a mod envelope sweeping a
865    /// note in from up to an octave away, one-sided).
866    pub fn mod_depth_shift_unipolar(x: f64) -> f64 {
867        mod_level_unipolar(SHIFT_PEAK_V, x)
868    }
869    /// Modulation depth for a ±5 V source landing on a `ModulatedParam`
870    /// knob+CV port (the ducker's `amount`).
871    ///
872    /// The parameter is normalized 0..1 like every `DepthScale::Normalized`
873    /// destination, but it is *reached* in volts on a ±5 V scale, so the same
874    /// musical depth costs ten times the attenuverter level. Full depth is
875    /// ±0.5 of the duck amount — half the parameter, matching
876    /// [`PEAK_NORMALIZED`]'s "half of full scale" everywhere else.
877    pub fn mod_depth_param_cv(x: f64) -> f64 {
878        mod_level_bipolar(PEAK_PARAM_CV, x)
879    }
880    /// [`mod_depth_param_cv`] for a 0–10 V source.
881    pub fn mod_depth_param_cv_unipolar(x: f64) -> f64 {
882        mod_level_unipolar(PEAK_PARAM_CV, x)
883    }
884    /// Modulation depth for a ±5 V source landing on a **dynamics threshold**
885    /// port (the compressor's and the gate's).
886    ///
887    /// The one destination where "half of full scale" is the wrong answer in
888    /// the *other* direction. The port spans 0..1 for 0..5 V, but the knob
889    /// reads it geometrically (see [`detector_volts`]) and the settings that
890    /// matter live between 0.05 V and 1 V — so the normalized taper's ±0.5 in
891    /// CV, i.e. ±2.5 V, would hold the threshold pinned at one rail or the
892    /// other for most of every cycle and the module would simply switch on and
893    /// off. [`PEAK_DETECTOR`] is ±0.5 V instead: around a typical setting that
894    /// is a full sweep from "always open" to a dozen dB above the key, and the
895    /// bottom of the knob buys the few-dB movement that reads as breathing.
896    pub fn mod_depth_detector(x: f64) -> f64 {
897        mod_level_bipolar(PEAK_DETECTOR, x)
898    }
899    /// [`mod_depth_detector`] for a 0–10 V source.
900    pub fn mod_depth_detector_unipolar(x: f64) -> f64 {
901        mod_level_unipolar(PEAK_DETECTOR, x)
902    }
903    /// Tempo for a [`quiver::modules::Clock`], which drives the euclidean
904    /// generator and the sample-and-hold op.
905    ///
906    /// The `bpm` port is `CvUnipolar`, and quiver's `voltage_range` for that
907    /// kind is **0–10 V, not 0–1**: `cv_to_bpm` is `20 · 15^(cv/10)`, so the
908    /// raw 0..1 knob would have spanned 20 BPM to 21.4 BPM — a rate control
909    /// with a 7% range, which is the class of defect this file has now found
910    /// four times. `10·x` spans the port's real 20–300 BPM, i.e. 0.33–5 Hz of
911    /// clock, which is the rhythmic band a five-second phrase can show.
912    pub fn clock_rate(x: f64) -> f64 {
913        x.clamp(0.0, 1.0) * CLOCK_CV_FULL_SCALE_V
914    }
915    /// Volts of `bpm` CV that reach the top of quiver's tempo map.
916    const CLOCK_CV_FULL_SCALE_V: f64 = 10.0;
917    /// Euclidean step count: quiver's `2 + (cv·14.99)` restricted to **4..16**
918    /// rather than 2..16.
919    ///
920    /// The two shortest patterns are dropped because they are what makes the
921    /// density knob below un-mappable, not because a two-step rhythm is
922    /// uninteresting: a pattern of two can hold either one pulse or two, and a
923    /// density floor low enough to keep a 1-of-16 rhythm reachable rounds to
924    /// *zero* pulses there. Four is the shortest count at which one CV floor
925    /// serves the whole range, and a 4-step pattern is still a bar of four.
926    pub fn euclid_steps(x: f64) -> f64 {
927        0.14 + 0.86 * x.clamp(0.0, 1.0)
928    }
929    /// Euclidean pulse density, bounded off both degenerate ends **at every
930    /// step count**.
931    ///
932    /// quiver takes `pulses = (cv · steps) as usize`, so a raw knob has two
933    /// dead corners the grammar would otherwise draw: below `1/steps` the
934    /// pattern has no pulses at all and the cable carries a constant 0 V, and
935    /// at exactly 1.0 every step fires and it carries a constant 5 V. The
936    /// first is not a rounding corner — with steps uniform over the range it
937    /// is about one draw in seven.
938    ///
939    /// The floor is `1/4`, the reciprocal of [`euclid_steps`]'s coarsest
940    /// count, so a pulse survives however few steps there are; the ceiling
941    /// leaves at least one rest for the same reason at the other end. Between
942    /// them every setting is a rhythm at every step count: 1..3 of four,
943    /// 4..15 of sixteen.
944    pub fn euclid_pulses(x: f64) -> f64 {
945        0.25 + 0.74 * x.clamp(0.0, 1.0)
946    }
947    /// Slew time for a [`quiver::modules::SlewLimiter`] `rise`/`fall` port.
948    ///
949    /// quiver's own map is `0.001 + cv²·10` seconds, which is already
950    /// square-law — so the musically useful glide times (10 ms to ~1.5 s) all
951    /// live below cv 0.39 and a raw knob would spend three fifths of its
952    /// travel freezing the modulator solid. `0.4·x` puts the whole range on
953    /// the plate: full travel is a 1.6 s glide and a uniform draw averages
954    /// ≈0.4 s, which reads as portamento rather than as a mute.
955    ///
956    /// [`crate::term::ModNode::Rand`]'s own `glide` knob keeps the raw map it
957    /// shipped with — it is a saved-patch parameter, and re-tapering it would
958    /// change how every existing S&H sounds.
959    pub fn slew_time(x: f64) -> f64 {
960        SLEW_MAX_CV * x.clamp(0.0, 1.0)
961    }
962    /// Top of the slew knob, on quiver's `0.001 + cv²·10` s map — 1.6 s.
963    const SLEW_MAX_CV: f64 = 0.4;
964    /// Detune: ±50 cents expressed in V/Oct.
965    pub fn detune_voct(x: f64) -> f64 {
966        (x * 2.0 - 1.0) * (50.0 / 1200.0)
967    }
968    /// Crossfader position: 0..1 → −5..+5 V.
969    pub fn xfade_pos(x: f64) -> f64 {
970        (x * 2.0 - 1.0) * 5.0
971    }
972}
973
974/// What a subtree hands back: mono (`right == None`) or a true stereo pair.
975///
976/// Only [`Reverb`] and [`Chorus`] widen — everything else is a mono processor,
977/// and feeding one a stereo signal downmixes (see [`Compiler::feed`]). Carrying
978/// the pair instead of dropping it is the whole point of having a chorus.
979#[derive(Clone, Copy)]
980struct Sig {
981    left: PortRef,
982    right: Option<PortRef>,
983}
984
985impl Sig {
986    fn mono(port: PortRef) -> Self {
987        Sig {
988            left: port,
989            right: None,
990        }
991    }
992    fn stereo(left: PortRef, right: PortRef) -> Self {
993        Sig {
994            left,
995            right: Some(right),
996        }
997    }
998}
999
1000struct Compiler {
1001    patch: Patch,
1002    pitch_out: PortRef,
1003    gate_out: PortRef,
1004    params: HashMap<String, ParamHandle>,
1005    /// Where each term node's audio leaves it — see [`CompiledVoice::taps`].
1006    /// Recorded by [`Self::build`] as the tree is walked, because that is the
1007    /// only point at which the association between a trace key and the quiver
1008    /// node that *ends* its chain exists: a term node compiles to anywhere
1009    /// between one and half a dozen quiver nodes, and which of them carries
1010    /// the audio out is a fact about the arm that built it.
1011    taps: Vec<(String, PortRef)>,
1012}
1013
1014impl Compiler {
1015    /// Pin the control input `port` on `node` to a constant `value`. Used only
1016    /// for fixed wiring decisions; user knobs go through [`Self::knob`].
1017    ///
1018    /// Implemented as [`Patch::set_param_by_id`]: the override is baked into
1019    /// the port's *default* when quiver builds its routing, so pinning costs
1020    /// no node and no per-sample work. (An earlier version cabled a pooled
1021    /// [`Offset`] node into each site, on the belief that a port default could
1022    /// not be overridden from outside the module; `set_param_by_id` does
1023    /// exactly that, and quiver's gather writes the identical constant either
1024    /// way, so this is bit-exact and strictly cheaper.)
1025    ///
1026    /// The one thing a baked default cannot do is coexist with a cable: gather
1027    /// sums the cables into a patched port and **ignores** its default. So any
1028    /// port that also receives a knob or modulation cable must be pinned by a
1029    /// real cable instead (the wavefolder threshold sums `#thresh` plus its
1030    /// mod source on one port), and [`Self::wire_pitch`] keeps a real
1031    /// [`Offset`] node because it *sums with* the incoming pitch CV rather
1032    /// than replacing an unpatched default.
1033    fn constant(&mut self, value: f64, node: NodeId, port: &str) -> Result<(), PatchError> {
1034        if self.patch.set_param_by_id(node, port, value) {
1035            Ok(())
1036        } else {
1037            Err(PatchError::InvalidPort {
1038                node,
1039                name: Some(port.to_string()),
1040                port: None,
1041                available: Vec::new(),
1042            })
1043        }
1044    }
1045
1046    /// Add a **live** knob: an [`ExternalInput`] whose atomic value the
1047    /// audio thread reads every sample, registered under the knob's trace
1048    /// address. Turning the knob writes the atomic — the sound changes
1049    /// immediately, and all filter/delay state survives.
1050    fn knob(
1051        &mut self,
1052        key: &str,
1053        site: &str,
1054        raw: f64,
1055        pmap: ParamMap,
1056        bipolar: bool,
1057        target: PortRef,
1058    ) -> Result<(), PatchError> {
1059        self.knob_to(key, site, raw, pmap, bipolar, &[target])
1060    }
1061
1062    /// [`Self::knob`] with the same atomic cabled to several ports.
1063    ///
1064    /// One trace site must stay one knob: the S&H glide drives a
1065    /// [`SlewLimiter`]'s `rise` *and* `fall`, and adding a second
1066    /// [`Self::knob`] for the second port would register a second node under
1067    /// the same name and silently overwrite the first's [`ParamHandle`] — the
1068    /// handle the panel then drags would move only half the module.
1069    fn knob_to(
1070        &mut self,
1071        key: &str,
1072        site: &str,
1073        raw: f64,
1074        pmap: ParamMap,
1075        bipolar: bool,
1076        targets: &[PortRef],
1077    ) -> Result<(), PatchError> {
1078        let value = Arc::new(AtomicF64::new(pmap.apply(raw)));
1079        let input = if bipolar {
1080            ExternalInput::cv_bipolar(Arc::clone(&value))
1081        } else {
1082            ExternalInput::cv(Arc::clone(&value))
1083        };
1084        let n = self.patch.add(format!("{key}:{site}!"), input);
1085        for target in targets {
1086            self.patch.connect(n.out("out"), *target)?;
1087        }
1088        self.params
1089            .insert(format!("{key}#{site}"), ParamHandle { value, map: pmap });
1090        Ok(())
1091    }
1092
1093    /// Wire a modulation term into `target`. `ModNode::None` wires nothing.
1094    ///
1095    /// `owner` is the *modulated* module — that is where `describe.rs`
1096    /// advertises the `#mdepth` knob, and the mod source's own nodes and knobs
1097    /// hang off `<owner>/m` (`node/m:lfo`, `node/m#rate`). The slot key is
1098    /// derived here rather than passed in: it is `<owner>/m` at every call
1099    /// site, and a hand-built key that disagrees with `owner` would put the
1100    /// depth knob on one module and the LFO's knobs under another.
1101    ///
1102    /// The depth is an [`Attenuverter`] driven by a real [`Self::knob`], not a
1103    /// baked-in cable attenuation. Turning it used to require a full
1104    /// recompile — a 6 ms fade-out, per-quantum voice rebuild and fade-in for
1105    /// the length of the drag, while every neighbouring knob swept
1106    /// continuously.
1107    ///
1108    /// `owner_input` is the signal the owning module is *about to process*,
1109    /// tapped before it enters. Only [`ModNode::Follow`] reads it, and it is
1110    /// `None` exactly where there is nothing to tap — a source's own mod slot
1111    /// (a wavetable or a pluck generates its input rather than receiving
1112    /// one). That case wires no modulation at all rather than failing: the
1113    /// grammar and the panel can both express "follower on an oscillator",
1114    /// and the honest compilation of it is silence on that cable, not a
1115    /// refusal to compile a term the prior can draw.
1116    ///
1117    /// `scale` says what kind of port `target` is. The source's polarity is
1118    /// decided here, but "how many volts is full depth" is a property of the
1119    /// destination, and the two together pick the taper — see [`DepthScale`].
1120    fn wire_mod(
1121        &mut self,
1122        m: &ModNode,
1123        owner: &str,
1124        depth: f64,
1125        target: PortRef,
1126        owner_input: Option<Sig>,
1127        scale: DepthScale,
1128    ) -> Result<(), PatchError> {
1129        let key = &format!("{owner}/m");
1130        let Some((src, unipolar)) = self.build_mod(m, key, owner_input)? else {
1131            return Ok(());
1132        };
1133        let att = self.patch.add(format!("{key}:depth"), Attenuverter::new());
1134        self.patch.connect(src, att.in_("in"))?;
1135        self.knob(
1136            owner,
1137            "mdepth",
1138            depth,
1139            scale.taper(unipolar),
1140            true,
1141            att.in_("level"),
1142        )?;
1143        self.patch.connect(att.out("out"), target)?;
1144        Ok(())
1145    }
1146
1147    /// Build a modulation term and return `(its output port, whether it swings
1148    /// 0–10 V rather than ±5 V)`, or `None` for a term that produces nothing.
1149    ///
1150    /// This is the recursion [`ModNode`] gained when modulation became a sort:
1151    /// [`ModNode::Op`] builds its subterm first and processes it,
1152    /// [`ModNode::Pair`] builds two. Subterm keys follow the audio tree's
1153    /// convention — `<key>/0` and `<key>/1` — which never collides with an
1154    /// audio node's, because every modulation key sits under a `/m`.
1155    ///
1156    /// # The polarity flag is a *scale* claim, not a sign claim
1157    ///
1158    /// It selects between [`map::mod_level_bipolar`]'s "a ±5 V source arrives
1159    /// at ±level" and [`map::mod_level_unipolar`]'s halving for an 0–10 V one,
1160    /// so what it really asks is **"can this term reach 10 V?"**. A gate
1161    /// reaches 5, so `Euclid` and the logic ops answer *no* even though they
1162    /// never go negative — answering yes would halve their depth for nothing.
1163    /// The shapers pass their subterm's answer through, because none of them
1164    /// changes the magnitude scale: rectifying ±5 V gives 0–5 V, which is
1165    /// still a signal whose extreme is 5 V.
1166    fn build_mod(
1167        &mut self,
1168        m: &ModNode,
1169        key: &str,
1170        owner_input: Option<Sig>,
1171    ) -> Result<Option<(PortRef, bool)>, PatchError> {
1172        let built = match m {
1173            ModNode::None => return Ok(None),
1174            ModNode::Lfo { wave, rate, .. } => {
1175                let lfo = self.patch.add(format!("{key}:lfo"), Lfo::new(self.sr()));
1176                self.knob(key, "rate", *rate, ParamMap::Unit, false, lfo.in_("rate"))?;
1177                (lfo.out(wave.port_name()), false)
1178            }
1179            ModNode::Rand { rate, glide, .. } => {
1180                // S&H burble: white noise sampled on an internal square-LFO
1181                // clock. The knob drives the clock rate.
1182                let clk = self.patch.add(format!("{key}:rclk"), Lfo::new(self.sr()));
1183                self.knob(key, "rate", *rate, ParamMap::Unit, false, clk.in_("rate"))?;
1184                let noise = self
1185                    .patch
1186                    .add(format!("{key}:rnoise"), NoiseGenerator::new());
1187                let snh = self.patch.add(format!("{key}:snh"), SampleAndHold::new());
1188                self.patch.connect(noise.out("white"), snh.in_("in"))?;
1189                self.patch.connect(clk.out("sqr"), snh.in_("trig"))?;
1190                // Glide turns the same source into two different modulators:
1191                // at 0 it is the stepped burble, and as it opens the steps
1192                // become a smooth random walk — the classic sample-and-glide.
1193                // Symmetric (one knob into both `rise` and `fall`) because an
1194                // asymmetric slew on a random signal reads as a *shape*, not
1195                // as glide, and that is a second timbral choice this module
1196                // has no faceplate room to offer.
1197                let slew = self
1198                    .patch
1199                    .add(format!("{key}:glide"), SlewLimiter::new(self.sr()));
1200                self.patch.connect(snh.out("out"), slew.in_("in"))?;
1201                self.knob_to(
1202                    key,
1203                    "glide",
1204                    *glide,
1205                    ParamMap::Unit,
1206                    false,
1207                    &[slew.in_("rise"), slew.in_("fall")],
1208                )?;
1209                (slew.out("out"), false)
1210            }
1211            ModNode::Follow { sens, release, .. } => {
1212                // The tap is the owning module's *own* input, taken before it
1213                // enters — so the follower measures what the module is about
1214                // to process rather than what it produced, which would be a
1215                // feedback loop through the parameter it drives.
1216                let Some(input) = owner_input else {
1217                    return Ok(None);
1218                };
1219                let f = self
1220                    .patch
1221                    .add(format!("{key}:follow"), EnvelopeFollower::new(self.sr()));
1222                self.feed(input, f.in_("in"))?;
1223                self.knob(key, "sens", *sens, ParamMap::Unit, false, f.in_("gain"))?;
1224                self.knob(
1225                    key,
1226                    "rel",
1227                    *release,
1228                    ParamMap::Unit,
1229                    false,
1230                    f.in_("release"),
1231                )?;
1232                self.constant(FOLLOW_ATTACK, f.id(), "attack")?;
1233                // 0–10 V detector output, so it shares the mod envelope's
1234                // taper rather than the bipolar one.
1235                (f.out("out"), true)
1236            }
1237            ModNode::Env { attack, decay, .. } => {
1238                let env = self.patch.add(format!("{key}:env"), Adsr::new(self.sr()));
1239                self.patch.connect(self.gate_out, env.in_("gate"))?;
1240                self.knob(
1241                    key,
1242                    "att",
1243                    *attack,
1244                    ParamMap::Unit,
1245                    false,
1246                    env.in_("attack"),
1247                )?;
1248                self.knob(key, "dec", *decay, ParamMap::Unit, false, env.in_("decay"))?;
1249                // AD shape: no sustain plateau, quick release.
1250                self.constant(0.0, env.id(), "sustain")?;
1251                self.constant(0.1, env.id(), "release")?;
1252                // Exponential contour, as on the amp envelope — a linear filter
1253                // sweep reads as a fader move, not as a decay.
1254                self.constant(GATE_TRUE, env.id(), "shape")?;
1255                (env.out("env"), true)
1256            }
1257            ModNode::Euclid {
1258                rate,
1259                steps,
1260                pulses,
1261                ..
1262            } => {
1263                let clk = self.patch.add(format!("{key}:eclk"), Clock::new(self.sr()));
1264                self.knob(
1265                    key,
1266                    "erate",
1267                    *rate,
1268                    ParamMap::ClockRate,
1269                    false,
1270                    clk.in_("bpm"),
1271                )?;
1272                let eu = self
1273                    .patch
1274                    .add(format!("{key}:euclid"), Euclidean::new(self.sr()));
1275                self.patch.connect(clk.out("out"), eu.in_("clock"))?;
1276                self.knob(
1277                    key,
1278                    "esteps",
1279                    *steps,
1280                    ParamMap::EuclidSteps,
1281                    false,
1282                    eu.in_("steps"),
1283                )?;
1284                self.knob(
1285                    key,
1286                    "epulses",
1287                    *pulses,
1288                    ParamMap::EuclidPulses,
1289                    false,
1290                    eu.in_("pulses"),
1291                )?;
1292                self.constant(EUCLID_ROTATION, eu.id(), "rotation")?;
1293                // `reset` stays unpatched: quiver's gather writes the port's
1294                // own 0 V default, and the pattern is already re-armed by its
1295                // own step counter wrapping. A reset cable would need a
1296                // per-note trigger, and a euclidean pattern that restarts on
1297                // every note is a fixed rhythm rather than a running one.
1298                //
1299                // The sample-and-hold is what turns the pattern into a
1300                // **gate**. quiver's `Euclidean` emits a `Trigger`, and its
1301                // implementation takes that literally: `out` is `GATE_HIGH_V`
1302                // on the single sample the clock's edge lands on and 0 V for
1303                // every other sample of the step. One sample in two thousand
1304                // is inaudible on any destination in this grammar — it is a
1305                // modulator that measures as a dead cable — so the pattern is
1306                // latched on the same clock that produced it. Holding it
1307                // stretches each hit across its whole step, which makes the
1308                // duty cycle `pulses/steps` and the output a real rhythm.
1309                //
1310                // Both modules are edge-triggered from the same clock and the
1311                // hold reads the generator, so quiver's topological order
1312                // evaluates the pattern first and the latch sees the fresh
1313                // step rather than the previous one.
1314                let gate = self.patch.add(format!("{key}:egate"), SampleAndHold::new());
1315                self.patch.connect(eu.out("out"), gate.in_("in"))?;
1316                self.patch.connect(clk.out("out"), gate.in_("trig"))?;
1317                (gate.out("out"), false)
1318            }
1319            ModNode::Op {
1320                kind,
1321                p0,
1322                p1,
1323                input,
1324                ..
1325            } => {
1326                let Some((src, unipolar)) =
1327                    self.build_mod(input, &format!("{key}/0"), owner_input)?
1328                else {
1329                    return Ok(None);
1330                };
1331                self.build_mod_op(*kind, *p0, *p1, key, src, unipolar)?
1332            }
1333            ModNode::Pair { kind, a, b, .. } => {
1334                let (a, b) = (
1335                    self.build_mod(a, &format!("{key}/0"), owner_input)?,
1336                    self.build_mod(b, &format!("{key}/1"), owner_input)?,
1337                );
1338                // A branch that produced nothing collapses to the other one
1339                // rather than to a constant, matching
1340                // [`ModNode::normalized`]. In practice only a `Follow` on a
1341                // source can get here — every other empty branch was already
1342                // folded away — and the honest compilation of "follow an
1343                // oscillator" is the rest of the term, not silence.
1344                match (a, b) {
1345                    (None, None) => return Ok(None),
1346                    (Some(x), None) | (None, Some(x)) => x,
1347                    (Some(a), Some(b)) => self.build_mod_pair(*kind, key, a, b)?,
1348                }
1349            }
1350        };
1351        Ok(Some(built))
1352    }
1353
1354    /// One [`ModOp`] over an already-built modulation signal.
1355    fn build_mod_op(
1356        &mut self,
1357        kind: ModOp,
1358        p0: f64,
1359        p1: f64,
1360        key: &str,
1361        src: PortRef,
1362        unipolar: bool,
1363    ) -> Result<(PortRef, bool), PatchError> {
1364        Ok(match kind {
1365            ModOp::Quantize => {
1366                // Scale into the quantizer's grid and back out again — see
1367                // [`QUANTIZE_IN_LEVEL`], which is where the whole musical
1368                // argument for this module lives.
1369                let a_in = self.patch.add(format!("{key}:qin"), Attenuverter::new());
1370                self.patch.connect(src, a_in.in_("in"))?;
1371                self.constant(QUANTIZE_IN_LEVEL, a_in.id(), "level")?;
1372                let q = self
1373                    .patch
1374                    .add(format!("{key}:quant"), ScaleQuantizer::new(self.sr()));
1375                self.patch.connect(a_in.out("out"), q.in_("in"))?;
1376                self.knob(key, "qroot", p0, ParamMap::Unit, false, q.in_("root"))?;
1377                // Straight through: quiver's own `(cv·6.99) as u8` is the
1378                // seven-way selector, so the knob *is* the categorical and
1379                // `crate::term::quant_scale_index` reads it the same way.
1380                self.knob(key, "qscale", p1, ParamMap::Unit, false, q.in_("scale"))?;
1381                let a_out = self.patch.add(format!("{key}:qout"), Attenuverter::new());
1382                self.patch.connect(q.out("out"), a_out.in_("in"))?;
1383                self.constant(QUANTIZE_OUT_LEVEL, a_out.id(), "level")?;
1384                (a_out.out("out"), unipolar)
1385            }
1386            ModOp::Slew => {
1387                let s = self
1388                    .patch
1389                    .add(format!("{key}:slew"), SlewLimiter::new(self.sr()));
1390                self.patch.connect(src, s.in_("in"))?;
1391                self.knob(key, "rise", p0, ParamMap::SlewTime, false, s.in_("rise"))?;
1392                self.knob(key, "fall", p1, ParamMap::SlewTime, false, s.in_("fall"))?;
1393                (s.out("out"), unipolar)
1394            }
1395            ModOp::Rectify => {
1396                let r = self.patch.add(format!("{key}:rect"), Rectifier::new());
1397                self.patch.connect(src, r.in_("in"))?;
1398                // `mode` is a choice of output *port*, not a CV: quiver's
1399                // `Rectifier` publishes all three at once and has no mode
1400                // input at all. The knob therefore picks a cable, and the
1401                // register of what it picked lives in the plate label.
1402                //
1403                // On a source that never goes negative — a mod envelope, a
1404                // follower, a euclidean gate — `full` and `positive` are both
1405                // the identity and `negative` is silence. That is a real dead
1406                // corner and it is left visible rather than special-cased:
1407                // rectification is a statement about a *bipolar* signal, and
1408                // hiding the fact that it says nothing about a unipolar one
1409                // would make the knob lie in the other direction.
1410                //
1411                // No [`Self::knob`] and so no [`ParamHandle`]: like every
1412                // other enum site in this grammar, turning it is a structural
1413                // change and the host has to recompile (`live::set_param`
1414                // returns false and the panel calls `set_patch`). Registering
1415                // a handle nothing reads would be a knob that drags smoothly
1416                // and never changes the sound.
1417                let port = match rect_mode_index(p0) {
1418                    0 => "full",
1419                    1 => "half_pos",
1420                    _ => "half_neg",
1421                };
1422                (r.out(port), unipolar)
1423            }
1424            ModOp::Hold => {
1425                let clk = self.patch.add(format!("{key}:hclk"), Clock::new(self.sr()));
1426                self.knob(key, "hrate", p0, ParamMap::ClockRate, false, clk.in_("bpm"))?;
1427                let snh = self.patch.add(format!("{key}:hold"), SampleAndHold::new());
1428                self.patch.connect(src, snh.in_("in"))?;
1429                self.patch.connect(clk.out("out"), snh.in_("trig"))?;
1430                (snh.out("out"), unipolar)
1431            }
1432        })
1433    }
1434
1435    /// One [`PairOp`] over two already-built modulation signals.
1436    fn build_mod_pair(
1437        &mut self,
1438        kind: PairOp,
1439        key: &str,
1440        a: (PortRef, bool),
1441        b: (PortRef, bool),
1442    ) -> Result<(PortRef, bool), PatchError> {
1443        // Min, max and the switch hand back one of their inputs, so the pair
1444        // can reach 10 V if either branch can. The logic gates emit a 5 V gate
1445        // whatever they were fed.
1446        let unipolar = a.1 || b.1;
1447        Ok(match kind {
1448            PairOp::Min => {
1449                let n = self.patch.add(format!("{key}:min"), Min::new());
1450                self.patch.connect(a.0, n.in_("a"))?;
1451                self.patch.connect(b.0, n.in_("b"))?;
1452                (n.out("out"), unipolar)
1453            }
1454            PairOp::Max => {
1455                let n = self.patch.add(format!("{key}:max"), Max::new());
1456                self.patch.connect(a.0, n.in_("a"))?;
1457                self.patch.connect(b.0, n.in_("b"))?;
1458                (n.out("out"), unipolar)
1459            }
1460            PairOp::And => {
1461                let n = self.patch.add(format!("{key}:and"), LogicAnd::new());
1462                self.patch.connect(a.0, n.in_("a"))?;
1463                self.patch.connect(b.0, n.in_("b"))?;
1464                (n.out("out"), false)
1465            }
1466            PairOp::Or => {
1467                let n = self.patch.add(format!("{key}:or"), LogicOr::new());
1468                self.patch.connect(a.0, n.in_("a"))?;
1469                self.patch.connect(b.0, n.in_("b"))?;
1470                (n.out("out"), false)
1471            }
1472            PairOp::Xor => {
1473                let n = self.patch.add(format!("{key}:xor"), LogicXor::new());
1474                self.patch.connect(a.0, n.in_("a"))?;
1475                self.patch.connect(b.0, n.in_("b"))?;
1476                (n.out("out"), false)
1477            }
1478            PairOp::Switch => {
1479                // quiver's `VcSwitch` needs a *third* input to choose with,
1480                // and `Pair` has only two branches to offer. The contract
1481                // proposed the voice gate; that is wrong, and measurably so:
1482                // the gate is high for the whole of every note and low only
1483                // between notes, when the VCA is shut — so `b` would be
1484                // selected for every sample anybody hears and the `a` branch
1485                // would be a module on the rack that is never once audible.
1486                //
1487                // `b` is its own control instead: the switch passes `b` while
1488                // `b` is above the 2.5 V gate threshold and `a` the rest of
1489                // the time. That makes `Switch(pad, euclid)` "punch this
1490                // rhythm in over that modulator", which is what the module is
1491                // for, and every branch is heard.
1492                let n = self.patch.add(format!("{key}:sw"), VcSwitch::new());
1493                self.patch.connect(a.0, n.in_("a"))?;
1494                self.patch.connect(b.0, n.in_("b"))?;
1495                self.patch.connect(b.0, n.in_("cv"))?;
1496                (n.out("out"), unipolar)
1497            }
1498        })
1499    }
1500
1501    /// Feed a subtree's output into a mono input. A stereo pair is summed at
1502    /// −6 dB per side (two cables into one input sum in quiver's gather), which
1503    /// is the level-preserving downmix for the correlated dry path.
1504    fn feed(&mut self, sig: Sig, target: PortRef) -> Result<(), PatchError> {
1505        match sig.right {
1506            None => {
1507                self.patch.connect(sig.left, target)?;
1508            }
1509            Some(r) => {
1510                self.patch.connect_attenuated(sig.left, target, 0.5)?;
1511                self.patch.connect_attenuated(r, target, 0.5)?;
1512            }
1513        }
1514        Ok(())
1515    }
1516
1517    /// Block DC ahead of the VCA, using an [`Svf`] highpass parked at its
1518    /// lowest corner.
1519    ///
1520    /// `DiodeLadderFilter::diode_sat` is asymmetric by design (`tanh(1.2x)` up,
1521    /// `tanh(0.8x)` down) and audio reaches it at nominal ±5 V, so it emits real
1522    /// DC. Blocking downstream of the VCA would be too late: the amp envelope
1523    /// has already multiplied that offset into a per-note thump whose spectrum
1524    /// reaches far above the offset itself. Removing it first means the thump is
1525    /// never created.
1526    ///
1527    /// An `x[n] − x[n−1] + R·y[n−1]` one-pole at 5 Hz, assembled from a `Mixer`
1528    /// and two `UnitDelay`s, is the textbook answer and measures correctly — but
1529    /// its state is not sanitized, so after a note ends it rings on as a smooth
1530    /// sub-audio decay that never reaches zero. That residue is inaudible and
1531    /// harmless to play, and *ruinous* to the feature extractor: spectral
1532    /// flatness is a geometric mean, and a tail of near-DC frames drags the
1533    /// phrase mean down by two orders of magnitude, which would silently
1534    /// corrupt every preference observation. `Svf` flushes its state, so its
1535    /// tail lands on exact zero.
1536    fn dc_blocker(&mut self, name: &str, input: PortRef) -> Result<PortRef, PatchError> {
1537        let f = self.patch.add(format!("{name}:hp"), Svf::new(self.sr()));
1538        self.patch.connect(input, f.in_("in"))?;
1539        self.constant(DC_BLOCK_CUTOFF, f.id(), "cutoff")?;
1540        self.constant(0.0, f.id(), "res")?;
1541        Ok(f.out("hp"))
1542    }
1543
1544    /// Build one channel of the mandatory voice tail:
1545    /// `input → DC blocker → VCA → Limiter`. Called twice when the tree ends
1546    /// in a stereo module, sharing the one amp envelope.
1547    fn voice_tail(
1548        &mut self,
1549        side: &str,
1550        input: PortRef,
1551        env: PortRef,
1552        block_dc: bool,
1553    ) -> Result<PortRef, PatchError> {
1554        let blocked = if block_dc {
1555            self.dc_blocker(&format!("voice:dc{side}"), input)?
1556        } else {
1557            input
1558        };
1559
1560        let vca = self.patch.add(format!("voice:vca{side}"), Vca::new());
1561        self.patch.connect(blocked, vca.in_("in"))?;
1562        self.patch.connect(env, vca.in_("cv"))?;
1563        // Exponential response. A linear VCA fed a linear envelope loses its
1564        // last 20 dB in the final instant of the decay, which is why the
1565        // instrument read as "wrong" before anyone could name why.
1566        self.constant(GATE_TRUE, vca.id(), "response")?;
1567
1568        let limiter = self
1569            .patch
1570            .add(format!("voice:limiter{side}"), Limiter::new(self.sr()));
1571        self.patch.connect(vca.out("out"), limiter.in_("in"))?;
1572        // A safety net, not a tone stage. Three things had to change together:
1573        // the threshold is 1.0 (= 5 V, the top of nominal quiver audio) rather
1574        // than the 0.8 default, which put every patch permanently inside the
1575        // knee; `soft` is off, so there is no continuous tanh shaping with a
1576        // release that pumps against the amp envelope; and the sidechain is
1577        // patched from the same signal, because quiver's gather writes the
1578        // port default (0 V) to an unpatched input, so the detector was
1579        // reading silence and the stage was in fact a bare hard clipper at
1580        // 4 V. Real limiting lives on the master bus, across the voice sum.
1581        self.constant(1.0, limiter.id(), "threshold")?;
1582        self.constant(GATE_FALSE, limiter.id(), "soft")?;
1583        self.patch
1584            .connect(vca.out("out"), limiter.in_("sidechain"))?;
1585        Ok(limiter.out("out"))
1586    }
1587
1588    /// Route pitch (plus a per-source V/Oct offset) into a `voct` input.
1589    ///
1590    /// Returns the [`Offset`]'s own `in` port — the grammar's one **pitch
1591    /// modulation** site. quiver's gather sums every cable into a patched
1592    /// input, so a second cable here adds to the incoming keyboard CV rather
1593    /// than replacing it, which is precisely why this stage is a real node and
1594    /// not a baked default (see [`Self::constant`]). A volt on that wire is an
1595    /// octave, so what arrives is transposition: vibrato, or a pitch envelope.
1596    ///
1597    /// A third cable lands here too, and it is the reason `oct` is a live
1598    /// site: an [`ExternalInput`] carrying an **octave trim**, zero at compile
1599    /// time, that the panel writes when the octave chip is cycled. Changing
1600    /// the octave used to be a recompile — a fade-out, a per-quantum voice
1601    /// rebuild and a re-attack of every held note, for a number that is one
1602    /// addition on a CV wire. See [`ParamMap::OctaveTrim`] for why the live
1603    /// value is a *trim* rather than the octave itself; the short version is
1604    /// that a trim of `+0.0` is the additive identity in the gather sum and an
1605    /// absolute octave is not, so this costs a node and no samples.
1606    fn wire_pitch(
1607        &mut self,
1608        key: &str,
1609        octave: i8,
1610        detune: f64,
1611        target: PortRef,
1612    ) -> Result<PortRef, PatchError> {
1613        let offset = octave as f64 + map::detune_voct(detune);
1614        let node = self.patch.add(format!("{key}:pitch"), Offset::new(offset));
1615        self.patch.connect(self.pitch_out, node.in_("in"))?;
1616        self.patch.connect(node.out("out"), target)?;
1617        self.knob(
1618            key,
1619            "oct",
1620            (octave + 2) as f64,
1621            ParamMap::OctaveTrim(octave),
1622            // The trim is signed, and the port is `CvBipolar`; a unipolar
1623            // `ExternalInput` here would be a validation warning on every
1624            // source in every patch.
1625            true,
1626            node.in_("in"),
1627        )?;
1628        Ok(node.in_("in"))
1629    }
1630
1631    fn sr(&self) -> f64 {
1632        self.patch.sample_rate()
1633    }
1634
1635    /// Build the audio subtree rooted at `node`; returns its output signal.
1636    /// Build `node` and record where its audio came out.
1637    ///
1638    /// A wrapper rather than a line in each of the thirty-odd arms of
1639    /// [`Self::build_node`]: the tap is the same fact for every kind — the
1640    /// `Sig` the arm returned — and writing it once means a new production
1641    /// cannot forget to. The left channel is the tap for a stereo `Sig`;
1642    /// metering both would report a width, not a level, and the flow
1643    /// animation asks how much signal is on the wire.
1644    fn build(&mut self, node: &AudioNode, key: &str) -> Result<Sig, PatchError> {
1645        let sig = self.build_node(node, key)?;
1646        self.taps.push((key.to_string(), sig.left));
1647        Ok(sig)
1648    }
1649
1650    fn build_node(&mut self, node: &AudioNode, key: &str) -> Result<Sig, PatchError> {
1651        match node {
1652            AudioNode::Vco {
1653                wave,
1654                octave,
1655                detune,
1656                mod_depth,
1657                modulation,
1658                ..
1659            } => {
1660                let vco = self.patch.add(format!("{key}:vco"), Vco::new(self.sr()));
1661                let pitch_in = self.wire_pitch(key, *octave, *detune, vco.in_("voct"))?;
1662                // The mod cable joins the keyboard CV at the pitch offset, not
1663                // at the oscillator: everything downstream of the summing node
1664                // sees one V/Oct signal, so vibrato and transposition are the
1665                // same mechanism and cannot disagree.
1666                self.wire_mod(
1667                    modulation,
1668                    key,
1669                    *mod_depth,
1670                    pitch_in,
1671                    // A source generates its own input, so there is nothing
1672                    // for a follower to tap — as on the wavetable and pluck.
1673                    None,
1674                    DepthScale::Pitch,
1675                )?;
1676                Ok(Sig::mono(vco.out(wave.port_name())))
1677            }
1678            AudioNode::Supersaw {
1679                octave,
1680                detune,
1681                mix,
1682                mod_depth,
1683                modulation,
1684                ..
1685            } => {
1686                let saw = self
1687                    .patch
1688                    .add(format!("{key}:supersaw"), Supersaw::new(self.sr()));
1689                let pitch_in = self.wire_pitch(key, *octave, 0.5, saw.in_("voct"))?;
1690                self.wire_mod(
1691                    modulation,
1692                    key,
1693                    *mod_depth,
1694                    pitch_in,
1695                    None,
1696                    DepthScale::Pitch,
1697                )?;
1698                self.knob(
1699                    key,
1700                    "det",
1701                    *detune,
1702                    ParamMap::Unit,
1703                    false,
1704                    saw.in_("detune"),
1705                )?;
1706                self.knob(key, "smix", *mix, ParamMap::Unit, false, saw.in_("mix"))?;
1707                Ok(Sig::mono(saw.out("out")))
1708            }
1709            AudioNode::Noise { color, .. } => {
1710                let noise = self
1711                    .patch
1712                    .add(format!("{key}:noise"), NoiseGenerator::new());
1713                Ok(Sig::mono(noise.out(color.port_name())))
1714            }
1715            // A `Vca` with nothing patched into its audio input, which is a
1716            // constant zero: quiver reads an unpatched port as 0.0, and the
1717            // gain that multiplies it cannot make it anything else.
1718            //
1719            // Chosen over the obvious `Offset::new(0.0)` because `Offset`'s
1720            // ports are `CvBipolar`, so feeding one to an audio consumer would
1721            // raise an Audio/CV signal-kind warning on every patch holding a
1722            // hole. `Vca` is `Audio` in and `Audio` out, so this production
1723            // adds no cable, no warning, and no new class to the allowlist in
1724            // `every_prior_sample_compiles`.
1725            AudioNode::Silence { .. } => {
1726                let z = self.patch.add(format!("{key}:silence"), Vca::new());
1727                Ok(Sig::mono(z.out("out")))
1728            }
1729            AudioNode::Wavetable {
1730                table,
1731                octave,
1732                morph,
1733                mod_depth,
1734                modulation,
1735                ..
1736            } => {
1737                let wt = self
1738                    .patch
1739                    .add(format!("{key}:wavetable"), Wavetable::new(self.sr()));
1740                // Detune 0.5 is the no-offset centre of `map::detune_voct`:
1741                // this module has no detune site, but pitch still goes
1742                // through the same Offset every other source uses.
1743                self.wire_pitch(key, *octave, 0.5, wt.in_("v_oct"))?;
1744                // The table is an enum site, and it used to be a baked default
1745                // on the reasoning that "changing it is a recompile either
1746                // way". That reasoning was circular: it was a recompile
1747                // *because* it was baked. The port is a crossfade position —
1748                // see [`map::table_cv`], which exists entirely to explain that
1749                // — so a live write does not switch tables, it **morphs**
1750                // between them, over the live path's own ~25 ms smoothing
1751                // ramp. This is the one control the module is named for, and
1752                // it was the only one that cost a fade-out, a voice rebuild
1753                // and a re-attack of every held note.
1754                //
1755                // Bit-exact with the constant it replaces: quiver writes an
1756                // unpatched port's default and sums a patched one's cables
1757                // starting from `+0.0`, and `0.0 + table_cv` is `table_cv`.
1758                self.knob(
1759                    key,
1760                    "table",
1761                    table.index() as f64,
1762                    ParamMap::TableIndex,
1763                    false,
1764                    wt.in_("table"),
1765                )?;
1766                // No hard sync: the grammar has no second oscillator to sync
1767                // *to*, and quiver retriggers phase on any positive edge, so
1768                // an unpinned Gate-kind port would be one stray cable away
1769                // from turning the oscillator into a buzz.
1770                self.constant(0.0, wt.id(), "sync")?;
1771                self.knob(key, "morph", *morph, ParamMap::Unit, false, wt.in_("morph"))?;
1772                self.wire_mod(
1773                    modulation,
1774                    key,
1775                    *mod_depth,
1776                    wt.in_("morph"),
1777                    None,
1778                    DepthScale::Normalized,
1779                )?;
1780                Ok(Sig::mono(wt.out("out")))
1781            }
1782            AudioNode::Pluck {
1783                octave,
1784                damping,
1785                brightness,
1786                mod_depth,
1787                modulation,
1788                ..
1789            } => {
1790                let ks = self
1791                    .patch
1792                    .add(format!("{key}:pluck"), KarplusStrong::new(self.sr()));
1793                self.wire_pitch(key, *octave, 0.5, ks.in_("voct"))?;
1794                // The note gate is the pluck. quiver edge-detects it, so a
1795                // held note excites the string exactly once and then rings —
1796                // which is why this source ignores the amp envelope's sustain
1797                // in a way no other source does.
1798                self.patch.connect(self.gate_out, ks.in_("trigger"))?;
1799                self.knob(
1800                    key,
1801                    "damp",
1802                    *damping,
1803                    ParamMap::Unit,
1804                    false,
1805                    ks.in_("damping"),
1806                )?;
1807                self.knob(
1808                    key,
1809                    "bright",
1810                    *brightness,
1811                    ParamMap::Unit,
1812                    false,
1813                    ks.in_("brightness"),
1814                )?;
1815                // No inharmonicity. `stretch` detunes the string's partials
1816                // away from the harmonic series, and quiver applies it as a
1817                // one-pole allpass inside the feedback loop, so it also moves
1818                // the pitch — a fifth knob that makes the module play out of
1819                // tune is not the fifth knob to have.
1820                self.constant(0.0, ks.id(), "stretch")?;
1821                self.wire_mod(
1822                    modulation,
1823                    key,
1824                    *mod_depth,
1825                    ks.in_("damping"),
1826                    None,
1827                    DepthScale::Normalized,
1828                )?;
1829                Ok(Sig::mono(ks.out("out")))
1830            }
1831            AudioNode::Formant {
1832                vowel,
1833                shift,
1834                octave,
1835                mod_depth,
1836                modulation,
1837                ..
1838            } => {
1839                let fo = self
1840                    .patch
1841                    .add(format!("{key}:formant"), FormantOsc::new(self.sr()));
1842                // Detune 0.5 is `map::detune_voct`'s no-offset centre: this
1843                // module has no detune site, but its pitch still goes through
1844                // the same Offset as every other source.
1845                self.wire_pitch(key, *octave, 0.5, fo.in_("v_oct"))?;
1846                self.knob(key, "vowel", *vowel, ParamMap::Unit, false, fo.in_("vowel"))?;
1847                self.knob(
1848                    key,
1849                    "fshift",
1850                    *shift,
1851                    ParamMap::FormantShift,
1852                    true,
1853                    fo.in_("formant_shift"),
1854                )?;
1855                self.constant(FORMANT_VIBRATO, fo.id(), "vibrato")?;
1856                // The vowel knob and the mod cable sum on one port, as on the
1857                // wavefolder threshold.
1858                self.wire_mod(
1859                    modulation,
1860                    key,
1861                    *mod_depth,
1862                    fo.in_("vowel"),
1863                    None,
1864                    DepthScale::Normalized,
1865                )?;
1866                Ok(Sig::mono(fo.out("out")))
1867            }
1868            AudioNode::Mix { balance, a, b, .. } => {
1869                let a_out = self.build(a, &format!("{key}/0"))?;
1870                let b_out = self.build(b, &format!("{key}/1"))?;
1871                let xf = self.patch.add(format!("{key}:mix"), Crossfader::new());
1872                self.feed(a_out, xf.in_("a"))?;
1873                self.feed(b_out, xf.in_("b"))?;
1874                self.knob(
1875                    key,
1876                    "bal",
1877                    *balance,
1878                    ParamMap::XfadePos,
1879                    true,
1880                    xf.in_("pos"),
1881                )?;
1882                Ok(Sig::mono(xf.out("out")))
1883            }
1884            AudioNode::Filter {
1885                kind,
1886                cutoff,
1887                resonance,
1888                mod_depth,
1889                input,
1890                modulation,
1891                ..
1892            } => {
1893                let in_out = self.build(input, &format!("{key}/0"))?;
1894                let (filt, out_port) = match kind {
1895                    FilterKind::SvfLp | FilterKind::SvfBp | FilterKind::SvfHp => {
1896                        let f = self.patch.add(format!("{key}:svf"), Svf::new(self.sr()));
1897                        let port = match kind {
1898                            FilterKind::SvfLp => "lp",
1899                            FilterKind::SvfBp => "bp",
1900                            _ => "hp",
1901                        };
1902                        (f, port)
1903                    }
1904                    FilterKind::Ladder => {
1905                        let f = self
1906                            .patch
1907                            .add(format!("{key}:ladder"), DiodeLadderFilter::new(self.sr()));
1908                        (f, "out")
1909                    }
1910                };
1911                self.feed(in_out, filt.in_("in"))?;
1912                self.knob(
1913                    key,
1914                    "cut",
1915                    *cutoff,
1916                    ParamMap::Unit,
1917                    false,
1918                    filt.in_("cutoff"),
1919                )?;
1920                self.knob(
1921                    key,
1922                    "res",
1923                    *resonance,
1924                    ParamMap::Resonance,
1925                    false,
1926                    filt.in_("res"),
1927                )?;
1928                // Keyboard tracking. Without it `keytrack_amt` stays at
1929                // quiver's 0.0 default and the corner never moves: a patch
1930                // dialled in at cutoff 0.3 (≈159 Hz) speaks at C3 and is gone
1931                // by C6. Half-tracking keeps the timbre recognisable across the
1932                // keyboard without following pitch so exactly that the filter
1933                // stops colouring anything.
1934                self.patch.connect(self.pitch_out, filt.in_("keytrack"))?;
1935                self.constant(KEYTRACK_AMT, filt.id(), "keytrack_amt")?;
1936                self.wire_mod(
1937                    modulation,
1938                    key,
1939                    *mod_depth,
1940                    filt.in_("fm"),
1941                    Some(in_out),
1942                    DepthScale::Normalized,
1943                )?;
1944                Ok(Sig::mono(filt.out(out_port)))
1945            }
1946            AudioNode::Fold {
1947                threshold,
1948                mod_depth,
1949                input,
1950                modulation,
1951                ..
1952            } => {
1953                let in_out = self.build(input, &format!("{key}/0"))?;
1954                let fold = self.patch.add(
1955                    format!("{key}:fold"),
1956                    Wavefolder::new(map::fold_threshold(*threshold)),
1957                );
1958                self.feed(in_out, fold.in_("in"))?;
1959                // `#thresh` is a live knob cabled into port 1, *not* the
1960                // constructor argument. `Wavefolder::new` only sets that port's
1961                // default, and quiver's gather ignores a default the moment any
1962                // cable arrives — so as soon as `wire_mod` patched the fold, the
1963                // threshold knob went silently dead. Two cables into one input
1964                // sum, which is exactly the offset-plus-modulation the module
1965                // has no dedicated port for.
1966                self.knob(
1967                    key,
1968                    "thresh",
1969                    *threshold,
1970                    ParamMap::FoldThreshold,
1971                    false,
1972                    fold.in_("threshold"),
1973                )?;
1974                self.wire_mod(
1975                    modulation,
1976                    key,
1977                    *mod_depth,
1978                    fold.in_("threshold"),
1979                    Some(in_out),
1980                    DepthScale::Normalized,
1981                )?;
1982                Ok(Sig::mono(fold.out("out")))
1983            }
1984            AudioNode::Delay {
1985                time,
1986                feedback,
1987                mix,
1988                mod_depth,
1989                input,
1990                modulation,
1991                ..
1992            } => {
1993                let in_out = self.build(input, &format!("{key}/0"))?;
1994                let dl = self
1995                    .patch
1996                    .add(format!("{key}:delay"), DelayLine::new(self.sr()));
1997                self.feed(in_out, dl.in_("in"))?;
1998                self.knob(key, "time", *time, ParamMap::Unit, false, dl.in_("time"))?;
1999                self.knob(
2000                    key,
2001                    "fb",
2002                    *feedback,
2003                    ParamMap::Feedback,
2004                    false,
2005                    dl.in_("feedback"),
2006                )?;
2007                self.knob(key, "dmix", *mix, ParamMap::Unit, false, dl.in_("mix"))?;
2008                // Modulating delay time is the only way this grammar reaches
2009                // tape wow, flange and doppler smear; the knob and the mod
2010                // cable sum on one port, as on the wavefolder.
2011                self.wire_mod(
2012                    modulation,
2013                    key,
2014                    *mod_depth,
2015                    dl.in_("time"),
2016                    Some(in_out),
2017                    DepthScale::Normalized,
2018                )?;
2019                Ok(Sig::mono(dl.out("out")))
2020            }
2021            AudioNode::Chorus {
2022                rate,
2023                depth,
2024                mix,
2025                mod_depth,
2026                input,
2027                modulation,
2028                ..
2029            } => {
2030                let in_out = self.build(input, &format!("{key}/0"))?;
2031                let ch = self
2032                    .patch
2033                    .add(format!("{key}:chorus"), Chorus::new(self.sr()));
2034                self.feed(in_out, ch.in_("in"))?;
2035                self.knob(key, "crate", *rate, ParamMap::Unit, false, ch.in_("rate"))?;
2036                self.knob(
2037                    key,
2038                    "cdepth",
2039                    *depth,
2040                    ParamMap::Unit,
2041                    false,
2042                    ch.in_("depth"),
2043                )?;
2044                self.knob(key, "cmix", *mix, ParamMap::Unit, false, ch.in_("mix"))?;
2045                self.wire_mod(
2046                    modulation,
2047                    key,
2048                    *mod_depth,
2049                    ch.in_("depth"),
2050                    Some(in_out),
2051                    DepthScale::Normalized,
2052                )?;
2053                // Width is the entire reason a chorus exists; port 10 (`out`)
2054                // is the mono sum of the two voices and throws it away.
2055                Ok(Sig::stereo(ch.out("left"), ch.out("right")))
2056            }
2057            AudioNode::Reverb {
2058                size,
2059                damp,
2060                mix,
2061                mod_depth,
2062                input,
2063                modulation,
2064                ..
2065            } => {
2066                let in_out = self.build(input, &format!("{key}/0"))?;
2067                let rv = self
2068                    .patch
2069                    .add(format!("{key}:reverb"), Reverb::new(self.sr()));
2070                self.feed(in_out, rv.in_("in"))?;
2071                self.knob(key, "rsize", *size, ParamMap::Unit, false, rv.in_("size"))?;
2072                self.knob(
2073                    key,
2074                    "rdamp",
2075                    *damp,
2076                    ParamMap::Unit,
2077                    false,
2078                    rv.in_("damping"),
2079                )?;
2080                self.knob(key, "rmix", *mix, ParamMap::Unit, false, rv.in_("mix"))?;
2081                self.wire_mod(
2082                    modulation,
2083                    key,
2084                    *mod_depth,
2085                    rv.in_("size"),
2086                    Some(in_out),
2087                    DepthScale::Normalized,
2088                )?;
2089                // The decorrelation between the two tanks *is* the reverb.
2090                Ok(Sig::stereo(rv.out("left"), rv.out("right")))
2091            }
2092            AudioNode::Distortion {
2093                drive,
2094                tone,
2095                mode,
2096                mod_depth,
2097                input,
2098                modulation,
2099                ..
2100            } => {
2101                let in_out = self.build(input, &format!("{key}/0"))?;
2102                let ds = self
2103                    .patch
2104                    .add(format!("{key}:dist"), Distortion::new(self.sr()));
2105                self.feed(in_out, ds.in_("in"))?;
2106                self.knob(key, "drive", *drive, ParamMap::Unit, false, ds.in_("drive"))?;
2107                self.knob(key, "tone", *tone, ParamMap::Unit, false, ds.in_("tone"))?;
2108                self.constant(map::drive_mode_cv(mode.index()), ds.id(), "mode")?;
2109                // Fully wet. quiver's `mix` blends the shaped signal back
2110                // against the dry one, which is a *second* wet/dry control on
2111                // top of whatever mixer the patch already has — and at low
2112                // drive the module is nearly transparent anyway, so the knob
2113                // would spend most of its travel duplicating `#drive`.
2114                self.constant(1.0, ds.id(), "mix")?;
2115                self.wire_mod(
2116                    modulation,
2117                    key,
2118                    *mod_depth,
2119                    ds.in_("drive"),
2120                    Some(in_out),
2121                    DepthScale::Normalized,
2122                )?;
2123                Ok(Sig::mono(ds.out("out")))
2124            }
2125            AudioNode::Bitcrush {
2126                bits,
2127                downsample,
2128                mod_depth,
2129                input,
2130                modulation,
2131                ..
2132            } => {
2133                let in_out = self.build(input, &format!("{key}/0"))?;
2134                let bc = self.patch.add(format!("{key}:crush"), Bitcrusher::new());
2135                self.feed(in_out, bc.in_("in"))?;
2136                self.knob(key, "bits", *bits, ParamMap::Unit, false, bc.in_("bits"))?;
2137                self.knob(
2138                    key,
2139                    "dsamp",
2140                    *downsample,
2141                    ParamMap::Unit,
2142                    false,
2143                    bc.in_("downsample"),
2144                )?;
2145                self.wire_mod(
2146                    modulation,
2147                    key,
2148                    *mod_depth,
2149                    bc.in_("bits"),
2150                    Some(in_out),
2151                    DepthScale::Normalized,
2152                )?;
2153                Ok(Sig::mono(bc.out("out")))
2154            }
2155            AudioNode::Phaser {
2156                rate,
2157                depth,
2158                feedback,
2159                mod_depth,
2160                input,
2161                modulation,
2162                ..
2163            } => {
2164                let in_out = self.build(input, &format!("{key}/0"))?;
2165                let ph = self
2166                    .patch
2167                    .add(format!("{key}:phaser"), Phaser::new(self.sr()));
2168                self.feed(in_out, ph.in_("in"))?;
2169                self.knob(key, "prate", *rate, ParamMap::Unit, false, ph.in_("rate"))?;
2170                self.knob(
2171                    key,
2172                    "pdepth",
2173                    *depth,
2174                    ParamMap::Unit,
2175                    false,
2176                    ph.in_("depth"),
2177                )?;
2178                self.knob(
2179                    key,
2180                    "pfb",
2181                    *feedback,
2182                    ParamMap::FeedbackBipolar,
2183                    true,
2184                    ph.in_("feedback"),
2185                )?;
2186                self.constant(PHASER_STAGES, ph.id(), "stages")?;
2187                self.constant(PHASER_SPREAD, ph.id(), "spread")?;
2188                self.constant(PHASER_MIX, ph.id(), "mix")?;
2189                self.wire_mod(
2190                    modulation,
2191                    key,
2192                    *mod_depth,
2193                    ph.in_("depth"),
2194                    Some(in_out),
2195                    DepthScale::Normalized,
2196                )?;
2197                // Ports 11/12 are the spread pair; port 10 is the mono sweep
2198                // and discards the decorrelation `spread` exists to create.
2199                Ok(Sig::stereo(ph.out("left"), ph.out("right")))
2200            }
2201            AudioNode::Flanger {
2202                rate,
2203                depth,
2204                feedback,
2205                mod_depth,
2206                input,
2207                modulation,
2208                ..
2209            } => {
2210                let in_out = self.build(input, &format!("{key}/0"))?;
2211                let fl = self
2212                    .patch
2213                    .add(format!("{key}:flanger"), Flanger::new(self.sr()));
2214                self.feed(in_out, fl.in_("in"))?;
2215                self.knob(key, "frate", *rate, ParamMap::Unit, false, fl.in_("rate"))?;
2216                self.knob(
2217                    key,
2218                    "fdepth",
2219                    *depth,
2220                    ParamMap::Unit,
2221                    false,
2222                    fl.in_("depth"),
2223                )?;
2224                // A `CvBipolar` port, exactly as on the phaser: negative
2225                // feedback deepens the notches, positive one sharpens the
2226                // peaks, and knob centre is neither.
2227                self.knob(
2228                    key,
2229                    "ffb",
2230                    *feedback,
2231                    ParamMap::FeedbackBipolar,
2232                    true,
2233                    fl.in_("feedback"),
2234                )?;
2235                self.constant(FLANGER_MIX, fl.id(), "mix")?;
2236                self.constant(FLANGER_SPREAD, fl.id(), "spread")?;
2237                self.wire_mod(
2238                    modulation,
2239                    key,
2240                    *mod_depth,
2241                    fl.in_("depth"),
2242                    Some(in_out),
2243                    DepthScale::Normalized,
2244                )?;
2245                // Ports 11/12 are the spread pair; port 10 is bit-identical to
2246                // `left` and throws the decorrelation away.
2247                Ok(Sig::stereo(fl.out("left"), fl.out("right")))
2248            }
2249            AudioNode::Tremolo {
2250                rate,
2251                depth,
2252                shape,
2253                mod_depth,
2254                input,
2255                modulation,
2256                ..
2257            } => {
2258                let in_out = self.build(input, &format!("{key}/0"))?;
2259                let tr = self
2260                    .patch
2261                    .add(format!("{key}:tremolo"), Tremolo::new(self.sr()));
2262                self.feed(in_out, tr.in_("in"))?;
2263                self.knob(key, "trate", *rate, ParamMap::Unit, false, tr.in_("rate"))?;
2264                self.knob(
2265                    key,
2266                    "tdepth",
2267                    *depth,
2268                    ParamMap::Unit,
2269                    false,
2270                    tr.in_("depth"),
2271                )?;
2272                // Sine at 0, triangle at 1 — the difference between a breathing
2273                // amplitude and a stepped one at the same rate.
2274                self.knob(
2275                    key,
2276                    "tshape",
2277                    *shape,
2278                    ParamMap::Unit,
2279                    false,
2280                    tr.in_("shape"),
2281                )?;
2282                self.wire_mod(
2283                    modulation,
2284                    key,
2285                    *mod_depth,
2286                    tr.in_("depth"),
2287                    Some(in_out),
2288                    DepthScale::Normalized,
2289                )?;
2290                Ok(Sig::mono(tr.out("out")))
2291            }
2292            AudioNode::Vibrato {
2293                rate,
2294                depth,
2295                mix,
2296                mod_depth,
2297                input,
2298                modulation,
2299                ..
2300            } => {
2301                let in_out = self.build(input, &format!("{key}/0"))?;
2302                let vb = self
2303                    .patch
2304                    .add(format!("{key}:vibrato"), Vibrato::new(self.sr()));
2305                self.feed(in_out, vb.in_("in"))?;
2306                self.knob(key, "vrate", *rate, ParamMap::Unit, false, vb.in_("rate"))?;
2307                self.knob(
2308                    key,
2309                    "vdepth",
2310                    *depth,
2311                    ParamMap::Unit,
2312                    false,
2313                    vb.in_("depth"),
2314                )?;
2315                // Kept as a knob rather than pinned wet, because the whole
2316                // travel is musical — it is just that the interesting half is
2317                // the top. Below ~0.7 the dry copy beats against the shifted
2318                // one and the module becomes a chorus, which is a different
2319                // module in this palette.
2320                self.knob(key, "vmix", *mix, ParamMap::Unit, false, vb.in_("mix"))?;
2321                self.wire_mod(
2322                    modulation,
2323                    key,
2324                    *mod_depth,
2325                    vb.in_("depth"),
2326                    Some(in_out),
2327                    DepthScale::Normalized,
2328                )?;
2329                Ok(Sig::mono(vb.out("out")))
2330            }
2331            AudioNode::Eq {
2332                low,
2333                mid,
2334                high,
2335                mod_depth,
2336                input,
2337                modulation,
2338                ..
2339            } => {
2340                let in_out = self.build(input, &format!("{key}/0"))?;
2341                let eq = self
2342                    .patch
2343                    .add(format!("{key}:eq"), ParametricEq::new(self.sr()));
2344                self.feed(in_out, eq.in_("in"))?;
2345                // All three bands are bipolar ±5 V ports read as `cv/5 · 12`
2346                // dB, so knob centre has to be 0 dB — a tone control whose
2347                // home position colours the sound is a tone control nobody can
2348                // reason about.
2349                self.knob(
2350                    key,
2351                    "low",
2352                    *low,
2353                    ParamMap::GainBipolar,
2354                    true,
2355                    eq.in_("low_gain"),
2356                )?;
2357                self.knob(
2358                    key,
2359                    "mid",
2360                    *mid,
2361                    ParamMap::GainBipolar,
2362                    true,
2363                    eq.in_("mid_gain"),
2364                )?;
2365                self.knob(
2366                    key,
2367                    "high",
2368                    *high,
2369                    ParamMap::GainBipolar,
2370                    true,
2371                    eq.in_("high_gain"),
2372                )?;
2373                self.constant(EQ_LOW_FREQ, eq.id(), "low_freq")?;
2374                self.constant(EQ_MID_FREQ, eq.id(), "mid_freq")?;
2375                self.constant(EQ_MID_Q, eq.id(), "mid_q")?;
2376                self.constant(EQ_HIGH_FREQ, eq.id(), "high_freq")?;
2377                // The mid band is the modulated one: it is the only band with
2378                // a centre rather than a corner, so a wobble there is heard as
2379                // the patch moving forward and back rather than as a fade.
2380                self.wire_mod(
2381                    modulation,
2382                    key,
2383                    *mod_depth,
2384                    eq.in_("mid_gain"),
2385                    Some(in_out),
2386                    DepthScale::Gain,
2387                )?;
2388                Ok(Sig::mono(eq.out("out")))
2389            }
2390            AudioNode::Granular {
2391                position,
2392                size,
2393                density,
2394                mod_depth,
2395                input,
2396                modulation,
2397                ..
2398            } => {
2399                let in_out = self.build(input, &format!("{key}/0"))?;
2400                // Allocates a fixed 96 000-sample buffer (≈768 KB) at
2401                // construction, on the same order as `Reverb`'s comb bank and
2402                // on the same thread — compile time, never the audio thread.
2403                let gr = self
2404                    .patch
2405                    .add(format!("{key}:granular"), Granular::new(self.sr()));
2406                self.feed(in_out, gr.in_("in"))?;
2407                self.knob(
2408                    key,
2409                    "gpos",
2410                    *position,
2411                    ParamMap::Unit,
2412                    false,
2413                    gr.in_("position"),
2414                )?;
2415                self.knob(key, "gsize", *size, ParamMap::Unit, false, gr.in_("size"))?;
2416                self.knob(
2417                    key,
2418                    "gdens",
2419                    *density,
2420                    ParamMap::Unit,
2421                    false,
2422                    gr.in_("density"),
2423                )?;
2424                self.constant(GRANULAR_PITCH, gr.id(), "pitch")?;
2425                self.constant(GRANULAR_SPRAY, gr.id(), "spray")?;
2426                self.constant(GRANULAR_FREEZE, gr.id(), "freeze")?;
2427                // Position is the slot: sweeping where in the buffer the
2428                // grains are read from is the gesture the module exists for,
2429                // and it is the one that reads as motion rather than as a
2430                // different setting.
2431                self.wire_mod(
2432                    modulation,
2433                    key,
2434                    *mod_depth,
2435                    gr.in_("position"),
2436                    Some(in_out),
2437                    DepthScale::Normalized,
2438                )?;
2439                Ok(Sig::mono(gr.out("out")))
2440            }
2441            AudioNode::RingMod { mix, a, b, .. } => {
2442                let a_out = self.build(a, &format!("{key}/0"))?;
2443                let b_out = self.build(b, &format!("{key}/1"))?;
2444                let rm = self.patch.add(format!("{key}:ring"), RingModulator::new());
2445                self.feed(a_out, rm.in_("carrier"))?;
2446                self.feed(b_out, rm.in_("modulator"))?;
2447                // Ring modulation replaces the fundamental with sum and
2448                // difference tones, so at full wet the patch loses its own
2449                // pitch. Crossfading against the dry *carrier* — not against
2450                // silence, and not against the modulator — is what makes the
2451                // knob a "how metallic" control rather than a "how atonal"
2452                // one, and is why `a` is the carrier.
2453                let xf = self.patch.add(format!("{key}:rgmix"), Crossfader::new());
2454                self.feed(a_out, xf.in_("a"))?;
2455                self.patch.connect(rm.out("out"), xf.in_("b"))?;
2456                self.knob(key, "rgmix", *mix, ParamMap::XfadePos, true, xf.in_("pos"))?;
2457                Ok(Sig::mono(xf.out("out")))
2458            }
2459            AudioNode::Shift {
2460                semis,
2461                window,
2462                mix,
2463                mod_depth,
2464                input,
2465                modulation,
2466                ..
2467            } => {
2468                let in_out = self.build(input, &format!("{key}/0"))?;
2469                let ps = self
2470                    .patch
2471                    .add(format!("{key}:shift"), PitchShifter::new(self.sr()));
2472                self.feed(in_out, ps.in_("in"))?;
2473                // `#semis` and the mod cable sum on one port, as on the
2474                // wavefolder threshold — which is why each is given half of
2475                // quiver's ±24-semitone range rather than all of it.
2476                self.knob(
2477                    key,
2478                    "semis",
2479                    *semis,
2480                    ParamMap::Semitones,
2481                    true,
2482                    ps.in_("shift"),
2483                )?;
2484                self.knob(
2485                    key,
2486                    "window",
2487                    *window,
2488                    ParamMap::Unit,
2489                    false,
2490                    ps.in_("window"),
2491                )?;
2492                self.knob(key, "smix", *mix, ParamMap::Unit, false, ps.in_("mix"))?;
2493                self.wire_mod(
2494                    modulation,
2495                    key,
2496                    *mod_depth,
2497                    ps.in_("shift"),
2498                    Some(in_out),
2499                    DepthScale::Shift,
2500                )?;
2501                Ok(Sig::mono(ps.out("out")))
2502            }
2503            AudioNode::Comp {
2504                threshold,
2505                ratio,
2506                makeup,
2507                mod_depth,
2508                input,
2509                sidechain,
2510                modulation,
2511                ..
2512            } => {
2513                let in_out = self.build(input, &format!("{key}/0"))?;
2514                let key_out = self.build(sidechain, &format!("{key}/1"))?;
2515                let cp = self
2516                    .patch
2517                    .add(format!("{key}:comp"), Compressor::new(self.sr()));
2518                self.feed(in_out, cp.in_("in"))?;
2519                // quiver normals an unpatched sidechain to the main input, so
2520                // the `/1` branch is what makes this a *sidechain* compressor
2521                // rather than a plain one — and it is the only thing the
2522                // branch does: port 6 reaches the detector and never the
2523                // output.
2524                self.feed(key_out, cp.in_("sidechain"))?;
2525                self.knob(
2526                    key,
2527                    "thresh",
2528                    *threshold,
2529                    ParamMap::DetectorThreshold,
2530                    false,
2531                    cp.in_("threshold"),
2532                )?;
2533                self.knob(key, "ratio", *ratio, ParamMap::Unit, false, cp.in_("ratio"))?;
2534                self.knob(
2535                    key,
2536                    "makeup",
2537                    *makeup,
2538                    ParamMap::Unit,
2539                    false,
2540                    cp.in_("makeup"),
2541                )?;
2542                self.constant(COMP_ATTACK, cp.id(), "attack")?;
2543                self.constant(COMP_RELEASE, cp.id(), "release")?;
2544                // Threshold is the slot: moving it is what turns a static gain
2545                // trim into an audible pump. The port is a plain 0..1 CV, but
2546                // it is *not* a `Normalized` destination — see
2547                // `DepthScale::Detector`.
2548                self.wire_mod(
2549                    modulation,
2550                    key,
2551                    *mod_depth,
2552                    cp.in_("threshold"),
2553                    Some(in_out),
2554                    DepthScale::Detector,
2555                )?;
2556                Ok(Sig::mono(cp.out("out")))
2557            }
2558            AudioNode::Duck {
2559                amount,
2560                threshold,
2561                release,
2562                mod_depth,
2563                input,
2564                key: key_input,
2565                modulation,
2566                ..
2567            } => {
2568                let in_out = self.build(input, &format!("{key}/0"))?;
2569                let key_out = self.build(key_input, &format!("{key}/1"))?;
2570                let dk = self
2571                    .patch
2572                    .add(format!("{key}:duck"), Ducker::new(self.sr()));
2573                self.feed(in_out, dk.in_("in"))?;
2574                self.feed(key_out, dk.in_("key"))?;
2575                // Both of these are `ModulatedParam` knob+CV ports, not plain
2576                // CVs — the knob arrives as an offset from quiver's own base.
2577                // See `map::duck_amount`.
2578                self.knob(
2579                    key,
2580                    "amount",
2581                    *amount,
2582                    ParamMap::DuckAmount,
2583                    true,
2584                    dk.in_("amount"),
2585                )?;
2586                self.knob(
2587                    key,
2588                    "dthresh",
2589                    *threshold,
2590                    ParamMap::DuckThreshold,
2591                    true,
2592                    dk.in_("threshold"),
2593                )?;
2594                self.knob(
2595                    key,
2596                    "drel",
2597                    *release,
2598                    ParamMap::Unit,
2599                    false,
2600                    dk.in_("release"),
2601                )?;
2602                self.constant(DUCK_ATTACK, dk.id(), "attack")?;
2603                self.wire_mod(
2604                    modulation,
2605                    key,
2606                    *mod_depth,
2607                    dk.in_("amount"),
2608                    Some(in_out),
2609                    DepthScale::ParamCv,
2610                )?;
2611                Ok(Sig::mono(dk.out("out")))
2612            }
2613            AudioNode::Gate {
2614                threshold,
2615                range,
2616                release,
2617                mod_depth,
2618                input,
2619                sidechain,
2620                modulation,
2621                ..
2622            } => {
2623                let in_out = self.build(input, &format!("{key}/0"))?;
2624                let key_out = self.build(sidechain, &format!("{key}/1"))?;
2625                let ng = self
2626                    .patch
2627                    .add(format!("{key}:gate"), NoiseGate::new(self.sr()));
2628                self.feed(in_out, ng.in_("in"))?;
2629                // As on the compressor: unpatched, port 5 normals to the main
2630                // input and the module is an ordinary gate. The branch is what
2631                // makes it keyed.
2632                self.feed(key_out, ng.in_("sidechain"))?;
2633                self.knob(
2634                    key,
2635                    "gthresh",
2636                    *threshold,
2637                    ParamMap::DetectorThreshold,
2638                    false,
2639                    ng.in_("threshold"),
2640                )?;
2641                self.knob(key, "range", *range, ParamMap::Unit, false, ng.in_("range"))?;
2642                self.knob(
2643                    key,
2644                    "grel",
2645                    *release,
2646                    ParamMap::Unit,
2647                    false,
2648                    ng.in_("release"),
2649                )?;
2650                self.constant(GATE_ATTACK, ng.id(), "attack")?;
2651                self.wire_mod(
2652                    modulation,
2653                    key,
2654                    *mod_depth,
2655                    ng.in_("threshold"),
2656                    Some(in_out),
2657                    DepthScale::Detector,
2658                )?;
2659                Ok(Sig::mono(ng.out("out")))
2660            }
2661            AudioNode::Vocoder {
2662                bands,
2663                attack,
2664                release,
2665                mod_depth,
2666                carrier,
2667                modulator,
2668                modulation,
2669                ..
2670            } => {
2671                let carrier_out = self.build(carrier, &format!("{key}/0"))?;
2672                let mod_out = self.build(modulator, &format!("{key}/1"))?;
2673                let vc = self
2674                    .patch
2675                    .add(format!("{key}:vocoder"), Vocoder::new(self.sr()));
2676                self.feed(carrier_out, vc.in_("carrier"))?;
2677                self.feed(mod_out, vc.in_("modulator"))?;
2678                self.knob(key, "bands", *bands, ParamMap::Unit, false, vc.in_("bands"))?;
2679                self.knob(
2680                    key,
2681                    "vatt",
2682                    *attack,
2683                    ParamMap::Unit,
2684                    false,
2685                    vc.in_("attack"),
2686                )?;
2687                self.knob(
2688                    key,
2689                    "vrel",
2690                    *release,
2691                    ParamMap::Unit,
2692                    false,
2693                    vc.in_("release"),
2694                )?;
2695                // Band count is the slot. quiver quantizes it (`round(4 +
2696                // 12·cv)`), so this is the one mod destination in the grammar
2697                // that steps rather than sweeps — which is the honest thing
2698                // for it to do: resolution is what a vocoder's band count
2699                // *is*, and sweeping it is heard as the vowel going from
2700                // legible to smeared and back. The ballistics were the
2701                // alternative, and they are a decay time, not a timbre.
2702                self.wire_mod(
2703                    modulation,
2704                    key,
2705                    *mod_depth,
2706                    vc.in_("bands"),
2707                    // The carrier is what the module processes, so it is the
2708                    // tap — the same `/0`-is-the-signal rule the child order
2709                    // follows.
2710                    Some(carrier_out),
2711                    DepthScale::Normalized,
2712                )?;
2713                Ok(Sig::mono(vc.out("out")))
2714            }
2715        }
2716    }
2717}
2718
2719/// Does this subtree contain a nonlinearity that can rectify, i.e. produce a
2720/// standing DC offset?
2721///
2722/// Two things in the palette can.
2723///
2724/// [`DiodeLadderFilter`]'s `diode_sat` is deliberately asymmetric
2725/// (`tanh(1.2x)` up, `tanh(0.8x)` down) and is applied at six points in the
2726/// ladder core.
2727///
2728/// [`Distortion`] in [`DriveMode::Tube`] is asymmetric *by definition* — it is
2729/// `1 − e^{−x}` above zero against `tanh(x)` below, which is the whole reason
2730/// the mode exists — so it emits DC at every drive setting above zero. Its two
2731/// siblings do not: soft clip is `tanh`, hard clip is a symmetric clamp, and
2732/// an odd nonlinearity cannot create DC from a zero-mean input. Skipping the
2733/// blocker on tube drive would put a per-note thump into every one of that
2734/// patch's feature vectors — and unlike a listener, the extractor cannot
2735/// discount it.
2736///
2737/// Everything else is linear or exactly odd-symmetric: `saturation::fold` is
2738/// `±2t − y`, the SVF's state clipper is `L·tanh(x/L)`, the bitcrusher's
2739/// quantizer is mid-tread (rounding, so unbiased), the ring modulator is a
2740/// product of two zero-mean signals, the limiter clamps symmetrically, and
2741/// every source is zero-mean — `KarplusStrong` explicitly zero-means its
2742/// excitation and leaks its loop for exactly this reason.
2743///
2744/// [`FormantOsc`] is the one that looks like an exception and is not. Its
2745/// glottal excitation is strictly **non-negative** (a half-sine open phase, a
2746/// quarter-cosine close, then zero), so it carries a large DC term — but it is
2747/// never heard directly: it reaches the output only through five parallel
2748/// 2-pole resonators whose numerator is `b0·(1 − z⁻²)`, which has an exact
2749/// zero at DC. The offset is annihilated in the filter bank, not by the voice
2750/// tail.
2751///
2752/// The 2A processors are all linear or amplitude-scaling: the flanger, vibrato
2753/// and granulator are (time-varying) delay reads, the EQ is a biquad cascade,
2754/// and the tremolo multiplies by a positive envelope — a gain, which cannot
2755/// create an offset a zero-mean input did not already have.
2756///
2757/// The 2B binaries are where this function stops being a plain recursion into
2758/// every child, and both directions matter:
2759///
2760/// - [`AudioNode::Comp`], [`AudioNode::Duck`] and [`AudioNode::Gate`] are
2761///   gains, so they pass their input's offset through — but their `/1` branch
2762///   reaches only the **detector** (quiver's ports 5/6/1 feed the envelope
2763///   follower and nothing else), so a ladder in the sidechain cannot put DC on
2764///   the output and must not buy a blocker.
2765/// - [`AudioNode::Vocoder`] is the opposite: both branches are consumed, and
2766///   *neither* can emit DC. Every band on both the analysis and the synthesis
2767///   side is a Chamberlin SVF bandpass, which has an exact zero at DC — at a
2768///   steady input the loop settles with `band = 0` — so the carrier's offset
2769///   is annihilated in the filter bank and the modulator's never reaches the
2770///   output at all. Same shape of argument as [`FormantOsc`] above, and the
2771///   same conclusion: no blocker.
2772fn makes_dc(node: &AudioNode) -> bool {
2773    match node {
2774        AudioNode::Comp { input, .. }
2775        | AudioNode::Duck { input, .. }
2776        | AudioNode::Gate { input, .. } => makes_dc(input),
2777        AudioNode::Vocoder { .. } => false,
2778        AudioNode::Vco { .. }
2779        | AudioNode::Supersaw { .. }
2780        | AudioNode::Noise { .. }
2781        | AudioNode::Wavetable { .. }
2782        | AudioNode::Pluck { .. }
2783        | AudioNode::Formant { .. }
2784        // A constant zero has no offset to block.
2785        | AudioNode::Silence { .. } => false,
2786        AudioNode::Mix { a, b, .. } | AudioNode::RingMod { a, b, .. } => makes_dc(a) || makes_dc(b),
2787        AudioNode::Filter { kind, input, .. } => {
2788            matches!(kind, FilterKind::Ladder) || makes_dc(input)
2789        }
2790        AudioNode::Distortion { mode, input, .. } => {
2791            matches!(mode, DriveMode::Tube) || makes_dc(input)
2792        }
2793        AudioNode::Fold { input, .. }
2794        | AudioNode::Delay { input, .. }
2795        | AudioNode::Chorus { input, .. }
2796        | AudioNode::Reverb { input, .. }
2797        | AudioNode::Bitcrush { input, .. }
2798        | AudioNode::Phaser { input, .. }
2799        | AudioNode::Flanger { input, .. }
2800        | AudioNode::Tremolo { input, .. }
2801        | AudioNode::Vibrato { input, .. }
2802        | AudioNode::Eq { input, .. }
2803        | AudioNode::Granular { input, .. }
2804        // A windowed buffer read plus a dry/wet blend: linear, so an offset
2805        // arrives unchanged rather than being created.
2806        | AudioNode::Shift { input, .. } => makes_dc(input),
2807    }
2808}
2809
2810/// Compile a patch term into a playable voice at the given sample rate.
2811pub fn compile(tree: &PatchTree, sample_rate: f64) -> Result<CompiledVoice, PatchError> {
2812    let mut patch = Patch::new(sample_rate);
2813    patch.set_validation_mode(ValidationMode::Warn);
2814
2815    let pitch = Arc::new(AtomicF64::new(0.0));
2816    let gate = Arc::new(AtomicF64::new(0.0));
2817    let pitch_in = patch.add("io:pitch", ExternalInput::voct(Arc::clone(&pitch)));
2818    let gate_in = patch.add("io:gate", ExternalInput::gate(Arc::clone(&gate)));
2819
2820    let mut c = Compiler {
2821        patch,
2822        pitch_out: pitch_in.out("out"),
2823        gate_out: gate_in.out("out"),
2824        params: HashMap::new(),
2825        taps: Vec::new(),
2826    };
2827
2828    // The evolved tree.
2829    let audio_out = c.build(&tree.root, "node")?;
2830
2831    // Mandatory voice stage: amp ADSR → VCA → limiter → stereo out.
2832    let adsr = c.patch.add("voice:adsr", Adsr::new(sample_rate));
2833    c.patch.connect(c.gate_out, adsr.in_("gate"))?;
2834    c.knob(
2835        "amp",
2836        "attack",
2837        tree.amp.attack,
2838        ParamMap::Unit,
2839        false,
2840        adsr.in_("attack"),
2841    )?;
2842    c.knob(
2843        "amp",
2844        "decay",
2845        tree.amp.decay,
2846        ParamMap::Unit,
2847        false,
2848        adsr.in_("decay"),
2849    )?;
2850    c.knob(
2851        "amp",
2852        "sustain",
2853        tree.amp.sustain,
2854        ParamMap::Unit,
2855        false,
2856        adsr.in_("sustain"),
2857    )?;
2858    c.knob(
2859        "amp",
2860        "release",
2861        tree.amp.release,
2862        ParamMap::Unit,
2863        false,
2864        adsr.in_("release"),
2865    )?;
2866    // Exponential contour. quiver's `shape` is a gate, not a curve amount: at
2867    // its 0 V default the whole instrument ran linear envelopes, and a linear
2868    // decay sounds like a fader being pulled, not like a note dying.
2869    c.constant(GATE_TRUE, adsr.id(), "shape")?;
2870
2871    let env = adsr.out("env");
2872    // Only pay for the blocker where DC can actually arise. It is an `Svf`,
2873    // and `Svf::tick` evaluates three transcendentals per sample — measured at
2874    // 0.057 s of render per patch, ~18% of a typical voice — so putting one on
2875    // every patch taxes the 91% that have no rectifying nonlinearity at all.
2876    let block_dc = makes_dc(&tree.root) || std::env::var("AUR_DCB_ALWAYS").is_ok();
2877    let left = c.voice_tail("", audio_out.left, env, block_dc)?;
2878    let right = match audio_out.right {
2879        Some(r) => Some(c.voice_tail("R", r, env, block_dc)?),
2880        None => None,
2881    };
2882
2883    let out = c.patch.add("voice:out", StereoOutput::new());
2884    c.patch.connect(left, out.in_("left"))?;
2885    // A mono tree leaves `right` unpatched — StereoOutput normals it to left,
2886    // and any cable at all would break that normal.
2887    if let Some(r) = right {
2888        c.patch.connect(r, out.in_("right"))?;
2889    }
2890
2891    let params = std::mem::take(&mut c.params);
2892    let recorded = std::mem::take(&mut c.taps);
2893    let mut patch = c.patch;
2894    patch.set_output(out.id());
2895    patch.compile()?;
2896    let warnings = patch.warnings().to_vec();
2897
2898    // Resolve each tap's `NodeId` back to the name it was added under. Done in
2899    // one pass here rather than by threading names through the builder: every
2900    // `Patch::add` call site would otherwise have to remember to record one,
2901    // and `Patch::nodes` already knows the answer for all of them.
2902    let names: HashMap<NodeId, &str> = patch.nodes().map(|(id, name, _)| (id, name)).collect();
2903    let taps = recorded
2904        .into_iter()
2905        .filter_map(|(key, port)| {
2906            names
2907                .get(&port.node)
2908                .map(|name| (key, (name.to_string(), port.port)))
2909        })
2910        .collect();
2911
2912    Ok(CompiledVoice {
2913        patch,
2914        pitch,
2915        gate,
2916        params,
2917        warnings,
2918        taps,
2919    })
2920}
2921
2922#[cfg(test)]
2923mod tests {
2924    use super::*;
2925    use crate::term::{
2926        AmpEnv, DriveMode, FilterKind, ModNode, NoiseColor, TableShape, Uid, Waveform,
2927    };
2928
2929    const SR: f64 = 44_100.0;
2930
2931    fn sustained(root: AudioNode) -> PatchTree {
2932        PatchTree {
2933            amp: AmpEnv {
2934                attack: 0.1,
2935                decay: 0.3,
2936                sustain: 1.0,
2937                release: 0.3,
2938            },
2939            root,
2940        }
2941    }
2942
2943    fn saw() -> AudioNode {
2944        AudioNode::Vco {
2945            uid: Uid::NEW,
2946            wave: Waveform::Saw,
2947            octave: 0,
2948            detune: 0.5,
2949            mod_depth: 0.0,
2950            modulation: ModNode::None,
2951        }
2952    }
2953
2954    /// Overwrite one of the compiler's baked constants — a `set_param_by_id`
2955    /// default on the named node's port — in an already compiled voice (the
2956    /// next tick recompiles and bakes it in). Every wiring decision in this
2957    /// module that is *not* a knob is such a constant, so this renders the
2958    /// exact counterfactual — the identical graph with one pinned value
2959    /// neutralized.
2960    fn set_constant(v: &mut CompiledVoice, node: &str, port: &str, value: f64) {
2961        let id = v
2962            .patch
2963            .get_node_id_by_name(node)
2964            .unwrap_or_else(|| panic!("no node `{node}`"));
2965        assert!(
2966            v.patch.set_param_by_id(id, port, value),
2967            "no control port `{port}` on `{node}`"
2968        );
2969    }
2970
2971    fn hold(v: &mut CompiledVoice, voct: f64, n: usize) -> Vec<(f64, f64)> {
2972        v.pitch.set(voct);
2973        v.gate.set(5.0);
2974        (0..n).map(|_| v.patch.tick()).collect()
2975    }
2976
2977    fn rms(buf: &[(f64, f64)]) -> f64 {
2978        (buf.iter().map(|(l, _)| l * l).sum::<f64>() / buf.len() as f64).sqrt()
2979    }
2980
2981    /// Every term node gets a tap, each tap names a port that resolves, and
2982    /// the taps read *different* signals from each other.
2983    ///
2984    /// The last clause is the one with teeth. A map that pointed every key at
2985    /// the root's output would satisfy "resolves and is nonzero" while being
2986    /// useless — the flow animation would show one number on every wire. So
2987    /// the fixture crossfades a saw against silence-adjacent noise and asserts
2988    /// the two branches read apart from each other, which only holds if each
2989    /// key resolved to the node that actually ends *its* chain.
2990    #[test]
2991    fn taps_name_a_live_port_on_every_term_node() {
2992        let tree = sustained(AudioNode::Mix {
2993            uid: Uid::NEW,
2994            balance: 0.5,
2995            a: Box::new(saw()),
2996            b: Box::new(AudioNode::Noise {
2997                uid: Uid::NEW,
2998                color: NoiseColor::White,
2999            }),
3000        });
3001        let mut v = compile(&tree, SR).expect("compiles");
3002
3003        for key in ["node", "node/0", "node/1"] {
3004            assert!(v.taps.contains_key(key), "no tap recorded for `{key}`");
3005        }
3006        assert_eq!(v.taps.len(), 3, "one tap per term node, no more");
3007
3008        // Read each tap while the voice sounds. Reading through the same
3009        // `get_output_value` path the observer uses keeps the test honest
3010        // about what a subscription would actually see.
3011        let read = |v: &CompiledVoice, key: &str| -> f64 {
3012            let (name, port) = v.taps.get(key).expect("tap");
3013            let id = v
3014                .patch
3015                .get_node_id_by_name(name)
3016                .unwrap_or_else(|| panic!("tap `{key}` names `{name}`, which is not in the patch"));
3017            v.patch.get_output_value(id, *port).expect("port resolves")
3018        };
3019
3020        let mut saw_trace = Vec::new();
3021        let mut noise_trace = Vec::new();
3022        v.pitch.set(0.0);
3023        v.gate.set(5.0);
3024        for _ in 0..2048 {
3025            v.patch.tick();
3026            saw_trace.push(read(&v, "node/0"));
3027            noise_trace.push(read(&v, "node/1"));
3028        }
3029
3030        let energy = |t: &[f64]| (t.iter().map(|s| s * s).sum::<f64>() / t.len() as f64).sqrt();
3031        assert!(energy(&saw_trace) > 0.0, "the saw branch reads silent");
3032        assert!(energy(&noise_trace) > 0.0, "the noise branch reads silent");
3033        // A saw is periodic and noise is not, so per-sample equality across a
3034        // 2048-sample window would take a coincidence that cannot happen.
3035        assert!(
3036            saw_trace
3037                .iter()
3038                .zip(&noise_trace)
3039                .any(|(a, b)| (a - b).abs() > 1e-9),
3040            "both branches read the same signal — the taps are not per-node"
3041        );
3042    }
3043
3044    /// Filter keytracking is wired and follows the keyboard. White noise is a
3045    /// pitch-independent source, so *any* change of level with pitch through a
3046    /// fixed-cutoff lowpass is the keytrack and nothing else — and with
3047    /// `keytrack_amt` neutralized the level must stop moving entirely.
3048    #[test]
3049    fn filter_tracks_the_keyboard() {
3050        let tree = sustained(AudioNode::Filter {
3051            uid: Uid::NEW,
3052            kind: FilterKind::SvfLp,
3053            cutoff: 0.3,
3054            resonance: 0.0,
3055            mod_depth: 0.0,
3056            input: Box::new(AudioNode::Noise {
3057                uid: Uid::NEW,
3058                color: NoiseColor::White,
3059            }),
3060            modulation: ModNode::None,
3061        });
3062        let level = |amt: Option<f64>, voct: f64| {
3063            let mut v = compile(&tree, SR).expect("compiles");
3064            if let Some(a) = amt {
3065                set_constant(&mut v, "node:svf", "keytrack_amt", a);
3066            }
3067            // Every leg hears the *same* noise. quiver's noise draws from a
3068            // thread-local RNG seeded from the system clock, so without this
3069            // each measurement gets a different realisation — and the patch
3070            // ends in a limiter, whose gain reduction tracks peak statistics
3071            // rather than RMS, so the difference between two realisations is
3072            // far larger than sampling error. Measured over 120 unseeded runs
3073            // the flat-control ratio spread from 0.0000 to 0.1332 against a
3074            // 0.1 tolerance: a 1.7%-per-run CI failure that says nothing about
3075            // keytracking. Seeded, both legs differ only by the thing under
3076            // test, which is also why the tolerance below can be tight.
3077            quiver::rng::seed(0x5EED_1E55);
3078            let out = hold(&mut v, voct, 88_200);
3079            rms(&out[44_100..])
3080        };
3081        let (low, mid, high) = (level(None, -2.0), level(None, 0.0), level(None, 2.0));
3082        assert!(
3083            low < mid && mid < high && high > low * 1.8,
3084            "cutoff does not follow pitch: C2 {low:.4}, C4 {mid:.4}, C6 {high:.4}"
3085        );
3086        // Counterfactual: amount 0 is quiver's default, i.e. the old behaviour.
3087        let (flat_low, flat_high) = (level(Some(0.0), -2.0), level(Some(0.0), 2.0));
3088        assert!(
3089            (flat_high / flat_low - 1.0).abs() < 0.01,
3090            "control is not flat, so the test proves nothing: \
3091             C2 {flat_low:.4}, C6 {flat_high:.4}"
3092        );
3093    }
3094
3095    /// The DC blocker removes the ladder's saturation offset. `diode_sat` is
3096    /// deliberately asymmetric and audio reaches it at nominal ±5 V, so without
3097    /// the blocker the amp envelope multiplies a standing offset into a thump
3098    /// on every note.
3099    #[test]
3100    fn dc_blocker_removes_the_ladder_offset() {
3101        let tree = sustained(AudioNode::Filter {
3102            uid: Uid::NEW,
3103            kind: FilterKind::Ladder,
3104            cutoff: 0.35,
3105            resonance: 0.3,
3106            mod_depth: 0.0,
3107            input: Box::new(saw()),
3108            modulation: ModNode::None,
3109        });
3110        let mut v = compile(&tree, SR).expect("compiles");
3111        let n = (SR * 5.0) as usize;
3112        let out = hold(&mut v, -2.0, n);
3113        let tail = &out[n / 2..];
3114        let dc = tail.iter().map(|(l, _)| l).sum::<f64>() / tail.len() as f64;
3115        let level = rms(tail);
3116        assert!(level > 0.1, "patch was silent, nothing to measure");
3117        assert!(
3118            dc.abs() / level < 2.0e-3,
3119            "standing DC offset {dc:.6} against {level:.4} rms"
3120        );
3121    }
3122
3123    /// Reverb and chorus keep both tanks all the way to the stereo output, and
3124    /// a mono tree still normals right to left rather than going silent.
3125    #[test]
3126    fn stereo_tanks_reach_the_output() {
3127        let mut wide = compile(
3128            &sustained(AudioNode::Reverb {
3129                uid: Uid::NEW,
3130                size: 0.7,
3131                damp: 0.4,
3132                mix: 0.6,
3133                mod_depth: 0.0,
3134                modulation: ModNode::None,
3135                input: Box::new(saw()),
3136            }),
3137            SR,
3138        )
3139        .expect("compiles");
3140        let out = hold(&mut wide, 0.0, 44_100);
3141        let tail = &out[22_050..];
3142        let width: f64 = tail.iter().map(|(l, r)| (l - r).abs()).sum::<f64>() / tail.len() as f64;
3143        assert!(
3144            width > 1.0e-2,
3145            "reverb collapsed to mono (width {width:.5})"
3146        );
3147
3148        let mut narrow = compile(&sustained(saw()), SR).expect("compiles");
3149        let out = hold(&mut narrow, 0.0, 4_410);
3150        assert!(
3151            out.iter().all(|(l, r)| l == r) && rms(&out) > 1.0e-3,
3152            "mono tree lost its right channel"
3153        );
3154    }
3155
3156    /// The wavefolder's `#thresh` knob is live *while the fold is modulated*.
3157    /// `Wavefolder::new` only sets port 1's default, and quiver ignores a
3158    /// default the moment any cable lands on the port — so the knob used to go
3159    /// silently dead exactly when a mod source was attached.
3160    #[test]
3161    fn fold_threshold_stays_live_under_modulation() {
3162        let tree = sustained(AudioNode::Fold {
3163            uid: Uid::NEW,
3164            threshold: 0.5,
3165            mod_depth: 0.6,
3166            input: Box::new(saw()),
3167            modulation: ModNode::Lfo {
3168                uid: Uid::NEW,
3169                wave: Waveform::Sine,
3170                rate: 0.4,
3171            },
3172        });
3173        let at = |thresh: f64| {
3174            let mut v = compile(&tree, SR).expect("compiles");
3175            v.params
3176                .get("node#thresh")
3177                .expect("fold threshold has no live handle")
3178                .set_normalized(thresh);
3179            let out = hold(&mut v, 0.0, 44_100);
3180            rms(&out[22_050..])
3181        };
3182        let (hard, soft) = (at(0.0), at(1.0));
3183        assert!(
3184            (hard - soft).abs() / soft.max(1.0e-9) > 0.05,
3185            "fold threshold knob is inaudible: {hard:.4} vs {soft:.4}"
3186        );
3187        // The mod depth advertised by `describe.rs` is a live handle too, so
3188        // dragging it no longer forces a whole-patch recompile.
3189        assert!(
3190            tree_params(&tree).contains(&"node#mdepth".to_string()),
3191            "mod depth has no live handle"
3192        );
3193    }
3194
3195    /// Zero crossings per window, over `windows` equal slices of `buf`.
3196    ///
3197    /// A sine's crossing count is a direct read of its instantaneous
3198    /// frequency and is blind to amplitude, so this survives the amp envelope
3199    /// and the limiter sitting between the oscillator and the measurement.
3200    fn crossings_per_window(buf: &[(f64, f64)], windows: usize) -> Vec<usize> {
3201        let w = buf.len() / windows;
3202        (0..windows)
3203            .map(|i| {
3204                buf[i * w..(i + 1) * w]
3205                    .windows(2)
3206                    .filter(|p| (p[0].0 < 0.0) != (p[1].0 < 0.0))
3207                    .count()
3208            })
3209            .collect()
3210    }
3211
3212    /// Pitch modulation is **in octaves**, and the taper's full depth is
3213    /// exactly ±0.5 of one.
3214    ///
3215    /// This is the wave-2A capability nothing else in the grammar offers, and
3216    /// the arithmetic behind it is a three-step chain that is easy to get
3217    /// wrong by a factor of five: the [`Attenuverter`]'s gain is `level / 5`,
3218    /// its ±5 V source therefore arrives at `±level` **volts**, and the
3219    /// [`Offset`] it lands on is V/Oct — so the level *is* the octave depth.
3220    /// At `mod_depth` 1.0 that is ±0.5 octave, i.e. the fastest moment of the
3221    /// sweep is a full **2×** the slowest. Asserting the ratio rather than
3222    /// "something moved" is what makes this a test of the mapping instead of
3223    /// a test that a cable exists.
3224    ///
3225    /// The LFO runs one full cycle across the render, so the measurement does
3226    /// not depend on where its phase starts.
3227    #[test]
3228    fn pitch_modulation_spans_exactly_one_octave_at_full_depth() {
3229        let vco = |mod_depth: f64| AudioNode::Vco {
3230            uid: Uid::NEW,
3231            wave: Waveform::Sine,
3232            octave: 0,
3233            detune: 0.5,
3234            mod_depth,
3235            modulation: ModNode::Lfo {
3236                uid: Uid::NEW,
3237                wave: Waveform::Sine,
3238                // 0.01·3000^x Hz ⇒ 0.5 Hz, one cycle in the 2 s rendered.
3239                rate: 0.4886,
3240            },
3241        };
3242        // Held two octaves up: a 100 ms window then spans ~420 crossings, so
3243        // the ±1 quantization of counting them is 0.2% rather than the 4% it
3244        // would be at C4 — which is the difference between "the depth-0 leg is
3245        // inert" being a measurement and being a hope.
3246        let span = |mod_depth: f64| {
3247            let mut v = compile(&sustained(vco(mod_depth)), SR).expect("compiles");
3248            let out = hold(&mut v, 2.0, 88_200);
3249            let counts = crossings_per_window(&out, 20);
3250            let hi = *counts.iter().max().expect("windows") as f64;
3251            let lo = *counts.iter().min().expect("windows") as f64;
3252            hi / lo.max(1.0)
3253        };
3254
3255        let full = span(1.0);
3256        assert!(
3257            (1.8..2.2).contains(&full),
3258            "full pitch depth spans {full:.3}× in frequency, not the 2.0× that \
3259             ±0.5 octave means — the attenuverter/V-Oct arithmetic is off"
3260        );
3261        // A tenth of the knob is the vibrato corner: ±0.05 octave ≈ ±60 cents,
3262        // so the span is 2^0.1 ≈ 1.072. Linear taper, so this follows from the
3263        // number above — and would not if the taper were square-law.
3264        let tenth = span(0.1);
3265        assert!(
3266            (1.03..1.12).contains(&tenth),
3267            "pitch depth 0.1 spans {tenth:.4}×, not the ~1.072× a linear taper gives"
3268        );
3269        // And the cable is inert at zero depth rather than merely quiet.
3270        let none = span(0.0);
3271        assert!(
3272            none < 1.02,
3273            "an empty pitch depth still moved the pitch by {none:.4}×"
3274        );
3275    }
3276
3277    /// The EQ's modulation slot lands on a **volt-scaled** port, and is taken
3278    /// to that port's own scale rather than the normalized one.
3279    ///
3280    /// quiver reads `ParametricEq`'s bands as `cv/5 · 12` dB. The taper every
3281    /// other slot in this grammar uses is sized for a 0..1 CV — half of full
3282    /// scale, i.e. ±0.5 V — which on this port is **±1.2 dB at full depth**,
3283    /// about the level JND. That is a mod slot that does nothing across its
3284    /// entire travel, and it reviews as correct because the cable is there.
3285    /// `DepthScale::Gain` reaches the port's own ±5 V, so full depth is a
3286    /// ±12 dB pump.
3287    ///
3288    /// The two tapers differ by exactly 10×, which is what makes this
3289    /// measurable rather than arguable: **`mod_depth` 0.1 under the gain taper
3290    /// is precisely what `mod_depth` 1.0 would have been under the normalized
3291    /// one**, so the same render measures both designs. A sine parked on the
3292    /// bell's centre (≈1.26 kHz, i.e. 2.27 octaves above C4) makes the band's
3293    /// gain the whole signal's gain; the voice limiter clips the boost half,
3294    /// so what the swing reports is the cut half reaching its full −12 dB.
3295    #[test]
3296    fn eq_modulation_reaches_the_bands_own_volt_scale() {
3297        let eq = |mod_depth: f64| AudioNode::Eq {
3298            uid: Uid::NEW,
3299            low: 0.5,
3300            mid: 0.5,
3301            high: 0.5,
3302            mod_depth,
3303            input: Box::new(AudioNode::Vco {
3304                uid: Uid::NEW,
3305                wave: Waveform::Sine,
3306                octave: 0,
3307                detune: 0.5,
3308                mod_depth: 0.0,
3309                modulation: ModNode::None,
3310            }),
3311            modulation: ModNode::Lfo {
3312                uid: Uid::NEW,
3313                wave: Waveform::Sine,
3314                rate: 0.4886, // 0.5 Hz — one cycle in the 2 s rendered
3315            },
3316        };
3317        let swing = |mod_depth: f64| {
3318            let mut v = compile(&sustained(eq(mod_depth)), SR).expect("compiles");
3319            let out = hold(&mut v, 2.27, 88_200);
3320            let w = out.len() / 20;
3321            let levels: Vec<f64> = (0..20).map(|i| rms(&out[i * w..(i + 1) * w])).collect();
3322            let hi = levels.iter().cloned().fold(0.0_f64, f64::max);
3323            let lo = levels.iter().cloned().fold(f64::MAX, f64::min);
3324            hi / lo.max(1.0e-12)
3325        };
3326
3327        // −12 dB is 3.98×; anything near it means the cable reached the port.
3328        let full = swing(1.0);
3329        assert!(
3330            full > 3.5,
3331            "eq modulation swings the level only {full:.3}× at full depth, not \
3332             the ~4× that ±12 dB on the mid band means"
3333        );
3334        // The counterfactual: the normalized taper's entire travel, which is
3335        // a hair over the level JND and 4% of the swing above.
3336        let as_normalized = swing(0.1);
3337        assert!(
3338            as_normalized < 1.25,
3339            "the normalized taper reaches {as_normalized:.3}× here — if that is \
3340             no longer ~1.15× the 10× ratio between the two tapers has moved"
3341        );
3342        // And the cable is inert at zero depth rather than merely quiet.
3343        let none = swing(0.0);
3344        assert!(
3345            none < 1.05,
3346            "an empty eq mod depth still moved the level by {none:.3}×"
3347        );
3348    }
3349
3350    /// Magnitude of `buf` at `hz`, normalized by length — a one-bin DFT.
3351    ///
3352    /// The pitch shifter's output is a windowed sum of two resampled grains,
3353    /// so counting zero crossings measures the grain boundaries as much as the
3354    /// pitch. Correlating against the tone being looked for does not.
3355    fn tone_mag(buf: &[(f64, f64)], hz: f64, sr: f64) -> f64 {
3356        let (mut re, mut im) = (0.0, 0.0);
3357        for (n, (l, _)) in buf.iter().enumerate() {
3358            let w = std::f64::consts::TAU * hz * n as f64 / sr;
3359            re += l * w.cos();
3360            im += l * w.sin();
3361        }
3362        (re * re + im * im).sqrt() / buf.len() as f64
3363    }
3364
3365    /// The semitone offset (over `range`) whose tone is strongest in `buf`,
3366    /// relative to `base_hz`.
3367    fn dominant_semitone(buf: &[(f64, f64)], base_hz: f64, range: i32) -> i32 {
3368        (-range..=range)
3369            .max_by(|a, b| {
3370                let m = |k: &i32| tone_mag(buf, base_hz * 2f64.powf(*k as f64 / 12.0), SR);
3371                m(a).total_cmp(&m(b))
3372            })
3373            .expect("non-empty range")
3374    }
3375
3376    fn sine_src() -> AudioNode {
3377        AudioNode::Vco {
3378            uid: Uid::NEW,
3379            wave: Waveform::Sine,
3380            octave: 0,
3381            detune: 0.5,
3382            mod_depth: 0.0,
3383            modulation: ModNode::None,
3384        }
3385    }
3386
3387    /// A plucked string, the default key/sidechain branch — and the quietest
3388    /// source in the palette, which is the whole reason the threshold knobs
3389    /// are geometric.
3390    fn pluck_key() -> AudioNode {
3391        AudioNode::Pluck {
3392            uid: Uid::NEW,
3393            octave: -1,
3394            damping: 0.4,
3395            brightness: 0.7,
3396            mod_depth: 0.0,
3397            modulation: ModNode::None,
3398        }
3399    }
3400
3401    /// Per-window RMS over `n` equal slices, with quiver's noise RNG seeded.
3402    ///
3403    /// The seed is not optional here: every one of these patches is keyed from
3404    /// a Karplus-Strong string, whose excitation is drawn from a clock-seeded
3405    /// thread-local RNG — so an unseeded run measures a different string each
3406    /// time, and a gate's close *time* moves by hundreds of milliseconds
3407    /// between realisations.
3408    fn window_rms(tree: &PatchTree, voct: f64, n: usize) -> Vec<f64> {
3409        quiver::rng::seed(0x2B_5EED);
3410        let mut v = compile(tree, SR).expect("compiles");
3411        let out = hold(&mut v, voct, 44_100);
3412        let w = out.len() / n;
3413        (0..n).map(|i| rms(&out[i * w..(i + 1) * w])).collect()
3414    }
3415
3416    /// The pitch shifter's `#semis` knob is in **semitones**, on quiver's own
3417    /// scale, with unison at knob centre.
3418    ///
3419    /// quiver reads the port as `cv/5 · 24` semitones and hard-clamps at ±24
3420    /// (`PitchShifter`, nonlinear.rs), so this is the third member of the
3421    /// family of errors wave 2A kept making: a control that reviews as correct
3422    /// because the cable exists and is off by a factor. Passing the raw 0..1
3423    /// knob would have given 0..+4.8 semitones with **no downward shift at
3424    /// all** — a "pitch shift" that can only go up, and only by a third.
3425    ///
3426    /// Measured as a one-bin DFT rather than by counting zero crossings,
3427    /// because the output is a windowed sum of two resampled grains: the grain
3428    /// boundaries cross zero too.
3429    #[test]
3430    fn pitch_shift_lands_on_quivers_semitone_scale() {
3431        // C4 held two octaves up, so a 1 s window resolves the interval and
3432        // the grain-rate sidebands sit further from the fundamental.
3433        let base = 261.625_565 * 4.0;
3434        let at = |semis: f64| {
3435            let tree = sustained(AudioNode::Shift {
3436                uid: Uid::NEW,
3437                semis,
3438                window: 0.5,
3439                mix: 1.0, // fully wet: the dry copy would win every bin
3440                mod_depth: 0.0,
3441                input: Box::new(sine_src()),
3442                modulation: ModNode::None,
3443            });
3444            let mut v = compile(&tree, SR).expect("compiles");
3445            let out = hold(&mut v, 2.0, 88_200);
3446            dominant_semitone(&out[44_100..], base, 14)
3447        };
3448        for (knob, want) in [(0.0, -12), (0.5, 0), (1.0, 12)] {
3449            let got = at(knob);
3450            assert!(
3451                (got - want).abs() <= 1,
3452                "shift knob {knob} transposes {got:+} semitones, not {want:+} — \
3453                 the ±12-at-the-ends, unison-at-centre map is off"
3454            );
3455        }
3456    }
3457
3458    /// ...and its modulation slot lands on that same semitone scale rather
3459    /// than on the normalized one every other slot in the grammar uses.
3460    ///
3461    /// The two tapers differ by exactly 5× (`SHIFT_PEAK_V` 2.5 V against
3462    /// `PEAK_NORMALIZED` 0.5), which is what makes this measurable rather than
3463    /// arguable: **`mod_depth` 0.2 under the shift taper is precisely what
3464    /// `mod_depth` 1.0 would have been under the normalized one**, so the same
3465    /// render measures both designs.
3466    #[test]
3467    fn pitch_shift_modulation_reaches_the_ports_own_semitone_scale() {
3468        let base = 261.625_565 * 4.0;
3469        let span = |mod_depth: f64| {
3470            let tree = sustained(AudioNode::Shift {
3471                uid: Uid::NEW,
3472                semis: 0.5,
3473                window: 0.5,
3474                mix: 1.0,
3475                mod_depth,
3476                input: Box::new(sine_src()),
3477                modulation: ModNode::Lfo {
3478                    uid: Uid::NEW,
3479                    wave: Waveform::Triangle,
3480                    rate: 0.4886, // ≈0.5 Hz — one cycle in the 2 s rendered
3481                },
3482            });
3483            let mut v = compile(&tree, SR).expect("compiles");
3484            let out = hold(&mut v, 2.0, 88_200);
3485            let w = out.len() / 16;
3486            let ks: Vec<i32> = (0..16)
3487                .map(|i| dominant_semitone(&out[i * w..(i + 1) * w], base, 14))
3488                .collect();
3489            ks.iter().max().expect("windows") - ks.iter().min().expect("windows")
3490        };
3491
3492        let full = span(1.0);
3493        assert!(
3494            full >= 18,
3495            "full shift depth sweeps only {full} semitones, not the ~24 that \
3496             ±12 means — the attenuverter arithmetic is off"
3497        );
3498        // The counterfactual: the normalized taper's *entire* travel.
3499        let as_normalized = span(0.2);
3500        assert!(
3501            as_normalized <= 8,
3502            "the normalized taper sweeps {as_normalized} semitones here — if \
3503             that is no longer ~5 the 5× ratio between the two tapers has moved"
3504        );
3505        assert_eq!(span(0.0), 0, "an empty shift depth still moved the pitch");
3506    }
3507
3508    /// The three dynamics thresholds are **geometric over 0.05–5 V**, and they
3509    /// have to be, because this instrument's sources are not on one level.
3510    ///
3511    /// quiver reads all three as a plain `cv · 5` volts against a smoothed
3512    /// `|x|` detector, so passing the raw knob through is the obvious thing —
3513    /// and it produces a gate that never opens. Measured mean `|x|` on a held
3514    /// note: sine vco 3.18 V, plucked string 0.14 V. The pluck is what a gate
3515    /// or a ducker is usually keyed from, and under the linear map its whole
3516    /// useful range sat below knob position 0.1.
3517    ///
3518    /// The arithmetic first, then the behaviour it buys: with the default key
3519    /// branch the gate must both **open** on the pluck's attack and **shut**
3520    /// again as the string decays, inside one held note.
3521    #[test]
3522    fn the_dynamics_threshold_knob_spans_the_levels_the_palette_produces() {
3523        let volts = |x: f64| map::detector_threshold(x) * 5.0;
3524        assert!((volts(0.0) - 0.05).abs() < 1e-9, "bottom of the knob moved");
3525        assert!((volts(1.0) - 5.0).abs() < 1e-9, "top of the knob moved");
3526        // Geometric: the midpoint is the geometric mean, not the arithmetic
3527        // one (which would be 2.5 V and put every non-oscillator off the dial).
3528        assert!((volts(0.5) - 0.5).abs() < 1e-3, "the knob is not geometric");
3529        assert!(
3530            volts(0.35) < 0.3,
3531            "knob 0.35 asks for {:.3} V — under the linear map it asked for \
3532             1.75 V, which no key in the palette ever reaches",
3533            volts(0.35)
3534        );
3535
3536        // The behaviour. `range` 0.7 means a shut gate passes 0.3 of the
3537        // signal, so open and shut differ by ~10 dB and are unmistakable.
3538        let gated = sustained(AudioNode::Gate {
3539            uid: Uid::NEW,
3540            threshold: 0.45,
3541            range: 0.7,
3542            release: 0.3,
3543            mod_depth: 0.0,
3544            input: Box::new(sine_src()),
3545            sidechain: Box::new(pluck_key()),
3546            modulation: ModNode::None,
3547        });
3548        let levels = window_rms(&gated, 0.0, 20);
3549        let (hi, lo) = (
3550            levels.iter().cloned().fold(0.0_f64, f64::max),
3551            levels.iter().cloned().fold(f64::MAX, f64::min),
3552        );
3553        assert!(
3554            hi / lo.max(1e-12) > 2.5,
3555            "the gate never changes state across a held note: {levels:?}"
3556        );
3557        // ...and in that order: open on the transient, shut on the decay.
3558        assert!(
3559            levels[1] > 2.0 * levels[19],
3560            "the gate did not open on the attack and shut on the decay: {levels:?}"
3561        );
3562    }
3563
3564    /// The ducker's two knobs are **offsets from quiver's own knob base**, not
3565    /// plain CVs — and getting that wrong is a control that is at full depth
3566    /// across its entire travel.
3567    ///
3568    /// `Ducker` reads `amount` and `threshold` through a `ModulatedParam`
3569    /// (`base + cv/5`, dynamics.rs) whose base is set in `Ducker::new` and is
3570    /// reachable only from Rust, not from a port. `amount`'s base is **1.0**,
3571    /// so passing the raw 0..1 knob would have run the parameter from 1.0 to
3572    /// 1.2 and clamped: full ducking at every knob position, including zero.
3573    #[test]
3574    fn the_ducker_knob_offsets_quivers_own_base() {
3575        let ducked = |amount: f64| {
3576            let tree = sustained(AudioNode::Duck {
3577                uid: Uid::NEW,
3578                amount,
3579                threshold: 0.4,
3580                release: 0.35,
3581                mod_depth: 0.0,
3582                input: Box::new(sine_src()),
3583                key: Box::new(pluck_key()),
3584                modulation: ModNode::None,
3585            });
3586            let levels = window_rms(&tree, 0.0, 10);
3587            // The key decays, so the deepest duck is at the start.
3588            levels[0]
3589        };
3590        let (open, deep) = (ducked(0.0), ducked(1.0));
3591        assert!(
3592            deep < open * 0.5,
3593            "full duck depth only reaches {deep:.3} against {open:.3} unducked"
3594        );
3595        // The end that the raw-knob bug would have destroyed: at zero the
3596        // module must be a wire.
3597        let dry = window_rms(&sustained(sine_src()), 0.0, 10)[0];
3598        assert!(
3599            (open - dry).abs() / dry < 0.02,
3600            "a ducker at amount 0 is not a wire: {open:.3} against {dry:.3}"
3601        );
3602        // Monotone in between, so the knob is a depth and not a switch.
3603        let mid = ducked(0.5);
3604        assert!(
3605            deep < mid && mid < open,
3606            "duck depth is not monotone: {deep:.3} {mid:.3} {open:.3}"
3607        );
3608    }
3609
3610    /// The ducker's modulation slot lands on a `ModulatedParam` port, which
3611    /// costs **ten times** the volts a normalized port does for the same
3612    /// musical depth.
3613    ///
3614    /// `PEAK_PARAM_CV` is 2.5 V against `PEAK_NORMALIZED`'s 0.5, so — as with
3615    /// the eq — `mod_depth` 0.2 here is exactly what `mod_depth` 1.0 would
3616    /// have been under the normalized taper, and one pair of renders measures
3617    /// both designs.
3618    #[test]
3619    fn duck_modulation_reaches_the_param_cv_scale() {
3620        let swing = |mod_depth: f64| {
3621            let tree = sustained(AudioNode::Duck {
3622                uid: Uid::NEW,
3623                // Mid depth, so the cable has room to move it both ways.
3624                amount: 0.5,
3625                threshold: 0.2,
3626                release: 0.35,
3627                mod_depth,
3628                input: Box::new(sine_src()),
3629                key: Box::new(pluck_key()),
3630                modulation: ModNode::Lfo {
3631                    uid: Uid::NEW,
3632                    wave: Waveform::Sine,
3633                    rate: 0.5595, // ≈0.9 Hz — a full cycle inside the render
3634                },
3635            });
3636            let levels = window_rms(&tree, 0.0, 20);
3637            let hi = levels.iter().cloned().fold(0.0_f64, f64::max);
3638            let lo = levels.iter().cloned().fold(f64::MAX, f64::min);
3639            hi / lo.max(1e-12)
3640        };
3641        let full = swing(1.0);
3642        assert!(
3643            full > 2.0,
3644            "duck modulation swings the level only {full:.3}× at full depth"
3645        );
3646        let as_normalized = swing(0.2);
3647        assert!(
3648            as_normalized < full * 0.6,
3649            "the normalized taper reaches {as_normalized:.3}× against the \
3650             gain taper's {full:.3}× — the 5× ratio between them has moved"
3651        );
3652    }
3653
3654    /// A vocoder emits no DC, so `makes_dc` is right to refuse it a blocker.
3655    ///
3656    /// The argument is that every band on both sides is a Chamberlin SVF
3657    /// *bandpass*, which has an exact zero at DC — so the carrier's offset is
3658    /// annihilated in the filter bank and the modulator's never reaches the
3659    /// output at all. That is a claim about quiver's arithmetic, and this
3660    /// measures it on the module rather than trusting it: a ladder in the
3661    /// carrier is the palette's own DC generator, and the ladder alone would
3662    /// fail the vetting gate's `|mean|/rms` test without a blocker.
3663    #[test]
3664    fn a_vocoder_annihilates_its_carriers_dc() {
3665        let tree = sustained(AudioNode::Vocoder {
3666            uid: Uid::NEW,
3667            bands: 0.6,
3668            attack: 0.25,
3669            release: 0.3,
3670            mod_depth: 0.0,
3671            carrier: Box::new(AudioNode::Filter {
3672                uid: Uid::NEW,
3673                kind: FilterKind::Ladder,
3674                cutoff: 0.6,
3675                resonance: 0.4,
3676                mod_depth: 0.0,
3677                modulation: ModNode::None,
3678                input: Box::new(saw()),
3679            }),
3680            modulator: Box::new(AudioNode::Formant {
3681                uid: Uid::NEW,
3682                vowel: 0.3,
3683                shift: 0.5,
3684                octave: 0,
3685                mod_depth: 0.0,
3686                modulation: ModNode::None,
3687            }),
3688            modulation: ModNode::None,
3689        });
3690        assert!(
3691            !makes_dc(&tree.root),
3692            "a vocoder must not buy the voice a DC blocker"
3693        );
3694        quiver::rng::seed(0x2B_5EED);
3695        let mut v = compile(&tree, SR).expect("compiles");
3696        let out = hold(&mut v, 0.0, 44_100);
3697        let tail = &out[22_050..];
3698        let dc = tail.iter().map(|(l, _)| l).sum::<f64>() / tail.len() as f64;
3699        let level = rms(tail);
3700        assert!(level > 1e-3, "the vocoder was silent, nothing to measure");
3701        assert!(
3702            dc.abs() / level < 2.0e-3,
3703            "standing DC offset {dc:.6} against {level:.4} rms — the bandpass \
3704             zero this skips the blocker for is not where it was thought to be"
3705        );
3706    }
3707
3708    fn tree_params(tree: &PatchTree) -> Vec<String> {
3709        compile(tree, SR)
3710            .expect("compiles")
3711            .params
3712            .keys()
3713            .cloned()
3714            .collect()
3715    }
3716
3717    /// Amp envelope and VCA run exponential, not linear. Measured as the
3718    /// convexity of the decay: a linear contour through a linear VCA is a
3719    /// straight line to the sustain floor, so it sits at exactly half its
3720    /// starting level halfway through the decay.
3721    #[test]
3722    fn amp_contour_is_exponential() {
3723        let tree = PatchTree {
3724            amp: AmpEnv {
3725                attack: 0.0,
3726                decay: 0.7, // ≈630 ms
3727                sustain: 0.0,
3728                release: 0.3,
3729            },
3730            root: saw(),
3731        };
3732        let half_life = |exp: bool| {
3733            let mut v = compile(&tree, SR).expect("compiles");
3734            if !exp {
3735                // The gate is baked on both `Adsr.shape` and `Vca.response`.
3736                set_constant(&mut v, "voice:adsr", "shape", GATE_FALSE);
3737                set_constant(&mut v, "voice:vca", "response", GATE_FALSE);
3738            }
3739            let out = hold(&mut v, 0.0, (SR * 0.7) as usize);
3740            // Peak amplitude in each 10 ms window, as an envelope follower.
3741            let win = (SR * 0.01) as usize;
3742            let env: Vec<f64> = out
3743                .chunks(win)
3744                .map(|c| c.iter().fold(0.0f64, |m, (l, _)| m.max(l.abs())))
3745                .collect();
3746            let start = env[2];
3747            env.iter()
3748                .position(|&e| e < start * 0.5)
3749                .unwrap_or(env.len()) as f64
3750                * 0.01
3751        };
3752        let (exp, lin) = (half_life(true), half_life(false));
3753        assert!(
3754            exp < lin * 0.8,
3755            "decay is not exponential: half-life {exp:.2}s exp vs {lin:.2}s linear"
3756        );
3757    }
3758
3759    /// Tube drive rectifies, and `makes_dc` is right to buy a blocker for it.
3760    ///
3761    /// The premise first, measured on quiver's module rather than asserted: a
3762    /// zero-mean sine through the asymmetric curve comes out with a standing
3763    /// offset, while the two symmetric curves leave it at zero (the ~0.0015
3764    /// floor below is the window's own partial cycle, not a signal).
3765    ///
3766    /// The offset is largest at *low* drive — 7.7% of RMS at drive 0.05,
3767    /// falling to 1.0% at drive 1.0 — because `1 − e^{−x}` and `tanh(x)` both
3768    /// saturate to ±1, so heavy drive is nearly symmetric and it is the gentle
3769    /// settings, the ones a patch is most likely to use, that rectify. −22 dB
3770    /// of DC multiplied by the amp envelope is an audible per-note thump, and
3771    /// one whose spectrum reaches far above the offset itself.
3772    #[test]
3773    fn dc_blocker_removes_the_tube_distortion_offset() {
3774        let raw_offset = |mode_cv: f64| {
3775            let mut p = Patch::new(SR);
3776            p.set_validation_mode(ValidationMode::Warn);
3777            let osc = p.add("osc", Vco::new(SR));
3778            let d = p.add("d", Distortion::new(SR));
3779            p.connect(osc.out("sin"), d.in_("in")).expect("wires");
3780            // Tone wide open, so nothing but the shaper is being measured.
3781            for (port, v) in [
3782                ("drive", 0.1),
3783                ("tone", 1.0),
3784                ("mode", mode_cv),
3785                ("mix", 1.0),
3786            ] {
3787                assert!(p.set_param_by_id(d.id(), port, v), "no port {port}");
3788            }
3789            let out = p.add("out", StereoOutput::new());
3790            p.connect(d.out("out"), out.in_("left")).expect("wires");
3791            p.set_output(out.id());
3792            p.compile().expect("compiles");
3793            let buf: Vec<f64> = (0..(SR as usize)).map(|_| p.tick().0).collect();
3794            let tail = &buf[SR as usize / 2..];
3795            let mean = tail.iter().sum::<f64>() / tail.len() as f64;
3796            let rms = (tail.iter().map(|x| x * x).sum::<f64>() / tail.len() as f64).sqrt();
3797            mean.abs() / rms.max(1.0e-12)
3798        };
3799        let (soft, hard, tube) = (
3800            raw_offset(map::drive_mode_cv(0)),
3801            raw_offset(map::drive_mode_cv(1)),
3802            raw_offset(map::drive_mode_cv(2)),
3803        );
3804        assert!(
3805            tube > 0.05 && soft < 5.0e-3 && hard < 5.0e-3,
3806            "the asymmetry premise is wrong: soft {soft:.5}, hard {hard:.5}, tube {tube:.5}"
3807        );
3808
3809        // ...and the compiled voice has none of it left. Sustain is well
3810        // under the limiter: clipping an asymmetric waveform is itself a
3811        // rectifier, downstream of the blocker, and it would be measured here
3812        // as a failure of a stage that cannot see it.
3813        let tree = PatchTree {
3814            amp: AmpEnv {
3815                attack: 0.05,
3816                decay: 0.3,
3817                sustain: 0.35,
3818                release: 0.3,
3819            },
3820            root: AudioNode::Distortion {
3821                uid: Uid::NEW,
3822                drive: 0.15,
3823                tone: 0.7,
3824                mode: DriveMode::Tube,
3825                mod_depth: 0.0,
3826                input: Box::new(saw()),
3827                modulation: ModNode::None,
3828            },
3829        };
3830        assert!(makes_dc(&tree.root), "tube drive must buy a blocker");
3831        let mut v = compile(&tree, SR).expect("compiles");
3832        let n = (SR * 3.0) as usize;
3833        let out = hold(&mut v, -1.0, n);
3834        let tail = &out[n / 2..];
3835        let dc = tail.iter().map(|(l, _)| l).sum::<f64>() / tail.len() as f64;
3836        let level = rms(tail);
3837        assert!(level > 0.1, "patch was silent, nothing to measure");
3838        assert!(
3839            dc.abs() / level < 2.0e-3,
3840            "standing DC offset {dc:.6} against {level:.4} rms"
3841        );
3842        // The symmetric modes pay nothing for it.
3843        for mode in [DriveMode::Soft, DriveMode::Hard] {
3844            let clean = sustained(AudioNode::Distortion {
3845                uid: Uid::NEW,
3846                drive: 0.15,
3847                tone: 0.7,
3848                mode,
3849                mod_depth: 0.0,
3850                input: Box::new(saw()),
3851                modulation: ModNode::None,
3852            });
3853            assert!(!makes_dc(&clean.root), "{mode:?} must not buy a blocker");
3854        }
3855    }
3856
3857    /// The envelope follower rides the owning module's *own* input, and
3858    /// degrades to silence rather than to a panic where there is no input.
3859    #[test]
3860    fn the_follower_reads_the_signal_below_it() {
3861        // A lowpass whose cutoff is opened by the level of what it is
3862        // filtering. Playing louder is not available, so the counterfactual is
3863        // the same tree with the depth knob at zero: identical graph, one
3864        // attenuverter neutralized.
3865        let tree = |depth: f64| {
3866            sustained(AudioNode::Filter {
3867                uid: Uid::NEW,
3868                kind: FilterKind::SvfLp,
3869                cutoff: 0.25,
3870                resonance: 0.0,
3871                mod_depth: depth,
3872                input: Box::new(saw()),
3873                modulation: ModNode::Follow {
3874                    uid: Uid::NEW,
3875                    sens: 0.8,
3876                    release: 0.3,
3877                },
3878            })
3879        };
3880        let level = |depth: f64| {
3881            let mut v = compile(&tree(depth), SR).expect("compiles");
3882            let out = hold(&mut v, 0.0, 44_100);
3883            rms(&out[22_050..])
3884        };
3885        let (off, on) = (level(0.0), level(0.9));
3886        assert!(
3887            on > off * 1.05,
3888            "the follower is inaudible: {off:.4} closed vs {on:.4} open"
3889        );
3890
3891        // A source's slot has nothing to tap. That must compile and stay
3892        // silent on the cable, because both the prior and the panel can put a
3893        // follower there.
3894        let lone = sustained(AudioNode::Wavetable {
3895            uid: Uid::NEW,
3896            table: crate::term::TableShape::Saw,
3897            octave: 0,
3898            morph: 0.4,
3899            mod_depth: 0.8,
3900            modulation: ModNode::Follow {
3901                uid: Uid::NEW,
3902                sens: 0.8,
3903                release: 0.3,
3904            },
3905        });
3906        let mut v = compile(&lone, SR).expect("a follower on a source must still compile");
3907        assert!(
3908            rms(&hold(&mut v, 0.0, 22_050)) > 1.0e-3,
3909            "the wavetable went silent"
3910        );
3911    }
3912
3913    /// `table_cv` has to land each table on an *exact* integer position in
3914    /// quiver's stack, because the port is a crossfade, not a selector: quiver
3915    /// takes `idx = floor(cv·7)` and blends table `idx` into `idx+1` by
3916    /// `frac + morph`. Any non-zero `frac` both mis-names the table on the
3917    /// plate and eats the top of the morph knob's travel, and neither symptom
3918    /// is visible in a diff — this is the guard that makes it visible.
3919    #[test]
3920    fn every_wavetable_shape_lands_on_its_own_table() {
3921        for i in 0..TableShape::ALL.len() {
3922            let pos = map::table_cv(i as f64) * 7.0;
3923            let frac = pos - pos.floor();
3924            assert!(
3925                frac < 1e-12 || (1.0 - frac) < 1e-12,
3926                "table {i} lands at {pos} — fraction {frac} blends it into its neighbour"
3927            );
3928            // …and inside the stack, so no shape is unreachable.
3929            assert!(
3930                (0.0..=7.0).contains(&pos),
3931                "table {i} maps outside the stack"
3932            );
3933        }
3934        // Distinct tables, in order: the plate's index IS the table you hear.
3935        let cvs: Vec<f64> = (0..TableShape::ALL.len())
3936            .map(|i| map::table_cv(i as f64))
3937            .collect();
3938        assert!(
3939            cvs.windows(2).all(|w| w[1] > w[0]),
3940            "table CVs are not monotonic"
3941        );
3942    }
3943
3944    /// ...and it is not a bass cut. A DC blocker that audits as "thin" has
3945    /// traded one defect for a worse one, so the passband is pinned where the
3946    /// instrument actually plays.
3947    #[test]
3948    fn dc_blocker_keeps_the_bass() {
3949        let tree = PatchTree {
3950            amp: AmpEnv {
3951                attack: 0.1,
3952                decay: 0.3,
3953                sustain: 0.3, // well under the limiter, so gains are readable
3954                release: 0.3,
3955            },
3956            // A ladder, so the patch actually receives a blocker; a sine
3957            // through it stays a sine, so output level reads as filter gain.
3958            root: AudioNode::Filter {
3959                uid: Uid::NEW,
3960                kind: FilterKind::Ladder,
3961                cutoff: 1.0,
3962                resonance: 0.0,
3963                mod_depth: 0.0,
3964                input: Box::new(AudioNode::Vco {
3965                    uid: Uid::NEW,
3966                    wave: Waveform::Sine,
3967                    octave: 0,
3968                    detune: 0.5,
3969                    mod_depth: 0.0,
3970                    modulation: ModNode::None,
3971                }),
3972                modulation: ModNode::None,
3973            },
3974        };
3975        let at = |voct: f64| {
3976            let mut v = compile(&tree, SR).expect("compiles");
3977            let out = hold(&mut v, voct, 44_100);
3978            rms(&out[22_050..])
3979        };
3980        let reference = at(0.0); // C4
3981                                 // C2 (65 Hz) within 1 dB, C3 within 0.5 dB.
3982        assert!(at(-2.0) > reference * 0.89, "C2 lost more than 1 dB");
3983        assert!(at(-1.0) > reference * 0.945, "C3 lost more than 0.5 dB");
3984    }
3985
3986    // ---------------------------------------------------------------------
3987    // Wave 2C: modulation as a sort.
3988    //
3989    // Every test below measures the *scale* of a control rather than the
3990    // presence of a cable, because four palette waves running the presence
3991    // check let four dead controls through.
3992    // ---------------------------------------------------------------------
3993
3994    /// A sine oscillator whose **pitch** is driven by `m`.
3995    ///
3996    /// Pitch is the measuring destination for the whole of this wave: it is
3997    /// the only mod target whose value can be read straight out of the
3998    /// rendered audio, by counting zero crossings, without a spectral estimate
3999    /// in the way.
4000    fn pitch_modulated(m: ModNode, mod_depth: f64) -> PatchTree {
4001        sustained(AudioNode::Vco {
4002            uid: Uid::NEW,
4003            wave: Waveform::Sine,
4004            octave: 0,
4005            detune: 0.5,
4006            mod_depth,
4007            modulation: m,
4008        })
4009    }
4010
4011    /// A slow triangle LFO: 0.125 Hz on quiver's `0.01·3000^cv` map, so one
4012    /// cycle takes 8 s and a 4 s render sweeps up and back down once.
4013    const SLOW_LFO_RATE: f64 = 0.3155;
4014
4015    /// Semitone offsets of each window relative to the lowest, read off the
4016    /// zero-crossing count. At C6 a 100 ms window holds ~209 crossings, so the
4017    /// ±1 count quantization is 0.08 of a semitone — fine enough to say
4018    /// whether a pitch landed on the 12-TET grid.
4019    fn semitone_track(out: &[(f64, f64)], windows: usize) -> Vec<f64> {
4020        let counts = crossings_per_window(out, windows);
4021        let lo = *counts.iter().min().expect("windows") as f64;
4022        counts
4023            .iter()
4024            .map(|c| 12.0 * (*c as f64 / lo.max(1.0)).log2())
4025            .collect()
4026    }
4027
4028    /// Transitions of more than a semitone between adjacent windows — how a
4029    /// gate arriving on a pitch cable reads.
4030    fn gate_edges(out: &[(f64, f64)], windows: usize) -> usize {
4031        let t = semitone_track(out, windows);
4032        t.windows(2).filter(|w| (w[0] - w[1]).abs() > 1.0).count()
4033    }
4034
4035    /// The quantizer snaps a modulator onto the **12-TET grid**, and the grid
4036    /// is sized so a fully-modulated pitch cable lands on whole semitones.
4037    ///
4038    /// This is the one module in the wave whose musical claim is arithmetic
4039    /// rather than taste, and it is arithmetic in three stages that multiply.
4040    /// `ScaleQuantizer` snaps in V/Oct on a fixed 1/12 V grid, so handed a
4041    /// modulator at its native ±5 V it emits 121 steps across ±60 semitones —
4042    /// a "quantizer" whose output is finer than the ear and, after the mod
4043    /// cable's own 0.1 gain, finer than a tenth of a semitone.
4044    /// [`QUANTIZE_IN_LEVEL`] scales the input into a ±6 semitone window and
4045    /// its inverse scales the output back out, so the *grid* is resized and
4046    /// the cable's gain is not.
4047    ///
4048    /// Both halves are asserted: that the modulation still spans its full
4049    /// ±0.5 octave (the round trip is unity, not an attenuation), and that
4050    /// what it visits on the way is a staircase on the semitone grid rather
4051    /// than a ramp.
4052    #[test]
4053    fn the_quantizer_lands_a_pitch_cable_on_whole_semitones() {
4054        let lfo = || ModNode::Lfo {
4055            uid: Uid::NEW,
4056            wave: Waveform::Triangle,
4057            rate: SLOW_LFO_RATE,
4058        };
4059        let track = |m: ModNode| {
4060            let mut v = compile(&pitch_modulated(m, 1.0), SR).expect("compiles");
4061            // Two octaves up, for the crossing-count resolution the grid
4062            // check needs.
4063            let out = hold(&mut v, 2.0, (SR * 4.0) as usize);
4064            semitone_track(&out, 40)
4065        };
4066        let quantized = track(ModNode::Op {
4067            uid: Uid::NEW,
4068            kind: ModOp::Quantize,
4069            p0: 0.0, // root C
4070            p1: 0.0, // chromatic — every semitone is reachable
4071            input: Box::new(lfo()),
4072        });
4073        let plain = track(lfo());
4074
4075        // 1. The round trip is transparent: a triangle sweeping the full
4076        //    ±0.5 octave still spans an octave after being quantized.
4077        let span = |t: &[f64]| t.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
4078        let (qs, ps) = (span(&quantized), span(&plain));
4079        assert!(
4080            ps > 8.0,
4081            "the control sweep is too small to measure: {ps:.2}"
4082        );
4083        assert!(
4084            (qs - ps).abs() < 1.5,
4085            "quantizing changed the modulation's range: {qs:.2} vs {ps:.2} \
4086             semitones — the input and output levels do not cancel"
4087        );
4088
4089        // 2. It is a staircase. Adjacent windows land on the *same* pitch far
4090        //    more often than a continuous sweep ever does…
4091        let plateaus = |t: &[f64]| {
4092            t.windows(2).filter(|w| (w[0] - w[1]).abs() < 0.05).count() as f64
4093                / (t.len() - 1) as f64
4094        };
4095        let (qp, pp) = (plateaus(&quantized), plateaus(&plain));
4096        assert!(
4097            qp > 0.3 && qp > pp + 0.15,
4098            "quantized pitch is not stepped: {:.0}% of windows held, against \
4099             {:.0}% for the unquantized control",
4100            100.0 * qp,
4101            100.0 * pp
4102        );
4103        // …and every pitch it holds is on the grid, which is the claim about
4104        // the grid's *size* rather than about its existence.
4105        let off_grid = |t: &[f64]| {
4106            let mut d: Vec<f64> = t.iter().map(|s| (s - s.round()).abs()).collect();
4107            d.sort_by(f64::total_cmp);
4108            d[d.len() / 2]
4109        };
4110        let (qg, pg) = (off_grid(&quantized), off_grid(&plain));
4111        assert!(
4112            qg < 0.15 && qg < pg * 0.7,
4113            "quantized pitch sits {qg:.3} semitones off the grid (control \
4114             {pg:.3}) — QUANTIZE_IN_LEVEL is not sizing the grid to the \
4115             destination"
4116        );
4117    }
4118
4119    /// A euclidean pattern's clock spans the tempo the port actually offers.
4120    ///
4121    /// `Clock`'s `bpm` port is `CvUnipolar`, which in quiver is **0–10 V and
4122    /// not 0–1**: `cv_to_bpm` is `20·15^(cv/10)`, so passing the raw knob
4123    /// through would have given 20 BPM at one end of the control and 21.4 at
4124    /// the other — a rate knob with a 7% range. `ParamMap::ClockRate` spans
4125    /// the port, and the ratio asserted here is most of the 15× the map can
4126    /// produce.
4127    #[test]
4128    fn the_euclid_clock_spans_the_ports_own_tempo_range() {
4129        let edges = |rate: f64| {
4130            let m = ModNode::Euclid {
4131                uid: Uid::NEW,
4132                rate,
4133                steps: 0.3,
4134                pulses: 0.6,
4135            };
4136            let mut v = compile(&pitch_modulated(m, 1.0), SR).expect("compiles");
4137            let out = hold(&mut v, 2.0, (SR * 8.0) as usize);
4138            gate_edges(&out, 400)
4139        };
4140        let (slow, fast) = (edges(0.0), edges(1.0));
4141        assert!(slow > 0, "the slowest clock never fired at all");
4142        assert!(
4143            fast as f64 / slow as f64 > 5.0,
4144            "the euclid rate knob spans only {:.1}× ({slow} to {fast} edges in \
4145             8 s) — the bpm port is 0–10 V, not 0–1",
4146            fast as f64 / slow as f64
4147        );
4148    }
4149
4150    /// Neither end of the euclid's `pulses` knob is a dead cable, at either
4151    /// end of its `steps` knob.
4152    ///
4153    /// quiver takes `pulses = (cv · steps) as usize`, so a raw knob emits
4154    /// nothing at all below `1/steps` — about one uniform draw in seven — and
4155    /// a solid gate at exactly 1.0. `ParamMap::EuclidSteps` gives up the two
4156    /// shortest patterns so that one CV floor can serve every step count, and
4157    /// this checks all four corners.
4158    #[test]
4159    fn every_corner_of_the_euclid_knobs_still_makes_a_rhythm() {
4160        let edges = |steps: f64, pulses: f64| {
4161            let m = ModNode::Euclid {
4162                uid: Uid::NEW,
4163                rate: 1.0,
4164                steps,
4165                pulses,
4166            };
4167            let mut v = compile(&pitch_modulated(m, 1.0), SR).expect("compiles");
4168            let out = hold(&mut v, 2.0, (SR * 8.0) as usize);
4169            gate_edges(&out, 400)
4170        };
4171        for (steps, pulses) in [(0.0, 0.0), (0.0, 1.0), (1.0, 0.0), (1.0, 1.0)] {
4172            assert!(
4173                edges(steps, pulses) > 0,
4174                "euclid at steps {steps} / pulses {pulses} emits a constant"
4175            );
4176        }
4177    }
4178
4179    /// The switch hears **both** of its branches.
4180    ///
4181    /// quiver's `VcSwitch` needs a third input to choose with and `Pair` has
4182    /// only two to give. The contract proposed the voice gate; that is a
4183    /// control that reviews as correct and does nothing, because the gate is
4184    /// high for the whole of every note and low only between notes when the
4185    /// VCA is shut — so `b` would win every sample anybody hears and `a` would
4186    /// be a module on the rack that is never once audible. Wiring `b` as its
4187    /// own control makes the module "punch `b` in over `a`", and the way to
4188    /// prove `a` is alive is to change only `a`.
4189    #[test]
4190    fn the_switch_is_not_stuck_on_one_branch() {
4191        let render = |a_rate: f64| {
4192            let m = ModNode::Pair {
4193                uid: Uid::NEW,
4194                kind: PairOp::Switch,
4195                a: Box::new(ModNode::Lfo {
4196                    uid: Uid::NEW,
4197                    wave: Waveform::Triangle,
4198                    rate: a_rate,
4199                }),
4200                b: Box::new(ModNode::Euclid {
4201                    uid: Uid::NEW,
4202                    rate: 0.7,
4203                    steps: 0.4,
4204                    pulses: 0.5,
4205                }),
4206            };
4207            let mut v = compile(&pitch_modulated(m, 1.0), SR).expect("compiles");
4208            let out = hold(&mut v, 2.0, (SR * 2.0) as usize);
4209            crossings_per_window(&out, 40)
4210        };
4211        let (slow, fast) = (render(SLOW_LFO_RATE), render(0.6));
4212        let moved = slow
4213            .iter()
4214            .zip(&fast)
4215            .filter(|(a, b)| (**a as i64 - **b as i64).abs() > 4)
4216            .count();
4217        assert!(
4218            moved * 4 > slow.len(),
4219            "changing only the switch's `a` branch moved {moved} of {} windows \
4220             — that branch is never selected",
4221            slow.len()
4222        );
4223    }
4224
4225    /// The slew limiter's useful glide times are on the plate rather than
4226    /// crammed into its first quarter, and its top is a freeze rather than a
4227    /// third of one.
4228    ///
4229    /// quiver's own map is `0.001 + cv²·10` seconds — already square-law — so
4230    /// a raw knob puts every glide under 1.5 s below position 0.39 and spends
4231    /// the remaining three fifths of its travel holding the modulator still.
4232    /// `ParamMap::SlewTime` is `0.4·x`, which is measured here at three
4233    /// points: a quarter turn already smooths, and the top of the knob has
4234    /// nearly stopped the modulator.
4235    #[test]
4236    fn the_slew_knob_spends_its_travel_on_audible_glide_times() {
4237        // Movement per window: how far the pitch jumps between adjacent
4238        // 50 ms windows. A stepped source jumps; a slewed one ramps.
4239        // The *largest* jump between adjacent 50 ms windows, which is the
4240        // step height a slew limiter exists to soften. A mean would be the
4241        // wrong statistic: slewing spreads one big jump over several windows,
4242        // so it moves the mean up while moving the maximum down.
4243        let jump = |m: ModNode| {
4244            let mut v = compile(&pitch_modulated(m, 1.0), SR).expect("compiles");
4245            let out = hold(&mut v, 2.0, (SR * 4.0) as usize);
4246            // 25 ms windows: fine enough that a 50 ms glide — a quarter turn
4247            // of the knob — spreads its step across more than one of them.
4248            let t = semitone_track(&out, 160);
4249            t.windows(2)
4250                .map(|w| (w[0] - w[1]).abs())
4251                .fold(0.0f64, f64::max)
4252        };
4253        let stepped = || ModNode::Euclid {
4254            uid: Uid::NEW,
4255            rate: 0.75,
4256            steps: 0.3,
4257            pulses: 0.5,
4258        };
4259        let slewed = |t: f64| ModNode::Op {
4260            uid: Uid::NEW,
4261            kind: ModOp::Slew,
4262            p0: t,
4263            p1: t,
4264            input: Box::new(stepped()),
4265        };
4266        let bare = jump(stepped());
4267        assert!(bare > 3.0, "the control source barely moves: {bare:.3}");
4268        let quarter = jump(slewed(0.25));
4269        assert!(
4270            quarter < bare * 0.75,
4271            "a quarter turn of slew changed the step height from {bare:.3} to \
4272             {quarter:.3} — the useful glide times are not on the plate"
4273        );
4274        let full = jump(slewed(1.0));
4275        assert!(
4276            full < quarter * 0.4,
4277            "full slew ({full:.3}) is not much slower than a quarter turn \
4278             ({quarter:.3})"
4279        );
4280    }
4281
4282    /// The rectifier picks an output **port**, and the three it offers are
4283    /// three different signals.
4284    ///
4285    /// quiver's `Rectifier` has no `mode` input at all — it publishes `full`,
4286    /// `half_pos` and `half_neg` simultaneously — so `rmode` chooses a cable
4287    /// at compile time rather than writing a CV, which is also why it is the
4288    /// one 2C knob with no live handle.
4289    #[test]
4290    fn the_three_rectifier_modes_are_three_different_signals() {
4291        let track = |mode: f64| {
4292            let m = ModNode::Op {
4293                uid: Uid::NEW,
4294                kind: ModOp::Rectify,
4295                p0: mode,
4296                p1: 0.0,
4297                input: Box::new(ModNode::Lfo {
4298                    uid: Uid::NEW,
4299                    wave: Waveform::Triangle,
4300                    rate: SLOW_LFO_RATE,
4301                }),
4302            };
4303            let mut v = compile(&pitch_modulated(m, 1.0), SR).expect("compiles");
4304            let out = hold(&mut v, 2.0, (SR * 4.0) as usize);
4305            crossings_per_window(&out, 40)
4306        };
4307        let differs = |a: &[usize], b: &[usize]| {
4308            a.iter()
4309                .zip(b)
4310                .filter(|(x, y)| (**x as i64 - **y as i64).abs() > 4)
4311                .count()
4312        };
4313        // Cell centres of a three-way split: full / positive / negative.
4314        let (full, pos, neg) = (track(0.1), track(0.5), track(0.9));
4315        assert!(
4316            differs(&full, &pos) * 5 > full.len(),
4317            "full-wave and positive-half rectification render the same"
4318        );
4319        assert!(
4320            differs(&pos, &neg) * 5 > pos.len(),
4321            "the two half-wave modes render the same"
4322        );
4323        // A full-wave rectified triangle is a triangle at twice the rate and
4324        // never goes below the base note, so the *lowest* pitch it visits is
4325        // the unmodulated one — which is what "folded into one polarity"
4326        // means and what distinguishes it from the bare LFO.
4327        let bare = {
4328            let mut v = compile(
4329                &pitch_modulated(
4330                    ModNode::Lfo {
4331                        uid: Uid::NEW,
4332                        wave: Waveform::Triangle,
4333                        rate: SLOW_LFO_RATE,
4334                    },
4335                    1.0,
4336                ),
4337                SR,
4338            )
4339            .expect("compiles");
4340            let out = hold(&mut v, 2.0, (SR * 4.0) as usize);
4341            crossings_per_window(&out, 40)
4342        };
4343        assert!(
4344            differs(&full, &bare) * 5 > full.len(),
4345            "rectifying a bipolar LFO changed nothing"
4346        );
4347    }
4348
4349    /// A modulation chain compiles as a chain: two processors over a leaf all
4350    /// reach the destination, and every knob in it is a real trace address.
4351    ///
4352    /// This is the shape the whole wave exists for — `s&h rand → quantize →
4353    /// slew` — and the thing that would break silently is the recursion
4354    /// dropping a level and wiring the leaf straight to the attenuverter.
4355    #[test]
4356    fn a_two_deep_mod_chain_reaches_the_destination_through_every_stage() {
4357        let chain = ModNode::Op {
4358            uid: Uid::NEW,
4359            kind: ModOp::Slew,
4360            p0: 0.3,
4361            p1: 0.3,
4362            input: Box::new(ModNode::Op {
4363                uid: Uid::NEW,
4364                kind: ModOp::Quantize,
4365                p0: 0.0,
4366                p1: 2.5 / 7.0, // minor
4367                input: Box::new(ModNode::Rand {
4368                    uid: Uid::NEW,
4369                    rate: 0.6,
4370                    glide: 0.0,
4371                }),
4372            }),
4373        };
4374        // Two processors over a leaf — the deepest term the default prior can
4375        // draw, so this is the shape the search actually has to survive and
4376        // not a hand-built extreme.
4377        assert_eq!(
4378            chain.depth(),
4379            1 + crate::PatchGrammarPrior::default().max_mod_depth
4380        );
4381        let tree = pitch_modulated(chain, 1.0);
4382        let v = compile(&tree, SR).expect("compiles");
4383        // Each stage's knobs live under its own key, one level deeper than
4384        // its parent's — `node/m` for the slew, `node/m/0` for the quantizer,
4385        // `node/m/0/0` for the S&H.
4386        for addr in [
4387            "node#mdepth",
4388            "node/m#rise",
4389            "node/m#fall",
4390            "node/m/0#qroot",
4391            "node/m/0#qscale",
4392            "node/m/0/0#rate",
4393            "node/m/0/0#glide",
4394        ] {
4395            assert!(
4396                v.params.contains_key(addr),
4397                "chain stage `{addr}` has no live handle"
4398            );
4399        }
4400        // And the top stage is what reaches the oscillator: a recursion that
4401        // dropped a level would wire the leaf straight to the attenuverter
4402        // and leave the hard steps on the pitch.
4403        //
4404        // Measured on the same two-deep shape with a **euclidean** leaf
4405        // rather than the S&H one above, because the comparison has to be
4406        // controlled: two patches containing a noise generator are two
4407        // different random signals, and the difference between them would
4408        // measure the noise rather than the slew.
4409        let jump = |m: ModNode| {
4410            let mut v = compile(&pitch_modulated(m, 1.0), SR).expect("compiles");
4411            let out = hold(&mut v, 2.0, (SR * 4.0) as usize);
4412            let t = semitone_track(&out, 160);
4413            t.windows(2)
4414                .map(|w| (w[0] - w[1]).abs())
4415                .fold(0.0f64, f64::max)
4416        };
4417        let inner = || ModNode::Op {
4418            uid: Uid::NEW,
4419            kind: ModOp::Quantize,
4420            p0: 0.0,
4421            p1: 2.5 / 7.0, // minor
4422            input: Box::new(ModNode::Euclid {
4423                uid: Uid::NEW,
4424                rate: 0.75,
4425                steps: 0.3,
4426                pulses: 0.5,
4427            }),
4428        };
4429        let raw = jump(inner());
4430        let slewed = jump(ModNode::Op {
4431            uid: Uid::NEW,
4432            kind: ModOp::Slew,
4433            p0: 1.0,
4434            p1: 1.0,
4435            input: Box::new(inner()),
4436        });
4437        assert!(raw > 3.0, "the two-stage control barely moves: {raw:.3}");
4438        assert!(
4439            slewed < raw * 0.5,
4440            "the slew stage did not reach the pitch: {slewed:.3} against \
4441             {raw:.3} without it"
4442        );
4443    }
4444
4445    /// FNV-1a over the *bits* of every sample a voice renders: 3000 frames
4446    /// with the gate high, then 1096 with it low, at a pitch of `7/12`.
4447    ///
4448    /// The pitch is a tempered fifth on purpose. It is not a binary fraction,
4449    /// so `pitch + (oct + detune)` and `(pitch + oct) + detune` disagree in
4450    /// the last bit — any change that reassociates the pitch sum shows up here
4451    /// rather than in someone's ears.
4452    fn render_fingerprint(mut v: CompiledVoice) -> u64 {
4453        v.pitch.set(7.0 / 12.0);
4454        v.gate.set(5.0);
4455        let mut h: u64 = 0xcbf2_9ce4_8422_2325;
4456        for i in 0..4096u32 {
4457            if i == 3000 {
4458                v.gate.set(0.0);
4459            }
4460            let (l, r) = v.patch.tick();
4461            for x in [l, r] {
4462                h ^= x.to_bits();
4463                h = h.wrapping_mul(0x0000_0100_0000_01b3);
4464            }
4465        }
4466        h
4467    }
4468
4469    /// Put a compiled voice back the way the compiler built it *before*
4470    /// `table` and `oct` were live: pull the two new [`ExternalInput`] cables
4471    /// out and write their values back where they used to be baked.
4472    ///
4473    /// This is what makes the regression test a real counterfactual rather
4474    /// than a captured constant. A hard-coded golden vector would pin the
4475    /// render to whichever machine captured it — quiver's transcendentals are
4476    /// a pure-Rust libm, but nothing in this repo guarantees that for every
4477    /// dependency on every target, and a flaky CI golden teaches people to
4478    /// re-baseline it, which is precisely the thing it exists to prevent.
4479    /// Rendering both graphs in the same process compares the arithmetic
4480    /// itself.
4481    ///
4482    /// (The stronger check was also run once, by hand, across the two real
4483    /// builds: 16 prior draws plus this corpus, hashed before and after the
4484    /// change, identical — which additionally covers what this cannot, that
4485    /// two extra nodes per source do not reorder the execution of the
4486    /// `NoiseGenerator`s that draw from quiver's shared RNG.)
4487    ///
4488    /// Returns how many cables it removed, so a test cannot quietly pass by
4489    /// comparing a patch with nothing in it.
4490    fn unlive_table_and_oct(v: &mut CompiledVoice) -> usize {
4491        let names: Vec<String> = v.patch.nodes().map(|(_, n, _)| n.to_string()).collect();
4492        let mut pulled = 0;
4493        for name in names {
4494            if let Some(key) = name.strip_suffix(":table!") {
4495                let src = v.patch.get_handle_by_name(&name).expect("just listed");
4496                let wt = v
4497                    .patch
4498                    .get_handle_by_name(&format!("{key}:wavetable"))
4499                    .expect("a `table!` knob belongs to a wavetable");
4500                // The atomic holds exactly what `constant` used to pin.
4501                let baked = v.params[&format!("{key}#table")].value.get();
4502                v.patch
4503                    .disconnect_ports(src.out("out"), wt.in_("table"))
4504                    .expect("the cable this stage added");
4505                assert!(v.patch.set_param_by_id(wt.id(), "table", baked));
4506                pulled += 1;
4507            } else if let Some(key) = name.strip_suffix(":oct!") {
4508                let src = v.patch.get_handle_by_name(&name).expect("just listed");
4509                let off = v
4510                    .patch
4511                    .get_handle_by_name(&format!("{key}:pitch"))
4512                    .expect("an `oct!` knob belongs to a pitch offset");
4513                // Nothing to bake back: the trim is zero at compile time and
4514                // the octave never left the `Offset`. That assertion *is* the
4515                // bit-exactness argument, so make it out loud.
4516                assert_eq!(
4517                    v.params[&format!("{key}#oct")].value.get(),
4518                    0.0,
4519                    "{key}: a freshly compiled octave trim must be exactly zero"
4520                );
4521                v.patch
4522                    .disconnect_ports(src.out("out"), off.in_("in"))
4523                    .expect("the cable this stage added");
4524                pulled += 1;
4525            }
4526        }
4527        v.patch.compile().expect("recompiles without the cables");
4528        pulled
4529    }
4530
4531    /// Every source that has an octave, at a non-zero octave, with something
4532    /// on its modulation slot — the exact shape the pitch sum is fragile in,
4533    /// since a mod cable joins the same gather. Plus one wavetable per table
4534    /// shape, `table` being the other constant that became a cable.
4535    fn pitch_and_table_corpus() -> Vec<PatchTree> {
4536        let lfo = || ModNode::Lfo {
4537            uid: Uid::NEW,
4538            wave: Waveform::Triangle,
4539            rate: 0.4,
4540        };
4541        let mut out = vec![
4542            sustained(AudioNode::Vco {
4543                uid: Uid::NEW,
4544                wave: Waveform::Saw,
4545                octave: -2,
4546                detune: 0.31,
4547                mod_depth: 0.6,
4548                modulation: lfo(),
4549            }),
4550            sustained(AudioNode::Vco {
4551                uid: Uid::NEW,
4552                wave: Waveform::Square,
4553                octave: 2,
4554                detune: 0.83,
4555                mod_depth: 0.0,
4556                modulation: ModNode::None,
4557            }),
4558            sustained(AudioNode::Supersaw {
4559                uid: Uid::NEW,
4560                octave: -1,
4561                detune: 0.4,
4562                mix: 0.6,
4563                mod_depth: 0.45,
4564                modulation: lfo(),
4565            }),
4566            sustained(AudioNode::Pluck {
4567                uid: Uid::NEW,
4568                octave: 1,
4569                damping: 0.6,
4570                brightness: 0.4,
4571                mod_depth: 0.3,
4572                modulation: lfo(),
4573            }),
4574            sustained(AudioNode::Formant {
4575                uid: Uid::NEW,
4576                vowel: 0.3,
4577                shift: 0.5,
4578                octave: -1,
4579                mod_depth: 0.2,
4580                modulation: lfo(),
4581            }),
4582        ];
4583        for (i, table) in TableShape::ALL.iter().enumerate() {
4584            out.push(sustained(AudioNode::Wavetable {
4585                uid: Uid::NEW,
4586                table: *table,
4587                octave: (i as i8 % 5) - 2,
4588                morph: 0.37,
4589                mod_depth: if i % 2 == 0 { 0.0 } else { 0.55 },
4590                modulation: if i % 2 == 0 { ModNode::None } else { lfo() },
4591            }));
4592        }
4593        out
4594    }
4595
4596    /// **The standing rule, in CI.** Making `table` and `oct` live must not
4597    /// move one sample of any patch that already exists — a rendered-audio
4598    /// change invalidates the bank's featurisation and the taste posterior,
4599    /// and needs a measured evolution revalidation, not a green `make check`.
4600    ///
4601    /// So the claim is checked rather than asserted: every patch is rendered
4602    /// twice in the same process, once through the live cables and once
4603    /// through [`unlive_table_and_oct`], and every bit of every sample must
4604    /// agree. Sixteen prior draws for breadth, then a corpus built to hit
4605    /// every octave-bearing source and every wavetable shape.
4606    #[test]
4607    fn table_and_oct_going_live_moved_no_sample() {
4608        use crate::prior::PatchGrammarPrior;
4609        use rand::rngs::StdRng;
4610        use rand::SeedableRng;
4611
4612        let prior = PatchGrammarPrior::default();
4613        let mut rng = StdRng::seed_from_u64(0x9E37_79B9_7F4A_7C15);
4614        let trees: Vec<PatchTree> = (0..16)
4615            .map(|_| prior.sample_with_rng(&mut rng))
4616            .chain(pitch_and_table_corpus())
4617            .collect();
4618
4619        let mut pulled_total = 0;
4620        for (i, tree) in trees.iter().enumerate() {
4621            // Noise draws from quiver's thread-local RNG, so both renders have
4622            // to start from the same state or the comparison means nothing.
4623            quiver::rng::seed(0x60_1DE5);
4624            let live = render_fingerprint(compile(tree, SR).expect("compiles"));
4625
4626            quiver::rng::seed(0x60_1DE5);
4627            let mut baked = compile(tree, SR).expect("compiles");
4628            pulled_total += unlive_table_and_oct(&mut baked);
4629            let baked = render_fingerprint(baked);
4630
4631            assert_eq!(
4632                format!("{live:#018x}"),
4633                format!("{baked:#018x}"),
4634                "patch {i} renders differently with `table`/`oct` live:\n{}",
4635                tree.to_sexpr()
4636            );
4637        }
4638        assert!(
4639            pulled_total >= trees.len(),
4640            "only {pulled_total} live cables across {} patches — the corpus is \
4641             not exercising the sites this test is about",
4642            trees.len()
4643        );
4644    }
4645}