Formats & decoding

Its own decoders first. Everything else is routed, and labelled.

crête ships decoders for every format the parity corpus depends on, so that a measurement never changes because a library did. The optional FFmpeg tier adds the formats it cannot reasonably own — and the routing rules that keep it out of the validated paths are strict, deliberate, and stated in the output.

01 — Native decoders

In every build, with no dependencies

Container parsing and PCM extraction are crête's own, including the FLAC decoder. The FORMAT suite transcodes every master into all of these and requires bit-identical metrics across them.

FormatExtensionNotes
WAV.wavPCM 8/16/24/32-bit, IEEE float 32/64-bit, WAVE_FORMAT_EXTENSIBLE.
RF64 / BW64.wav.rf64.bw6464-bit sizes via the ds64 chunk — the escape hatch past 4 GB (EBU Tech 3306 / ITU-R BS.2088). Same payload decoder as WAV.
Wave64.w64GUID chunk ids and 64-bit sizes. Same payload decoder again.
AIFF / AIFF-C.aif.aiffBig-endian standard; AIFF-C uncompressed NONE and sowt little-endian. There are no float variants because AIFF proper is integer-only.
FLAC.flacBuilt-in decoder, no libFLAC dependency.
DSD — DSF.dsfSony format, LSB-first. Decimated to PCM by the vendored kernel.
DSD — DFF.dffPhilips DSDIFF, MSB-first. Same decode engine; gated against DSF sample-for-sample.
SACD disc image.isoScarlet Book. Both areas read from the disc TOC, DSD and DST — and dispatched by content, not by extension.
Cue sheet.cueSplits a monolithic file — or several, one per side or disc — into per-track results.

The 4 GB ceiling, and the crash it exposed

Plain RIFF stops at 4 GB, which is not an exotic limit for this material — a 43-minute 5.1 96 kHz/24 extract is 4.47 GB. All three containers now share one payload decoder and measure identically to plain WAV. The rewrite also fixed a reachable crash: a truncated WAV declaring more data than it holds read past the end of the buffer and segfaulted. All three walkers now clamp to the bytes actually present and report the audio that is there.

Cue slicing

The sheet is parsed, each referenced file located case-insensitively, decoded once, then sliced by track boundary and run through the full pipeline per slice. A sheet may name several files — one per vinyl side or per disc — and INDEX times restart in each, so since 0.19.1 every track is cut from the file its INDEX 01 sits in; before that, all of them were cut from the last one. A track whose range is empty is dropped with a warning and the run exits 2. Track times are held as CDDA frames and converted at the actual decoded rate, so one sheet works for 44.1 kHz WAV and 352.8 kHz DSD-decoded PCM without rounding loss. Tracks are named to match what the common splitters produce, so a sliced monolith pairs cleanly against the per-track album.

A memory warning, not a footnote

crête loads a file before decoding it. At the default DSD output rate a monolithic 60-minute DSD64 album decodes to about 2.6 GB of float64; at 352.8 kHz that is 8× more. Cue slicing adds one slice plus roughly twice its size as working set. Since 0.19.0 crête plans for this itself: the default worker count is fitted to free memory, and each unit is costed by its shape — a cue sheet as its whole monolith, a DVD title set as all its fragments. The SACD reader is the one exception to holding a whole file: a disc image is far too large to hold, so it seeks, and an area is costed as its largest track. --memory-limit overrides the budget and -j still pins the count.

02 — Damaged files

What each container lets crête prove about itself

A meter's worst failure is not refusing a file — it is measuring a broken one and reporting a number as though nothing happened. Before 0.17.0 crête did exactly that. It now verifies what each format allows, and says so.

CheckFormatsWhat it catches
Frame CRC-8 + CRC-16FLACAn altered sample value — the bit-flip nothing else can see. The only detector a format hands over for free.
Declared vs decoded lengthFLAC STREAMINFO, WAV / RF64 / Wave64 data, AIFF COMM/SSNDTruncation, an interrupted copy, frames lost to a resync. STREAMINFO’s sample count was already read as an allocation hint and never compared with the decode.
Sample peak ≤ 0 dBFSAny integer PCMA decode that produced values the format cannot represent. Deliberately not applied to float PCM, decimated DSD or FFmpeg’s float decoders, all of which can legitimately exceed full scale.

A damaged file is still measured

crête is a meter, not a repair tool: it reports what the bytes decode to and marks every affected number untrustworthy. That separation is what keeps the intact-file path bit-identical to the one that existed before these checks — verified across 62 files spanning FLAC, WAV (integer and float), AIFF, RF64, Wave64, ALAC, TrueHD, AC-3 and DTS-HD MA in mono, stereo, quad, 5.1 and 7.1.

Always present, so it can be tested

Every warning also appears in -f json under warnings, which is always emitted — an empty array is the positive statement "nothing was wrong", rather than the absence of evidence a missing key would be. num_tracks counts what was measured, so a file that failed to decode contributes a warning and no track. The run exits 2.

