A pure Rust, zero-dependency implementation of the QOA audio format with both decoding and encoding support.
This code is based off the reference C implementation and was written before the release of the specification.
- Decode QOA files from any
io::Readsource — streaming, one sample at a time - Encode 16-bit PCM audio to QOA — one-shot or frame-at-a-time streaming
- Zero unsafe code — the crate enforces
#![forbid(unsafe_code)] - Zero required dependencies —
rodioandhoundare optional features - Faster than C — the encoder beats the C reference by ~56% while remaining 100% safe
On Apple Silicon (M-series), encoding a 54-second stereo 44.1kHz file:
| Implementation | Decode | Encode |
|---|---|---|
C reference (qoa.h, gcc -O3) |
— | ~210 ms |
| Rust | ~24 ms | ~93 ms |
The encoder's hot path is a brute-force search over 16 scalefactors × 20
samples per slice. Key optimizations include a combined 4-element LMS
prediction and weights-penalty computation that shares register loads, a
compile-time combined quantize+dequantize lookup table that eliminates a
serialized load dependency in the inner loop, precomputation of first-sample
results for all scalefactors with rank-sorted search order that breaks early
once no better scalefactor is possible, proportional early rejection of
underperforming scalefactors at the halfway point, and direct buffer writes
bypassing io::Write trait overhead. With full LTO and codegen-units = 1,
LLVM generates tight scalar code that pipelines efficiently on wide
out-of-order cores — outperforming manual SIMD approaches for these small
4-element operations.
cargo benchuse qoaudio::{QoaDecoder, QoaItem};
use std::io::BufReader;
use std::fs::File;
let file = File::open("audio.qoa").unwrap();
let decoder = QoaDecoder::new(BufReader::new(file)).unwrap();
for item in decoder {
match item.unwrap() {
QoaItem::Sample(s) => { /* process i16 sample */ }
QoaItem::FrameHeader(h) => { /* new frame: h.num_channels, h.sample_rate */ }
}
}use qoaudio::{encode_all, QoaDesc};
let desc = QoaDesc {
channels: 2,
sample_rate: 44100,
samples: samples_per_channel,
};
let encoded: Vec<u8> = encode_all(&pcm_samples, &desc).unwrap();use qoaudio::{QoaEncoder, QoaDesc, QOA_FRAME_LEN};
let desc = QoaDesc { channels: 1, sample_rate: 44100, samples: total_samples };
let mut encoder = QoaEncoder::new(&desc).unwrap();
let mut output = Vec::new();
encoder.write_header(&mut output).unwrap();
for chunk in pcm_data.chunks(QOA_FRAME_LEN * desc.channels as usize) {
encoder.encode_frame(chunk, &mut output).unwrap();
}Play a QOA file (requires rodio feature):
cargo run --release --example play --features rodio -- fixtures/julien_baker_sprained_ankle.qoaEncode a WAV file to QOA (requires hound feature):
cargo run --release --example encode --features hound -- input.wav output.qoaMore audio samples can be found at phoboslab.org/files/qoa-samples.
Licensed under either of Apache License, Version 2.0 or MIT license at your option.