Skip to content

Replace the FLAC encoder library with an own implementation#194

Open
douglas-carmichael wants to merge 1 commit into
git-moss:mainfrom
douglas-carmichael:own-flac-encoder
Open

Replace the FLAC encoder library with an own implementation#194
douglas-carmichael wants to merge 1 commit into
git-moss:mainfrom
douglas-carmichael:own-flac-encoder

Conversation

@douglas-carmichael

Copy link
Copy Markdown
Contributor

Problem

The bundled FLAC encoder library crashes with an ArrayIndexOutOfBoundsException (in Subframe_Fixed.encodeSamples) on any sample whose length modulo 4096 is 2, 3 or 4 and whose trailing samples are not all identical: the fixed-predictor order selection reads 4 samples ahead without checking the length of the final short block. A real-world example is a 65538-frame drum sample (16 × 4096 + 2).

Affected writers:

  • Renoise: falls back to storing the sample as uncompressed WAV inside the XRNI ('The FLAC encoder failed for sample ...').
  • SFZ (FLAC option), Bliss, Synclavier Regen: no fallback existed, the preset failed; for Synclavier Regen a fallback is not even possible since SFLC files must be FLAC.

Solution

A new encoder file/flac/FlacEncoder written from the FLAC specification (RFC 9639), in the same spirit as the existing NCW/SF2/RIFF/FastLZ implementations. It supports constant, verbatim, fixed prediction (order 0-4) and linear prediction (LPC, order up to 8) sub-frames, Rice coding with per-partition parameters and per-frame stereo de-correlation (left/side, side/right, mid/side), each chosen by the smallest encoded size. Compression is on par with the old library (within ~2% on a real drum library); losslessness is guaranteed by construction since the residuals are calculated with the exact integer arithmetic of the decoder. Short trailing blocks - the case the old library crashed on - are handled correctly for all lengths.

The library remains in use for reading FLAC files. Two quirks of its jFLAC based reader are deliberately catered for so ConvertWithMoss can re-read its own output: a PADDING block always terminates the meta-data (the reader fails if STREAMINFO is the only block) and blocks shorter than 16 samples are stored verbatim (the reader mis-decodes predicted sub-frames there). AudioFileUtils.compressToFLAC now returns the bytes directly, which also removes the temporary-file workarounds in the Renoise, Bliss and Synclavier Regen creators.

Verification

  • 36-file test matrix (every trailing-block length 0-15 plus longer ones, 1-5 frame files, 8/16/24 bit, mono/stereo, correlated stereo, silence, noise, odd sample rates, 32-bit int/float sources): every file passes flac -t (flac 1.5.0, including its MD5 check of the decoded audio), decodes byte-identical with the reference decoder and round-trips through the application's own FLAC reader.
  • A 1277-sample Decent Sampler drum library (which triggered the original crash) converts to Renoise with all samples as valid FLAC and no WAV fallbacks.

The bundled encoder crashed on samples whose length modulo 4096 is 2, 3
or 4 with a non-constant tail (e.g. 'The FLAC encoder failed for sample
...' when writing Renoise files; SFZ with the FLAC option, Bliss and
Synclavier Regen failed hard since they have no WAV fallback). The new
encoder in file/flac supports constant, verbatim, fixed and LPC
sub-frames, Rice partitioning and stereo de-correlation and compresses
on par with the old library. The library remains in use for reading
FLAC files.
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.

1 participant