Skip to content

Use variable-blocksize encoding consistently in split FLAC tracks#126

Merged
davidnewhall merged 3 commits intogolift:mainfrom
oceanplexian:fix/flac-split-inconsistent-blocksize
Mar 3, 2026
Merged

Use variable-blocksize encoding consistently in split FLAC tracks#126
davidnewhall merged 3 commits intogolift:mainfrom
oceanplexian:fix/flac-split-inconsistent-blocksize

Conversation

@oceanplexian
Copy link
Contributor

@oceanplexian oceanplexian commented Mar 3, 2026

When a FLAC+CUE file is split into individual tracks, frames that fall entirely within a track were returned from buildOutputFrame as-is from the source stream. For the common case of a fixed-blocksize source (BlockSizeMin == BlockSizeMax), those frames carry HasFixedBlockSize=true in their headers. Boundary-split frames (those that span a track edge) were correctly created with HasFixedBlockSize=false (variable-blocksize).

The mix is invalid FLAC: fixed-blocksize frames encode a frame number in the header, while variable-blocksize frames encode a sample position. Decoders such as GStreamer's flacparse detect this inconsistency and abort with streaming stopped, reason error (-5). VLC appears to tolerate it but shows a broken progress bar because the sample positions it reads are meaningless frame-count values treated as sample offsets.

Fix: remove the early-return fast path in buildOutputFrame so that every frame written to an output track is freshly constructed with HasFixedBlockSize=false. The mewkiz encoder auto-sets the Num field to the running sample position so no additional tracking is needed.

Also zero-out FrameSizeMin/FrameSizeMax in the initial StreamInfo passed to the encoder. The mewkiz encoder has a TODO and never updates those fields on Close(), so copying the source values would embed stale wrong byte-size metadata into every split track.

Adds TestCueVariableBlockSizeConsistency to catch regressions.

…tracks

When a FLAC+CUE file is split into individual tracks, frames that fall
entirely within a track were returned from buildOutputFrame as-is from
the source stream. For the common case of a fixed-blocksize source
(BlockSizeMin == BlockSizeMax), those frames carry HasFixedBlockSize=true
in their headers. Boundary-split frames (those that span a track edge)
were correctly created with HasFixedBlockSize=false (variable-blocksize).

The mix is invalid FLAC: fixed-blocksize frames encode a frame number
in the header, while variable-blocksize frames encode a sample position.
Decoders such as GStreamer's flacparse detect this inconsistency and
abort with streaming stopped, reason error (-5). VLC appears to tolerate
it but shows a broken progress bar because the sample positions it reads
are meaningless frame-count values treated as sample offsets.

Fix: remove the early-return fast path in buildOutputFrame so that every
frame written to an output track is freshly constructed with
HasFixedBlockSize=false. The mewkiz encoder auto-sets the Num field to
the running sample position so no additional tracking is needed.

Also zero-out FrameSizeMin/FrameSizeMax in the initial StreamInfo passed
to the encoder. The mewkiz encoder has a TODO and never updates those
fields on Close(), so copying the source values would embed stale wrong
byte-size metadata into every split track.

Adds TestCueVariableBlockSizeConsistency to catch regressions.

Fixes: Unpackerr/unpackerr#141
… splitting

TestCueSplitRealFLAC uses ffmpeg (skipped if not in PATH) to generate a
fixed-blocksize FLAC — the encoding all mainstream tools produce — and
then exercises ExtractCUE against it.

The existing synthetic tests use the mewkiz encoder which already emits
variable-blocksize frames, so they never triggered the bug where interior
frames were passed through as-is with HasFixedBlockSize=true while
boundary-split frames had HasFixedBlockSize=false.

The test also includes requireFixedBlocksizeFLAC, which asserts the
source file actually has fixed-blocksize encoding, so the test will
loudly fail if ffmpeg's behavior ever changes rather than silently
passing while no longer covering the relevant code path.
…short vars

- buildOutputFrame: remove the origSamples parameter entirely since it is
  no longer used after the early-return fast-path was deleted; update the
  sole call site in writeTrackFLAC accordingly
- TestCueSplitRealFLAC: switch exec.Command -> exec.CommandContext to
  satisfy the noctx linter rule
- TestCueVariableBlockSizeConsistency / TestCueSplitRealFLAC /
  requireFixedBlocksizeFLAC: rename short variable f -> trackFile/srcFile
  to satisfy the varnamelen linter rule
@davidnewhall davidnewhall merged commit 379595d into golift:main Mar 3, 2026
8 checks 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.

Support for splitting single-file FLAC files with cue.

2 participants