Skip to content

Recipes (book Part IX) + the tap.harmony~ kernel - #27

Merged
tap merged 12 commits into
mainfrom
claude/taptools-book-recipes-vdmgr6
Aug 5, 2026
Merged

Recipes (book Part IX) + the tap.harmony~ kernel#27
tap merged 12 commits into
mainfrom
claude/taptools-book-recipes-vdmgr6

Conversation

@tap

@tap tap commented Aug 5, 2026

Copy link
Copy Markdown
Owner

What this changes

Adds Part IX — Recipes — to Tools on Tap (a part opener plus eleven recipe chapters, from the TR-808-through-four-decades kits to the Bon Iver harmonizer patches), and ships the harmonizer kernel the drafting audit demanded: taptools/harmonizer.h (tap::tools::harmony), its Catch2 battery, capi + ctypes-bridge exposure, and the executed notebooks/harmonizer.ipynb. Also: CI badges on the README, and small shipped-chapter corrections the audit caught.

Why

The book needed a third kind of chapter — whole patches chasing named sounds — and drafting them against the wrapper sources doubled as an audit of the object offerings. That audit's findings live in TapTools-Max's plans/recipes-improvements.md; its first shipped consequence is here: the "Hide and Seek" mechanism (a formant-corrected multi-voice harmonizer) had no object, and the DspTap pvoc substrate already carried everything needed. The audit also caught real drift in shipped chapters (the pitchaccum chapter documented pitch1/feedback1; the wrapper spells them trans1/fb1 on a 0–99 scale), fixed here.

Verification

  • Kernel: seven Catch2 scenarios in tests/harmonizer_test.cpp, oracle-based (DspTap yin) — full suite green locally and in CI, clang-format/clang-tidy clean.
  • Measured, not remembered: notebooks/harmonizer.ipynb is committed executed — max interval error 0.04 cents across two octaves of voicings; dry-path alignment residual 3.7×10⁻⁸; formant band-centroid 1058 → 1154 Hz on a +7 shift with the flag on versus 1439 Hz riding the full ratio with it off. The choir-of-one chapter cites these cells.
  • Book builds with mdbook (create-missing = false, so every SUMMARY entry exists).
  • Not done here: the in-Max validation of the wrapper (TapTools-Max side) and a proper object chapter for the harmonizer.

Notes for the reviewer

  • Notebooks executed, per the verification-layer rule — harmonizer.ipynb is new and committed executed.
  • Recipes provenance discipline: pattern grids are labeled starting points, not transcriptions; documented production history is separated from folklore; the Orange Vocoder section builds our own VA carrier and states the no-reverse-engineering-shipping-products rule in print.
  • Companion PR: TapTools-Max carries the tap.harmony~ wrapper (pin pointed at this branch), the recipes-improvements plan, and the package README. After this merges by rebase/squash, repoint that pin at the identical tree on main.
  • Heads-up: the ADSR virtual-analog rewrite (improvements plan §1) begins on this same branch next, so further commits will land here before review completes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01V6CC6kpK5Lx2YkJWuNNQpw


Generated by Claude Code

claude added 10 commits August 5, 2026 01:29
A third kind of chapter for Tools on Tap: Parts I–VII say what each
object is for, Part VIII says why to trust it, and a recipe puts several
objects on one patch cord and chases a specific sound. The part opener
states the rules (every knob spelled as the attribute is spelled;
settings are starting points while measurements stay citations;
provenance honest; ingredients ranked).

Two recipes ship with the part:

- "One machine, four decades" — four TR-808 kits off one
  phasor~/tap.808.seq~ scaffold: Planet Rock electro, Sexual Healing
  slow soul, Miami bass (the tuned long-decay kick as bassline), and
  trap with polymeter hat-roll rows.
- "Three oscillators into a ladder" — the oscillator chapter's Moog
  recipe completed into a playable voice: stack, ladder, two tap.adsr~
  contours, gate/pitch plumbing, and bass + lead settings tables.

Every attribute and message was drafted against the TapTools-Max
wrapper sources rather than from memory — which is what caught the
snare's absent decay knob, the clap's tail spelling, the sequencer's
hits/accents/velocities/step surface, the ladder's numeric mode index,
and tap.adsr~'s level-gate contract. PLAN-recipes.md is the drafting
record and carries the backlog (acid line, robot voice, shimmer,
envelope filter, IR field guide, comb tunings).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6CC6kpK5Lx2YkJWuNNQpw
"The patches with names on them" drives the previous recipe's
three-oscillator voice at four records, each as a settings delta from
its bass/lead tables: Steve Winwood's "While You See a Chance" hook,
Bernie Worrell's stacked-Minimoog "Flash Light" bass, Rick Wright's
"Shine On You Crazy Diamond" lead, and Keith Emerson's one-take
"Lucky Man" modular solo. Gear provenance is stated per patch —
documented history versus reconstruction — per the part opener's rules.

