Skip to main content

auracle_grammar/
term.rs

1//! The patch term: a typed tree over the v1 module palette.
2//!
3//! The Audio/Mod sort distinction is enforced by the Rust type system itself —
4//! an ill-sorted term (an LFO where audio is expected, a filter in a
5//! modulation slot) is unrepresentable. The grammar prior samples these types
6//! directly and the compiler interprets them into a quiver [`Patch`](quiver);
7//! there is no unvalidated intermediate representation.
8//!
9//! All continuous parameters are **normalized to `[0, 1]`** (uniform prior
10//! support); musical mappings (log cutoff scales, bounded resonance/feedback)
11//! live in [`crate::compile`]. Discrete parameters are small enums.
12
13use std::hash::{Hash, Hasher};
14use std::sync::atomic::{AtomicU64, Ordering};
15
16use serde::{Deserialize, Serialize};
17
18/// A stable identity for one node, independent of where it sits.
19///
20/// # Why the tree needs one
21///
22/// Every other handle on a node in this system is its **position**: the trace
23/// key (`node/0/1`), and therefore the trace address of every knob on it, and
24/// therefore every lock, every remembered hand position, every selection. That
25/// is fine until the structure moves. Insert one filter at the top of a chain
26/// and every key below it shifts by one segment; delete a mixer branch and the
27/// survivor is re-rooted; run one generation of refinement and the tree is
28/// rebuilt from a trace with no memory of the object graph at all. Under
29/// positional identity the only honest response to a structural edit is to
30/// throw the UI's state away — which is exactly what the workbench used to do,
31/// with the comment "structure changed — locks cleared" — and that destroys the
32/// loop the graph editor exists to serve: build a routing by hand, pin it, and
33/// breed around it.
34///
35/// A `Uid` is minted once, travels with the node through clones, splices and
36/// refinement, and is dropped only when the node itself is.
37///
38/// # It is deliberately invisible to the model
39///
40/// `uid` is UI identity, nothing else. It must never reach the generative
41/// model, the featurizer, the compiler, or anything content-addressed, because
42/// two patches that differ only in uids are the *same patch* and every one of
43/// those systems is entitled to say so:
44///
45/// - **Equality ignores it.** [`PartialEq`] here is `true` unconditionally, so
46///   the derived `PartialEq` on [`AudioNode`]/[`PatchTree`] keeps comparing
47///   patches by content. The engine's pool dedup (`self.pool.iter().any(|c|
48///   c.tree == end)`) and refinement's own "did this walk move at all"
49///   (`current != *seed`) are both that comparison, and both would break the
50///   moment a fresh uid could make two identical trees differ.
51/// - **Hashing ignores it**, for the same reason and so the two stay coherent.
52/// - **It is skipped in JSON when unset**, and
53///   `auracle_features::cache::canonical_tree_json` clears uids before hashing,
54///   so the render memo's content address is byte-identical to what it was
55///   before uids existed. A cache key that moved with the UI's bookkeeping
56///   would miss on every refinement step *and* invalidate every persisted row.
57/// - **It is not a choice site.** [`crate::genome`] neither encodes nor decodes
58///   it, so `to_trace`/`from_trace` round-trips carry no uid — which is why
59///   [`PatchTree::inherit_uids`] exists.
60///
61/// # `Uid::NEW` and settling
62///
63/// Construction sites write [`Uid::NEW`] (zero, "unset") rather than minting
64/// eagerly. The prior samples thousands of trees per refinement walk and all
65/// but one are thrown away; minting there would burn identities for nothing and
66/// make the counter's value depend on how much search ran. Instead a tree is
67/// *settled* — [`PatchTree::ensure_uids`] — at the few points where it becomes
68/// something a person can point at: admitted to the pool, adopted onto the
69/// bench, restored from a save. Everything before that is anonymous.
70#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
71#[serde(transparent)]
72pub struct Uid(pub u64);
73
74/// The mint. One process-global counter, monotonic from 1.
75///
76/// Not seeded, not per-engine, and deliberately not derived from the tree:
77/// uniqueness *within a session* is the whole contract, and a content-derived
78/// id would give two identical sibling modules the same identity — which is
79/// precisely the confusion uids exist to end.
80static UID_SOURCE: AtomicU64 = AtomicU64::new(1);
81
82impl Uid {
83    /// The unset identity: a node that has not been settled yet.
84    pub const NEW: Uid = Uid(0);
85
86    /// Mint a fresh identity.
87    pub fn mint() -> Uid {
88        Uid(UID_SOURCE.fetch_add(1, Ordering::Relaxed))
89    }
90
91    /// Note that this identity already exists, so the mint never reissues it.
92    ///
93    /// The counter is per *process*, and a restored session is the case that
94    /// makes that a problem rather than a detail: a save carries the uids it
95    /// was written with (that is the entire point — a hand-placed layout has to
96    /// survive a reload), but a fresh page load starts the counter at 1. Insert
97    /// one module into a restored patch and the mint would hand out an id that
98    /// tree is already using, and two nodes would answer to one lock. So every
99    /// identity the engine *sees* pushes the counter past itself, and a restore
100    /// — which settles the whole bank on the way in — leaves it above every id
101    /// in the save.
102    pub fn observe(id: Uid) {
103        UID_SOURCE.fetch_max(id.0.saturating_add(1), Ordering::Relaxed);
104    }
105
106    /// Whether this node still needs an identity.
107    pub fn is_new(&self) -> bool {
108        self.0 == 0
109    }
110}
111
112// Two patches that differ only in uids are the same patch. See the type docs:
113// pool dedup, refinement's fixed-point test and every golden-tree assertion in
114// the suite are this comparison, and all of them mean "same content".
115impl PartialEq for Uid {
116    fn eq(&self, _: &Self) -> bool {
117        true
118    }
119}
120impl Eq for Uid {}
121impl Hash for Uid {
122    fn hash<H: Hasher>(&self, _: &mut H) {}
123}
124
125/// Every [`AudioNode`] variant, in declaration order — the one list the uid
126/// accessors expand over, so a new module cannot be added without one.
127macro_rules! audio_variants {
128    ($mac:ident) => {
129        $mac!(
130            Vco, Supersaw, Noise, Wavetable, Pluck, Formant, Silence, Mix, Filter, Fold, Delay,
131            Chorus, Reverb, Distortion, Bitcrush, Phaser, Flanger, Tremolo, Vibrato, Eq, Granular,
132            RingMod, Shift, Comp, Duck, Gate, Vocoder
133        )
134    };
135}
136
137/// Every [`AudioNode`] variant that carries a modulation slot — all of them
138/// but `Noise`, `Silence`, `Mix` and `RingMod`.
139macro_rules! modulated_variants {
140    ($mac:ident) => {
141        $mac!(
142            Vco, Supersaw, Wavetable, Pluck, Formant, Filter, Fold, Delay, Chorus, Reverb,
143            Distortion, Bitcrush, Phaser, Flanger, Tremolo, Vibrato, Eq, Granular, Shift, Comp,
144            Duck, Gate, Vocoder
145        )
146    };
147}
148
149/// Every non-empty [`ModNode`] variant. `None` is excluded on purpose: an
150/// empty slot is not a module and carries no identity.
151macro_rules! mod_variants {
152    ($mac:ident) => {
153        $mac!(Lfo, Env, Rand, Follow, Euclid, Op, Pair)
154    };
155}
156
157/// Oscillator / LFO waveform. Index order matches the trace-site categorical
158/// and the quiver output-port table.
159#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
160pub enum Waveform {
161    /// Sine
162    Sine,
163    /// Triangle
164    Triangle,
165    /// Sawtooth
166    Saw,
167    /// Square
168    Square,
169}
170
171impl Waveform {
172    /// All waveforms, in categorical-site index order.
173    pub const ALL: [Waveform; 4] = [
174        Waveform::Sine,
175        Waveform::Triangle,
176        Waveform::Saw,
177        Waveform::Square,
178    ];
179
180    /// Categorical-site index of this waveform.
181    pub fn index(self) -> usize {
182        Self::ALL.iter().position(|w| *w == self).expect("in table")
183    }
184
185    /// Waveform from a categorical-site index.
186    pub fn from_index(i: usize) -> Self {
187        Self::ALL[i % Self::ALL.len()]
188    }
189
190    /// The quiver output-port name on `Vco` / `Lfo` for this waveform.
191    pub fn port_name(self) -> &'static str {
192        match self {
193            Waveform::Sine => "sin",
194            Waveform::Triangle => "tri",
195            Waveform::Saw => "saw",
196            Waveform::Square => "sqr",
197        }
198    }
199}
200
201/// Noise color; maps to `NoiseGenerator` output ports.
202#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
203pub enum NoiseColor {
204    /// White noise (flat spectrum).
205    White,
206    /// Pink noise (1/f spectrum).
207    Pink,
208}
209
210impl NoiseColor {
211    /// All colors, in categorical-site index order.
212    pub const ALL: [NoiseColor; 2] = [NoiseColor::White, NoiseColor::Pink];
213
214    /// Categorical-site index.
215    pub fn index(self) -> usize {
216        Self::ALL.iter().position(|c| *c == self).expect("in table")
217    }
218
219    /// From a categorical-site index.
220    pub fn from_index(i: usize) -> Self {
221        Self::ALL[i % Self::ALL.len()]
222    }
223
224    /// The quiver output-port name on `NoiseGenerator`.
225    pub fn port_name(self) -> &'static str {
226        match self {
227            NoiseColor::White => "white",
228            NoiseColor::Pink => "pink",
229        }
230    }
231}
232
233/// Filter kind: three SVF responses plus the diode ladder.
234#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
235pub enum FilterKind {
236    /// State-variable lowpass.
237    SvfLp,
238    /// State-variable bandpass.
239    SvfBp,
240    /// State-variable highpass.
241    SvfHp,
242    /// Diode ladder lowpass (TB-303 flavor).
243    Ladder,
244}
245
246impl FilterKind {
247    /// All kinds, in categorical-site index order.
248    pub const ALL: [FilterKind; 4] = [
249        FilterKind::SvfLp,
250        FilterKind::SvfBp,
251        FilterKind::SvfHp,
252        FilterKind::Ladder,
253    ];
254
255    /// Categorical-site index.
256    pub fn index(self) -> usize {
257        Self::ALL.iter().position(|k| *k == self).expect("in table")
258    }
259
260    /// From a categorical-site index.
261    pub fn from_index(i: usize) -> Self {
262        Self::ALL[i % Self::ALL.len()]
263    }
264}
265
266/// Wavetable shape. Index order matches the trace-site categorical and
267/// quiver's own `WavetableType` table, which the `table` CV selects into.
268#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
269pub enum TableShape {
270    /// Pure sine.
271    Sine,
272    /// Triangle.
273    Tri,
274    /// Sawtooth.
275    Saw,
276    /// Square.
277    Square,
278    /// 25% pulse.
279    Pulse25,
280    /// 12.5% pulse.
281    Pulse12,
282    /// Vowel "ah" formant stack.
283    FormantA,
284    /// Vowel "oh" formant stack.
285    FormantO,
286}
287
288impl TableShape {
289    /// All shapes, in categorical-site index order.
290    pub const ALL: [TableShape; 8] = [
291        TableShape::Sine,
292        TableShape::Tri,
293        TableShape::Saw,
294        TableShape::Square,
295        TableShape::Pulse25,
296        TableShape::Pulse12,
297        TableShape::FormantA,
298        TableShape::FormantO,
299    ];
300
301    /// Categorical-site index.
302    pub fn index(self) -> usize {
303        Self::ALL.iter().position(|s| *s == self).expect("in table")
304    }
305
306    /// From a categorical-site index.
307    pub fn from_index(i: usize) -> Self {
308        Self::ALL[i % Self::ALL.len()]
309    }
310
311    /// Silkscreen label; also the s-expression tag.
312    pub fn label(self) -> &'static str {
313        match self {
314            TableShape::Sine => "sine",
315            TableShape::Tri => "tri",
316            TableShape::Saw => "saw",
317            TableShape::Square => "square",
318            TableShape::Pulse25 => "pulse 25",
319            TableShape::Pulse12 => "pulse 12",
320            TableShape::FormantA => "formant a",
321            TableShape::FormantO => "formant o",
322        }
323    }
324}
325
326/// Distortion shaping curve.
327///
328/// quiver's `Distortion` also offers a foldback mode; it is deliberately
329/// absent here because [`AudioNode::Fold`] already *is* that module, and a
330/// second production for one timbre only splits the prior mass that teaches
331/// the model what "folded" means.
332#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
333pub enum DriveMode {
334    /// Bounded `tanh` — symmetric, warm.
335    Soft,
336    /// Hard clip — symmetric, brittle.
337    Hard,
338    /// Asymmetric tube curve. The only member that rectifies, so it is also
339    /// the only one that costs a DC blocker (see `compile::makes_dc`).
340    Tube,
341}
342
343impl DriveMode {
344    /// All modes, in categorical-site index order.
345    pub const ALL: [DriveMode; 3] = [DriveMode::Soft, DriveMode::Hard, DriveMode::Tube];
346
347    /// Categorical-site index.
348    pub fn index(self) -> usize {
349        Self::ALL.iter().position(|m| *m == self).expect("in table")
350    }
351
352    /// From a categorical-site index.
353    pub fn from_index(i: usize) -> Self {
354        Self::ALL[i % Self::ALL.len()]
355    }
356
357    /// Silkscreen label; also the s-expression tag.
358    pub fn label(self) -> &'static str {
359        match self {
360            DriveMode::Soft => "soft",
361            DriveMode::Hard => "hard",
362            DriveMode::Tube => "tube",
363        }
364    }
365}
366
367/// A unary CV processor: something that sits *between* a modulator and its
368/// destination. Index order matches the `#modop` categorical.
369///
370/// The four are quiver's CV utilities that survive contact with this grammar.
371/// The ones that did not, and why, are recorded on [`ModNode`].
372#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
373#[serde(rename_all = "snake_case")]
374pub enum ModOp {
375    /// Snap the incoming CV to a musical scale (`ScaleQuantizer`).
376    Quantize,
377    /// Rate-limit it, with separate up and down times (`SlewLimiter`).
378    Slew,
379    /// Fold it into one polarity (`Rectifier`).
380    Rectify,
381    /// Sample it on an internal clock (`Clock` → `SampleAndHold`).
382    Hold,
383}
384
385impl ModOp {
386    /// All kinds, in categorical-site index order.
387    pub const ALL: [ModOp; 4] = [ModOp::Quantize, ModOp::Slew, ModOp::Rectify, ModOp::Hold];
388
389    /// Categorical-site index.
390    pub fn index(self) -> usize {
391        Self::ALL.iter().position(|k| *k == self).expect("in table")
392    }
393
394    /// From a categorical-site index.
395    pub fn from_index(i: usize) -> Self {
396        Self::ALL[i % Self::ALL.len()]
397    }
398
399    /// Silkscreen title, and the `RackModule::kind` tag.
400    pub fn label(self) -> &'static str {
401        match self {
402            ModOp::Quantize => "quantize",
403            ModOp::Slew => "slew",
404            ModOp::Rectify => "rectify",
405            ModOp::Hold => "hold",
406        }
407    }
408
409    /// The continuous trace sites this op owns, in `(p0, p1)` order.
410    ///
411    /// Two ops carry one parameter rather than two, and the unused `p1` is
412    /// **not** a trace site: sampling a choice nothing reads would cost prior
413    /// mass and hand MH a proposal that can never change the sound. It is
414    /// pinned to 0 by [`ModNode::normalized`] so the term still round-trips
415    /// through its own encoding.
416    pub fn param_sites(self) -> &'static [&'static str] {
417        match self {
418            ModOp::Quantize => &["qroot", "qscale"],
419            ModOp::Slew => &["rise", "fall"],
420            ModOp::Rectify => &["rmode"],
421            ModOp::Hold => &["hrate"],
422        }
423    }
424}
425
426/// A binary CV combiner over two modulation terms. Index order matches the
427/// `#pairop` categorical.
428///
429/// None of the six takes a continuous parameter, which is what keeps
430/// `#pairop` cheap: a `Pair` costs its two subterms and one categorical.
431#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
432#[serde(rename_all = "snake_case")]
433pub enum PairOp {
434    /// Lower of the two (`Min`).
435    Min,
436    /// Higher of the two (`Max`).
437    Max,
438    /// Gate AND (`LogicAnd`).
439    And,
440    /// Gate OR (`LogicOr`).
441    Or,
442    /// Gate XOR (`LogicXor`).
443    Xor,
444    /// `b` while `b` is above the gate threshold, `a` otherwise (`VcSwitch`).
445    Switch,
446}
447
448impl PairOp {
449    /// All kinds, in categorical-site index order.
450    pub const ALL: [PairOp; 6] = [
451        PairOp::Min,
452        PairOp::Max,
453        PairOp::And,
454        PairOp::Or,
455        PairOp::Xor,
456        PairOp::Switch,
457    ];
458
459    /// Categorical-site index.
460    pub fn index(self) -> usize {
461        Self::ALL.iter().position(|k| *k == self).expect("in table")
462    }
463
464    /// From a categorical-site index.
465    pub fn from_index(i: usize) -> Self {
466        Self::ALL[i % Self::ALL.len()]
467    }
468
469    /// Silkscreen title, and the `RackModule::kind` tag.
470    pub fn label(self) -> &'static str {
471        match self {
472            PairOp::Min => "min",
473            PairOp::Max => "max",
474            PairOp::And => "and",
475            PairOp::Or => "or",
476            PairOp::Xor => "xor",
477            PairOp::Switch => "switch",
478        }
479    }
480
481    /// Whether this op emits a 0–5 V **gate** rather than passing its inputs'
482    /// own voltage range through. The three logic gates do; min, max and the
483    /// switch hand back one of their inputs.
484    pub fn is_gate(self) -> bool {
485        matches!(self, PairOp::And | PairOp::Or | PairOp::Xor)
486    }
487}
488
489/// The scales quiver's `ScaleQuantizer` selects between, in **its own** index
490/// order — which is not `quiver::modules::Scale`'s.
491///
492/// `ScaleQuantizer` does not use that enum at all: it matches on
493/// `(scale_cv · 6.99) as u8` against its own inline table of **seven** scales
494/// (`utilities.rs`), and Mixolydian — index 6 of the eight-member `Scale` enum
495/// — is simply absent from it. Taking `Scale`'s order would have selected
496/// blues whenever the plate said mixolydian, and nothing at all for blues.
497pub const QUANT_SCALES: [&str; 7] = [
498    "chromatic",
499    "major",
500    "minor",
501    "penta major",
502    "penta minor",
503    "dorian",
504    "blues",
505];
506
507/// Which of [`QUANT_SCALES`] a normalized `qscale` knob selects — quiver's own
508/// `(cv · 6.99) as u8`, so the plate and the module cannot disagree.
509pub fn quant_scale_index(x: f64) -> usize {
510    ((x.clamp(0.0, 1.0) * 6.99) as usize).min(QUANT_SCALES.len() - 1)
511}
512
513/// Root-note names for the quantizer's `root` port, which quiver reads as
514/// `(cv · 11.99) as i32` semitones above C.
515pub const QUANT_ROOTS: [&str; 12] = [
516    "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B",
517];
518
519/// Which of [`QUANT_ROOTS`] a normalized `qroot` knob selects.
520pub fn quant_root_index(x: f64) -> usize {
521    ((x.clamp(0.0, 1.0) * 11.99) as usize).min(QUANT_ROOTS.len() - 1)
522}
523
524/// Rectifier polarities, in the order `rmode` selects them.
525///
526/// quiver's `Rectifier` has **no `mode` port**: it publishes all three at once
527/// as separate output ports (`full`, `half_pos`, `half_neg`), so this knob
528/// picks a cable at compile time rather than writing a CV.
529pub const RECT_MODES: [&str; 3] = ["full", "positive", "negative"];
530
531/// Which of [`RECT_MODES`] a normalized `rmode` knob selects — cell centres on
532/// a three-way split, the convention the other quantized knobs use.
533pub fn rect_mode_index(x: f64) -> usize {
534    ((x.clamp(0.0, 1.0) * 2.99) as usize).min(RECT_MODES.len() - 1)
535}
536
537/// A modulation-slot term: what drives a processor's mod input.
538///
539/// Modulation is a **recursive sort** with a depth bound, not a flat list of
540/// leaves: [`ModNode::Op`] wraps one modulation term in a CV processor and
541/// [`ModNode::Pair`] combines two, so `s&h rand → quantize → slew` is a term
542/// the grammar writes, the taste model reads and the rack draws.
543///
544/// The bound is [`crate::prior::PatchGrammarPrior::max_mod_depth`]. Without it
545/// the mod sort has no parsimony pressure at all — nothing in the audio tree's
546/// prior mass objects to a forty-node CV chain that moves one knob.
547///
548/// # What quiver ships that is deliberately *not* here
549///
550/// - **`StepSequencer`** — its eight step values are `steps: [f64; 8]`
551///   *internal state* with no ports (`utilities.rs`, and its own comment says
552///   so). It would need eight genome sites baked at compile time and could not
553///   be edited live, which breaks both the four-knob faceplate budget and the
554///   "every knob is a trace address you can turn" contract the instrument
555///   rests on.
556/// - **`Quantizer`** — subsumed by `ScaleQuantizer`, which is the same module
557///   with a scale rather than raw semitones.
558/// - **`Comparator`** — its useful output is a gate, and [`PairOp`]'s logic
559///   ops already make gates out of the sources that matter.
560/// - **`Multiple`, `PrecisionAdder`** — the tree already fans out, and
561///   quiver's gather already sums several cables into one port.
562/// - **`Attenuverter`** — this *is* `mod_depth`. It is already in every mod
563///   cable, one per slot.
564/// - **`EdgeDetector`** — a primitive the clocked modules use internally.
565/// - **`ChordMemory`, `Arpeggiator`** — polyphonic pitch generators; the
566///   instrument already has an arpeggiator in the keybed and a per-voice one
567///   would fight it.
568/// - **`MidSideEncode`/`Decode`** — needs a stereo sort the grammar has no
569///   way to name.
570/// - **`SamplePlayer`** — no asset pipeline, and it would make this something
571///   other than a synth.
572/// - **`Crosstalk`, `GroundLoop`, `Oversampler`, `UnitDelay`, `Mixer`** —
573///   analog imperfection, a wrapper, a one-sample primitive, and a module
574///   `Crossfader` already covers.
575///
576/// `Default` is [`ModNode::None`], which is what `#[serde(default)]` on the
577/// modulation slots that the v2 palette *added* to already-shipped variants
578/// resolves to — see [`AudioNode::Delay`].
579#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
580pub enum ModNode {
581    /// No modulation attached.
582    #[default]
583    None,
584    /// A low-frequency oscillator.
585    Lfo {
586        /// LFO waveform.
587        wave: Waveform,
588        /// Normalized rate (0-1).
589        rate: f64,
590        /// Stable identity for this node; see [`Uid`].
591        #[serde(default, skip_serializing_if = "Uid::is_new")]
592        uid: Uid,
593    },
594    /// An attack/decay envelope retriggered by the note gate.
595    Env {
596        /// Normalized attack time (0-1).
597        attack: f64,
598        /// Normalized decay time (0-1).
599        decay: f64,
600        /// Stable identity for this node; see [`Uid`].
601        #[serde(default, skip_serializing_if = "Uid::is_new")]
602        uid: Uid,
603    },
604    /// A random stepped source (noise sampled-and-held on an internal
605    /// clock) — the classic S&H burble.
606    Rand {
607        /// Normalized clock rate (0-1).
608        rate: f64,
609        /// Normalized slew between steps (0 = hard steps, 1 = a drift).
610        glide: f64,
611        /// Stable identity for this node; see [`Uid`].
612        #[serde(default, skip_serializing_if = "Uid::is_new")]
613        uid: Uid,
614    },
615    /// An envelope follower riding the *owning module's own input* — the
616    /// patch's dynamics fed back into its timbre (a filter that opens on the
617    /// transient, a fold that hardens when the note is loud).
618    ///
619    /// Deliberately a **leaf**: it takes no audio subterm of its own, so the
620    /// grammar gains a modulation source without gaining a second recursion
621    /// or a second audio-sort site. Its source is whatever the compiler has
622    /// already built for the module below it.
623    Follow {
624        /// Normalized sensitivity (detector output gain).
625        sens: f64,
626        /// Normalized release time.
627        release: f64,
628        /// Stable identity for this node; see [`Uid`].
629        #[serde(default, skip_serializing_if = "Uid::is_new")]
630        uid: Uid,
631    },
632    /// A clocked euclidean gate pattern — pulses spread as evenly as the step
633    /// count allows. A **leaf**, like the four above: it generates rather than
634    /// processes, and its clock is its own.
635    Euclid {
636        /// Normalized clock rate (0-1 → 20..300 BPM).
637        rate: f64,
638        /// Normalized step count (0-1 → 2..16 steps).
639        steps: f64,
640        /// Normalized pulse density (0-1 → 1..steps−1 pulses).
641        pulses: f64,
642        /// Stable identity for this node; see [`Uid`].
643        #[serde(default, skip_serializing_if = "Uid::is_new")]
644        uid: Uid,
645    },
646    /// A unary CV processor wrapping another modulation term.
647    ///
648    /// `p0`/`p1` are the op's two continuous knobs; which sites they occupy is
649    /// [`ModOp::param_sites`]. The ops that take one parameter pin `p1` to 0
650    /// and do not encode it.
651    Op {
652        /// Which processor.
653        kind: ModOp,
654        /// First continuous parameter (root / rise / mode / rate).
655        p0: f64,
656        /// Second continuous parameter (scale / fall); 0 and unused on the
657        /// one-parameter ops.
658        p1: f64,
659        /// The modulation term being processed. Never [`ModNode::None`] — a
660        /// processor with nothing under it is a dead cable, so the grammar
661        /// renormalizes it away rather than drawing it (see
662        /// [`ModNode::normalized`]).
663        input: Box<ModNode>,
664        /// Stable identity for this node; see [`Uid`].
665        #[serde(default, skip_serializing_if = "Uid::is_new")]
666        uid: Uid,
667    },
668    /// A binary CV combiner over two modulation terms.
669    Pair {
670        /// Which combiner.
671        kind: PairOp,
672        /// First input. Never [`ModNode::None`].
673        a: Box<ModNode>,
674        /// Second input. Never [`ModNode::None`]; on
675        /// [`PairOp::Switch`] it is also the control.
676        b: Box<ModNode>,
677        /// Stable identity for this node; see [`Uid`].
678        #[serde(default, skip_serializing_if = "Uid::is_new")]
679        uid: Uid,
680    },
681}
682
683/// An audio-sort term: sources at the leaves, processors and mixers above.
684#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
685pub enum AudioNode {
686    /// Band-limited analog-style oscillator.
687    ///
688    /// Its modulation slot lands on **pitch**, not on a timbre parameter — the
689    /// one destination no other module in the grammar can offer, and the
690    /// reason vibrato and pitch envelopes exist at all here. See
691    /// [`crate::compile`]'s `wire_pitch`.
692    ///
693    /// `mod_depth`/`modulation` are `#[serde(default)]` for the reason spelled
694    /// out on [`AudioNode::Delay`], and more urgently: a vco is in *every*
695    /// saved patch, so without the defaults no session on disk deserializes at
696    /// all.
697    Vco {
698        /// Output waveform.
699        wave: Waveform,
700        /// Octave offset, −2..=+2.
701        octave: i8,
702        /// Normalized detune (0-1 → ±50 cents).
703        detune: f64,
704        /// Normalized pitch-modulation depth (0-1 → ±0.5 octave). Absent in
705        /// pre-2A saves; defaults to 0, i.e. no pitch modulation.
706        #[serde(default)]
707        mod_depth: f64,
708        /// Pitch modulation source. Absent in pre-2A saves.
709        #[serde(default)]
710        modulation: ModNode,
711        /// Stable identity for this node; see [`Uid`].
712        #[serde(default, skip_serializing_if = "Uid::is_new")]
713        uid: Uid,
714    },
715    /// Seven-voice detuned saw stack with sub oscillator.
716    Supersaw {
717        /// Octave offset, −2..=+2.
718        octave: i8,
719        /// Normalized voice detune spread (0-1).
720        detune: f64,
721        /// Normalized center/stack blend (0-1).
722        mix: f64,
723        /// Normalized pitch-modulation depth (0-1 → ±0.5 octave). Absent in
724        /// pre-2A saves — see [`AudioNode::Vco`].
725        #[serde(default)]
726        mod_depth: f64,
727        /// Pitch modulation source. Absent in pre-2A saves.
728        #[serde(default)]
729        modulation: ModNode,
730        /// Stable identity for this node; see [`Uid`].
731        #[serde(default, skip_serializing_if = "Uid::is_new")]
732        uid: Uid,
733    },
734    /// Noise source.
735    Noise {
736        /// Noise color.
737        color: NoiseColor,
738        /// Stable identity for this node; see [`Uid`].
739        #[serde(default, skip_serializing_if = "Uid::is_new")]
740        uid: Uid,
741    },
742    /// Morphing wavetable oscillator: eight bandlimited tables, crossfaded.
743    ///
744    /// It has no `detune` — the faceplate budget is four knobs and `morph` is
745    /// the reason this module exists, so detune loses. The compiler still
746    /// routes pitch through the usual offset, pinned at "no detune".
747    Wavetable {
748        /// Base table.
749        table: TableShape,
750        /// Octave offset, −2..=+2.
751        octave: i8,
752        /// Normalized morph toward the next table (0-1).
753        morph: f64,
754        /// Normalized modulation depth (0-1, cable attenuation).
755        mod_depth: f64,
756        /// Morph modulation source.
757        modulation: ModNode,
758        /// Stable identity for this node; see [`Uid`].
759        #[serde(default, skip_serializing_if = "Uid::is_new")]
760        uid: Uid,
761    },
762    /// Karplus-Strong plucked string, retriggered by the note gate.
763    Pluck {
764        /// Octave offset, −2..=+2.
765        octave: i8,
766        /// Normalized loop damping (0-1; higher rings longer and brighter).
767        damping: f64,
768        /// Normalized excitation brightness (0-1, noise-vs-impulse blend).
769        brightness: f64,
770        /// Normalized modulation depth (0-1).
771        mod_depth: f64,
772        /// Damping modulation source.
773        modulation: ModNode,
774        /// Stable identity for this node; see [`Uid`].
775        #[serde(default, skip_serializing_if = "Uid::is_new")]
776        uid: Uid,
777    },
778    /// Formant (vocal-tract) oscillator: a glottal pulse through five
779    /// parallel resonators.
780    ///
781    /// `vowel` is a **continuous** position interpolated across A/E/I/O/U
782    /// rather than a five-way switch, which is what makes it worth a
783    /// modulation slot: sweeping it moves the formant peaks, and that is a
784    /// spectral movement `φ`'s centroid and rolloff coordinates measure
785    /// directly.
786    Formant {
787        /// Normalized vowel position (0-1 across A/E/I/O/U).
788        vowel: f64,
789        /// Normalized formant shift (0-1 → 0.5×..2× formant frequencies;
790        /// 0.5 is no shift).
791        shift: f64,
792        /// Octave offset, −2..=+2.
793        octave: i8,
794        /// Normalized modulation depth (0-1).
795        mod_depth: f64,
796        /// Vowel modulation source.
797        modulation: ModNode,
798        /// Stable identity for this node; see [`Uid`].
799        #[serde(default, skip_serializing_if = "Uid::is_new")]
800        uid: Uid,
801    },
802    /// A source that makes no sound.
803    ///
804    /// The seventh source leaf, and the only one a player can mean literally:
805    /// an unplugged socket. Before this existed the rack drew a dashed EMPTY
806    /// plate over a substitute `Vco`, so the plate was honest and the patch
807    /// underneath was not — the model was taught on a tree containing a source
808    /// the player believed was silent, and every φ coordinate measured a
809    /// render that had it in.
810    ///
811    /// It carries no parameters, which makes it the only source whose whole
812    /// genome is `#leaf` and `#src`. Its prior weight is deliberately tiny
813    /// (see [`PatchGrammarPrior::source_weights`](crate::PatchGrammarPrior))
814    /// but **not zero**: at zero the grammar gives `p = 0` to any tree
815    /// containing one, the Boltzmann target is −∞, and MH rejects every
816    /// proposal touching a hand-made hole — so unplugging a socket would
817    /// quietly make a patch un-evolvable. Small and nonzero instead means a
818    /// `Silence`-only tree renders silent, the vet gate quarantines it, and
819    /// evolution learns to avoid it. That is a path rather than a wall, and it
820    /// is the designed one.
821    ///
822    /// Appended **after** `Formant` on purpose: `#src` is a categorical whose
823    /// *index* is what a saved trace stores, so a new kind may only ever be
824    /// added at the end. Inserting one here would silently re-point every
825    /// persisted genome at a different oscillator.
826    Silence {
827        /// Stable identity for this node; see [`Uid`].
828        #[serde(default, skip_serializing_if = "Uid::is_new")]
829        uid: Uid,
830    },
831    /// Equal-power crossfade of two audio terms.
832    Mix {
833        /// Normalized balance (0 = all `a`, 1 = all `b`).
834        balance: f64,
835        /// First input.
836        a: Box<AudioNode>,
837        /// Second input.
838        b: Box<AudioNode>,
839        /// Stable identity for this node; see [`Uid`].
840        #[serde(default, skip_serializing_if = "Uid::is_new")]
841        uid: Uid,
842    },
843    /// A filter over an audio term, with an optional cutoff modulation.
844    Filter {
845        /// Which filter.
846        kind: FilterKind,
847        /// Normalized cutoff (0-1, exponential inside quiver).
848        cutoff: f64,
849        /// Normalized resonance (0-1, mapped to a bounded range).
850        resonance: f64,
851        /// Normalized modulation depth (0-1, cable attenuation).
852        mod_depth: f64,
853        /// Audio input.
854        input: Box<AudioNode>,
855        /// Cutoff modulation source.
856        modulation: ModNode,
857        /// Stable identity for this node; see [`Uid`].
858        #[serde(default, skip_serializing_if = "Uid::is_new")]
859        uid: Uid,
860    },
861    /// Wavefolder over an audio term, with optional threshold modulation.
862    Fold {
863        /// Normalized fold threshold (0-1; lower folds harder).
864        threshold: f64,
865        /// Normalized modulation depth (0-1).
866        mod_depth: f64,
867        /// Audio input.
868        input: Box<AudioNode>,
869        /// Threshold modulation source.
870        modulation: ModNode,
871        /// Stable identity for this node; see [`Uid`].
872        #[serde(default, skip_serializing_if = "Uid::is_new")]
873        uid: Uid,
874    },
875    /// Delay line over an audio term, with optional delay-time modulation.
876    ///
877    /// `mod_depth` and `modulation` are `#[serde(default)]` because this
878    /// variant **shipped without them**: the v2 palette added a modulation
879    /// slot to a module users already have saved patches of. Serde requires
880    /// every field of a struct variant unless told otherwise, so without the
881    /// defaults a single v1-era delay anywhere in a bank fails the
882    /// `SessionState` deserialize — and that failure is not local to the
883    /// patch, it takes the whole save down: bank, observation log, lineage.
884    /// A grammar that grows knobs on existing modules must default them, and
885    /// the defaults must be the *v1 behaviour* (depth 0, no source), so a
886    /// restored patch sounds like the one that was saved. Same for
887    /// [`AudioNode::Chorus`] and [`AudioNode::Reverb`]. The variants the v2
888    /// palette *introduced* (wavetable, pluck, distortion, bitcrush, phaser,
889    /// ring mod) need no such treatment: no old save can contain one.
890    Delay {
891        /// Normalized delay time (0-1).
892        time: f64,
893        /// Normalized feedback (0-1, mapped to a bounded range).
894        feedback: f64,
895        /// Normalized wet/dry mix (0-1).
896        mix: f64,
897        /// Normalized modulation depth (0-1). Absent in v1 saves; defaults
898        /// to 0, which is "no modulation reaches the delay time".
899        #[serde(default)]
900        mod_depth: f64,
901        /// Audio input.
902        input: Box<AudioNode>,
903        /// Delay-time modulation source. Absent in v1 saves; defaults to
904        /// [`ModNode::None`].
905        #[serde(default)]
906        modulation: ModNode,
907        /// Stable identity for this node; see [`Uid`].
908        #[serde(default, skip_serializing_if = "Uid::is_new")]
909        uid: Uid,
910    },
911    /// Chorus over an audio term, with optional depth modulation.
912    Chorus {
913        /// Normalized modulation rate (0-1).
914        rate: f64,
915        /// Normalized modulation depth (0-1).
916        depth: f64,
917        /// Normalized wet/dry mix (0-1).
918        mix: f64,
919        /// Normalized modulation depth of the *modulation slot* (0-1).
920        /// Absent in v1 saves — see [`AudioNode::Delay`].
921        #[serde(default)]
922        mod_depth: f64,
923        /// Audio input.
924        input: Box<AudioNode>,
925        /// Chorus-depth modulation source. Absent in v1 saves.
926        #[serde(default)]
927        modulation: ModNode,
928        /// Stable identity for this node; see [`Uid`].
929        #[serde(default, skip_serializing_if = "Uid::is_new")]
930        uid: Uid,
931    },
932    /// Algorithmic reverb (Freeverb) over an audio term, with optional size
933    /// modulation.
934    Reverb {
935        /// Normalized room size (0-1).
936        size: f64,
937        /// Normalized damping (0-1).
938        damp: f64,
939        /// Normalized wet/dry mix (0-1).
940        mix: f64,
941        /// Normalized modulation depth (0-1). Absent in v1 saves — see
942        /// [`AudioNode::Delay`].
943        #[serde(default)]
944        mod_depth: f64,
945        /// Audio input.
946        input: Box<AudioNode>,
947        /// Room-size modulation source. Absent in v1 saves.
948        #[serde(default)]
949        modulation: ModNode,
950        /// Stable identity for this node; see [`Uid`].
951        #[serde(default, skip_serializing_if = "Uid::is_new")]
952        uid: Uid,
953    },
954    /// Waveshaping distortion over an audio term.
955    Distortion {
956        /// Normalized drive (0-1).
957        drive: f64,
958        /// Normalized tone (0-1; a real one-pole lowpass, dark to open).
959        tone: f64,
960        /// Shaping curve.
961        mode: DriveMode,
962        /// Normalized modulation depth (0-1).
963        mod_depth: f64,
964        /// Audio input.
965        input: Box<AudioNode>,
966        /// Drive modulation source.
967        modulation: ModNode,
968        /// Stable identity for this node; see [`Uid`].
969        #[serde(default, skip_serializing_if = "Uid::is_new")]
970        uid: Uid,
971    },
972    /// Bit-depth and sample-rate reduction over an audio term.
973    Bitcrush {
974        /// Normalized bit depth (0-1 → 1..16 bits).
975        bits: f64,
976        /// Normalized sample-rate reduction (0-1).
977        downsample: f64,
978        /// Normalized modulation depth (0-1).
979        mod_depth: f64,
980        /// Audio input.
981        input: Box<AudioNode>,
982        /// Bit-depth modulation source.
983        modulation: ModNode,
984        /// Stable identity for this node; see [`Uid`].
985        #[serde(default, skip_serializing_if = "Uid::is_new")]
986        uid: Uid,
987    },
988    /// Swept allpass phaser over an audio term.
989    Phaser {
990        /// Normalized sweep rate (0-1).
991        rate: f64,
992        /// Normalized sweep depth (0-1).
993        depth: f64,
994        /// Normalized resonance feedback (0-1, mapped bipolar and bounded).
995        feedback: f64,
996        /// Normalized modulation depth (0-1).
997        mod_depth: f64,
998        /// Audio input.
999        input: Box<AudioNode>,
1000        /// Sweep-depth modulation source.
1001        modulation: ModNode,
1002        /// Stable identity for this node; see [`Uid`].
1003        #[serde(default, skip_serializing_if = "Uid::is_new")]
1004        uid: Uid,
1005    },
1006    /// Flanging comb over an audio term: a 1–10 ms swept delay against the
1007    /// dry signal, with signed feedback. Stereo — its `spread` decorrelates
1008    /// the two sweeps, exactly as the phaser's does.
1009    Flanger {
1010        /// Normalized sweep rate (0-1).
1011        rate: f64,
1012        /// Normalized sweep depth (0-1).
1013        depth: f64,
1014        /// Normalized feedback (0-1, mapped bipolar and bounded — the sign is
1015        /// a timbre, as on the phaser).
1016        feedback: f64,
1017        /// Normalized modulation depth (0-1).
1018        mod_depth: f64,
1019        /// Audio input.
1020        input: Box<AudioNode>,
1021        /// Sweep-depth modulation source.
1022        modulation: ModNode,
1023        /// Stable identity for this node; see [`Uid`].
1024        #[serde(default, skip_serializing_if = "Uid::is_new")]
1025        uid: Uid,
1026    },
1027    /// Amplitude modulation over an audio term, sine-to-triangle.
1028    Tremolo {
1029        /// Normalized LFO rate (0-1).
1030        rate: f64,
1031        /// Normalized depth (0-1).
1032        depth: f64,
1033        /// Normalized waveform blend (0 = sine, 1 = triangle).
1034        shape: f64,
1035        /// Normalized modulation depth (0-1).
1036        mod_depth: f64,
1037        /// Audio input.
1038        input: Box<AudioNode>,
1039        /// Depth modulation source.
1040        modulation: ModNode,
1041        /// Stable identity for this node; see [`Uid`].
1042        #[serde(default, skip_serializing_if = "Uid::is_new")]
1043        uid: Uid,
1044    },
1045    /// Pitch wobble over an audio term (a modulated delay read).
1046    ///
1047    /// The distinction from [`AudioNode::Chorus`] is `mix`: a half-wet
1048    /// vibrato *is* a chorus, because the dry and the pitch-shifted copies
1049    /// beat against each other. This module earns its place by running wet.
1050    Vibrato {
1051        /// Normalized LFO rate (0-1).
1052        rate: f64,
1053        /// Normalized depth (0-1).
1054        depth: f64,
1055        /// Normalized wet/dry mix (0-1; 1 is the module's own idiom).
1056        mix: f64,
1057        /// Normalized modulation depth (0-1).
1058        mod_depth: f64,
1059        /// Audio input.
1060        input: Box<AudioNode>,
1061        /// Depth modulation source.
1062        modulation: ModNode,
1063        /// Stable identity for this node; see [`Uid`].
1064        #[serde(default, skip_serializing_if = "Uid::is_new")]
1065        uid: Uid,
1066    },
1067    /// Three-band tone control over an audio term (low shelf, parametric mid,
1068    /// high shelf), each ±12 dB with unity at the knob's centre.
1069    Eq {
1070        /// Normalized low-shelf gain (0-1; 0.5 is 0 dB).
1071        low: f64,
1072        /// Normalized mid-bell gain (0-1; 0.5 is 0 dB).
1073        mid: f64,
1074        /// Normalized high-shelf gain (0-1; 0.5 is 0 dB).
1075        high: f64,
1076        /// Normalized modulation depth (0-1).
1077        mod_depth: f64,
1078        /// Audio input.
1079        input: Box<AudioNode>,
1080        /// Mid-gain modulation source.
1081        modulation: ModNode,
1082        /// Stable identity for this node; see [`Uid`].
1083        #[serde(default, skip_serializing_if = "Uid::is_new")]
1084        uid: Uid,
1085    },
1086    /// Granular re-reading of an audio term: overlapping Hann-windowed grains
1087    /// taken from a rolling buffer of what the input just played.
1088    Granular {
1089        /// Normalized playback position in the buffer (0-1).
1090        position: f64,
1091        /// Normalized grain size (0-1 → 10..500 ms).
1092        size: f64,
1093        /// Normalized grain density (0-1 → 1..20 grains/second).
1094        density: f64,
1095        /// Normalized modulation depth (0-1).
1096        mod_depth: f64,
1097        /// Audio input.
1098        input: Box<AudioNode>,
1099        /// Position modulation source.
1100        modulation: ModNode,
1101        /// Stable identity for this node; see [`Uid`].
1102        #[serde(default, skip_serializing_if = "Uid::is_new")]
1103        uid: Uid,
1104    },
1105    /// Ring modulation of two audio terms, crossfaded against the dry
1106    /// carrier. The grammar's **second** binary node.
1107    RingMod {
1108        /// Normalized dry/ring balance (0 = all carrier, 1 = all ring).
1109        mix: f64,
1110        /// Carrier (also the dry side of the crossfade).
1111        a: Box<AudioNode>,
1112        /// Modulator.
1113        b: Box<AudioNode>,
1114        /// Stable identity for this node; see [`Uid`].
1115        #[serde(default, skip_serializing_if = "Uid::is_new")]
1116        uid: Uid,
1117    },
1118    /// Grain-based pitch shifter over an audio term: two Hann-windowed grains
1119    /// read from a rolling buffer at a resampled rate.
1120    ///
1121    /// Unary, unlike the four modules below it. Wave one cut it alongside the
1122    /// compressor and the vocoder on the belief that it needed a second audio
1123    /// input; it does not — quiver's `PitchShifter` is `in`/`shift`/`window`/
1124    /// `mix`, one signal in and one out.
1125    Shift {
1126        /// Normalized transposition (0-1; 0.5 is unison, the ends are ∓12
1127        /// semitones).
1128        semis: f64,
1129        /// Normalized grain window (0-1 → 10..100 ms).
1130        window: f64,
1131        /// Normalized wet/dry mix (0-1).
1132        mix: f64,
1133        /// Normalized modulation depth (0-1).
1134        mod_depth: f64,
1135        /// Audio input.
1136        input: Box<AudioNode>,
1137        /// Transposition modulation source.
1138        modulation: ModNode,
1139        /// Stable identity for this node; see [`Uid`].
1140        #[serde(default, skip_serializing_if = "Uid::is_new")]
1141        uid: Uid,
1142    },
1143    /// Compressor whose detector runs on a **second** audio term: the
1144    /// grammar's third binary node, and the first whose `/1` branch is heard
1145    /// only as a control.
1146    ///
1147    /// The four binary nodes below all follow [`AudioNode::Mix`]'s child
1148    /// order: `/0` is the signal you hear, `/1` the signal that shapes it.
1149    Comp {
1150        /// Normalized threshold (0-1 → 0..5 V of detector level).
1151        threshold: f64,
1152        /// Normalized ratio (0-1 → 1:1..20:1).
1153        ratio: f64,
1154        /// Normalized makeup gain (0-1 → 1×..4×).
1155        makeup: f64,
1156        /// Normalized modulation depth (0-1).
1157        mod_depth: f64,
1158        /// The signal being compressed.
1159        input: Box<AudioNode>,
1160        /// The signal the detector listens to.
1161        sidechain: Box<AudioNode>,
1162        /// Threshold modulation source.
1163        modulation: ModNode,
1164        /// Stable identity for this node; see [`Uid`].
1165        #[serde(default, skip_serializing_if = "Uid::is_new")]
1166        uid: Uid,
1167    },
1168    /// Ducker: a key signal pulls the main signal down, in proportion to how
1169    /// far the key's envelope sits above the threshold.
1170    ///
1171    /// The difference from [`AudioNode::Comp`] is what the gain reduction is
1172    /// proportional to — a compressor reduces by the *excess over* the
1173    /// threshold in dB, a ducker by the key's level *relative to* it, up to a
1174    /// fixed depth. That is the sidechain-pump gesture rather than dynamics
1175    /// control, and it is the one people actually reach for.
1176    Duck {
1177        /// Normalized duck depth (0-1; 1 is full attenuation on a loud key).
1178        amount: f64,
1179        /// Normalized key level for full ducking (0-1 → 0..5 V).
1180        threshold: f64,
1181        /// Normalized recovery time (0-1 → 10..1000 ms).
1182        release: f64,
1183        /// Normalized modulation depth (0-1).
1184        mod_depth: f64,
1185        /// The signal being ducked.
1186        input: Box<AudioNode>,
1187        /// The key that ducks it.
1188        key: Box<AudioNode>,
1189        /// Duck-amount modulation source.
1190        modulation: ModNode,
1191        /// Stable identity for this node; see [`Uid`].
1192        #[serde(default, skip_serializing_if = "Uid::is_new")]
1193        uid: Uid,
1194    },
1195    /// Noise gate keyed by a second audio term.
1196    Gate {
1197        /// Normalized open threshold (0-1 → 0..5 V of detector level).
1198        threshold: f64,
1199        /// Normalized gate range (0-1; 1 closes to silence, 0 is a no-op).
1200        range: f64,
1201        /// Normalized release time (0-1 → 10..500 ms).
1202        release: f64,
1203        /// Normalized modulation depth (0-1).
1204        mod_depth: f64,
1205        /// The signal being gated.
1206        input: Box<AudioNode>,
1207        /// The signal that opens the gate.
1208        sidechain: Box<AudioNode>,
1209        /// Threshold modulation source.
1210        modulation: ModNode,
1211        /// Stable identity for this node; see [`Uid`].
1212        #[serde(default, skip_serializing_if = "Uid::is_new")]
1213        uid: Uid,
1214    },
1215    /// Vocoder: a bank of bandpass filters whose per-band envelopes are taken
1216    /// from the **modulator** and imposed on the **carrier**.
1217    ///
1218    /// Both branches are audible in the sense that both shape the output, but
1219    /// only the carrier's *waveform* reaches it — the modulator contributes
1220    /// its spectral envelope and nothing else, which is why `/1` is still the
1221    /// control side under this family's child-order convention.
1222    Vocoder {
1223        /// Normalized band count (0-1 → 4..16 bands).
1224        bands: f64,
1225        /// Normalized envelope attack (0-1 → 10..200 ms).
1226        attack: f64,
1227        /// Normalized envelope release (0-1 → 10..200 ms).
1228        release: f64,
1229        /// Normalized modulation depth (0-1).
1230        mod_depth: f64,
1231        /// The signal that is spectrally shaped (wants harmonic richness).
1232        carrier: Box<AudioNode>,
1233        /// The signal whose spectrum is measured (wants formants).
1234        modulator: Box<AudioNode>,
1235        /// Band-count modulation source.
1236        modulation: ModNode,
1237        /// Stable identity for this node; see [`Uid`].
1238        #[serde(default, skip_serializing_if = "Uid::is_new")]
1239        uid: Uid,
1240    },
1241}
1242
1243/// The mandatory amplitude envelope on every voice (ADSR, normalized 0-1).
1244#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1245pub struct AmpEnv {
1246    /// Normalized attack.
1247    pub attack: f64,
1248    /// Normalized decay.
1249    pub decay: f64,
1250    /// Normalized sustain level.
1251    pub sustain: f64,
1252    /// Normalized release.
1253    pub release: f64,
1254}
1255
1256/// A complete patch genome: an audio term wrapped in the mandatory voice
1257/// stage (amp ADSR → VCA → limiter → stereo out, added by the compiler).
1258#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1259pub struct PatchTree {
1260    /// Amplitude envelope parameters.
1261    pub amp: AmpEnv,
1262    /// The evolved audio-term tree.
1263    pub root: AudioNode,
1264}
1265
1266impl ModNode {
1267    /// This term's stable identity — `None` for [`ModNode::None`], which is an
1268    /// empty slot rather than a module.
1269    pub fn uid(&self) -> Option<Uid> {
1270        macro_rules! arms {
1271            ($($v:ident),*) => {
1272                match self { $(ModNode::$v { uid, .. } => Some(*uid),)* ModNode::None => None }
1273            };
1274        }
1275        mod_variants!(arms)
1276    }
1277
1278    /// Overwrite this term's identity (a no-op on [`ModNode::None`]).
1279    pub fn set_uid(&mut self, id: Uid) {
1280        macro_rules! arms {
1281            ($($v:ident),*) => {
1282                match self { $(ModNode::$v { uid, .. } => *uid = id,)* ModNode::None => {} }
1283            };
1284        }
1285        mod_variants!(arms)
1286    }
1287
1288    /// Sub-terms in key-index order (`Op`'s input is `/0`; a `Pair`'s two
1289    /// branches are `/0` and `/1`).
1290    pub fn children(&self) -> Vec<&ModNode> {
1291        match self {
1292            ModNode::Op { input, .. } => vec![input],
1293            ModNode::Pair { a, b, .. } => vec![a, b],
1294            _ => Vec::new(),
1295        }
1296    }
1297
1298    /// Mutable [`Self::children`].
1299    pub fn children_mut(&mut self) -> Vec<&mut ModNode> {
1300        match self {
1301            ModNode::Op { input, .. } => vec![input],
1302            ModNode::Pair { a, b, .. } => vec![a, b],
1303            _ => Vec::new(),
1304        }
1305    }
1306
1307    /// Number of probabilistic-choice sites this mod term occupies.
1308    pub fn site_count(&self) -> usize {
1309        match self {
1310            ModNode::None => 1,          // #mod
1311            ModNode::Lfo { .. } => 3,    // #mod #wave #rate
1312            ModNode::Env { .. } => 3,    // #mod #att #dec
1313            ModNode::Rand { .. } => 3,   // #mod #rate #glide
1314            ModNode::Follow { .. } => 3, // #mod #sens #rel
1315            // #mod #erate #esteps #epulses
1316            ModNode::Euclid { .. } => 4,
1317            // #mod #modop, the op's own knobs, then the subterm.
1318            ModNode::Op { kind, input, .. } => 2 + kind.param_sites().len() + input.site_count(),
1319            // #mod #pairop and two subterms — no continuous sites of its own.
1320            ModNode::Pair { a, b, .. } => 2 + a.site_count() + b.site_count(),
1321        }
1322    }
1323
1324    /// Nesting depth of this modulation term: an empty slot is 0, a leaf is 1,
1325    /// and every processor above one adds a level.
1326    ///
1327    /// This is the quantity [`crate::prior::PatchGrammarPrior::max_mod_depth`]
1328    /// bounds and that `φ`'s `mod_depth_mean` averages.
1329    pub fn depth(&self) -> usize {
1330        match self {
1331            ModNode::None => 0,
1332            ModNode::Lfo { .. }
1333            | ModNode::Env { .. }
1334            | ModNode::Rand { .. }
1335            | ModNode::Follow { .. }
1336            | ModNode::Euclid { .. } => 1,
1337            ModNode::Op { input, .. } => 1 + input.depth(),
1338            ModNode::Pair { a, b, .. } => 1 + a.depth().max(b.depth()),
1339        }
1340    }
1341
1342    /// Number of nodes in this modulation term (an empty slot is 0).
1343    pub fn size(&self) -> usize {
1344        match self {
1345            ModNode::None => 0,
1346            ModNode::Op { input, .. } => 1 + input.size(),
1347            ModNode::Pair { a, b, .. } => 1 + a.size() + b.size(),
1348            _ => 1,
1349        }
1350    }
1351
1352    /// The canonical form of a hand-built modulation term.
1353    ///
1354    /// Two normalizations, both of which the prior enforces by construction
1355    /// (it renormalizes the `#mod` categorical over the non-empty kinds below
1356    /// an `Op` or inside a `Pair`) but which an explicit term arriving from
1357    /// the panel through
1358    /// [`StructOp::SetModTree`](crate::mutate::StructOp::SetModTree) can
1359    /// violate:
1360    ///
1361    /// - **A processor over nothing is nothing.** `Op` with an empty input is
1362    ///   a quantizer fed 0 V; `Pair` with two empty inputs is a logic gate
1363    ///   fed two zeroes. Both compile to a cable carrying a constant, which is
1364    ///   a module on the rack that does nothing. A `Pair` with *one* empty
1365    ///   input collapses to the other side rather than to nothing — `And(x, 0)`
1366    ///   is identically low and `Min(x, 0)` throws away the positive half, so
1367    ///   there is no reading under which the empty branch is a musical choice.
1368    /// - **An unused parameter is pinned to 0.** [`ModOp::param_sites`] does
1369    ///   not encode `p1` for the one-parameter ops, so a term carrying a
1370    ///   non-zero one would not survive its own trace round-trip.
1371    ///
1372    /// Folding here rather than in the prior is deliberate: the generative
1373    /// model and [`crate::genome`]'s encoding are asserted site-for-site
1374    /// identical, so a prior that *sampled* a degenerate term and then folded
1375    /// it would emit choices the encoding does not.
1376    pub fn normalized(self) -> ModNode {
1377        match self {
1378            ModNode::Op {
1379                kind,
1380                p0,
1381                p1,
1382                input,
1383                ..
1384            } => match input.normalized() {
1385                ModNode::None => ModNode::None,
1386                input => ModNode::Op {
1387                    uid: Uid::NEW,
1388                    kind,
1389                    p0,
1390                    p1: if kind.param_sites().len() > 1 {
1391                        p1
1392                    } else {
1393                        0.0
1394                    },
1395                    input: Box::new(input),
1396                },
1397            },
1398            ModNode::Pair { kind, a, b, .. } => match (a.normalized(), b.normalized()) {
1399                (ModNode::None, ModNode::None) => ModNode::None,
1400                (a, ModNode::None) => a,
1401                (ModNode::None, b) => b,
1402                (a, b) => ModNode::Pair {
1403                    uid: Uid::NEW,
1404                    kind,
1405                    a: Box::new(a),
1406                    b: Box::new(b),
1407                },
1408            },
1409            leaf => leaf,
1410        }
1411    }
1412}
1413
1414impl AudioNode {
1415    /// This node's stable identity ([`Uid::NEW`] until the tree is settled).
1416    pub fn uid(&self) -> Uid {
1417        macro_rules! arms {
1418            ($($v:ident),*) => { match self { $(AudioNode::$v { uid, .. } => *uid,)* } };
1419        }
1420        audio_variants!(arms)
1421    }
1422
1423    /// Overwrite this node's identity. Only the settling and inheritance
1424    /// passes have any business calling this.
1425    pub fn set_uid(&mut self, id: Uid) {
1426        macro_rules! arms {
1427            ($($v:ident),*) => { match self { $(AudioNode::$v { uid, .. } => *uid = id,)* } };
1428        }
1429        audio_variants!(arms)
1430    }
1431
1432    /// Audio children in **key-index order** — the same `/0`, `/1` order
1433    /// `mutate::child_mut`, `describe` and `genome` all use, so index `i` here
1434    /// and the key segment `i` there address the same node.
1435    pub fn children(&self) -> Vec<&AudioNode> {
1436        match self {
1437            AudioNode::Mix { a, b, .. } | AudioNode::RingMod { a, b, .. } => vec![a, b],
1438            AudioNode::Comp {
1439                input,
1440                sidechain: other,
1441                ..
1442            }
1443            | AudioNode::Gate {
1444                input,
1445                sidechain: other,
1446                ..
1447            }
1448            | AudioNode::Duck {
1449                input, key: other, ..
1450            }
1451            | AudioNode::Vocoder {
1452                carrier: input,
1453                modulator: other,
1454                ..
1455            } => vec![input, other],
1456            AudioNode::Shift { input, .. }
1457            | AudioNode::Filter { input, .. }
1458            | AudioNode::Fold { input, .. }
1459            | AudioNode::Delay { input, .. }
1460            | AudioNode::Chorus { input, .. }
1461            | AudioNode::Reverb { input, .. }
1462            | AudioNode::Distortion { input, .. }
1463            | AudioNode::Bitcrush { input, .. }
1464            | AudioNode::Phaser { input, .. }
1465            | AudioNode::Flanger { input, .. }
1466            | AudioNode::Tremolo { input, .. }
1467            | AudioNode::Vibrato { input, .. }
1468            | AudioNode::Eq { input, .. }
1469            | AudioNode::Granular { input, .. } => vec![input],
1470            // The sources. Spelled out rather than caught by a wildcard so
1471            // that a new production cannot quietly become childless — and so
1472            // become invisible to identity — by being forgotten here.
1473            AudioNode::Vco { .. }
1474            | AudioNode::Supersaw { .. }
1475            | AudioNode::Noise { .. }
1476            | AudioNode::Wavetable { .. }
1477            | AudioNode::Pluck { .. }
1478            | AudioNode::Formant { .. }
1479            | AudioNode::Silence { .. } => Vec::new(),
1480        }
1481    }
1482
1483    /// Mutable [`Self::children`].
1484    pub fn children_mut(&mut self) -> Vec<&mut AudioNode> {
1485        match self {
1486            AudioNode::Mix { a, b, .. } | AudioNode::RingMod { a, b, .. } => vec![a, b],
1487            AudioNode::Comp {
1488                input,
1489                sidechain: other,
1490                ..
1491            }
1492            | AudioNode::Gate {
1493                input,
1494                sidechain: other,
1495                ..
1496            }
1497            | AudioNode::Duck {
1498                input, key: other, ..
1499            }
1500            | AudioNode::Vocoder {
1501                carrier: input,
1502                modulator: other,
1503                ..
1504            } => vec![input, other],
1505            AudioNode::Shift { input, .. }
1506            | AudioNode::Filter { input, .. }
1507            | AudioNode::Fold { input, .. }
1508            | AudioNode::Delay { input, .. }
1509            | AudioNode::Chorus { input, .. }
1510            | AudioNode::Reverb { input, .. }
1511            | AudioNode::Distortion { input, .. }
1512            | AudioNode::Bitcrush { input, .. }
1513            | AudioNode::Phaser { input, .. }
1514            | AudioNode::Flanger { input, .. }
1515            | AudioNode::Tremolo { input, .. }
1516            | AudioNode::Vibrato { input, .. }
1517            | AudioNode::Eq { input, .. }
1518            | AudioNode::Granular { input, .. } => vec![input],
1519            // The sources. Spelled out rather than caught by a wildcard so
1520            // that a new production cannot quietly become childless — and so
1521            // become invisible to identity — by being forgotten here.
1522            AudioNode::Vco { .. }
1523            | AudioNode::Supersaw { .. }
1524            | AudioNode::Noise { .. }
1525            | AudioNode::Wavetable { .. }
1526            | AudioNode::Pluck { .. }
1527            | AudioNode::Formant { .. }
1528            | AudioNode::Silence { .. } => Vec::new(),
1529        }
1530    }
1531
1532    /// The modulation term hanging off this node's one mod slot, if the module
1533    /// has one (`Mix`, `RingMod`, `Noise` and `Shift` do not).
1534    pub fn modulation(&self) -> Option<&ModNode> {
1535        macro_rules! arms {
1536            ($($v:ident),*) => {
1537                match self {
1538                    $(AudioNode::$v { modulation, .. } => Some(modulation),)*
1539                    // The four productions with nothing worth modulating:
1540                    // `Noise` has only a colour, `Silence` has nothing at all,
1541                    // and `Mix`/`RingMod` have two audio inputs and a blend.
1542                    // Named rather than wildcarded so a new module with a slot
1543                    // cannot silently land here.
1544                    AudioNode::Noise { .. }
1545                    | AudioNode::Silence { .. }
1546                    | AudioNode::Mix { .. }
1547                    | AudioNode::RingMod { .. } => None,
1548                }
1549            };
1550        }
1551        modulated_variants!(arms)
1552    }
1553
1554    /// Mutable [`Self::modulation`].
1555    pub fn modulation_mut(&mut self) -> Option<&mut ModNode> {
1556        macro_rules! arms {
1557            ($($v:ident),*) => {
1558                match self {
1559                    $(AudioNode::$v { modulation, .. } => Some(modulation),)*
1560                    // The four productions with nothing worth modulating:
1561                    // `Noise` has only a colour, `Silence` has nothing at all,
1562                    // and `Mix`/`RingMod` have two audio inputs and a blend.
1563                    // Named rather than wildcarded so a new module with a slot
1564                    // cannot silently land here.
1565                    AudioNode::Noise { .. }
1566                    | AudioNode::Silence { .. }
1567                    | AudioNode::Mix { .. }
1568                    | AudioNode::RingMod { .. } => None,
1569                }
1570            };
1571        }
1572        modulated_variants!(arms)
1573    }
1574
1575    /// Tree depth (a source leaf is depth 1).
1576    pub fn depth(&self) -> usize {
1577        match self {
1578            AudioNode::Vco { .. }
1579            | AudioNode::Supersaw { .. }
1580            | AudioNode::Noise { .. }
1581            | AudioNode::Wavetable { .. }
1582            | AudioNode::Pluck { .. }
1583            | AudioNode::Formant { .. }
1584            | AudioNode::Silence { .. } => 1,
1585            AudioNode::Mix { a, b, .. } | AudioNode::RingMod { a, b, .. } => {
1586                1 + a.depth().max(b.depth())
1587            }
1588            // The control branch is a full audio subtree that has to be built
1589            // and rendered, so it counts toward depth exactly as a mixer's
1590            // second input does — the render budget cannot tell the two apart.
1591            AudioNode::Comp {
1592                input,
1593                sidechain: other,
1594                ..
1595            }
1596            | AudioNode::Gate {
1597                input,
1598                sidechain: other,
1599                ..
1600            }
1601            | AudioNode::Duck {
1602                input, key: other, ..
1603            }
1604            | AudioNode::Vocoder {
1605                carrier: input,
1606                modulator: other,
1607                ..
1608            } => 1 + input.depth().max(other.depth()),
1609            AudioNode::Shift { input, .. }
1610            | AudioNode::Filter { input, .. }
1611            | AudioNode::Fold { input, .. }
1612            | AudioNode::Delay { input, .. }
1613            | AudioNode::Chorus { input, .. }
1614            | AudioNode::Reverb { input, .. }
1615            | AudioNode::Distortion { input, .. }
1616            | AudioNode::Bitcrush { input, .. }
1617            | AudioNode::Phaser { input, .. }
1618            | AudioNode::Flanger { input, .. }
1619            | AudioNode::Tremolo { input, .. }
1620            | AudioNode::Vibrato { input, .. }
1621            | AudioNode::Eq { input, .. }
1622            | AudioNode::Granular { input, .. } => 1 + input.depth(),
1623        }
1624    }
1625
1626    /// Number of audio nodes in the tree.
1627    pub fn size(&self) -> usize {
1628        match self {
1629            AudioNode::Vco { .. }
1630            | AudioNode::Supersaw { .. }
1631            | AudioNode::Noise { .. }
1632            | AudioNode::Wavetable { .. }
1633            | AudioNode::Pluck { .. }
1634            | AudioNode::Formant { .. }
1635            | AudioNode::Silence { .. } => 1,
1636            AudioNode::Mix { a, b, .. } | AudioNode::RingMod { a, b, .. } => {
1637                1 + a.size() + b.size()
1638            }
1639            AudioNode::Comp {
1640                input,
1641                sidechain: other,
1642                ..
1643            }
1644            | AudioNode::Gate {
1645                input,
1646                sidechain: other,
1647                ..
1648            }
1649            | AudioNode::Duck {
1650                input, key: other, ..
1651            }
1652            | AudioNode::Vocoder {
1653                carrier: input,
1654                modulator: other,
1655                ..
1656            } => 1 + input.size() + other.size(),
1657            AudioNode::Shift { input, .. }
1658            | AudioNode::Filter { input, .. }
1659            | AudioNode::Fold { input, .. }
1660            | AudioNode::Delay { input, .. }
1661            | AudioNode::Chorus { input, .. }
1662            | AudioNode::Reverb { input, .. }
1663            | AudioNode::Distortion { input, .. }
1664            | AudioNode::Bitcrush { input, .. }
1665            | AudioNode::Phaser { input, .. }
1666            | AudioNode::Flanger { input, .. }
1667            | AudioNode::Tremolo { input, .. }
1668            | AudioNode::Vibrato { input, .. }
1669            | AudioNode::Eq { input, .. }
1670            | AudioNode::Granular { input, .. } => 1 + input.size(),
1671        }
1672    }
1673
1674    /// Number of probabilistic-choice sites in this subtree (including the
1675    /// structural `#leaf`/`#src`/`#op` sites and any modulation subterm).
1676    pub fn site_count(&self) -> usize {
1677        // Every node carries #leaf plus either #src or #op.
1678        match self {
1679            // #wave #oct #det #mdepth
1680            AudioNode::Vco { modulation, .. } => 2 + 4 + modulation.site_count(),
1681            // #oct #det #smix #mdepth
1682            AudioNode::Supersaw { modulation, .. } => 2 + 4 + modulation.site_count(),
1683            AudioNode::Noise { .. } => 2 + 1, // #color
1684            // #vowel #fshift #oct #mdepth
1685            AudioNode::Formant { modulation, .. } => 2 + 4 + modulation.site_count(),
1686            AudioNode::Wavetable { modulation, .. } => 2 + 4 + modulation.site_count(), // #table #oct #morph #mdepth
1687            AudioNode::Pluck { modulation, .. } => 2 + 4 + modulation.site_count(), // #oct #damp #bright #mdepth
1688            // The only production that is nothing but its two structural
1689            // sites: no parameters, no modulation slot, nothing to draw.
1690            AudioNode::Silence { .. } => 2,
1691            AudioNode::Mix { a, b, .. } => 2 + 1 + a.site_count() + b.site_count(),
1692            AudioNode::RingMod { a, b, .. } => 2 + 1 + a.site_count() + b.site_count(),
1693            AudioNode::Filter {
1694                input, modulation, ..
1695            } => 2 + 4 + input.site_count() + modulation.site_count(),
1696            AudioNode::Fold {
1697                input, modulation, ..
1698            } => 2 + 2 + input.site_count() + modulation.site_count(),
1699            AudioNode::Delay {
1700                input, modulation, ..
1701            } => 2 + 4 + input.site_count() + modulation.site_count(),
1702            AudioNode::Chorus {
1703                input, modulation, ..
1704            } => 2 + 4 + input.site_count() + modulation.site_count(),
1705            AudioNode::Reverb {
1706                input, modulation, ..
1707            } => 2 + 4 + input.site_count() + modulation.site_count(),
1708            AudioNode::Distortion {
1709                input, modulation, ..
1710            } => 2 + 4 + input.site_count() + modulation.site_count(),
1711            AudioNode::Bitcrush {
1712                input, modulation, ..
1713            } => 2 + 3 + input.site_count() + modulation.site_count(),
1714            AudioNode::Phaser {
1715                input, modulation, ..
1716            } => 2 + 4 + input.site_count() + modulation.site_count(),
1717            AudioNode::Flanger {
1718                input, modulation, ..
1719            } => 2 + 4 + input.site_count() + modulation.site_count(),
1720            AudioNode::Tremolo {
1721                input, modulation, ..
1722            } => 2 + 4 + input.site_count() + modulation.site_count(),
1723            AudioNode::Vibrato {
1724                input, modulation, ..
1725            } => 2 + 4 + input.site_count() + modulation.site_count(),
1726            AudioNode::Eq {
1727                input, modulation, ..
1728            } => 2 + 4 + input.site_count() + modulation.site_count(),
1729            AudioNode::Granular {
1730                input, modulation, ..
1731            } => 2 + 4 + input.site_count() + modulation.site_count(),
1732            // #semis #window #smix #mdepth
1733            AudioNode::Shift {
1734                input, modulation, ..
1735            } => 2 + 4 + input.site_count() + modulation.site_count(),
1736            // Four knobs plus a modulation subterm, as the unary processors
1737            // have — and *two* audio subterms, as the binary ones do.
1738            AudioNode::Comp {
1739                input,
1740                sidechain,
1741                modulation,
1742                ..
1743            } => 2 + 4 + input.site_count() + sidechain.site_count() + modulation.site_count(),
1744            AudioNode::Duck {
1745                input,
1746                key,
1747                modulation,
1748                ..
1749            } => 2 + 4 + input.site_count() + key.site_count() + modulation.site_count(),
1750            AudioNode::Gate {
1751                input,
1752                sidechain,
1753                modulation,
1754                ..
1755            } => 2 + 4 + input.site_count() + sidechain.site_count() + modulation.site_count(),
1756            AudioNode::Vocoder {
1757                carrier,
1758                modulator,
1759                modulation,
1760                ..
1761            } => 2 + 4 + carrier.site_count() + modulator.site_count() + modulation.site_count(),
1762        }
1763    }
1764
1765    /// Compact s-expression rendering for logs and tests.
1766    pub fn to_sexpr(&self) -> String {
1767        match self {
1768            AudioNode::Vco {
1769                wave,
1770                octave,
1771                detune,
1772                modulation,
1773                ..
1774            } => format!(
1775                "(vco {} {octave:+} {detune:.2} {})",
1776                wave.port_name(),
1777                mod_sexpr(modulation)
1778            ),
1779            AudioNode::Supersaw {
1780                octave,
1781                detune,
1782                mix,
1783                modulation,
1784                ..
1785            } => format!(
1786                "(supersaw {octave:+} {detune:.2} {mix:.2} {})",
1787                mod_sexpr(modulation)
1788            ),
1789            AudioNode::Noise { color, .. } => format!("(noise {})", color.port_name()),
1790            AudioNode::Silence { .. } => "(silence)".into(),
1791            AudioNode::Formant {
1792                vowel,
1793                shift,
1794                octave,
1795                modulation,
1796                ..
1797            } => format!(
1798                "(formant v={vowel:.2} s={shift:.2} {octave:+} {})",
1799                mod_sexpr(modulation)
1800            ),
1801            AudioNode::Wavetable {
1802                table,
1803                octave,
1804                morph,
1805                modulation,
1806                ..
1807            } => format!(
1808                "(wavetable {} {octave:+} m={morph:.2} {})",
1809                table.label(),
1810                mod_sexpr(modulation)
1811            ),
1812            AudioNode::Pluck {
1813                octave,
1814                damping,
1815                brightness,
1816                modulation,
1817                ..
1818            } => format!(
1819                "(pluck {octave:+} d={damping:.2} b={brightness:.2} {})",
1820                mod_sexpr(modulation)
1821            ),
1822            AudioNode::Mix { balance, a, b, .. } => {
1823                format!("(mix {balance:.2} {} {})", a.to_sexpr(), b.to_sexpr())
1824            }
1825            AudioNode::RingMod { mix, a, b, .. } => {
1826                format!("(ringmod {mix:.2} {} {})", a.to_sexpr(), b.to_sexpr())
1827            }
1828            AudioNode::Filter {
1829                kind,
1830                cutoff,
1831                resonance,
1832                input,
1833                modulation,
1834                ..
1835            } => format!(
1836                "(filter {kind:?} c={cutoff:.2} r={resonance:.2} {} {})",
1837                mod_sexpr(modulation),
1838                input.to_sexpr()
1839            ),
1840            AudioNode::Fold {
1841                threshold,
1842                input,
1843                modulation,
1844                ..
1845            } => format!(
1846                "(fold t={threshold:.2} {} {})",
1847                mod_sexpr(modulation),
1848                input.to_sexpr()
1849            ),
1850            AudioNode::Delay {
1851                time,
1852                feedback,
1853                mix,
1854                input,
1855                modulation,
1856                ..
1857            } => format!(
1858                "(delay t={time:.2} fb={feedback:.2} mix={mix:.2} {} {})",
1859                mod_sexpr(modulation),
1860                input.to_sexpr()
1861            ),
1862            AudioNode::Chorus {
1863                rate,
1864                depth,
1865                mix,
1866                input,
1867                modulation,
1868                ..
1869            } => format!(
1870                "(chorus r={rate:.2} d={depth:.2} mix={mix:.2} {} {})",
1871                mod_sexpr(modulation),
1872                input.to_sexpr()
1873            ),
1874            AudioNode::Reverb {
1875                size,
1876                damp,
1877                mix,
1878                input,
1879                modulation,
1880                ..
1881            } => format!(
1882                "(reverb s={size:.2} d={damp:.2} mix={mix:.2} {} {})",
1883                mod_sexpr(modulation),
1884                input.to_sexpr()
1885            ),
1886            AudioNode::Distortion {
1887                drive,
1888                tone,
1889                mode,
1890                input,
1891                modulation,
1892                ..
1893            } => format!(
1894                "(dist {} g={drive:.2} t={tone:.2} {} {})",
1895                mode.label(),
1896                mod_sexpr(modulation),
1897                input.to_sexpr()
1898            ),
1899            AudioNode::Bitcrush {
1900                bits,
1901                downsample,
1902                input,
1903                modulation,
1904                ..
1905            } => format!(
1906                "(bitcrush b={bits:.2} r={downsample:.2} {} {})",
1907                mod_sexpr(modulation),
1908                input.to_sexpr()
1909            ),
1910            AudioNode::Phaser {
1911                rate,
1912                depth,
1913                feedback,
1914                input,
1915                modulation,
1916                ..
1917            } => format!(
1918                "(phaser r={rate:.2} d={depth:.2} fb={feedback:.2} {} {})",
1919                mod_sexpr(modulation),
1920                input.to_sexpr()
1921            ),
1922            AudioNode::Flanger {
1923                rate,
1924                depth,
1925                feedback,
1926                input,
1927                modulation,
1928                ..
1929            } => format!(
1930                "(flanger r={rate:.2} d={depth:.2} fb={feedback:.2} {} {})",
1931                mod_sexpr(modulation),
1932                input.to_sexpr()
1933            ),
1934            AudioNode::Tremolo {
1935                rate,
1936                depth,
1937                shape,
1938                input,
1939                modulation,
1940                ..
1941            } => format!(
1942                "(tremolo r={rate:.2} d={depth:.2} s={shape:.2} {} {})",
1943                mod_sexpr(modulation),
1944                input.to_sexpr()
1945            ),
1946            AudioNode::Vibrato {
1947                rate,
1948                depth,
1949                mix,
1950                input,
1951                modulation,
1952                ..
1953            } => format!(
1954                "(vibrato r={rate:.2} d={depth:.2} mix={mix:.2} {} {})",
1955                mod_sexpr(modulation),
1956                input.to_sexpr()
1957            ),
1958            AudioNode::Eq {
1959                low,
1960                mid,
1961                high,
1962                input,
1963                modulation,
1964                ..
1965            } => format!(
1966                "(eq l={low:.2} m={mid:.2} h={high:.2} {} {})",
1967                mod_sexpr(modulation),
1968                input.to_sexpr()
1969            ),
1970            AudioNode::Granular {
1971                position,
1972                size,
1973                density,
1974                input,
1975                modulation,
1976                ..
1977            } => format!(
1978                "(granular p={position:.2} s={size:.2} d={density:.2} {} {})",
1979                mod_sexpr(modulation),
1980                input.to_sexpr()
1981            ),
1982            AudioNode::Shift {
1983                semis,
1984                window,
1985                mix,
1986                input,
1987                modulation,
1988                ..
1989            } => format!(
1990                "(shift s={semis:.2} w={window:.2} mix={mix:.2} {} {})",
1991                mod_sexpr(modulation),
1992                input.to_sexpr()
1993            ),
1994            AudioNode::Comp {
1995                threshold,
1996                ratio,
1997                makeup,
1998                input,
1999                sidechain,
2000                modulation,
2001                ..
2002            } => format!(
2003                "(comp t={threshold:.2} r={ratio:.2} m={makeup:.2} {} {} {})",
2004                mod_sexpr(modulation),
2005                input.to_sexpr(),
2006                sidechain.to_sexpr()
2007            ),
2008            AudioNode::Duck {
2009                amount,
2010                threshold,
2011                release,
2012                input,
2013                key,
2014                modulation,
2015                ..
2016            } => format!(
2017                "(duck a={amount:.2} t={threshold:.2} r={release:.2} {} {} {})",
2018                mod_sexpr(modulation),
2019                input.to_sexpr(),
2020                key.to_sexpr()
2021            ),
2022            AudioNode::Gate {
2023                threshold,
2024                range,
2025                release,
2026                input,
2027                sidechain,
2028                modulation,
2029                ..
2030            } => format!(
2031                "(gate t={threshold:.2} rg={range:.2} r={release:.2} {} {} {})",
2032                mod_sexpr(modulation),
2033                input.to_sexpr(),
2034                sidechain.to_sexpr()
2035            ),
2036            AudioNode::Vocoder {
2037                bands,
2038                attack,
2039                release,
2040                carrier,
2041                modulator,
2042                modulation,
2043                ..
2044            } => format!(
2045                "(vocoder b={bands:.2} a={attack:.2} r={release:.2} {} {} {})",
2046                mod_sexpr(modulation),
2047                carrier.to_sexpr(),
2048                modulator.to_sexpr()
2049            ),
2050        }
2051    }
2052}
2053
2054fn mod_sexpr(m: &ModNode) -> String {
2055    match m {
2056        ModNode::None => "nomod".to_string(),
2057        ModNode::Lfo { wave, rate, .. } => format!("(lfo {} {rate:.2})", wave.port_name()),
2058        ModNode::Env { attack, decay, .. } => format!("(env a={attack:.2} d={decay:.2})"),
2059        ModNode::Rand { rate, glide, .. } => format!("(rand r={rate:.2} g={glide:.2})"),
2060        ModNode::Follow { sens, release, .. } => format!("(follow s={sens:.2} r={release:.2})"),
2061        ModNode::Euclid {
2062            rate,
2063            steps,
2064            pulses,
2065            ..
2066        } => format!("(euclid r={rate:.2} s={steps:.2} p={pulses:.2})"),
2067        ModNode::Op {
2068            kind,
2069            p0,
2070            p1,
2071            input,
2072            ..
2073        } => match kind.param_sites().len() {
2074            1 => format!("({} {p0:.2} {})", kind.label(), mod_sexpr(input)),
2075            _ => format!("({} {p0:.2} {p1:.2} {})", kind.label(), mod_sexpr(input)),
2076        },
2077        ModNode::Pair { kind, a, b, .. } => {
2078            format!("({} {} {})", kind.label(), mod_sexpr(a), mod_sexpr(b))
2079        }
2080    }
2081}
2082
2083fn spine_tags(n: &AudioNode, out: &mut Vec<&'static str>) {
2084    match n {
2085        AudioNode::Vco { wave, .. } => out.push(wave.port_name()),
2086        AudioNode::Supersaw { .. } => out.push("ssaw"),
2087        AudioNode::Noise { .. } => out.push("noiz"),
2088        AudioNode::Wavetable { table, .. } => out.push(match table {
2089            TableShape::Sine => "wsin",
2090            TableShape::Tri => "wtri",
2091            TableShape::Saw => "wsaw",
2092            TableShape::Square => "wsqr",
2093            TableShape::Pulse25 | TableShape::Pulse12 => "wpul",
2094            TableShape::FormantA | TableShape::FormantO => "wfmt",
2095        }),
2096        AudioNode::Pluck { .. } => out.push("plk"),
2097        AudioNode::Formant { .. } => out.push("vox"),
2098        AudioNode::Silence { .. } => out.push("mute"),
2099        AudioNode::Mix { a, .. } => {
2100            spine_tags(a, out);
2101            out.push("mix");
2102        }
2103        // The carrier is the spine; the modulator is a side branch, exactly as
2104        // for `Mix`.
2105        AudioNode::RingMod { a, .. } => {
2106            spine_tags(a, out);
2107            out.push("ring");
2108        }
2109        AudioNode::Filter { kind, input, .. } => {
2110            spine_tags(input, out);
2111            out.push(match kind {
2112                FilterKind::Ladder => "ladr",
2113                FilterKind::SvfLp => "lp",
2114                FilterKind::SvfBp => "bp",
2115                FilterKind::SvfHp => "hp",
2116            });
2117        }
2118        AudioNode::Fold { input, .. } => {
2119            spine_tags(input, out);
2120            out.push("fold");
2121        }
2122        AudioNode::Delay { input, .. } => {
2123            spine_tags(input, out);
2124            out.push("dly");
2125        }
2126        AudioNode::Chorus { input, .. } => {
2127            spine_tags(input, out);
2128            out.push("cho");
2129        }
2130        AudioNode::Reverb { input, .. } => {
2131            spine_tags(input, out);
2132            out.push("rvb");
2133        }
2134        AudioNode::Distortion { input, mode, .. } => {
2135            spine_tags(input, out);
2136            out.push(match mode {
2137                DriveMode::Soft => "drv",
2138                DriveMode::Hard => "clip",
2139                DriveMode::Tube => "tube",
2140            });
2141        }
2142        AudioNode::Bitcrush { input, .. } => {
2143            spine_tags(input, out);
2144            out.push("crsh");
2145        }
2146        AudioNode::Phaser { input, .. } => {
2147            spine_tags(input, out);
2148            out.push("phsr");
2149        }
2150        AudioNode::Flanger { input, .. } => {
2151            spine_tags(input, out);
2152            out.push("flng");
2153        }
2154        AudioNode::Tremolo { input, .. } => {
2155            spine_tags(input, out);
2156            out.push("trem");
2157        }
2158        AudioNode::Vibrato { input, .. } => {
2159            spine_tags(input, out);
2160            out.push("vib");
2161        }
2162        AudioNode::Eq { input, .. } => {
2163            spine_tags(input, out);
2164            out.push("eq");
2165        }
2166        AudioNode::Granular { input, .. } => {
2167            spine_tags(input, out);
2168            out.push("gran");
2169        }
2170        AudioNode::Shift { input, .. } => {
2171            spine_tags(input, out);
2172            out.push("shft");
2173        }
2174        // `/0` is the spine and `/1` the side branch, exactly as for `Mix` and
2175        // `RingMod` — and here the convention is not just a convention: the
2176        // control branch is never heard on its own.
2177        AudioNode::Comp { input, .. } => {
2178            spine_tags(input, out);
2179            out.push("comp");
2180        }
2181        AudioNode::Duck { input, .. } => {
2182            spine_tags(input, out);
2183            out.push("duck");
2184        }
2185        AudioNode::Gate { input, .. } => {
2186            spine_tags(input, out);
2187            out.push("gate");
2188        }
2189        AudioNode::Vocoder { carrier, .. } => {
2190            spine_tags(carrier, out);
2191            out.push("voc");
2192        }
2193    }
2194}
2195
2196impl PatchTree {
2197    /// Total probabilistic-choice sites (amp envelope + tree).
2198    pub fn site_count(&self) -> usize {
2199        4 + self.root.site_count()
2200    }
2201
2202    /// Short human-readable signature along the main signal spine
2203    /// (`saw·ladr·dly`) — the default display name for unnamed patches.
2204    pub fn signature(&self) -> String {
2205        let mut tags = Vec::new();
2206        spine_tags(&self.root, &mut tags);
2207        if tags.len() > 4 {
2208            let skipped = tags.len() - 4;
2209            let tail: Vec<&str> = tags[skipped..].to_vec();
2210            format!("{}+·{}", skipped, tail.join("·"))
2211        } else {
2212            tags.join("·")
2213        }
2214    }
2215
2216    /// Compact s-expression rendering for logs and tests.
2217    pub fn to_sexpr(&self) -> String {
2218        format!(
2219            "(voice a={:.2} d={:.2} s={:.2} r={:.2} {})",
2220            self.amp.attack,
2221            self.amp.decay,
2222            self.amp.sustain,
2223            self.amp.release,
2224            self.root.to_sexpr()
2225        )
2226    }
2227
2228    /// Give every node that still lacks an identity a fresh one, and break any
2229    /// duplicates.
2230    ///
2231    /// This is the *settle* step: the point at which an anonymous tree becomes
2232    /// a thing a person can point at. Call it wherever a tree is adopted —
2233    /// admitted to the pool, put on the bench, restored from a save, arrived
2234    /// from the panel — and nowhere in the middle of search.
2235    ///
2236    /// De-duplication is not paranoia. A whole-tree replace from the panel is
2237    /// arbitrary client JSON, and the one gesture that most obviously produces
2238    /// a repeat is "duplicate this module": copying a subtree copies its uids,
2239    /// and two nodes claiming one identity is worse than no identity at all —
2240    /// a lock on either would light both. First occurrence in walk order keeps
2241    /// the id; the copies are minted fresh.
2242    pub fn ensure_uids(&mut self) {
2243        let mut seen = std::collections::HashSet::new();
2244        settle_audio(&mut self.root, &mut seen);
2245    }
2246
2247    /// Strip every identity back to [`Uid::NEW`].
2248    ///
2249    /// Used where a tree must hash or serialize as pure content — the render
2250    /// memo's key, above all, which would otherwise miss on every refinement
2251    /// step and invalidate every persisted row the moment uids started moving.
2252    pub fn clear_uids(&mut self) {
2253        walk_audio_mut(&mut self.root, &mut |n| n.set_uid(Uid::NEW), &mut |m| {
2254            m.set_uid(Uid::NEW)
2255        });
2256    }
2257
2258    /// Carry `parent`'s identities onto this tree wherever the structure is
2259    /// unchanged.
2260    ///
2261    /// **This is what makes locks and hand positions survive ⚡ evolve.**
2262    /// Refinement does not mutate a tree in place — `EvolutionChain` proposes
2263    /// over the *trace*, and every accepted step reconstructs the whole genome
2264    /// through [`crate::genome`]'s decoder, which knows nothing about uids and
2265    /// cannot: a trace is a map from address to value. So a refined child comes
2266    /// back structurally near-identical to its seed and completely anonymous,
2267    /// and without this pass every generation would look to the UI like a brand
2268    /// new patch — the exact failure (`R6`) that would make pinned routings and
2269    /// freeform layout read as broken.
2270    ///
2271    /// The match is positional and shallow-by-variant: walking both trees in
2272    /// lockstep, a node inherits its counterpart's identity **only if it is the
2273    /// same variant**, but the walk descends either way. That last part
2274    /// matters — a step that swaps one filter for a reverb should not orphan
2275    /// the entire chain beneath it, which was not touched.
2276    ///
2277    /// Anything left unmatched (a genuinely new module, a branch that grew)
2278    /// stays [`Uid::NEW`] and is minted by the following [`Self::ensure_uids`].
2279    pub fn inherit_uids(&mut self, parent: &PatchTree) {
2280        inherit_audio(&mut self.root, &parent.root);
2281    }
2282}
2283
2284/// Depth-first over an audio subtree and every modulation term hanging off it.
2285fn walk_audio_mut(
2286    n: &mut AudioNode,
2287    fa: &mut impl FnMut(&mut AudioNode),
2288    fm: &mut impl FnMut(&mut ModNode),
2289) {
2290    fa(n);
2291    if let Some(m) = n.modulation_mut() {
2292        walk_mod_mut(m, fm);
2293    }
2294    for c in n.children_mut() {
2295        walk_audio_mut(c, fa, fm);
2296    }
2297}
2298
2299fn walk_mod_mut(n: &mut ModNode, fm: &mut impl FnMut(&mut ModNode)) {
2300    fm(n);
2301    for c in n.children_mut() {
2302        walk_mod_mut(c, fm);
2303    }
2304}
2305
2306fn settle_audio(n: &mut AudioNode, seen: &mut std::collections::HashSet<u64>) {
2307    let uid = n.uid();
2308    if uid.is_new() || !seen.insert(uid.0) {
2309        let fresh = Uid::mint();
2310        seen.insert(fresh.0);
2311        n.set_uid(fresh);
2312    } else {
2313        Uid::observe(uid);
2314    }
2315    if let Some(m) = n.modulation_mut() {
2316        settle_mod(m, seen);
2317    }
2318    for c in n.children_mut() {
2319        settle_audio(c, seen);
2320    }
2321}
2322
2323fn settle_mod(n: &mut ModNode, seen: &mut std::collections::HashSet<u64>) {
2324    // `None` is an empty slot, not a module: it draws no plate, owns no knobs
2325    // and cannot be locked, so it has nothing to be identified by — and
2326    // `ModNode::uid` says so by returning `None` for it.
2327    if let Some(uid) = n.uid() {
2328        if uid.is_new() || !seen.insert(uid.0) {
2329            let fresh = Uid::mint();
2330            seen.insert(fresh.0);
2331            n.set_uid(fresh);
2332        } else {
2333            Uid::observe(uid);
2334        }
2335    }
2336    for c in n.children_mut() {
2337        settle_mod(c, seen);
2338    }
2339}
2340
2341fn inherit_audio(child: &mut AudioNode, parent: &AudioNode) {
2342    if std::mem::discriminant(&*child) == std::mem::discriminant(parent) {
2343        child.set_uid(parent.uid());
2344    }
2345    if let (Some(cm), Some(pm)) = (child.modulation_mut(), parent.modulation()) {
2346        inherit_mod(cm, pm);
2347    }
2348    let pk = parent.children();
2349    for (i, c) in child.children_mut().into_iter().enumerate() {
2350        if let Some(p) = pk.get(i) {
2351            inherit_audio(c, p);
2352        }
2353    }
2354}
2355
2356fn inherit_mod(child: &mut ModNode, parent: &ModNode) {
2357    if std::mem::discriminant(&*child) == std::mem::discriminant(parent) {
2358        if let Some(u) = parent.uid() {
2359            child.set_uid(u);
2360        }
2361    }
2362    let pk = parent.children();
2363    for (i, c) in child.children_mut().into_iter().enumerate() {
2364        if let Some(p) = pk.get(i) {
2365            inherit_mod(c, p);
2366        }
2367    }
2368}