Skip to content

Hammerwork Grand and Skinwork Kit, physically modeled, plus shared state for voice machines - #23

Merged
nooga merged 6 commits into
masterfrom
claude/physical-piano-drums
Oct 16, 2026
Merged

nooga merged 6 commits into
masterfrom
claude/physical-piano-drums

Conversation

@nooga

@nooga nooga commented Oct 16, 2026

Copy link
Copy Markdown
Owner

Two clean-room physical models, the kernel layer under them, and host support for state that every voice of a machine acts on.

Machines (docs/41)

  • Hammerwork Grand (grand). Felt hammers strike up to three detuned stiff strings per key. The strings ring into a 12-mode soundboard through a moving bridge, with a resistive term for the board's dense upper modes. Velocity changes the tone through the hammer's nonlinear felt. Unisons give the two-stage decay, and PEDAL lifts the dampers. Six presets. The default hammer is now softer (HARD −0.6), which matches the VCSL Kawai's early spectral centroid at mezzo velocity: 821 / 801 / 1002 Hz at C3 / C4 / C5 against about 870–960 Hz. At HARD 0 it was 1240–1380 Hz and sounded like a clavinet.
  • Skinwork Kit (kit). Kick, snare with wires, side stick, three toms, closed / pedal / open hats, crash, ride and bell on the GM map. Five presets.
  • kernels/10-physical: an exact modal step, implicit striker contact, and membrane modes.

Host

  • Heap state regions for machines whose state is over 2 KB. A voice can now hold up to 32 KB.
  • Shared state (docs/04 §Shared state):
    • shared-size! declares one region per machine, and "word" period shared! names its render word. Voices and that word step in lockstep, voices one at a time in voice order, so renders stay bit-exact at any thread count.
    • voice-level! lets a voice heard only through the shared word report its own level for idle skipping.
    • While the shared word runs, ctx.clock is the step's first sample, so an ifte on it is versioned.
    • None of this knows about any instrument.

The shared-state work was built for a port of frankensim's piano. That port was dropped: it costs about 4× more CPU than Hammerwork and sounded phased in listening. The host features stay because they are general.

Testing

  • zig build test passes. New tests cover shared state ordering and idling, the shared-step clock across odd-length blocks, and voice-level!.
  • zig build bench -- --all --check --no-sheets: every machine with goldens is bit-exact. Grand's goldens are re-recorded for the new default. The command still exits 1 because shift2, resonator2, buffer2 and phaser2 came from master without golden files, which is unrelated to this branch.
  • I only checked the GUI briefly, opening a demo project and confirming it loads. I haven't played the machines by hand.

🤖 Generated with Claude Code

nooga and others added 6 commits October 16, 2026 22:47
…contact, membrane modes (docs/41)

kernels/10-physical: zoh-coefs/modal-step advance a damped mode exactly
under a held force; contact-solve is the power-law Hunt-Crossley striker
solved implicitly by Newton on the compliance equation; bessel.fy holds
the circular membrane's first 40 modes, generated by tools/fit/bessel.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A state region is inline up to MAX_STATE; a machine declaring more, up to
16 KB, gets MAX_REGIONS heap regions of its own size at create. Every
region access goes through stateRegion. Displays still read the first
2 KB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cs/41)

grand: per key up to three stiff unison strings as mode banks, a felt
hammer from Chaigne and Askenfelt's fits through the implicit contact,
and a 12-mode soundboard plus a resistive dense-mode term behind a moving
bridge; dampers and a live pedal. Measured against the CC0 VCSL Kawai.

kit: Bessel-mode heads with tension glide, Hertz sticks and a felt
beater, lumped snare wires that collide and buzz, and plates with a
bounded cubic coupling and a diffuse-field wash; GM map, hat choke.

Clean-room: from the cited literature and our own estimates only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… lockstep with the voices (docs/04)

A voice machine can ask for one region for the whole machine
(shared-size!) and a render word over it (shared!). The host renders the
sounding voices for the declared period, then the shared word over the
same samples, one voice at a time in voice order, so writes into the
region land in a fixed order. The word wakes on note-on and sleeps once
the voices are idle and its own output stays under the idle floor. Every
entry word sees the region as ctx.shared.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s/04 §Shared state)

- `offset voice-level!` names a voice state field that reports the
  voice's level; idling watches it besides the voice's own output, so a
  voice heard only through the shared word sleeps once both are quiet.
- While the shared word runs, ctx.clock is its step's first sample,
  constant over the call, so an ifte on it is versioned and the untaken
  arm costs nothing.
- MAX_STATE_BIG goes from 16 KB to 32 KB, still heap regions per voice.

Fixture machines/raw_fixtures/shared_clock.fy and a test cover the
clock across odd-length blocks and voice-level idling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-0.6)

At HARD 0 the felt was hard enough to sound like a clavinet: an early
spectral centroid of 1240-1380 Hz against about 900 Hz for the VCSL
Kawai at mezzo velocity. At -0.6 it lands on the Kawai (821 / 801 /
1002 Hz at C3 / C4 / C5). The knob's range and mapping are unchanged.

The presets voiced at the piano's own voicing (default, dry-studio,
sustain-wash) move with it; felt-ballad goes to -1.0 to stay softer;
bright-pop and honky-tonk keep their bright hammers. Goldens
re-recorded, docs/20 regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nooga
nooga force-pushed the claude/physical-piano-drums branch from 0ebd782 to f368737 Compare October 16, 2026 21:49
@nooga
nooga merged commit d7abfa9 into master Oct 16, 2026
1 check passed
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.

1 participant