So a file that is not audio must not be read as damaged audio. Since 0.22.0 a folder scan skips macOS AppleDouble sidecars, the ._<name> metadata files a Mac writes next to every file on a USB stick or an SMB share; before, each one was reported as a damaged track and the run exited 2. A sidecar named on the command line is still attempted.

The limit, stated plainly

WAV and AIFF carry no checksum. Altered sample values in them are undetectable — by crête or by any other meter — because a run of zeros inside a data chunk is indistinguishable from a passage of digital silence the artist put there. Only length and range can be checked; FLAC is the one that can prove itself. The test suite asserts that a WAV with 4 KB of zeros written into its audio comes back clean, so the blind spot stays visible in a green run.

Warning: 04 - damaged.flac: FLAC: CORRUPT -- 1 of 215 frames failed the CRC-16 check (0.5%).
Warning: 04 - damaged.flac: FLAC: TRUNCATED -- STREAMINFO declares 882000 samples per channel but 877904 decoded (4096 missing, 0.5%).
Warning: 04 - damaged.flac: CORRUPT -- sample peak 45.32 dBFS on 16-bit integer PCM, which cannot exceed 0 dBFS.
Error:   05 - unreadable.flac: FLAC: invalid magic

03 — The FFmpeg tier

Opt-in, LGPL-only, decode-only, statically linked

A one-time script builds a minimal static FFmpeg with no GPL and no nonfree components, so crete-ffmpeg is still a single self-contained binary. The default make target remains zero-dependency and nothing about this tier is required for PCM or DSD analysis.

Routing is strict, for parity reasons

Native formats always use crête's own decoders

Even in the FFmpeg build. This is what preserves parity against the reference data.

FFmpeg handles only what crête does not own.

In zero-dependency builds these formats are rejected with a hint rather than guessed at.

DSD is never routed through FFmpeg.

Its decimation is a different filter chain and would break DSD parity outright.

FormatExtensionNotes
ALAC.m4aLossless — metrics agree with the FLAC equivalent to float noise.
MP3 / AAC / Opus.mp3.m4a.opusLossy. Measured values reflect the decoded lossy signal.
TrueHD / MLP.mkv.thdLossless disc audio up to 7.1. Channel bed only — an Atmos carrier reports Dolby TrueHD + Dolby Atmos and marks the layout as a bed.
DTS / DTS-HD MA.mkv.dtsCore plus the lossless MA extension. The profile is reported without a bed qualifier — there is nothing unrendered about plain DTS-HD MA.
E-AC-3 / AC-3.mkv.eac3.ac3Lossy disc audio. Channel bed only; the E-AC-3 5.1 core is what a JOC Atmos stream decodes to.
DVD-Video.vobMPEG program stream: AC-3, DTS or LPCM beside MPEG-2 video. Any one fragment of a title measures the whole title set.
DVD-Audio.aobMPEG program stream: MLP or LPCM, no video. Measures the first audio group of a title set and says so when there is more than one.
DVD LPCM.vob.aobLossless — DVD’s own LPCM framing, with its own header and big-endian samples, not plain PCM.

crete-ffmpeg --version advertises the linked FFmpeg, and the About panel lists every extra codec. Video containers work directly — crête ignores video and subtitle streams, so music Blu-ray rips need no preparation. Also accepted in folder scans and dialogs: Vorbis, WMA v1/v2/Pro/Lossless, WavPack, APE, Musepack and TAK.

04 — Disc audio

Three ways a Blu-ray will hand you the wrong number

Disc audio is where a meter quietly stops measuring what is on the disc. Each of these was found by measuring, and each is now either fixed or labelled in the output.

It is not one album. It is six.

One UHD remux of a single title carried six audio streams — LPCM 5.1 at 96 kHz, DTS-HD MA 5.1, a TrueHD 7.1 Atmos track, an AC-3 core, and stereo LPCM and DTS-HD MA. Those are different mixes with different DR, not different encodings of one mix.

With no explicit choice, crête takes FFmpeg's own pick, which ranks by bitrate and channel count. That is reasonable but it is not the container's default flag and not stable across remuxes: on the full file it selects the 13.8 Mbps LPCM track; on a 60-second stream copy of the same six streams it selects the TrueHD one, because the copy carries no bitrate tags. Same audio, different measurement, and originally nothing in the output said so.

Warning: album.mkv has 6 audio streams;
measured stream 2 (PCM_S24LE, 6 ch, 96000 Hz).
Use --stream N to choose.

The chosen index and the stream count appear in the detail output and in JSON, so a measurement is reproducible from its own record. Single-stream files gain no warning and no extra keys — verified byte-identical.

Dolby DRC is disabled

AC-3 and E-AC-3 carry dynamic-range-control metadata and FFmpeg's decoder applies it by default. That is correct for a player and wrong for a meter: DRC is a playback-time compressor driven by authoring metadata, so measuring it means measuring a rendition that is not on the disc. crête pins the DRC scale to zero.

