feat(studio): waveform strip, clip light, peak marks and video beat source (5/8) - #4817
Conversation
miguel-heygen
left a comment
There was a problem hiding this comment.
Reviewed incremental #4817 at 65b1bf6d3846389aa251407268c23e3fa65c36f7 against a244373ae644e6cf8085f7a7804cfc4a91eb08ef.
The peak accumulator preserves absolute native-channel levels and carries split frames across chunks (packages/studio-server/src/helpers/peakMap.ts:41). The CLIP light reads the program meter upstream of monitor gain, matching export intent (packages/studio/src/components/nle/AudioMeterStrip.tsx:229, existing packages/core/src/runtime/webAudioTransport.ts:147).
Blocker: packages/studio-server/src/helpers/peakMap.ts:90 — The channel probe explicitly selects a:0 (:71), but decoding leaves stream selection to FFmpeg. With a mono first stream and a stereo second stream, FFmpeg can select the second while the accumulator still treats every float as a mono frame. A real generated one-second fixture with no default dispositions returned 40 bins at binSeconds=0.05 (advertising two seconds), with peaks 0.9 from stream two instead of 0.1 from stream one. Consequently peak markers have the wrong level and time positions. Select the same stream in both commands, and add a multistream regression test.
Audited: complete peak decoder/cache/route, peak-run/marks components, audible-video strip and render dispatcher, meter latch integration and cleanup, CLI/Studio beat-source predicates, carve-source exclusion; traced waveform requests and preview program-meter placement.
Trusting: parent audio-runtime implementation and #4816 fixes; full Studio route/layout and packaged renderer; unmodified thumbnail scheduling internals. No prior reviews or comments on this PR.
Verification: 46 existing targeted tests passed at the exact head, including real generated single-track video decode tests; 1 independent real-FFmpeg multistream witness passed. Cached Vitest 3.2.4 differs from declared 4.1.11, with worktree source aliases. Initial two broader UI suites could not collect with the cached dependencies; 50 shared UI tests passed at #4818 (unchanged meter/render dispatcher source); these are supplemental results, not exact-head runs. All observed exact-head CI checks pass. The later browser fixture exercised source components only, not full Studio/backend E2E.
— Magi
Verdict: REQUEST CHANGES
Reasoning: Peak maps violate their per-bin timing/level contract on valid multistream media despite the single-stream tests and green CI.
65b1bf6 to
d2cf398
Compare
Edit accuracy: 953 passing here, 953 on the base branchThe gate passes. Quarantined, measured but not gated (0) |
8d11a98 to
f4b275d
Compare
f4b275d to
a12e2f9
Compare
miguel-heygen
left a comment
There was a problem hiding this comment.
Fix-delta review at a12e2f91b6cb88300ab4569a5bc25637438ba3af, against stacked parent dbcbca85d2e088cd05cb28320015aa3109aec27d; compared with my previous review at 65b1bf6d.
The stream-selection blocker is repaired: the channel probe selects a:0, decoding explicitly maps 0:a:0 (packages/studio-server/src/helpers/peakMap.ts:69, :96), and the peaks-v2 cache invalidates previously incorrect maps (:8). The regression constructs a mono-first, louder-stereo-second fixture with no default dispositions and checks both length and 0.1 peak level.
Verification: all 6 peak-map tests pass with real FFmpeg on stack head 88255f82e; helper/test files are byte-identical to this PR head. Cached Vitest 3.2.4 and source aliases. No waveform browser, live Studio/backend, playback or export acceptance this round. No new code blocker in the two-file repair delta.
The required Studio and player captures check remains red for missing Before/After evidence. Earlier changes-requested state remains; this is not an approval.
— Magi
Verdict: COMMENT
Reasoning: The original multistream defect is fixed and regression-covered, but the required capture gate is unresolved.
a12e2f9 to
b9a59e5
Compare
jerrai-bot-heygen
left a comment
There was a problem hiding this comment.
At b9a59e5f, the first-audio-stream probe/decode repair remains, but the new peak-map decoder treats partial output followed by FFmpeg failure as success and caches an incomplete map. Inline has the concrete failure and regression request. Current-head checks pass except required Studio/player Before/After captures, which are red; no visual waiver was supplied. No local tests, build, merge or release. — Jerrai
| reject(new Error(`ffmpeg produced no audio samples (exit ${code})`)); | ||
| return; | ||
| } | ||
| resolvePromise(finishPeaks(acc)); |
There was a problem hiding this comment.
A decode that emits some samples and then exits nonzero resolves here as a complete peak map. The /peaks route caches that array (routes/peaks.ts:53–64), so a truncated/corrupt audio source can permanently show an incomplete clipping map instead of the existing decode-error response. Please reject on a nonzero close code even when acc.frame > 0, and add a partial-output-then-failure regression. The first-stream probe/map fix itself is present.
4ecac65 to
f9da60d
Compare
jerrai-bot-heygen
left a comment
There was a problem hiding this comment.
The 24-file layer patch is preserved at the exact #4816-based head. Peak decoding selects the same first audio stream as probing and rejects a nonzero FFmpeg close before returning/caching any partial bins; peakMap.decodeFailure.test.ts runs a fake FFmpeg that emits samples then exits 1 and asserts rejection. This closes my previous code hold; the capture check passes. Miguel's prior review and remaining CI are independent merge gates. — Jerrai
f9da60d to
a0d266d
Compare
jerrai-bot-heygen
left a comment
There was a problem hiding this comment.
Exact a0d266da rebase review: all 24 layer paths and patch hunks match the previously approved parent→head diff, with no concurrent parent edit on those paths. At this head FFmpeg peak decoding still rejects a nonzero exit after partial sample output before returning/caching peaks; the regression emits samples and exits 1. Code approval for this layer only; #4815 viewport CI and Miguel's older review remain independent stack gates, and the new head's own checks are still running. No merge/release action. — Jerrai
miguel-heygen
left a comment
There was a problem hiding this comment.
Re-reviewed #4817 at a0d266da27da8ed2a114ce5d51e83470502610d4 against #4816 4f68e1720; this supersedes my changes-requested review 5374292610, and confirms Jerrai's decoder repair.
Probe and decode both select the first audio stream (a:0 / -map 0:a:0), with the versioned peak cache. decodePeakMap now rejects every nonzero close before returning bins, including partial output, so the route cannot cache that result as a success. I read the helper/cache route and ran all seven peak tests, including real FFmpeg fixtures and the partial-output/exit-1 binary. Those helper and test files are byte-identical between this layer and the tested #4818 export.
Current capture and other fetched checks pass. Cached Vitest 3.2.4/source aliases; supplemental combined-tip execution, not a full isolated package build. No live Studio, waveform browser, playback or render acceptance was performed.
Verdict: APPROVE
Reasoning: the original stream mismatch and the later incomplete-decode success path are repaired and directly regression-covered. No new blocker found in the reviewed layer.
— Magi
a0d266d to
89fb3b8
Compare
miguel-heygen
left a comment
There was a problem hiding this comment.
Re-approved at 89fb3b8c16d43352dd00afe060d5b39cb26bb743 against stacked parent eaaf6e1b6d9e243490b0f6dc1d4e5fe366462794, following my review 5387236487.
The parent-to-head patch has identical paths and hunks to the layer I previously approved, ignoring only object indexes and hunk locations. The restack preserves the repaired matching FFmpeg audio-stream selection, cache version and nonzero-exit rejection. This is a rebase verification; I did not repeat the earlier exact-source tests or the full package suite.
Current checks have no failure, but CI remains in progress; approval does not establish merge readiness. Prior evidence limits (cached tooling, no live Studio/audio acceptance) remain. No merge, release or protection bypass performed.
Verdict: APPROVE
Reasoning: The inspected layer is the same reviewed patch on its new stacked parent, with no additional code change or new layer-specific blocker.
— Magi
89fb3b8 to
044e969
Compare
miguel-heygen
left a comment
There was a problem hiding this comment.
Re-approved at 044e9696493e9b822d021a0fb38049f4a130b513 against stacked parent 9849ba697393839b2134f5a82b79a694163bcb5c, following my approval 5387641276.
I independently compared the complete old/new parent-to-head patches: same 24-path set and identical hunks, ignoring only object indexes and hunk positions. Every changed-layer source and test blob is also identical between the approved head and this head; there are no differing layer file blobs. The prior repairs, test cases and save/decode behavior, survive unchanged.
This is a rebase verification, not a new local test run. The prior focused tests and their stated cached-tooling limits remain the evidence. No live Studio/playback, full package acceptance or merge/release performed. No task worktree, server or browser was created.
Current-head checks have no failure but are still in progress. Approval does not waive any required check or establish merge readiness.
Verdict: APPROVE
Reasoning: The entire inspected layer patch and its executable sources/tests match the previously approved layer on the new stacked parent.
— Magi
044e969 to
833259d
Compare
An audible video keeps its thumbnails and gains the audio waveform along the bottom 38% of the clip (also when thumbnails are off); muted and silent video are unchanged. The waveform route already decodes video audio; a test now proves it on an mp4. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iling The light turns red when the preview master sample peak reaches -1 dBFS (the export true-peak ceiling) and stays lit until clicked. Mutation-tested threshold and latch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
studio-server computes an absolute per-50ms sample-peak map once per media file (native channels, cached by size+mtime next to the waveform cache); the timeline paints red marks where the source at the clip's own volume reaches -1 dBFS and names the loudest point. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Studio's isMusicSourceElement accepts an audible, unmuted video for the music track and the beat fallback (lane zoning unchanged); core findMusicAudioSrc accepts <video data-has-audio="true"> without muted; the beats CLI message mentions video. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e source Concurrent peak requests for one file share a single ffmpeg decode; Duck under voice is withheld on a clip another bed already carves against. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The channel probe read stream a:0 while the decode left stream choice to ffmpeg, which picks a later stereo stream over a mono first one; every frame was then read at the wrong width, doubling the bins and taking the other stream's level. Both commands now select a:0, and the cache key moves to peaks-v2 so maps built from the wrong stream are rebuilt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he input Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A decode that emits some samples and then fails no longer resolves as a complete map, so /peaks cannot cache a truncated clipping map. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
833259d to
b99c794
Compare
miguel-heygen
left a comment
There was a problem hiding this comment.
Re-approved at b99c7941333980c0cb0515f0e86e381a151cc25e against 9244c47328e41e59034bd9bbec293fe3f7310cf6, compared with my previously checked 833259d63dfdfae17e01fc47a7f7fd9cae28b31f layer.
Audited: the complete 24-path parent-to-head patch and every layer file's blob. Paths and patch hunks are identical, ignoring object indexes/hunk positions; all file contents (source, tests and manifests) are byte-identical. Independent git range-diff marks all 9 commits =. The matching-stream/nonzero-exit peak decoding repairs remain unchanged.
Trusting / not rerun: the earlier focused tests and their documented environment limits; no new local tests, live Studio/playback or full package build. No new task worktree/server/browser was created. Current checks have no failure but are still running, so this is code approval rather than merge readiness. No merge, enqueue, release or CI bypass performed.
Verdict: APPROVE
Reasoning: The full rebased layer and its file contents match the previously reviewed layer, with no new code change or blocker.
— Magi
Summary
Seeing sound on the timeline: a waveform strip under video clips that carry sound, a latching CLIP light on the master meter at the export ceiling, peak marks where a clip redlines, and a video with sound as the beat source.
Changes
peakMap) plus a/peaksroute.AudibleVideoClipContentwaveform strip,ClipPeakMarks/clipPeakRuns, and a CLIP light onAudioMeterStrip(theme tokens). Each peak map is decoded once, and Duck is never offered on a carve source.Testing
Static gates on the layer tip:
bun install,bun run build,tsc --noEmitfor every touched package (0 errors),oxlintandoxfmt --checkon files changed vs main (clean),gen:skills-manifest --check(in sync),scripts/comment-ratchet.mjs(ok).Notes
🤖 Generated with Claude Code
Before
After
Timeline: strip under sound-bearing videos. At this layer the strip draws flat (1px canvas); the next layer fixes the lane height.