A capture hook, so the UI can be recorded deterministically - #17
Open
spacedevin wants to merge 2 commits into
Open
A capture hook, so the UI can be recorded deterministically#17spacedevin wants to merge 2 commits into
spacedevin wants to merge 2 commits into
Conversation
Recording Deckard playing a set meant screencasting it in real time, which drops frames under load and never renders the same way twice. The UI already subscribes to `transport.position` on the signal bus to drive the playhead, grid and timeline — so a recorder can publish that position itself, one frame at a time, and screenshot each one. Every frame is then drawn at exactly the timecode it will occupy in the video. The seam was missing: the signal bus is created inside the App component and nothing outside can reach it. With `?capture=1` this exposes the runtime handle and the bus, extending the same `rt` escape-hatch object the app already keeps for `setActiveDeck` and the undo/redo entry points. Gated on the query string, so it is not an API the app ships to ordinary users, and nothing changes for a normal load.
The hook exposed the runtime handle and the signal bus, which was enough to place the playhead and load sets — but not enough to show the app being USED. A recording made through it had the crossfader parked dead centre and every knob untouched for its whole length, because the mixing was happening to model fields behind the UI's back. midiControl and midiNote are the doors a hardware surface already uses. Driving those moves the 4-way fader, turns the deck FILTER and LO knobs, and lights pads, because the writes go through the app's own path — deckEq mutate, applyDeckEq, scheduleDeckMix, deckMixRev — exactly as they do when a controller is plugged in. It also keeps a capture honest about what the controls can express: the deck EQ knobs are +/-12 dB, so a bass cut written straight into the model could ask for a value the hardware cannot reach.
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.
Recording Deckard playing a set meant screencasting it in real time — which drops frames under load
and never renders the same way twice.
The UI already subscribes to
transport.positionon the signal bus to drive the playhead, grid andtimeline. So a recorder can publish that position itself, one frame at a time, and screenshot each
one: every frame is then drawn at exactly the timecode it will occupy in the video.
The seam was missing.
signalsis created inside the App component and nothing outside can reach it.With
?capture=1, this exposes the runtime handle and the bus:It extends the same
rtescape-hatch object the app already keeps forsetActiveDeckand theundo/redo entry points, rather than inventing a new mechanism. Gated on the query string, so it is
not an API the app ships to ordinary users, and a normal load is unchanged.
What it is for
A capture harness in the chuggie repo (
scripts/deckard-mix) that loads preset sets onto decks,crossfades between them, renders the mix offline, and records the UI against it. It drives Deckard's
real model, real preset sets and real sequencer — the same move
renderDeckLoopToBuffer()alreadymakes internally, widened from one mono deck to a stereo multi-deck mix.
Building it surfaced two bugs worth knowing about here, both in the render path rather than in this
diff:
setTimeoutagainst wall clock. An offline render finishes aminute of audio in about a second, so those cleanup timers fire mid-render and tear down notes
that are still sounding. Deferring them for one render recovered 7 dB of RMS and a truncated
tail. Live playback is unaffected — wall clock and audio time agree there — but anything rendering
offline through these voices is losing note tails today.
Math.random(), so no two renders match. The harness installs a seededPRNG; after that, two runs differ by less than one 16-bit LSB.
Verification
npm test— 516 pass, 0 fail.__deckardCapture.rt.projectreachable,signals.publishpresent,
rt.setProjectre-renders after loading sets onto decks.confirmed advancing between frames.