The difference is large and easy to miss.

DR barely moves because it is peak minus RMS and DRC scales both. If crête disagrees with a player's readout, this is usually why.

On one 5.1 bed, leaving DRC enabled moved

Sample peak6.2 dB
RMS3.9 dB
Integrated loudness4.0 LU
DR0.24

Object audio is measured as its bed, and now says so

There is no Atmos or DTS:X renderer in the chain. What gets decoded is the underlying channel bed — TrueHD 7.1, or E-AC-3 5.1 for Dolby Digital Plus with Atmos — and the object metadata is discarded. crête's numbers for such a file are the bed's numbers, which is exactly what a non-Atmos playback chain delivers, and not what a renderer would produce.

That is a complete measurement of something real, but an unqualified "crête measures Atmos" would be false — so the profile is reported separately from the layout, and JSON carries codec_profile and an immersive_bed boolean. It is identification, not decode, following the precedent MQA detection already set: name the premium layer from an authoritative bitstream field, never imply it was rendered. The field is descriptive — no measured value depends on it.

A non-immersive profile such as DTS-HD MA is reported too, without the bed qualifier: there is nothing unrendered about it. And the profile comes only from the FFmpeg tier, since the zero-dependency binary cannot decode these codecs at all.

Format:   TRUEHD 24-bit / 48000 Hz
Profile:  Dolby TrueHD + Dolby Atmos
Layout:   7.1 · L R C LFE Lrs Rrs Lss Rss
          [bed -- Atmos objects not rendered]

Auro-3D is not detectable this way

FFmpeg reports an Auro carrier as plain DTS-HD MA, so no profile field can see it. On the one disc measured here the "Auro-3D 9.1" label was a container title tag, with the height channels buried in the carrier's low bits — a real signature, since on a solo-piano recording whose LFE is otherwise digitally silent that carrier held 803,853 non-zero samples within ±24 against the DTS:X carrier's exact zero. But reading it is inference rather than a declared field, so if it is ever built it belongs in a confidence-scored forensics namespace, gated off, never in the metrics.

A discrete Auro-3D file is a different case: one PCM channel per speaker, as downloads ship and as the Auro encoder takes them. It declares its layout in its channel mask, and since 0.21.0 crête labels and weights it from that mask — see Standards.

One thing the disc proves about itself

Two of those six streams are the same 5.1 96 kHz master delivered as LPCM and as DTS-HD MA. Measured on a clip, every metric is exactly equal — DR, per-channel DR, sample and true peak, all three loudness figures, LRA, PLR, PSR — to 0.000e+00. Only RMS moves, by 1.2e-04 dB, and the sample counts explain it: one DTS frame of decoder tail, and RMS is the only metric that divides by total sample count. The file gates itself, with no external oracle — which matters, because none of these formats has reference data.

05 — DSD

A clean-room decimation kernel, on four declared axes

DSD bitstreams are decimated to PCM by libdsddpcm, vendored and statically linked. It is a clean-room BSD-2-Clause kernel: linear-phase FIR only, so group delay is constant, and unity DC gain — the +6 dB SACD makeup gain is not applied. crête keeps its own container parsing and its one-thread-per-channel decode.

It replaced an LGPL engine transcribed from another player's source, which resolved a licensing conflict with crête's MIT terms and made the project cleanly MIT plus BSD-2-Clause. Different clean-room filters mean different numbers, so every DSD reference baseline was regenerated against the commercial reference rather than carried over.

--dsd-mode multistage | direct

multistage (default) is the measurement-grade path: LUT decimation to 8fs then a halving cascade, brickwalled at Nyquist at 44.1 kHz out, valid for DSD64 through 512 at every rate. direct is a single wide FIR with a gentle ~30 kHz corner that passes an attenuated top octave — DSD64 to 44.1 kHz only.

--dsd-out-rate 44100 | 88200 | 176400 | 352800

44100 is the default and the parity-guaranteed rate.

--dsd-filter default | firls | kaiser

The ranked final stage, honoured only at 44.1 kHz output: equiripple at 383 taps and roughly 154 dB stopband (the golden default), least-squares at 767 taps as a cross-method check, or a Kaiser window at 4095 taps as the maximum decode.

--dsd-bandwidth default | full | Hz

Since 0.22.0. The output rate sets how densely the signal is sampled; the bandwidth sets how much of it is signal. default appends a linear-phase low-pass with a 50 kHz corner (Scarlet Book Annex D.1), which acts at 176.4 and 352.8 kHz output; at 44.1 kHz the final stage already brickwalls, and at 88.2 kHz the corner sits above Nyquist. full is the ≤ 0.21.0 decode.

Support matrix

4410088200 / 176400 / 352800
multistageDSD64–512, any filterDSD64–512, default filter only; --dsd-bandwidth acts at 176400 and 352800
directDSD64 only, default onlyunsupported

