Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ header adds no nested namespace, the class) the kernel lives in.
| `grm_comb.h` | `tap.5comb~` | GRM comb-bank recreation (`tap::tools::fivecomb`) |
| `grm_pitchaccum.h` | `tap.pitchaccum~` | GRM PitchAccum recreation (`tap::tools::pitchaccum`) |

**Tape and loops**

| Kernel | Max object | Contents |
|---|---|---|
| `tape_loop.h` | *(shared)* | Tape reel, wow/flutter transport, generation-loss wear (`tap::tools::tape`) |
| `discreet.h` | `tap.discreet~` | *Discreet Music* two-machine regeneration loop (`tap::tools::discreet`) |
| `airport.h` | `tap.airport~` | *Music for Airports* incommensurate loop bank (`tap::tools::airport`) |
| `garden.h` | `tap.garden~` | Generative event loop on the Bloom principle (`tap::tools::garden`) |

`taptools.h` is the umbrella header that pulls in every kernel above. `stft.h`, `tune.h`,
`harmonizer.h` and `conv_engine.h` reach into `tap::dsp` (the pinned DspTap submodule) for the
real FFT and the pitch primitives; every other kernel is standard library only.
Expand Down
217 changes: 217 additions & 0 deletions book/PLAN-eno-chapters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
# Plan — the Eno-family chapters

> **Status: drafted.** All six chapters are written and live in `src/` per the placement
> below (2026-08-12). This file remains as the drafting record, the plans-directory way.

Planning document for the *Tools on Tap* chapters covering the 2026-08 Eno-family work
(`tap.discreet~`, `tap.airport~`, `tap.garden~`; `taptools/tape_loop.h`, `discreet.h`,
`airport.h`, `garden.h`). Six chapters: three user-facing, three machine appendices. This
file is the outline to draft from; it is not part of the built book.

Every measured claim below already exists as an executed notebook cell or a pinned test —
each section lists its evidence so the chapters keep the book's "measured, not remembered"
promise without new lab work.

The family's single thesis, which every chapter should serve and none should re-derive from
scratch: three Brian Eno works are the same idea at three levels of abstraction —
*Discreet Music* (1975) recirculates **audio**, *Music for Airports* "2/1" (1978) phases
**loops**, and the Bloom principle (2008) recirculates **events** — and in all three,
**degradation is the stability mechanism**. Where every other feedback loop in this library
caps its gain below one (`delay.h`'s `k_fb_max`, the comb bank's calibrated ring), these
kernels let regeneration reach exactly 1.0 and stay bounded because each pass is worn:
darkened and saturated in the tape kernels, decayed and softened in the garden.

## Placement in SUMMARY.md

Insert a new part after Part III (Strings, rooms, and spirals); Parts IV–IX renumber to
V–X. The three machine entries slot after `machine/overdrive.md`, keeping the file-by-file
order chronological.

```md
# Part IV — Tape and time

- [The tape that forgets slowly](discreet.md)
- [Loops that never line up](airport.md)
- [The garden that plays itself](garden.md)

# Part IX — The machine, file by file
...existing entries...
- [The clipper in the loop: overdrive.h](machine/overdrive.md)
- [Wear as the stabilizer: tape_loop.h and discreet.h](machine/tape.md)
- [Free-running heads, one shared clock: airport.h](machine/airport.md)
- [Events, not audio: garden.h](machine/garden.md)
```

The user chapters cross-reference `recipes/shimmer.md` (which already credits Eno/Lanois
for the shimmer school) rather than re-telling that lineage.

## Figures

Hand-authored block diagrams in the house style (grey main path, colored emphasis paths,
dashed rate regions): `images/discreet/block-diagram.svg` (the two-machine loop, wear path
in red like the comb's feedback ring), `images/airport/block-diagram.svg` (seven reels, one
free-running head each), `images/garden/block-diagram.svg` (event ring feeding the bell
pool, the gardener dashed).

Measured figures generated by `book/figures/eno.py` (the `overdrive.py` regeneration
contract: drives the shipping kernels through the C ABI, never re-implementations):
`images/discreet/generation-loss.svg` (per-pass two-tone decay vs. the analytic wear
transfer), `images/airport/raster.svg` (return raster of two incommensurate loops, lcm
marked), `images/garden/staircase.svg` (the decay-0.5 return staircase).

---

## Chapter 1 (user-facing) — *The tape that forgets slowly* (`src/discreet.md`)

The image: a machine whose memory is the instrument — everything you play into it comes
back five seconds later a little darker and a little softer, forever if you ask. The
inversion to sell in one paragraph: this is the one delay in the house allowed to run at
regeneration 1.0, *because* it forgets.

