Repository navigation
fix(engine): offset clips from a WAV with a wrong byte rate play the right sound to their end - #7541
Merged
Merged
Conversation
…right sound to their end A WAV header that states twice its real byte rate sent the mixer's input seek to twice the clip's in-point: the clip played later words, then silence. Such a source is now cut by decoded timestamps instead of by a byte seek.
Contributor
Edit accuracy: accurate 267 (base branch 267), smooth 100 of thoseThe gate passes. Quarantined, measured but not gated (8)
Unstable (2)
|
Contributor
There was a problem hiding this comment.
Approving at 8f8bb8e1.
- The root cause and the fix match. An input seek (
-ssbefore-i) on a PCM WAV becomes a byte offset computed from the header's byte rate, so a doubled rate lands at twice the in-point.misstatesPcmByteRatecompares ffprobe's streambit_ratewithsample_rate × channels × bits_per_sample. The probe already runs-show_streams, so both fields are present. A flagged source falls through to the existingatrimpath, which cuts by decoded timestamps. The check fails safe: a false positive only costs a decode from the start, and any non-PCM or incomplete stream keeps the input seek, so other sources are unchanged. - The "fails without the fix:" claim holds; I confirmed it locally. Both
audioMixer.wavByteRate.test.tscases pass on this head. With the!metadata?.misstatedByteRateguard removed, they fail withexpected [ 2800, 3200, null, null ] to deeply equal [ 1600, 2000, 2400, 2800 ], as the body says. The test builds its own WAVs with ffmpeg and patches the header bytes, so it doesn't depend on a fixture. - The rest of the engine
audioMixer*andffprobe*tests: the 6 that fail in my sandbox (HDR PNG fixture, group FX) fail the same way with main'saudioMixer.tsandffprobe.ts, so they come from my environment. CI is green on this head.
Non-blocking: a WAVE_FORMAT_EXTENSIBLE file whose container sample size differs from its valid bits could also be flagged. That direction is safe (it only takes the slower trim), so no change is needed.
This was referenced Nov 11, 2026
Merged
meefs
pushed a commit
to meefs/hyperframes
that referenced
this pull request
Nov 12, 2026
…render plays it (heygen-com#7618) * fix(cli): check hears a WAV whose header misstates its byte rate, as render plays it A PCM WAV whose header byte rate is wrong (some speech generators write 2x) sent the silence decode's input seek past the file's end, so every clip past the middle was reported audio_clip_silent. Render already cuts such a file by decoded time (heygen-com#7541); the byte-rate rule now lives in parsers and the CLI decoder seeks after decoding starts for those files. * test(cli): keep the misstated byte-rate case with the other single-clip checks * style(cli): format the WAV test helper * style(parsers): keep the byte-rate rule's comment within the line limit
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.
What
A sound clip that starts partway into a WAV (
data-media-startgreater than zero) now plays the right sound for its whole length, even when the WAV's header states the wrong byte rate.Why
Some speech WAVs (24 kHz, mono, 16-bit) carry a header whose byte rate is 96,000 bytes a second, twice the real 48,000, as if the samples were stereo. The samples themselves are fine, and a player that decodes from the start (the browser preview) plays them correctly.
The mixer cut each clip with an input seek (
-ss <in-point> -i file.wav). ffmpeg turns that time into a byte offset using the header's byte rate, so the cut started at twice the in-point. The clip played later words, then went silent once it ran past the end of the file. A clip starting past the halfway point of its file played nothing. The render still completed with no error.Example: a clip with an in-point of 38.2 s in a 135.5 s file started at 76.4 s. It played the remaining 59.1 s and then fell silent. A clip with an in-point of 0 was not affected, because seeking to 0 is right whatever the rate.
The bug is not new: the same seek is in 0.9.14, 0.9.29 and 0.9.32. Local and cloud renders are both affected.
Related work
None of the open audio PRs touches this seek.
How
ffprobe.ts): the probe now reads each audio stream's stated bit rate and sample size. It flags a PCM stream whose stated rate is not sample rate × channels × sample size.audioMixer.ts): a flagged source is cut by decoded timestamps, using the timestamp trim (atrim) the mixer already uses for streams that start late. It is no longer cut by a byte seek.Test plan
audioMixer.wavByteRate.test.tsbuilds an 8 s WAV that changes pitch every second. It writes two copies with lying headers (byte rate doubled; byte rate doubled plus a stereo block align). A 4 s clip with a 3 s in-point is rendered from each copy and from the true file, and the pitch is read at 0.5, 1.5, 2.5 and 3.5 s.expected [ 2800, 3200, null, null ] to deeply equal [ 1600, 2000, 2400, 2800 ]: the wrong seconds, then silence.reads many clips of one URL at once(expected 16 to be greater than 16), fails the same way onmainon the same 32-core box, so this change does not cause it. Typecheck passes.mainagainst this branch:main, the 38.2 s clip goes silent 63.6 s in and the 129.6 s clip is silent. On this branch, the only silences are the gaps between clips, the same as with a correct header.mainand on this branch.