Add the Eno family: tap.discreet~, tap.airport~, tap.garden~ - #30
Merged
Conversation
tape_loop.h (tap::tools::tape) factors the pieces both Eno-family tape kernels compose, the way swing_vca.h holds the drum family's shared stages: reel (position-addressed circular tape, Hermite reads wrapped at a settable loop length), wow_flutter (deterministic dual-sine transport per the AES tape-echo literature), wear (darkening one-pole -> bounded swing_shape saturation -> normalized DC blocker), and the delay.h ramp. discreet.h (tap.discreet~) recreates the two-machine long-delay rig from the Discreet Music back-cover schematic. The design inversion the kernel exists to state: regeneration legally reaches 1.0, and stability comes from the wear path, not a feedback cap — with drive engaged the loop is BIBO-bounded at any regen, and at drive 0 the sub-cutoff band sustains indefinitely (the Frippertronics contract). Loop-time changes ride the ramp as honest tape-speed doppler. Tests pin the promises: echo grid to the sample, regen-1.0 non-growth over ten seconds, per-pass darkening measured against the analytic wear transfer, DC non-accumulation, wow depth measured by the YIN oracle against the closed-form deviation, bitwise mix endpoints, doppler glide re-lock, and bit-exact reproducibility. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016zgrm1MGS1eir4hCfZBJaR
C ABI section, ctypes bridge class (Discreet), and the executed discreet.ipynb: the echo grid to the sample, twenty seconds of bounded regen-1.0 sustain, per-pass generation loss measured against the analytic wear transfer (0.292 and 0.890 per pass, both matching the prediction to three decimals), the wow transport pitch-tracked at 10.9 cents peak against the 10.9-cent closed form, and the fade-the-send performance move rendered end to end. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016zgrm1MGS1eir4hCfZBJaR
airport.h (tap.airport~) recreates the "2/1" tape system from Eno's published accounts: up to eight free-running loops of unequal lengths, each a tape_loop.h reel with a single head that both plays and records. The phase is never reset by any setter — the free-run is the piece — and a length change is an honest splice that re-wraps without rewinding. Per-loop level, equal-power pan (exact endpoints, the delay.h multitap law), and a playback darken corner that bypasses to bit-transparency at the band ceiling; no per-pass generation loss, because a loop replays the same magnetic imprint. composite_period_seconds() reports the lcm of the active lengths, +inf on overflow. Tests pin the structural promises exactly: bit-exact returns on the loop grid through a mid-run setter storm, bit-exact freeze at record-off, the lcm realignment of coprime lengths (and non-realignment at half), hard pans bitwise absent from the far bus, the darken shade measured against the analytic one-pole transfer, and the splice law. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016zgrm1MGS1eir4hCfZBJaR
C ABI section, ctypes bridge class (Airport, with per-loop sequence setters on the Multitap template), and the executed airport.ipynb: bit-exact returns on the loop grid, the 2.5 s lcm of a 24000/30000 sample pair confirmed by composite_period_seconds and by the rendered coincidence raster, an airport-scale seven-loop bank whose composite period overflows to infinity, the per-loop shade measured on the analytic one-pole transfer (0.169 vs 0.169), and two minutes of "2/1"-like material rendered and embedded. Notebook audio previews are embedded decimated (16 kHz) to keep the executed notebooks near house size; discreet.ipynb re-executed the same way. Full-rate listening copies come from the render tool. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016zgrm1MGS1eir4hCfZBJaR
garden.h (tap.garden~) is the family's third abstraction level: discreet recirculates audio, airport phases loops, garden recirculates events. A planted note snaps to the scale at entry, sounds a two-operator FM bell (Chowning 1973, ratio 3 — harmonic, so YIN reads the fundamental), and returns at its loop position every pass, velocity times decay and brightness times soften, until it retires below the floor. The stability inversion carries up a level: per-pass decay IS the stabilizer, the population converges by construction, and a fixed sixteen-bell pool (quietest-stolen, envelopes re-aimed rather than reset) hard-bounds the audio. Left idle, a seeded gardener (tr808::white_noise, the house xorshift64* contract) plants about one scale note per pass. Recreates the published principle only — no app tables, timings, or sounds; named for Eno's gardener-not-architect metaphor. Tests pin the return grid to a sixth of a millisecond, the decay ratio per return, the strictly-purifying FM sideband, scale membership by the YIN oracle, the full seeded triad (bit-exact per seed, differing across seeds, seed-irrelevant with idling off), the idle threshold, oldest-drop at the 64-event ring, and pool/finiteness bounds under heavy stealing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016zgrm1MGS1eir4hCfZBJaR
C ABI section, ctypes bridge class (Garden), and the executed garden.ipynb: the decay-0.5 return staircase measured at 0.795, 0.399, 0.2, 0.1, 0.05 and then retirement to zero live events; the softening FM sideband fading per pass while the fundamental holds; a chromatic sweep of thirteen plants all landing on the pentatonic by the YIN oracle; the seed triad demonstrated on two gardeners; and two minutes of self-tending garden with the population breathing around its converged size (20 events, 16 bells at the end). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016zgrm1MGS1eir4hCfZBJaR
The Eno kernels are long-timescale systems, so offline rendering is the only practical audition. Five scenarios to WAV: the two-machine loop at regen 0.95, the regen-1.0 Frippertronics wash with the send faded at the halfway mark, three stereo minutes of seven incommensurate airport loops, four planted garden notes recirculating to silence, and two minutes of the seeded gardener left alone. Verified non-silent and unclipped end to end. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016zgrm1MGS1eir4hCfZBJaR
book/PLAN-eno-chapters.md: the drafting record for three user-facing chapters and three machine appendices, per the PLAN-pitch-chapters.md shape — SUMMARY placement diff, per-chapter outlines with every numbered item carrying its evidence (notebook section or pinned test scenario), figure list, and drafting notes. Status: planned; flips to drafted when the chapters land. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016zgrm1MGS1eir4hCfZBJaR
A new Part IV for the Eno family — "The tape that forgets slowly", "Loops that never line up", "The garden that plays itself" — plus three machine appendices (tape_loop.h/discreet.h, airport.h, garden.h) closing Part IX, with later parts renumbered. Hand-authored block diagrams in the house style for all three objects, measured figures generated by book/figures/eno.py against the shipping kernels (generation loss on the analytic wear transfer, the incommensurate-loop raster with its 2.5 s lcm, the decay staircase), and the PLAN flipped to drafted. The part's through-line is the family thesis: three works, one idea at three abstraction levels, and in all three degradation is the stability mechanism. Every number in the chapters cites its executed notebook cell or pinned test scenario; mdbook 0.4.40 (the CI pin) builds clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016zgrm1MGS1eir4hCfZBJaR
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
Adds a new kernel family recreating three Brian Eno systems — the Discreet Music two-machine tape loop (
discreet.h/tap.discreet~), the Music for Airports incommensurate loop bank (airport.h/tap.airport~), and a generative event loop on the Bloom principle (garden.h/tap.garden~) — plus shared tape machinery (tape_loop.h), the full verification stack for each (Catch2 scenarios, C ABI, ctypes bridge, executed notebooks), aneno_renderlistening tool, and a new Tools on Tap part ("Tape and time": three user chapters, three machine appendices, block diagrams, measured figures).Why
The library's families so far recreate instruments and effects; this one recreates systems, and it exists to state a design inversion the house has not had before: every prior regenerating loop is stabilized by a feedback cap (
delay.h'sk_fb_max = 0.99), while these kernels let regeneration reach exactly 1.0 and stay bounded because each pass is degraded — darkened and saturated on the tape, decayed and softened in the garden. Wear is the stabilizer. The three works are the same idea at three abstraction levels (audio → loops → events), which the book part uses as its through-line.Provenance per house IP policy: the Discreet Music back-cover schematic + the AES tape-echo literature (Arnardóttir/Abel/Smith 2008, Välimäki et al.); Eno's published accounts of "2/1"; the Bloom principle from published interviews and Eno's 1996 generative-music talk (no app tables, timings, or sounds — "Bloom" is a live Opal Ltd trademark, hence the neutral object name; bell synthesis cites Chowning 1973).
Verification
discreet_test.cpp,airport_test.cpp,garden_test.cpp; full suite green locally on Linux/GCC Release. Highlights: regen-1.0 two-window RMS non-growth over 10 s; per-pass generation loss measured against the analytic wear transfer (0.292 at 6 kHz, 0.890 at 300 Hz — both matching prediction to three decimals); wow depth by the YIN oracle at 10.9 cents vs the 10.9-cent closed form; bit-exact loop freeze and lcm realignment (24000/30000 samples → exactly 2.5 s) in airport; the decay-0.5 return staircase (0.795/0.399/0.2/0.1/0.05 → retirement) and the full seeded triad in garden.discreet.ipynb,airport.ipynb,garden.ipynbdrive the shipping C++ through the C ABI; committed executed. Audio previews are embedded decimated (16 kHz) to keep notebook sizes near house norms; full-rate listening copies come fromeno_render(all five scenario WAVs verified non-silent and unclipped).mdbook0.4.40 (the CI pin) builds the book clean with the new part;create-missing = falsesatisfied.longwidth is handled (heads wrap explicitly), but LLP64 is untested locally.Notes for the reviewer
submodules/taptoolspin. After this merges by rebase/squash, the consumer pin should point at the identical tree onmain.taptools_py.pygainedDiscreet,Airport,Gardenclasses; the three new notebooks are committed executed.garden.his the library's first randomized event source (seededtr808::white_noise, the established seed-triad contract);step_seq.h's "no randomness anywhere" promise is untouched, and the contrast is documented in both headers' banners.airport.h's worst case (8 × 30 s reels ≈ 92 MB double @ 48 kHz) is stated in its banner;prepare(sr, max_loop_seconds)sizes it down freely.🤖 Generated with Claude Code
https://claude.ai/code/session_016zgrm1MGS1eir4hCfZBJaR
Generated by Claude Code