AquaSynth/docs/pink-trombone-parity.md

197 lines
9.1 KiB
Markdown
Raw Normal View History

2026-05-25 18:36:06 +01:00
# Pink Trombone Parity Packet
## Objective
Use Pink Trombone as old-school speech parity pressure, not as a vague mascot
2026-05-25 19:36:21 +01:00
for "more vocal knobs." The current AquaSynth tract voice is a tract-shaped
voice treatment with Pink Trombone-style controls. Pink Trombone itself is still
a moving vocal-tract waveguide. Those are related machines, but not the same
machine.
2026-05-25 18:36:06 +01:00
This packet exists so the mismatch stays inspectable.
## Reference Source
Primary source for this packet:
- `chdh/pink-trombone-mod`: MIT TypeScript modularization of Neil Thapen's Pink
Trombone, <https://github.com/chdh/pink-trombone-mod>
2026-05-26 22:20:42 +01:00
- Pinned local source: `external/pink-trombone-mod` at
`359c2d3b42b10280404c1650dc601902112b4c90`
2026-05-25 18:36:06 +01:00
- Source files inspected:
- `src/Synthesizer.ts`
- `src/Glottis.ts`
- `src/Tract.ts`
- `src/TractShaper.ts`
The original interactive/programmatic Pink Trombone lineage is also visible in
`zakaton/Pink-Trombone`, but that package is GPL-3.0. Treat it as reference
pressure only unless a later license review says otherwise.
## Current Mechanism
Pink Trombone's source-readable graph:
```text
frequency/tenseness/intensity
-> LF-style glottal source plus aspiration noise
-> 44-section main tract diameter array
-> squared diameters become section areas
-> adjacent section areas become reflection coefficients
-> right/left traveling-wave delay-line state
-> 28-section nasal branch through a velum junction
-> lip output + nose output
```
The synthesizer steps the tract twice per output sample. Tract shape changes are
not static formants: the shaper slews section diameters toward target diameters,
updates reflections at block boundaries, injects turbulence at constrictions,
and emits a transient when a closure opens.
2026-05-26 11:21:21 +01:00
The 44 oral and 28 nasal counts are Pink Trombone's implementation grid, not
the AquaSynth abstraction. At 44.1 kHz, a 17 cm tract split into 44 cells has an
acoustic travel time of roughly 0.5 samples per cell, so the twice-per-sample
update is better understood as a discretization/wave-clock choice. AquaSynth's
live target is continuous morphology plus fractional-delay waveguide lowering,
with the grid chosen by the backend.
2026-05-25 18:36:06 +01:00
## AquaSynth Capability Map
Current AquaSynth can express:
- runtime parameters with stable paths;
2026-05-26 11:21:21 +01:00
- reusable `tract_shape` declarations with authored diameter or area samples,
physical length, normalized interpolation, resampling, and per-cell acoustic
delay derivation;
- derived tract areas and adjacent-section reflection coefficients;
- reusable `glottis` declarations for tract excitation quality;
- reusable `tract_injection` declarations for positioned frication/burst
pressure;
- reusable `nasal_branch` declarations with velum-controlled junctions;
- reusable `tract_motion` declarations for slew and obstruction thresholds;
2026-05-25 19:36:21 +01:00
- a `tract` voice command owned by `Voice.Tract`;
- Pink Trombone-shaped controls for intensity, tenseness, tongue body,
constriction, velum/nasal opening, turbulence, lip opening, and end
reflections;
- a Faust-lowered tract voice proxy with LF-style excitation, aspiration,
frication, oral resonators, and a velum-controlled nasal lane;
- generated oral bidirectional waveguide equations over a `tract_shape` section
field when `propagation=waveguide`;
2026-05-25 18:36:06 +01:00
- oscillator/noise sources;
- ADSR and staged rate/level envelopes;
- low/high/band/notch filters;
- static and frame-scanned formant banks;
- patch-level and voice-local modulation;
- compiled Faust control sweeps through `/speech/output/N`.
2026-05-25 19:36:21 +01:00
Current AquaSynth cannot exactly express:
2026-05-25 18:36:06 +01:00
2026-05-26 11:21:21 +01:00
- fractional-delay tract propagation whose clock derives from physical length,
propagation speed, and sample rate;
2026-05-26 22:20:42 +01:00
- exact Pink Trombone block timing and source/noise/transient behavior inside
the Faust-lowered Aqua graph.
## Reference Renderer Authority
`PinkTromboneReferenceRenderer` is a source-port of the pinned MIT TypeScript
implementation, not a Pink-Trombone-flavored proxy. Its inner graph mirrors the
upstream DSP authorities:
- `Synthesizer`: 512-sample blocks, tract stepping twice per output sample;
- `Glottis`: LF waveform coefficient solve, aspiration noise, vibrato/wobble,
and intensity/tenseness/frequency smoothing;
- `Tract`: 44 oral cells, 28 nasal cells, area-derived reflections, three-way
nasal junction, lip/nose radiation sum, filtered frication, and closure
transients;
- `TractShaper`: tongue/rest diameter targets, touch-style constriction
reduction, wall slew, velum slew, and obstruction release detection.
The renderer keeps AquaSynth's fixture control record as the authoring surface,
but those controls are now translated into the source graph rather than into a
hand-rolled approximation. Reference WAVs written before
`20260526T211902375` were produced by the old approximation and are invalid for
parity golf.
2026-05-25 18:36:06 +01:00
2026-05-25 19:36:21 +01:00
## Current Parity Rung
2026-05-25 18:36:06 +01:00
2026-05-25 19:36:21 +01:00
The current committed rung is an expressive tract voice, not exact Pink
Trombone anatomy:
2026-05-25 18:36:06 +01:00
- `PinkTromboneReference.ToReferencePatch()` records the source, tract features,
and relevant controls.
- `ReferenceRebuildCatalog.PinkTromboneRebuilds` contains the current
2026-05-25 19:36:21 +01:00
AquaSynth tract-voice proxy as a deliberately non-passing rebuild.
- The `tract` DSL command parses into an ordinary `Voice` with `Voice.Tract`,
so this remains a voice with a very expressive tract treatment.
2026-05-26 11:21:21 +01:00
- `tract_shape` owns reusable continuous diameter/area functions. The current
proxy consumes its shape summaries and derived reflection energy; waveguide
lowering consumes a compiled grid sampled from that continuous owner, so
authoring sample count and backend section count can differ.
- `glottis` and `tract_injection` own excitation and positioned noise/burst
controls consumed by tract voices.
- `propagation=waveguide` emits an oral right/left tube from the derived
reflection field. It is the first low-level propagation path, not full PT.
- `nasal_branch` adds a second diameter/area tube and generated three-way
oral/nasal scattering junction when used by a waveguide tract.
- `tract_injection` pressure is distributed into waveguide section updates by
position and width when waveguide propagation is active.
- `tract_motion` smooths tract controls and lets the waveguide derive burst
pressure from obstruction history.
- waveguide lowering derives live per-section diameter targets, areas, and
reflection coefficients from shape and gesture controls.
- waveguide lowering now defaults to an acoustic unit-delay compiled grid from
physical tract length, currently turning a 17 cm oral tract into 22 compiled
sections at the 44.1 kHz target instead of treating PT's 44 cells as anatomy.
2026-05-26 11:21:21 +01:00
- `substeps` is now legacy waveguide clock pressure. Current Faust lowering
consumes it through drive/loss scaling, but the coherent target is
fractional-delay propagation from physical tract length and wave speed.
2026-05-25 21:33:33 +01:00
- `PinkTromboneParityFixtures` defines fixed Aqua DSL workouts for open,
front, nasal, sibilant, and closure cases using the reusable low-level
primitives.
- `PinkTromboneReferenceRenderer` renders deterministic test-only traveling-wave
references for those fixture controls.
- `PinkTromboneLogMelParityTests` renders Aqua waveguide candidates,
writes listening WAV/report artifacts under
`artifacts/parity/pink-trombone-logmel/`, and reports the current waveguide
baseline: open vowel cosine 0.6143, front vowel 0.6349, nasal 0.5425,
sibilant 0.3744, closure-release -0.0820.
- Waveguide lowering now uses one Faust feedback component
`wg_loop ~ si.bus(...)` for the compiled oral tract and nasal branch. The
previous scalar named-recursion form was cut because it was the wrong Faust
shape, and the PT 44/28 grid is now reference pressure rather than the
default emitted topology.
2026-05-25 19:36:21 +01:00
- The proxy script parses and emits Faust with live controls, but its missing
features explicitly name the waveguide authorities AquaSynth does not own.
- `tools/vocal-tract-playground` exposes the same control surface for fast
knob-twiddling through a small WebAudio witness.
2026-05-25 18:36:06 +01:00
- `PinkTromboneReferenceDeclaresMissingWaveguideAuthority` prevents the proxy
from being mistaken for real tract parity.
2026-05-25 19:36:21 +01:00
That is the correct shape for now. It should be fun to touch, but still honest
about the parts of Pink Trombone it cannot claim.
2026-05-25 18:36:06 +01:00
## Next Cut
Do not add another formant workaround. The next coherent implementation is one
of these:
2026-05-25 19:36:21 +01:00
1. Promote `Voice.Tract` from tract-shaped proxy lowering to a dedicated tract
DSP model in AquaSynth.Faust/Core: typed tract sections, diameter curves,
reflection calculation, glottal source, nasal branch, and a Faust emitter for
that structure.
2026-05-26 11:21:21 +01:00
2. Replace `substeps` as a musical surface with fractional-delay waveguide
lowering. Physical length, propagation speed, and sample rate decide delay;
runtime morphology controls move diameters and junction openings without
recompiling unless topology changes.
3. Use the log-mel artifact loop to golf fixture-by-fixture now that the real
tract path renders.
2026-05-25 18:36:06 +01:00
The new owner should be explainable as:
```text
TractPlan owns section diameters and source/injection events so tract acoustics
remain true during rendering.
```
If that sentence becomes awkward, the design is probably drifting back into
proxy mud.