Test the audio graph by rendering it - #32
Merged
Merged
Conversation
The README said the graph could not be tested, and gave the reason in audio-buffer.ts: Node has no Web Audio, so the encoders could be checked against a stub of an AudioBuffer while the thing that fills one could not. That was true of the stub and not of the graph. The export path is an OfflineAudioContext rendering faster than real time, which is a plain function of its inputs in every sense that matters — the same project gives the same samples, with no page, no device and no clock. It only ever needed an implementation. node-web-audio-api is one, in Rust, and it carries every node this app builds with. So fifteen tests that ask the questions that are actually about sound, measured off the rendered samples rather than reasoned about: - a cue starts on the sample its time works out to, and there is silence before it. This is what makes an exported file line up when it is dropped on a track at zero, so it is the deliverable rather than an internal detail - an end-anchored cue begins a length before its marker, which is the rule the whole anchor idea rests on - mute silences a cue and a layer; solo silences everything else - gain on a cue and on a layer are both applied - the same placed sound renders identically every time, and two placements of it differ — the seed comes from the cue's own id, and a seed that were not actually read would pass the first and fail the second - the stems add back up to the mix, sample for sample, which is the claim renderStems makes and the one that fails first if anything in the chain is drawn fresh per render - the file runs past the video so a tail is not cut off, stops at it when asked, and always starts at zero Every one was checked by putting its fault back into the code and watching it fail: the anchor ignored, solo ignored, the seed made random, every cue nudged eighty milliseconds late, and trimToDuration ignored. Six tests failed across those five, each the one that names the fault. Writing them also cost a minute a run until I noticed the sum was calling getChannelData inside its own per-sample loop. It hands back a copy here rather than a view, so that was a copy of the whole channel per sample. Hoisted, the file runs in six seconds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ux1kydvUkLRoMbHp82ofDi
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
Replaces #31, which was branched off the wrong base and so conflicted. Same commit, rebased onto
main.The README said the graph could not be tested, and
audio-buffer.tsgave the reason: Node has no Web Audio, so the encoders could be checked against a stub of anAudioBufferwhile the thing that fills one could not.That was true of the stub and not of the graph. The export path is an
OfflineAudioContextrendering faster than real time, which is a plain function of its inputs in every sense that matters — the same project gives the same samples, with no page, no device and no clock. It only ever needed an implementation.node-web-audio-apiis one, in Rust, and it carries every node this app builds with: convolver, biquad, compressor, shaper, panner and the rest. It goes in as a devDependency and stays out of the bundle;test/web-audio.tsputs its names on the global before the tests run, so nothing underaudio/imports from a test package. The package ships prebuilt N-API binaries inside the tarball for every platform includinglinux-x64-gnu, so there is no compile step on install and the same binary serves Node 20 and 22.What is checked
Fifteen tests, measured off the rendered samples rather than reasoned about.
Where a sound lands
What silences it
How loud it is
The same sound twice
Stems
renderStemsmakes, and the first thing to break if anything in the chain is drawn fresh per renderHow long the file is
Checking
Every test was checked by putting its fault back into the code and watching it fail: the end anchor ignored, solo ignored, the seed made random, every cue nudged eighty milliseconds late, and
trimToDurationignored. Six tests failed across those five faults, each one the test that names it.Writing them also cost a minute a run until I noticed my own sum was calling
getChannelDatainside its per-sample loop. It hands back a copy here rather than a view onto the buffer, so that was a copy of the whole channel per sample. Hoisted, the file runs in six seconds.214 unit tests (was 199) in 6.7s, 55 browser tests, build clean — all re-run on a clean clone from
npm ci.The README's "what is not tested yet" now names the live playback path — the clock, the buses and the scheduling that happen while you are listening rather than exporting.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Ux1kydvUkLRoMbHp82ofDi
Generated by Claude Code