1. Provenance: the schematic on the *Discreet Music* back cover; Fripp's rig; the AES
Echoplex-model literature for the tape path. *Evidence: header @details; no measurement.*
2. The echo grid and what "one loop later" means. *Evidence: discreet.ipynb §1; scenario
"the loop echoes at exactly the loop period".*
3. `regen` — and why 1.0 is legal here and illegal in tap.delay~. The wear path as the
stabilizer. *Evidence: discreet.ipynb §2 (20 s bounded RMS at regen 1.0); scenario
"regen 1.0 with drive engaged is bounded and does not grow".*
4. `darken` and `drive` — generation loss, measured against the analytic per-pass transfer
(0.292 at 6 kHz, 0.890 at 300 Hz per pass, both matching prediction to three decimals).
*Evidence: discreet.ipynb §3; scenario "every pass through the loop is darker by the
wear filter".* Figure: `generation-loss.svg`.
5. `wow`/`flutter` — the transport, in cents (10.9 measured vs 10.9 predicted), and the
determinism contract. *Evidence: discreet.ipynb §4; scenario "wow bends pitch by the set
depth, and two runs are bit-exact".*
6. `loop` moves are tape speed — the doppler is honest, not a defect. *Evidence: scenario
"a loop-time change glides as tape speed, not a splice".*
7. `input_level` — the performance move: fade the send, the piece continues. *Evidence:
discreet.ipynb §5; eno_render discreet_sustain.*
8. Recipes: the Discreet Music bed; Frippertronics duo (regen 1.0, drive up); haunted
slapback (short loop, heavy wow); infinite pad sustainer.
9. When it is not the right tool: rhythmic delays that must not bend pitch (tap.delay~);
multitap patterns (tap.multitap~); anything needing a dry-signal guarantee at regen 1.0.
10. Checkpoint.

## Chapter 2 (user-facing) — *Loops that never line up* (`src/airport.md`)

The image: seven tape loops of awkward lengths, each holding one phrase, all turning at
once — composition by coincidence. The chapter should make the reader feel that the
*lengths are the score*.

