mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-29 04:27:47 -06:00
- 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
64 lines
3.4 KiB
Markdown
64 lines
3.4 KiB
Markdown
# filmcraft-flac
|
||
|
||
A clean-room FLAC encoder (Free Lossless Audio Codec, [RFC 9639](https://www.rfc-editor.org/rfc/rfc9639)),
|
||
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
|
||
|
||
```rust
|
||
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.
|