The Lucky Man section carries the answer to whether the kit needs a
Moog modular object: no, because a modular is routing freedom and Max
is the patch panel — the modules already ship, and the wiring the solo
needs is the previous chapter's diagram. The genuinely modular idiom
(the sequenced line: tap.303.seq~ -> mtof~ -> the stack) is sketched
once and added to the plan-file backlog as its own future recipe.

Shared performance plumbing is stated once: constant-cents vibrato via
the Hz-calibrated FM inlet (depth ~ 0.006 x f for +/-10 cents), and the
sequencer pair driving the voice through mtof~ with adsr's level-gate
contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6CC6kpK5Lx2YkJWuNNQpw
The second wave, each drafted against a fresh wrapper-source sweep:
the acid line (the Phuture knob-riding method, lane messages, the
measured accent-run bloom), the sequenced modular (Berlin school and
"I Feel Love" off tap.303.seq~ through mtof~ into the Moog voice), the
robot voice (carrier casting with the noise layer as sibilance budget),
shimmer (the full spiral-into-wash chain with variants), the funk
envelope filter (clav chop, bass quack, cocked wah, and the sidechain/
envelope-outlet patch points), a field guide to rooms (IR curation and
placement for tap.convolve~), and comb drones (five keepable tunings
and the morph gesture).

The drafting audit also caught and fixed a shipped-chapter drift: the
pitchaccum chapter documented pitch1/feedback1 with 0..1 feedback
values, but the wrapper spells them trans1/fb1 on a 0-99 scale; the
rooms recipe documents the buffer~-plus-set loading contract explicitly.
New findings (uninterpolated tap.delay~/tap.multitap~, tap.verb~'s
std::rand() nondeterminism, the vco signal-inlet glide gap, the missing
vocoder sibilance path) are recorded in TapTools-Max's
plans/recipes-improvements.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6CC6kpK5Lx2YkJWuNNQpw
Four studies in casting, with provenance labeled per the part's rule:
"In the Air Tonight" as the anti-robot patch (the VP-330 ghost choir —
wide bands, slow mouth, no noise layer, mixed under the dry whisper),
the front-and-center robots (ELO, Styx, the Beastie Boys, Kraftwerk's
monophony) with the talkbox distinction kept straight, "Hide and Seek"
honestly labeled a harmonizer rather than a vocoder — with the closer
route built from parallel tap.shift~ voices fed by tap.semitone2ratio,
capped at close voicings because granular shifting moves formants —
and a plugin-era "Orange-school" carrier voiced from the package's own
oscillators, with the house rule against reverse-engineering shipping
products stated in print. Plan record updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6CC6kpK5Lx2YkJWuNNQpw
…d dry

taptools/harmonizer.h (tap::tools::harmony::harmonizer) is the
keyboard-harmonizer effect — up to four pitch-shifted copies of a
monophonic source holding musical intervals, summed with a dry path the
kernel delays to the voices' emission time so chords land as chords
rather than slapback. Each voice is a tap::dsp::pvoc (Laroche-Dolson
peak-locked shifting) with LPC formant preservation on by default; both
algorithms live in DspTap and come from published literature, so this
kernel is composition and control. Intervals are fractional semitones
clamped to +/-24 (exactly the pvoc ratio contract) gliding through a
semitone-domain one-pole; gains ride their own slews; silent voices are
skipped for CPU and re-enter cold by design, stated in the header with
the other honest limits (one-frame latency, pvoc transient smearing,
voice-oriented formant model).