1. Provenance: Eno's published account of "2/1"; the machine keeps the loops turning, the
incommensurability does the composing. *Evidence: header @details.*
2. Record and return: punch-in at the head, bit-exact freeze, no downbeat, no reset —
the free-run is the piece. *Evidence: airport.ipynb §1; scenarios "a recorded phrase
returns every loop period and no setter resets the phase", "record off freezes the tape
bit-exactly".*
3. The composite period: 24000- and 30000-sample loops realign at exactly 2.5 s
(`composite_period_seconds`), and seven airport-scale loops overflow to infinity —
which is the point. *Evidence: airport.ipynb §2; scenario "two incommensurate loops
realign only at the lcm".* Figure: `raster.svg`.
4. Level, pan, darken: placing phrases in the field; the shade is a playback tone, not
generation loss (a frozen loop replays the same imprint — the honest non-feature),
measured at 0.169 vs 0.169 predicted. *Evidence: airport.ipynb §3; scenarios
"a hard-panned loop is bitwise absent from the far bus", "darken shades one loop's
playback and only that loop's".*
5. Splices: what a length change does and why it may click. *Evidence: scenario "a length
change is a splice: phase re-wraps and never rewinds".*
6. Recipes: the "2/1" bed (seven loops, published-spirit ratios); two-loop phase study
(Reich-adjacent); one-loop sound-on-sound sketchpad; run sources through tap.discreet~
first for tape breath (cross-reference, wow is deliberately absent here).
7. When it is not the right tool: synchronized loopers (this one never lines up by
design); beat-locked material; per-pass degradation (that is tap.discreet~'s job).
8. Checkpoint.

## Chapter 3 (user-facing) — *The garden that plays itself* (`src/garden.md`)

The image: an instrument you tend rather than play — plant a note, it returns each pass a
step quieter and purer until it fades; stop playing and the garden keeps itself. Name the
IP posture plainly (the principle from published descriptions; no Bloom tables, timings, or
sounds; "Bloom" is Opal's trademark — the tune.md history paragraph is the template for
this kind of honesty).

1. Provenance and the third abstraction level: audio → loops → events; per-pass decay is
the same stabilizer wearing its third costume. *Evidence: header @details.*
2. Plant and return: the staircase (0.795, 0.399, 0.2, 0.1, 0.05, silence) and the
retirement arithmetic — the population converges by construction. *Evidence:
garden.ipynb §1; scenarios "a planted note blooms again every loop period", "each
return is quieter by the decay ratio and the bloom retires below the floor".* Figure:
`staircase.svg`.
3. `soften` — returns get purer, not just quieter: the FM sideband fades while the
fundamental holds. *Evidence: garden.ipynb §2; scenario "each return is purer: the fm
partial fades by the soften ratio".*
4. The scale contract: thirteen chromatic plants, every bloom on the pentatonic by the
YIN oracle; quantize-at-entry and why wrong notes are impossible. *Evidence:
garden.ipynb §3; scenario "every bloom lands on the scale".*
5. The gardener: idle threshold, one plant per pass, and the seed triad (bit-exact /
different / cannot-matter) — the library's first randomized event source, with the
tr808 seed contract as the bridge back to reproducibility. *Evidence: garden.ipynb §4;
scenarios "the seeded garden is bit-exact per seed...", "left alone, the garden starts
playing after idle_seconds — and never when idle is disabled".*
6. Bounds you can lean on: 64 events (oldest yields), 16 bells (quietest stolen,
envelopes re-aimed not reset). *Evidence: scenarios "when the garden is full the
oldest bloom yields to the newest", "the bell pool never exceeds its size...".*
7. Recipes: the lobby garden (defaults, long idle); the music box (fast decay, no
gardener); the endless install (seeded, level low, walk away); duet mode (idle short,
trade phrases with the gardener).
8. When it is not the right tool: melodies with wrong notes in them (quantization is
always on); rhythm outside the loop grid; any timbre that is not a soft bell.
9. Checkpoint.

## Chapter 4 (machine) — *Wear as the stabilizer: tape_loop.h and discreet.h*
(`src/machine/tape.md`)

The centerpiece of the family's engineering story. Sections in code order: the shared
header decision (class-with-state → shared header, the swing_vca.h precedent; the ramp and
Hermite read as cited copies); `reel` and the one wrap that serves two topologies;
`wow_flutter` and the periodic-only decision (testability as a design force); `wear` and
the boundedness argument (swing_shape bounded by 1/drive ⇒ BIBO at regen 1.0; the
normalized DC blocker; what drive 0 promises and what it does not); the doppler decision
told as a design choice (moving the read head IS the tape speed — no crossfade mode); the
LLP64 head-wrap note. One section told as a finding: the notebook's per-pass measurement
landing on the analytic transfer to three decimals — the moment the model and the
arithmetic agreed. The engineering ledger: analytic-transfer oracle, two-window RMS
non-growth (the grm_comb swell story inherited), YIN as transport oracle, bitwise endpoint
laws. *Evidence: discreet_test.cpp scenarios (all), discreet.ipynb §§1–5.*

## Chapter 5 (machine) — *Free-running heads, one shared clock: airport.h*
(`src/machine/airport.md`)

Sections: the loop_state shape (multitap's fixed-array idiom with a reel per slot); the
phase discipline (never reset — enumerate what may and may not touch it, and the
setter-storm test that pins it); record semantics (replace at the head, read-before-write,
the two-sample Hermite blend at the punch, no overdub by provenance); the splice
arithmetic; the darken bypass at the band ceiling (bit-transparency as a testable
contract); composite_period_seconds (gcd/lcm in long long, overflow → +inf as a feature).
Finding section: the raster plot making the 2.5 s lcm visible before the assertion pinned
it. Ledger: bitwise structural assertions over spectral ones wherever the promise allows.
*Evidence: airport_test.cpp scenarios (all), airport.ipynb §§1–4.*

## Chapter 6 (machine) — *Events, not audio: garden.h* (`src/machine/garden.md`)

Sections: the event ring (fixed 64, seq-numbered, oldest-yields — the musical argument for
the overflow policy); the fire/bloom split and why note() does not sound the voice itself
(the double-trigger it avoids); the bell (2-op FM at ratio 3, why harmonicity was a test
requirement before it was an aesthetic; decay_env reuse; steal-by-re-aim); quantize-at-
entry (the tune.h mask idiom, copied not included, and the coupling argument); the
gardener (consumption discipline: rng touched only when idling — the cannot-matter leg of
the triad depends on it); the population-convergence arithmetic as the header's stated
theorem. Finding section: the onset-detector rewrite — exponential tails never reach zero,
so "returns on the grid" had to be pinned by threshold, an honest lesson about testing
envelopes. Ledger: the seed triad as contract, YIN for the scale promise, Goertzel for the
softening trajectory. *Evidence: garden_test.cpp scenarios (all), garden.ipynb §§1–5.*

## Notes for drafting

- Voice: the person patching, not the person marketing. The family chapters may assume
the reader has met `delay.h`'s feedback story (Part III) — the inversion lands harder
against it.
- Title alternates considered and rejected: "The two tape machines" (names the rig, not
the image), "Airport music" (flip), "Bloom, recreated" (trademark in a title — no).
- Figures: regenerate rather than screenshot (`book/figures/eno.py`); notebook previews
are decimated for repo size, figures are full-rate measurements.
- Cross-repo linking: none needed here (all four headers live in this repo); the
machine/spectral.md convention is not required.
- The render tool (`eno_render`) is the listening companion; chapters may point at its
scenario names for "hear this" moments but must not cite it for numbers — numbers come
from the notebooks and tests only.
Loading
Loading