Unsupported combinations fail fast with an explanatory message rather than producing a number. DSD1024 and above are rejected — the library's input enum tops out at DSD512. crete --version prints the resolved chain, decimation, tap count and group delay for every rate at the current settings.

Read this before quoting a DSD number

DSD dynamic range depends on the output rate, and only 44.1 kHz carries a parity claim. Above 48 kHz output the halving cascade alone keeps the modulator's shaped ultrasonic noise, which inflates the second-highest block peak far more than it inflates RMS. Up to 0.21.0, at 352.8 kHz, DR read +1 to +3 higher than at 44.1 kHz, and the ultrasonic content alone drove the meter past full scale — a sample peak of +1.7 dBFS on one track.

Since 0.22.0 the band limit lives in the decoder: --dsd-bandwidth, a 50 kHz linear-phase low-pass by default, acting at 176.4 and 352.8 kHz. A band-limiting flag had been refused while it would have produced a number no reference implementation could check; in the decoder, the exports the reference measures carry it too, and the 352.8 kHz references were re-recorded with it. DSD64 RMS and DR at 88.2, 176.4 and 352.8 kHz now agree within 0.07, every Thriller track is back under full scale, and in weekly #106 album DR at 352.8 kHz matches the reference on all eight DSD albums. --dsd-bandwidth full gives the old numbers back.

It is still not parity. A 50 kHz band keeps 20 to 50 kHz content that the 44.1 kHz decode removes, and the corner is fixed for every DSD rate, so DSD256 and DSD512 lose clean content above 50 kHz. Results at 88.2 / 176.4 / 352.8 kHz are offered for analysis. Treat them as a different measurement, not a more precise one.

Validation

crête on a .dsf and crête on the WAV exported from it agree exactly, because both go through the same decode. Reference rows come from measuring those 32-bit-float exports at a pinned decode configuration, at both 44.1 and 352.8 kHz — so the exporter is load-bearing for the entire DSD corpus, and it has its own gate, which since 0.22.0 also runs at 352.8 kHz with the band limit on and off. Re-recording the 44.1 kHz references on the 0.22.0 pipeline reproduced all 1869 July values exactly; the July 352.8 kHz rows are kept as the references for full. The caveat is stated: this validates metrics end to end, not the decimation, whose correctness rests on the library's own golden vectors plus crête's internal parity suites.

06 — SACD disc images

The disc is the input. Both areas of it.

SACD rips circulate overwhelmingly as .iso, so for a meter with this much DSD machinery that was the gap a real collection hits first — until crête read them, a disc had to be extracted to .dsf or .dff before it could be measured at all.

crête reads the master TOC, both area TOCs, the track list and the audio-sector packet stream, and hands per-channel DSD to the same decimation chain every .dsf and .dff goes through. So the four DSD axes above apply unchanged, and crête on an ISO must agree with crête on an extracted rip of the same disc — a crête-versus-crête gate that needs no external oracle.

A disc image is a third shape alongside "one file" and "cue-sliced": the track list comes from the disc, and each track is decoded from its own sector range rather than sliced out of one decode. It is also the one decoder that seeks rather than loading the whole file — the test image is 3.88 GiB, and its stereo area alone decodes to 1.8 GB of float64 at the default output rate, or 14.6 GB at 352.8 kHz.

--sacd-area auto | stereo | multichannel

auto (default) prefers the 2-channel area: it is the layer crête’s DR references are built on, the layer comparable with every other release of an album, and usually the uncompressed one. A disc with no multichannel area refuses multichannel by name rather than quietly falling back.

crete album.iso                          # auto: the 2-channel area
crete --sacd-area multichannel album.iso # the 5.1 area
crete --sacd-area stereo       album.iso

Dispatch is by content, not extension. .iso names every disc image ever made, so crête checks for the SACD master TOC signature at a fixed sector and quietly ignores anything else rather than trying to meter a data DVD. Since 0.19.5 a DVD image is named as one — DVD-Audio or DVD-Video — with the instruction to mount it and measure its AUDIO_TS or VIDEO_TS folder. It is identified, not read: title sets are not read out of an image.

Validation

On the 2018 Dark Side of the Moon SACD all ten stereo-area track durations match the published running order — 42:57 against the area's own declared 42:59 — and one track returns peak −5.489 / RMS −19.753 against the −5.49 / −19.75 already recorded for that album from a .dsf rip. Every non-ISO path was compared byte-for-byte across the change: FLAC, AC-3, TrueHD, WAV and DFF all identical.

Gated since 0.17.0, and not before — the reader and the DST decoder were validated by hand once and then left, which is uncomfortable for code whose failure mode is silence. The suite measures a reduced image: the first tracks of each area, the multichannel one relocated so the file need not span the 1.85 GB gap before it, 229 MB instead of ~4 GB. Every audio sector in it is the disc's own bytes; only the TOC fields describing extent are rewritten, and the builder measures every kept track in both the reduced and the full image and requires identical results, so its own address arithmetic cannot certify its own mistake. 6 passed / 2 skipped on its first weekly. The 2 skips are the .dsf parity tier, which is written but needs the DSD64 corpus staged alongside and so is not running in CI yet.

DST, and the only check that caught a wrong decoder

DST is SACD's lossless compression, and multichannel areas commonly use it — on that disc the stereo area is plain DSD at 1.72 GiB while the 5.1 area is DST at 2.16 GiB against the 5.16 GiB it would need uncompressed. crête carries its own decoder, written from the published coding syntax and zero-dependency like everything else, so both areas of a hybrid disc go through the same chain and nothing downstream can tell which the disc used. DST is lossless; this is not an approximation.

Every frame must consume exactly its own coded length, and the decoder asserts it. Arithmetic coding is exact: a decoder consumes the encoder's bits if and only if it applies the encoder's probabilities. That assertion exists because the first version of this decoder was wrong in a way nothing else could see.

It read a comparison from a syntax figure that the clause prose contradicts. Every frame decoded without throwing. Every header parsed to stable, sane values. Mispredictions came out at a healthy-looking 0.75 %. The output was 1-bit data at the right density that decimated to audible sound. It was the prediction filter free-running on its own output, consuming 15 % of each frame.

The signal that was availablebrokencorrect
Frames decoded without errorallall
Misprediction rate0.75 %0.75 %
Per-bin probability checkpassespasses
Frame bits consumed15 %100.0 %
2–15 kHz band+19 dB−57 dB
Sample peak+2.06−13.87 dBFS

The two checks reached for first are structurally incapable of catching this. The misprediction rate is low because the decoder consumes few bits, and per-bin probability consistency is vacuous — an arithmetic decoder fed any probability sequence produces events matching that sequence. It proves the coder, not the probabilities.

It is not fast: roughly 2× realtime for six channels, so a 43-minute 5.1 disc takes about 20 minutes. All channels share one arithmetic decoder, so there is no parallelism inside a frame — but the filter history is reinitialised per frame, so frames are independent and could be decoded in parallel. Not done yet.

To validate a DSD de-interleave, look at the noise floor

Frames are byte-interleaved across channels. The other reading — contiguous per-channel blocks — was implemented first and "confirmed" with the obvious test, the left/right split. That test is worthless here: both readings give a plausible DR and a plausible L/R difference, because a wrong de-interleave of a 1-bit stream still decimates to something music-shaped.

What settles it is the property DSD exists for. Byte-interleaved gives the textbook picture — music at low frequency, a quiet 2–20 kHz band, shaped noise climbing above 40 kHz. Blocks give a floor rising at every frequency with no quiet band anywhere. The two are 34 dB apart where it matters.

Two more traps in the same reader produced equally plausible wrong answers. The frame-format value for compressed is zero and the uncompressed forms are 2 and 3, so reading it backwards labels a DST area uncompressed — and DST bytes fed to the DSD path decode to something that still looks like a measurement. And a DST sector's frame header carries one byte more than a DSD sector's, which taken unconditionally overruns the first packet and drops that sector's audio entirely: 14 % of every track lost with no error, 58.65 seconds of a 68.45-second one. Both are now cross-checked, and the header length is read from the sector rather than assumed from the area.

Mean level by band, decimated to 352.8 kHz

0–2k2–15k15–20k24–40k40–100k
byte-interleaved25.8−20.2−20.4−17.615.4
per-channel blocks27.513.717.723.230.4

Provenance

Both readers are written from the format descriptions — structure layouts and coding syntax are facts about a format, used as documentation. No implementation was consulted: the reference extractor is GPL-2.0, which is strictly worse for an MIT project than the LGPL conflict the DSD engine swap existed to remove, and its DST decoder is a console SPU offload with no software path in it at all. The other software DST decoder available is LGPL. crête stays MIT plus BSD-2-Clause.

07 — DVD title sets

One title, several files, one stream

A DVD is the first container crête reads that is not one file holding one stream. The spec caps a file at 1 GB, so a title's program stream is split at a pack boundary and numbered — and metering the pieces separately is wrong twice over: each is metered alone, and the split lands wherever 1 GB fell, so the 3 s DR block straddling it belongs to neither piece.

Point crête at any one fragment and it measures the whole title set. The pieces are presented to FFmpeg as a single stream through one demuxer, so the boundary costs nothing. On the test corpus the joined MLP duration is 49.632 s against 25.324 + 24.289 s read separately — 19 ms longer, that being the frame the split cut in half. DVD LPCM joins exactly, 23.498 + 23.721 = 47.219 s either way, because its packets are self-contained within a pack.

It also removes a trap. The FFmpeg stream index is not stable across fragments of one title: on the DSOTM 50th DVD the same three substreams come back in a different order in the second fragment than in the first, because the index follows order of first appearance in the program stream. A --stream pin correct for one fragment selects a different codec in the next. One stream means one probe and one answer, which is what makes --stream meaningful here at all.

VIDEO_TS/VTS_02_1.VOB  _2.VOB  _3.VOB   one 43-minute title
AUDIO_TS/ATS_01_1.AOB  ...     _5.AOB   one DVD-Audio title set

         VTS_02_1.VOB      VTS_02_2.VOB
index 2  ac3               lpcm
index 3  ac3               ac3
index 4  lpcm              ac3

Validation

Both test discs carry a lossless stream — MLP on the DVD-Audio, DVD LPCM on the DVD-Video — and the corpus ships each decoded to FLAC, so crête through the disc container must equal crête on the FLAC exactly: two independent decode paths over the same samples, nothing to calibrate. The suite is 10/10 on its first weekly, #85, and fails all ten against the build before it. With foobar measuring each FLAC it now holds twelve tests, and has passed all twelve in every weekly since 0.19.5. The whole JSON tracks array is byte-identical to 0.17.0 on all seven disc-audio carriers, so reading DVDs moved nothing that was already measured.

A DVD-Audio title set holds more than one mix

Fleetwood Mac's Rumours (2001, Warner) is one title set holding four titles — a 96 kHz 5.1 mix, a 96 kHz stereo mix, a 48 kHz 5.1 version and a one-second tail — 39 tracks across five .aob fragments, and the group boundaries do not align with the fragments. The 6-to-2-channel change falls partway through ATS_01_3.AOB, so even a single .aob can cross one.

Left to itself FFmpeg decodes straight through the change into a resampler configured for the previous format, and reports nothing but a parity-check failure and a timestamp discontinuity on stderr. The numbers stay in range and nothing says they are meaningless. crête stops at the change, measures the first group, names it, and exits 2 — a warning over the wrong numbers would be the silent wrong answer in camouflage.

The probe had the same blind spot one level up: it can read deep enough to reach a later group and report 2 channels for a stream whose first frame is 6. So the channel layout and rate now come from the first decoded frame, not the container — the decision already taken for the sample format, extended to the two fields the probe can get wrong the same way.

Warning: audio format changes partway through
this stream (a DVD-Audio title set holds several
audio groups); measured the first group only,
6 ch / 96000 Hz

Reading the disc's own tables

make dvda-info builds a zero-dependency tool that parses a title set's formats, titles and track lengths without decoding anything — one second on a 4.5 GB title set. On Rumours the track lengths sum to each title's own stated total exactly, on all four titles, in fields held in separate structures; a wrong offset or byte order cannot produce four exact matches. The three format entries agree, in order, with the three groups decoding had already found.

crête does not yet use it to measure per track or per title. Durations do not give byte offsets — MLP is variable-rate — and the table that would is still a hypothesis. A wrong offset into a lossless stream yields audio that plays and a DR that looks reasonable, so it waits for the same arithmetic proof the track table met.

One caveat on --stream for .vob: the compact FFmpeg lists one more audio stream than the disc carries. It cannot identify the MPEG-2 video, content-probes it and matches it as MP3, so three audio streams list as four and the extra decodes to 0.34 s at 16 kHz with a +23 dBFS peak. A full FFmpeg types it correctly. It cannot be filtered on crête's side, because MPEG audio is a legal DVD audio format. Automatic selection is unaffected — it ranks by bitrate and the phantom has none — and every genuine stream is reachable by index; only the listing is wrong.

08 — MQA

Reported unconditionally. There is no flag to turn it off.

MQA is a lossy codec delivered inside an ordinary lossless-looking container — 44.1 or 48 kHz, stereo, 16 or 24-bit — with the encoded data hidden in the low bits. A dynamic range measurement on such a file describes lossy audio dressed as hi-res, so crête flags it every time.

Detection is deterministic, not statistical

The primary MQA bitstream is the XOR of one low bit from each left/right sample pair. crête reconstructs that bitstream across candidate carrier positions — bit 8 for 24-bit hi-res MQA, bit 0 for 16-bit MQA-CD, which shifts the whole encoding down a byte — and searches for the datasync packet: a 40-bit sync value followed by a well-formed field structure.

A match is proof rather than a guess; the odds of a coincidental 40-bit magic number plus valid fields are negligible, and in practice only the true carrier plane ever hits. From the packet crête reads the authored sample rate, the authentication level that distinguishes Studio from green, the render filter and bit-depth indices, and the carrier bit position.

A raw low-bit entropy figure is exposed in JSON as a diagnostic and is never treated as a verdict — near-full low-bit entropy is equally consistent with analog tape hiss or a prior lossy generation, so using it to claim MQA would false-positive on legitimate lossless masters.

==================================================
⚠  MQA LOSSY ENCODING DETECTED
source is not lossless
--------------------------------------------------
11 track(s) · MQA Studio · authored 44.1 kHz
carrier bit 8 · auth level 9
--------------------------------------------------
DR is measured on the delivered PCM stream;
content encoded above the base band is not
reconstructed (no open MQA decoder exists).
==================================================

What it does not do

crête does not unfold or render MQA. That DSP exists only in the proprietary licensed decoder, so DR is always measured on the delivered base-band PCM and the warning says so rather than leaving you to infer it.

Provenance: the bitstream layout is a clean-room reimplementation of the format description in a BSD-2-Clause project's specification and in the published patent — facts and an interface, no decoder code — so crête stays MIT.

09 — Build & platforms

One executable, everywhere it is built

make setup-deps-check       # report what is missing
make setup-deps             # install it
make                        # zero-dep CLI
make cli-json               # + JSON output
make cli-json-ffmpeg        # + FFmpeg tier
make setup-imgui && make gui
make gui-ffmpeg
make dsd2wav                # DSD → WAV exporter
make dvda-info              # DVD-Audio title-set tables
make debug                  # asan + ubsan
make STATIC=1               # fully static link

GUI prerequisites are SDL2 and an OpenGL development package; Dear ImGui is fetched once by make setup-imgui. The FFmpeg tier needs its compact static build run once, and pkg-config.

setup-deps checks by capability, not package name, so a compiler you built yourself still counts; it prints every command before running it, supports --check and --dry-run, and never installs something already present. Homebrew, dnf, apt, pacman, zypper and MSYS2 are covered. It installs the toolchain; the three setup-* targets fetch the sources crête vendors. Two things it checks are easy to get wrong on your own: an x86 host with no assembler fails the FFmpeg build as a deep, confusing error rather than a missing dependency, and MinGW's win32 threading model ships a standard library with no threads at all — so such a build links cleanly and then dies on the first album.

Platform status

Linux x86-64 built & gated

CI agent. STATIC=1 produces a genuinely static binary.

Linux aarch64 built & gated

Second CI agent; bit-identical to x86-64 on the gated fields.

macOS builds

Development host, and a Homebrew tap since 0.19.3. One ULP apart from Linux on log10, so its JSON is not equality-comparable.

Windows x86-64 built & self-checked

Built natively under MSYS2 on a Windows 11 agent for 0.20.0 and 0.21.0. 0.22.0 has no Windows build: on its release the agent’s Application Control policy blocked MSYS2’s bash.exe before anything compiled, so that release carries no Windows zip. The JSON build runs check_fix12.sh, check_lfe_peak.sh and a JSON round trip. STATIC=1 is not optional — without it the executable needs three runtime DLLs and the single-binary promise breaks — and an import of a MinGW runtime, SDL2 or FFmpeg DLL fails the build. No weekly result on this site was measured on Windows.

Windows on ARM not buildable yet

Needs a second toolchain; the current one targets x86 only.

Windows GUI built, unverified

Since 0.20.0 crete-gui.exe ships in the release zip with SDL2 linked statically, so the missing-SDL2 blocker is gone. Nothing checks it: the build job exercises only the CLI, and the repo records no run of the GUI on Windows.

There is not one POSIX-threads call in the source — only C++17 standard threading — and file dialogs and directory walking are already branched per platform. The only thing that ever mattered for Windows was the toolchain's threading model. Linux packages ship from the openSUSE Build Service since 0.19.2, a Homebrew tap covers macOS since 0.19.3, and Windows binaries and a Scoop bucket since 0.20.0, below. None of the binaries is code-signed; a signed release matrix is scoped but not built.

10 — Packages

The FFmpeg tiers, packaged for Linux, macOS and Windows

Every channel ships the same two builds — crete from make cli-ffmpeg and crete-gui from make gui-ffmpeg — with crête's own compact LGPL FFmpeg linked statically. Since 0.19.4 a pushed release tag publishes them: OBS first, and the Homebrew tap only once every OBS repository has built and published that version; since 0.20.0 the Windows build is attached to the Codeberg release and the Scoop bucket follows it.

Linux — the openSUSE Build Service

Published from the project home:abksh:crete since 0.19.2 — one source package, built offline for every target below. The one-click page generates the right commands for your distribution.

PackageInstallsBuilt from
crete/usr/bin/cretemake cli-ffmpeg — the native decoders plus the compact FFmpeg set. No -f json.
crete-gui/usr/bin/crete-gui, a desktop entry, fonts in /usr/share/cretemake gui-ffmpeg. File dialogs use zenity or kdialog, whichever is installed.
DistributionRepositoryArchitectures
openSUSE TumbleweedopenSUSE_TumbleweedopenSUSE_Factory_ARMx86_64 · aarch64 (Factory_ARM)
openSUSE Leap 16.016.0x86_64 · aarch64
Fedora 43, 44, RawhideFedora_43Fedora_44Fedora_Rawhidex86_64 · aarch64 (Rawhide x86_64 only)
Debian 12, 13Debian_12Debian_13x86_64
Ubuntu 22.04, 24.04, 26.04xUbuntu_22.04xUbuntu_24.04xUbuntu_26.04x86_64
Arch LinuxArchx86_64

