filmcraft/crates/flac
Claude 6c13dda2bf
export: 32-bit integer and 32-bit float audio; Sample Size lists every size and greys out the unsupported ones
- AudioSettings gains `float`; `bits` takes 16, 24 or 32. `SampleFormat` (Int16 / Int24 /
  Int32 / Float32) and `ExportSettings::supports_sample_format` decide what each codec and
  container stores: WAV, AIFF and QuickTime PCM all four, MXF integers, FLAC integers.
  `validate` refuses float without 32 bits and float in FLAC or MXF.
- WAV float is WAVE_FORMAT_IEEE_FLOAT (EXTENSIBLE for more than two channels) with a `fact`
  chunk; AIFF float is AIFF-C `fl32`; QuickTime float is `lpcm` with the float flag; MXF writes
  32-bit integers. Float keeps the mix as it is, peaks above 0 dBFS included; 16 and 24 bit
  write the same bytes as before.
- FLAC encoder: 8-32 bits. 32-bit stereo is coded independently (a side channel would need 33
  bits), the sample range check no longer overflows, and 32-bit frames point at STREAMINFO for
  the size, which symphonia 0.5 (FilmCraft's own FLAC import) and ffmpeg both read.
- Export mode: Sample Size shows 16 bit, 24 bit, 32 bit and 32 bit float like Audio Format
  does, greying out what the format cannot store with the reason on hover; a size that stops
  being available (float after switching to FLAC) becomes 32-bit integer. es, fr, pt-br, id,
  ja and zh-cn strings.
- Tests: writer layouts and ffprobe codecs for every new format, round trips through ffmpeg
  and FilmCraft's own readers in WAV, AIFF, MOV, MXF and FLAC, headroom of a +6 dB tone,
  refused combinations, full-scale 32-bit FLAC through ffmpeg (MD5 too) and symphonia, and a
  UI test of the selector.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015nzevSsTNZxHPpUngfGTAw
2026-10-12 05:42:44 +00:00
..
src export: 32-bit integer and 32-bit float audio; Sample Size lists every size and greys out the unsupported ones 2026-10-12 05:42:44 +00:00
tests export: 32-bit integer and 32-bit float audio; Sample Size lists every size and greys out the unsupported ones 2026-10-12 05:42:44 +00:00
Cargo.toml Export: FLAC (clean-room encoder; .flac files and FLAC audio in MP4/MOV) 2026-10-10 18:26:28 +07:00
README.md export: 32-bit integer and 32-bit float audio; Sample Size lists every size and greys out the unsupported ones 2026-10-12 05:42:44 +00:00

README.md

filmcraft-flac

A clean-room FLAC encoder (Free Lossless Audio Codec, RFC 9639), written from the RFC. Layer L0: it depends only on filmcraft-bitstream and thiserror, has no unsafe, and builds for wasm32-unknown-unknown.

No libFLAC, FFmpeg or other FLAC implementation was read or ported. ffmpeg is used only as an external test oracle (decoding our streams). Decoding FLAC in FilmCraft is still done by the symphonia bootstrap decoder in filmcraft-codecs.

API

use filmcraft_flac::{Encoder, EncoderConfig, STREAMINFO_OFFSET};

let mut cfg = EncoderConfig::new(48_000, 2, 24);   // rate, channels (1–8), bits (8–32)
cfg.level = 8;                                     // 0 fastest … 8 smallest; 5 by default
let mut enc = Encoder::new(cfg)?;
let mut file = enc.header();                       // fLaC + STREAMINFO + VORBIS_COMMENT (vendor)
file.extend(enc.encode(&[&left, &right])?);        // planar i32 samples, any chunk size → whole frames
file.extend(enc.finish());                         // the last, shorter frame
// frame sizes, total samples and MD5 are only known now: patch STREAMINFO (or seek back in a file)
let at = STREAMINFO_OFFSET as usize;
file[at..at + 34].copy_from_slice(&enc.streaminfo());

// MP4 / QuickTime (`fLaC` + `dfLa`): one frame per sample, the STREAMINFO body in `dfLa`
let frames: Vec<Vec<u8>> = enc.encode_frames(&[&left, &right])?;   // each a full block
let last_len = enc.pending_samples();                               // duration of the frame finish() writes

What it writes

Part
Metadata STREAMINFO (block size, frame sizes, rate, channels, sample size, total samples, MD5 of the audio) and a VORBIS_COMMENT with the vendor string
Frames fixed block size (4096 by default), frame numbers, sample rate and size codes, CRC-8 header and CRC-16 frame checksums
Subframes constant, verbatim, fixed predictors (order 0–4) and linear prediction (up to order 12), whichever is smallest
Linear prediction Tukey(0.5) windowed autocorrelation, Levinson–Durbin, 12/15-bit quantised coefficients with error feedback
Residual partitioned Rice codes (4- or 5-bit parameters), the partition order chosen per subframe
Stereo independent, left/side, side/right or mid/side per frame, whichever is smallest (32-bit: independent only, a side channel would need 33 bits)

Not written: variable block sizes, wasted-bits shifts, escape-coded partitions, seek tables. 32-bit samples (frame headers point at STREAMINFO for the size) decode only in recent decoders (libFLAC 1.4+, ffmpeg 6+).

Level LPC order Rice partition order Stereo decorrelation
0 fixed only ≤ 3 no
1–2 fixed only ≤ 3 yes
3 ≤ 6 ≤ 4 no
4 ≤ 8 ≤ 4 yes
5 (default) ≤ 8 ≤ 5 yes
6 ≤ 8 ≤ 6 yes
7–8 ≤ 12 ≤ 6 yes

Tests

cargo test -p filmcraft-flac: CRC check values, the RFC 1321 MD5 suite, frame-number coding, predictor round trips, and tests/ffmpeg_oracle.rs, which decodes every stream with ffmpeg and requires every sample back exactly, no decoder errors, and the STREAMINFO MD5 equal to ffmpeg's hash of the decoded audio. It covers music-like, sweep, noise, full-scale square, silence and click signals; 8/16/20/24-bit; mono, stereo and 5.1; common and odd sample rates; block sizes 16 to 65535; and every level. Skipped (with a SKIPPED line) without ffmpeg.