Seven Catch2 scenarios pin the contract, house-style: solo voices land
their intervals under the DspTap yin oracle (both directions), the dry
path is sample-aligned with a unity voice to 1e-6, chords stay bounded
with both intervals present, a synthetic formant bump stays home only
with the flag on, glide walks the pitch through the middle, and a
re-enabled voice returns finite and audible. The C ABI and ctypes
bridge gain the kernel (taptools_harmonizer_*, Harmonizer with a
chord() helper) per the verification-layer rule; README's pitch table
gains the row.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6CC6kpK5Lx2YkJWuNNQpw
The chapter the audit demanded: tap.harmony~ documented as an
instrument (corrector into harmonizer, formant as the point, chord as
the performance surface, glide as the inhuman slide), with the honest
latency number and the kernel test battery cited for every claim. Two
worked examples carry it: "Woods" as the stacked chapel — with the
overdub-honesty note that one harmonizer pass is one performance, and
takes are the faithful version — and "715 - CRZZKS" as the
Messina-school live stack (dry voice equal citizen inside the chord,
the octave-down under falsetto, no reverb this once). The vocoder
songbook's Hide and Seek section now ranks tap.harmony~ as the right
tool, keeping the shifter stack as the manual fallback; SUMMARY and the
plan record grow to match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6CC6kpK5Lx2YkJWuNNQpw
Formatting only — the style job caught the harmonizer header, test,
and capi include block; no behavior change, suite still green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6CC6kpK5Lx2YkJWuNNQpw
notebooks/harmonizer.ipynb drives the shipping kernel through the capi
bridge, house-style: solo voices across two octaves of voicings land
within 0.04 cents of their equal-tempered targets under the DspTap yin
oracle; the dry path aligns with a unity voice to a 3.7e-8 residual
(why chords land as chords, not flams); the formant section shifts a
synthetic 960 Hz-bump voice up a fifth and quantifies the preservation
honestly with a band centroid (1058 -> 1154 Hz with the flag on, 1439
riding the full ratio with it off); and the last section plots the
"715"-school chord spectrum and the 300 ms glide walking unison to
octave. The book's choir-of-one recipe now cites the notebook's
numbers instead of leaning on the tests alone, and the plan record
marks the notebook shipped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6CC6kpK5Lx2YkJWuNNQpw
build, Tap House Style, and Docs workflow badges under the title,
each linking to its workflow's run history.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6CC6kpK5Lx2YkJWuNNQpw
The pitchaccum, vocoder, and tune chapters' wrong-tool lists predate
the harmonizer and recommended workarounds for exactly what it now
does; each names the object.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6CC6kpK5Lx2YkJWuNNQpw
claude added 2 commits August 5, 2026 11:29
taptools/adsr.h (tap::tools::adsr::generator) rebuilds the envelope as
a circuit model, per the recipes-improvements plan: the default analog
mode charges its attack toward a 1.4x overshoot target and truncates at
full scale (the CEM 3310 architecture from the datasheet; standard
Electronotes ADSR practice), and its decay/release are true RC
discharges that taper into their targets. Contract numbers documented
and pinned: attack reaches full scale at the knob time with the
truncated-charge midpoint at 0.652; decay/release close 95 percent of
their gap at the knob time (tau = t/3); release ends at exact zero
below 1e-6. Retrigger always rises from the current level. The 2003
Jamoma TTAdsr curves are ported verbatim as hybrid/linear/exponential
compatibility modes, magic constants and all.

The family trigger contract lands at last: the gate opens above a
threshold (default 0.005 - above the trigger bus's 1e-3 floor, below
the sequencer's 0.01 plain level, retiring the old hard-coded 0.5 that
a default accented row could never open), and gate amplitude is
velocity under a sensitivity control (1 + s*(amp - 1), so 303-style
2.0 accents hit twice as hard at full sensitivity; s = 0 is the legacy
amplitude-blind behavior).

Eight Catch2 scenarios pin all of the above, including the legacy
hybrid curve's linear attack and dB-linear decay. The C ABI, ctypes
bridge (Adsr class), and the executed notebooks/adsr.ipynb ship
alongside per the verification-layer rule - the notebook measures
99.8 ms to full scale on the 100 ms knob, midpoint 0.652 on the law's
0.652, 95.0 percent decay closure, and the plain-hit threshold test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6CC6kpK5Lx2YkJWuNNQpw
The gate contract sentence now speaks threshold rather than the retired
0.5 level, the Moog voice notes the analog default gives its contours
real RC curves, and the sequenced-modular chapter's accent caveat
becomes an accent feature via the new velocity sensitivity.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6CC6kpK5Lx2YkJWuNNQpw
@tap
tap merged commit d878622 into main Aug 5, 2026
12 checks passed
tap pushed a commit to tap/TapTools-Max that referenced this pull request Aug 5, 2026
tap/TapTools#27 landed on main by rebase; main's head d878622 carries
the identical tree to the branch commit this pin referenced, so the
pin moves there per the release flow and stays reachable after branch
cleanup. No content change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6CC6kpK5Lx2YkJWuNNQpw
tap pushed a commit to tap/TapTools-Max that referenced this pull request Aug 5, 2026
tap/TapTools#27 landed on main by rebase; main's head d878622 carries
the identical tree to the branch commit this pin referenced, so the
pin moves there per the release flow and stays reachable after branch
cleanup. No content change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6CC6kpK5Lx2YkJWuNNQpw
tap pushed a commit to tap/TapTools-Max that referenced this pull request Aug 5, 2026
tap/TapTools#27 landed on main by rebase; main's head d878622 carries
the identical tree to the branch commit this pin referenced, so the
pin moves there per the release flow and stays reachable after branch
cleanup. No content change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V6CC6kpK5Lx2YkJWuNNQpw
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants