A single clamped index — a partition scale forced to 0 before it indexed a pulse-cache row it should have addressed as lm + 1 — was the entire reason OxiAudio’s conformant Opus encoder had been stuck at roughly 32 kbps.
Today we released OxiAudio 0.2.1 — a deep Opus codec conformance-hardening release for the COOLJAPAN Pure-Rust audio codec and DSP layer. The headline fix raises the CELT frame-size ceiling from 80 bytes to the RFC’s own 1275-byte limit (510 kbps mono), and it’s joined by a CBR sizing fix, entropy-exact hybrid packets, three allocation-DoS closures in the container decoders, and OGG page CRC validation.
No libFLAC. No libvorbis. No libopus. No dr_libs. No FFI. No -sys crates. Default features carry zero C/C++/Fortran dependencies — just clean, memory-safe Rust that compiles to a single binary and goes wherever Rust goes.
Why OxiAudio 0.2.1 is a game changer
Four defects had been quietly limiting OxiAudio’s own Opus encoder below its real potential, and none of them were things a “does it decode?” test could ever catch:
- A pulse-cache row bug capped every CELT frame at 80 bytes (~32 kbps) ever since the conformant CELT path landed — not because 80 bytes was ever a real ceiling, but because
celt_bits2pulses/celt_pulses2bitsclamped a partition scale it should not have, and the cause of the decoder mismatch above ~90 bytes/frame had never been found. - CBR frames silently shrank by up to 36 bytes (~14 kbps) on roughly 80% of requested sizes, because the range coder’s finishing routine wrote a boundary byte to its own index instead of merging it into the last range byte the way libopus’s
ec_enc_donedoes. - Hybrid-mode packets decoded to the right sample count while carrying the wrong symbols. A missing redundancy flag and a hardcoded 512-bit layer budget meant every CELT symbol after the SILK/CELT boundary landed one bit position away from what a real decoder expects.
- The default
encode_opusentry point wasn’t RFC 6716-conformant at all. It emitted a non-conformant 4-bit placeholder quantization that no standard Opus decoder would accept — invisible to a byte-compatibility test, only visible to a real one. - Three container decoders trusted attacker-controlled size fields directly into allocation requests (WavPack block size, AIFF frame×channel count, Musepack SV7 frame count), and OGG page checksums were parsed and then discarded instead of checked.
OxiAudio 0.2.1 closes all of it.
Technical Deep Dive: what actually changed under the hood
opus_celt.rs/opus_celt_bands.rs/opus_celt_rate.rs— the pulse-cache row bug. A band that splits four times (band 20 at LM=3: 176 → 88 → 44 → 22 → 11 bins) reaches partition scalelm = -1, which libopus indexes as pulse-cache row 0. The clamp instead selected row 1, giving those leaves a different pulse count than the reference decoder derives — and four-deep splits only happen once a band’s budget clears the cache maximum, which is exactly why the old 80-byte cap hid the bug for as long as it did.final_rangeequality against the reference decoder is now swept fromMIN_CELT_FRAME_BYTESto 1275 across ten fixtures (tones from 120 Hz to 19 kHz, speech, music, full-scale and near-silent noise, an impulse train, digital silence) × two frame positions each.opus_range.rs— two independent range-coder bugs.RangeEncoder::finish_to_size_checkedwrote its partial raw-bit window at its own byte index rather than|=-merging it into the shared boundary byte the wayec_enc_donedoes, so a frame always needed one byte more than libopus expected. Separately,carry_out’s shift usedEC_CODE_BITS - EC_SYM_BITS(24) where libopus’sec_encusesEC_CODE_BITS - EC_SYM_BITS - 1(23, now a namedEC_CODE_SHIFTconstant) — an off-by-one that dropped bit 23 of every emitted byte.opus_encoder.rs/ hybrid layer — entropy exactness. The encoder never wrote the hybrid redundancy flag (logp = 12) a decoder reads between the SILK and CELT layers, andencode_celt_hybrid_layer_intohardcodedTARGET_BITS_HYBRID = 512while the packet itself was assembled at a variable length. The hybrid writer is now real CBR (encode_hybrid_frame_conformant_sized) and passes its actual emitted length into the layer;final_rangeequality now holds across[MIN_HYBRID_FRAME_BYTES, MAX_HYBRID_FRAME_BYTES]=[20, 1275].- Decoder-side hardening.
wavpack.rs,aiff.rs, andmusepack.rsnow validate size and frame-count header fields against the bytes actually available before sizing an allocation or a slice read;ogg_reader.rsnow checksums each page (CRC field zeroed, compared against the stored value) before its packets ever reach the Opus/Vorbis decoders.
Getting Started
cargo add oxiaudio
The core pipeline is unchanged — decode, run DSP, re-encode:
use std::path::Path;
// Decode any supported format
let buf = oxiaudio::decode_file(Path::new("input.flac")).expect("decode failed");
println!("{} frames @ {} Hz", buf.frame_count(), buf.sample_rate);
// DSP: normalize, then add reverb
let mut out = buf.clone();
oxiaudio::dsp::normalize(&mut out, -1.0);
let with_reverb = oxiaudio::dsp::reverb(&out, 0.6, 0.4, 0.3);
// Re-encode as FLAC
oxiaudio::encode_flac(&with_reverb, Path::new("output.flac")).expect("encode failed");
What’s new is the headroom on the Opus side — encode_opus_file’s target_bitrate_kbps can now genuinely reach the frame sizes it asks for, up to the RFC’s own 510 kbps mono ceiling instead of stalling out around 32 kbps. Opus takes 48 kHz input only (the encoder does not resample):
use std::path::Path;
// Opus operates on 48 kHz input only -- the encoder does not resample.
let src = oxiaudio::decode_file(Path::new("input48k.wav")).expect("decode failed");
// 256 kbps mono Opus -- unreachable before 0.2.1, when the CELT frame-size
// ceiling silently topped out around 32 kbps (80 bytes/frame).
oxiaudio::encode_opus_file(&src, Path::new("output.opus"), 256)
.expect("opus encode failed");
What’s New in 0.2.1
- Added: the CELT frame-size ceiling raised to the RFC’s 1275-byte limit with the root-cause pulse-cache fix; exact CBR frame sizing; entropy-exact hybrid packets; an encoder-side bitstream trace (
CeltEncodeTrace) cross-checked stage-by-stage against the existing decoder-side verifier; a newoxiaudio-integration-testsworkspace member (publish = false, 130 tests) hosting twelve cross-crate round-trip files plus aproptestfuzz harness for the container decoders; a Pure-Rust SILK narrowband analysis-by-synthesis encoder (~1,326 new lines, unvoiced-excitation scope); an exact CELT PVQ shape search (op_pvq_search) replacing the earlier greedy pulse allocator. - Changed:
encode_opus/OpusStreamEncodernow route every 20 ms frame through the RFC 6716–conformant per-frame encoders by default; the pre-0.2.1 non-conformant byte layout is preserved asencode_opus_structuralfor byte-compatibility only. - Fixed: allocation-DoS bugs in
wavpack.rs,aiff.rs, andmusepack.rs; OGG page CRC-32 validation;mdct_forward/aac_mdct_forwardno longer panic on mismatched input length; a negative-shift panic inbitexact_log2tan; the range-coder carry-shift off-by-one. - Security:
crossbeam-epoch0.9.18 → 0.9.20 (resolves RUSTSEC-2026-0204, reached transitively via rayon);spin0.12.1 → 0.12.2 (the pinned version had been yanked from crates.io, reached transitively via oxifft).cargo deny checknow reports clean advisories. - Documentation: corrected stale “silence-only” SILK descriptions across four sites now that the real analysis-by-synthesis encoder has landed; the README format-support table and Status section now state
encode_opus’s conformance change honestly, including the not-yet-transparent-quality caveats. - Dependencies:
oxifft0.3.2 → 0.4.2.
Tips
- If you were capping Opus requests below ~32 kbps to work around silent truncation, you can stop.
target_bitrate_kbpsnow genuinely drives frame size up to 510 kbps mono; the fit is proven bit-exact against the reference decoder over the whole range, not just assumed. - Don’t reach for
encode_opus_structuralunless you specifically need the pre-0.2.1 byte layout. It’s kept only for byte-compatibility and OGG-framing-only tests — it is not RFC 6716-conformant, and new code should callencode_opus(orencode_opus_auto) instead. - Hybrid mode’s low band is still silent —
OpusConformantMode::Hybridcarries an inactive SILK frame pending a wideband-capable SILK encoder, soselect_conformant_modenever picks it automatically. For real audio today, CELT (music/general content) and unvoiced-only narrowband SILK are the two conformant modes that actually carry signal. - Run
cargo deny check bansafter upgrading.deny.tomlnow carries the full COOLJAPAN banned-crate table plus a dated, scoped exception for therubato → realfft → rustffttransitive edge — no oxiaudio crate depends onrustfftdirectly, andoxifftremains the direct dependency for the workspace’s own spectral/MDCT code. - If you parse untrusted audio containers yourself,
crates/oxiaudio-integration-tests/tests/fuzz_decoders.rsis a good reference: ~20proptest!properties across WavPack, Musepack, AIFF/AIFF-C, AU, AAC/ADTS, MIDI, raw PCM, format detection,OpusHead, and gapless parsing, all asserted panic/abort/hang-free under randomized and magic-prefixed input.
This is the foundation
OxiAudio is part of NoFFI — the COOLJAPAN initiative to replace every C/C++/Fortran/-sys FFI dependency in the Rust world with a clean, memory-safe, 100% Pure Rust implementation. Its spectral layer sits on OxiFFT, the ecosystem’s Pure-Rust FFT, and its codecs and DSP already power OxiSound (the COOLJAPAN audio playback layer, which pins oxiaudio and oxiaudio-core) and VoiRS (speech/voice synthesis, which pins oxiaudio-core and oxiaudio-encode directly for its FLAC output path).
As of this release: 1,136 tests passing / 5 skipped (default features), 1,242 / 6 skipped (--all-features), plus 63 doc tests, 0 clippy warnings, 0 rustdoc warnings, across 39,706 lines of production Rust in the 6 published crates.
Repository: https://github.com/cool-japan/oxiaudio
Star the repo if “a bit rate you asked for should be the bit rate you get” is a bar every audio encoder should clear.
The era of a codec silently capping itself below its own ceiling is over. Pure Rust audio — fast, safe, and sovereign — is here.
— KitaSan at COOLJAPAN OÜ August 6, 2026