openSUSE

sudo zypper addrepo https://download.opensuse.org/repositories/home:abksh:crete/openSUSE_Tumbleweed/home:abksh:crete.repo
sudo zypper install crete crete-gui

Fedora

sudo dnf config-manager addrepo --from-repofile=https://download.opensuse.org/repositories/home:abksh:crete/Fedora_44/home:abksh:crete.repo
sudo dnf install crete crete-gui

Debian / Ubuntu

echo 'deb https://download.opensuse.org/repositories/home:/abksh:/crete/Debian_13/ /' | sudo tee /etc/apt/sources.list.d/crete.list
curl -fsSL https://download.opensuse.org/repositories/home:abksh:crete/Debian_13/Release.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/crete.gpg > /dev/null
sudo apt update && sudo apt install crete crete-gui

Substitute the repository name from the table for the one in each URL. The examples use openSUSE_Tumbleweed, Fedora_44 and Debian_13.

macOS — a Homebrew tap

brew tap abksh/crete https://codeberg.org/abksh/homebrew-crete
brew install abksh/crete/crete        # command-line meter
brew install abksh/crete/crete-gui    # graphical front-end

Since 0.19.3. Formulae, not casks, compiled from the release tag on install — so nothing is quarantined and Gatekeeper does not block the unsigned binaries, and an install takes a few minutes, because each formula builds FFmpeg 7.1 from source. Homebrew's own ffmpeg is --enable-gpl, and linking it would make crête GPL; both formula tests fail if the binary links any libav* dylib. Install by the full abksh/crete/… name: that is what tells Homebrew to trust a third-party tap.

There is no macOS build agent. The tap is bumped once the 15 Linux builds of the same source have published — evidence that the tag builds, not that the formula builds on a Mac, so a failure there would first surface on a user's brew install.

A Homebrew build is a macOS build, so it is one ULP apart from Linux on log10 and its output is not equality-comparable with the gated Linux agents.

Windows — a Scoop bucket, or the zip

scoop bucket add crete https://codeberg.org/abksh/scoop-crete
scoop install crete/crete             # crete and crete-gui

Since 0.20.0. Each Codeberg release carries a Windows x86_64 zip with its .sha256: crete.exe and crete-gui.exe, both STATIC=1, so there are no DLLs and no installer — keep the fonts folder beside the GUI. The Scoop bucket installs that zip, puts both commands on the path with a Start-menu shortcut for the GUI, and is bumped by the release job. A winget package is prepared; its first submission is made by hand. The 0.22.0 Windows build did not run — the agent's Application Control policy blocked MSYS2's bash — so that release has no zip and the bucket still installs 0.21.0.

Built natively under MSYS2 on a Windows agent. The job runs the synthetic self-checks and a JSON round trip on its JSON build; the shipped crete.exe is checked for its version and its DLL imports, and nothing runs the GUI. The binaries are not code-signed.

Built with crête's flags, not the distribution's

Every Linux recipe — the spec for openSUSE and Fedora, the Debian set, the PKGBUILD — removes the distribution's CFLAGS and LDFLAGS from the build environment and turns off LTO and debug builds. Injected optimisation flags would give each distribution a different binary and void the x86_64 / aarch64 bit-identity that -ffp-contract=off exists for.

The compact FFmpeg — LGPL-2.1, decode-only — is built from source inside the package build and linked statically, exactly as CI builds it; nothing is downloaded at build time. To produce the upload set yourself: scripts/make_obs_source.sh OUTDIR [REF], which is deterministic — the same ref gives the same sha256.

What the packages are not

Not the JSON build

The packaged crete, on every channel, is make cli-ffmpeg, which has no -f json. For the CI shape, build make cli-json-ffmpeg from source.

Smoke-tested, not measured

The only check inside a Linux package build is that the binary runs and reports the version it was packaged as. The Homebrew crete formula's test measures a 20 s 1 kHz sine at −6 dBFS and asserts DR0, a −6.02 dBFS sample peak and −6.01 LUFS — but Homebrew runs it only on brew test, not on install. For the Linux packages, bit-identity with the gated builds holds by construction — the same flags — not by comparison. No figure on this site was measured with a packaged binary: the current census is weekly #96, on 0.20.0.

Not built on a Mac before release

The release job has no macOS agent. A Homebrew formula is published on the strength of the Linux builds, and a macOS build failure would first surface on a user's brew install.

x86_64 only outside openSUSE and Fedora

Debian, Ubuntu, Arch and Fedora Rawhide are built for x86_64 alone, and so is Windows: there is no Windows on ARM build.

crête 0.23.0 MIT License — Ashwin Balaji 2026. DSD decode by libdsddpcm (BSD-2-Clause). FFmpeg tier under LGPL 2.1+. codeberg.org/abksh/Crete