Expand description
§syom
AAC to planar f32.
An AAC codec in Rust. AAC-LC, HE-AAC v1/v2, and AAC-LD. ADTS, LATM/LOAS,
M4A, and bounded fragmented MP4. Encode defaults to AAC-LC at 128 kbps,
ADTS or M4A. HE and LD are opt-in. Output is planar f32 at the
bitstream rate (HE is twice the core rate). The product path pulls no
crates. ffmpeg is an offline oracle, not a runtime dependency.
§Examples
fn main() -> syom::Result<()> {
let pcm: Vec<f32> = (0..48_000).map(|i| 0.3 * (i as f32 * 0.06).sin()).collect();
let adts = syom::encode(&[&pcm], 48_000)?;
let back = syom::decode_with(&adts, &syom::DecodeOptions::audio())?;
assert_eq!((back.channels.len(), back.sample_rate), (1, 48_000));
Ok(())
}decode is the speech default (mono mix, 2 h at up to 48 kHz; higher
rates keep that sample budget). DecodeOptions::audio()
keeps the coded channels. From a path: syom::read. Push decode and
encode: Decoder::feed, Encoder::feed. The
guide has one compiling
example per task.
| task | section |
|---|---|
| one-call encode / decode, speech vs full fidelity | 1 |
borrowed PCM, Read / Write / Seek | 2 |
| priming, tail, exact length | 3 |
push Encoder / Decoder, bounded memory | 4 |
raw access units and AudioSpecificConfig | 5 |
| probe, sample-exact M4A seek | 6 |
| LC, quality VBR, HE v1/v2, surround | 7 |
| limits and typed errors | 8 |
| profiles and containers | 9 |
| behaviour changes since 0.6.0 | 10 |
§Decode and encode
| ADTS | M4A | LATM/LOAS | fMP4 | |
|---|---|---|---|---|
| AAC-LC decode | yes | yes | yes | yes |
| HE-AAC v1/v2 decode | yes | yes | yes | yes |
| AAC-LD decode (512) | no | yes | yes | yes |
| LC encode | yes | yes | yes | no |
| HE v1/v2 encode | yes | yes | yes | no |
| AAC-LD encode | no | yes | yes | no |
LC encode is the default; M/S is per-band. with_he, with_he_v2, and
with_ld are opt-in. ADTS cannot signal AAC-LD. Fragmented MP4 uses the
same ASC path as M4A, so AAC-LD decodes there too; there is no separate
LD fMP4 golden. Surround LC is 3, 4, 5, 6, or 8 planes
(channel_configuration 3–7). Seven planes have no layout.
No resample.
§Compared with rusty_aac, Symphonia, oxideav-aac
| syom | rusty_aac 0.5 | symphonia 0.6 | oxideav-aac 0.1.7 | |
|---|---|---|---|---|
| Job | AAC codec | LC decoder | media pipeline | ADTS decoder |
| Default deps | none | none | several | oxideav-core |
| HE-AAC v1/v2 decode | yes | signalled | LC core | ADTS |
| AAC-LD | yes | no | no | no |
| M4A / LATM / fMP4 | yes | no | M4A, no fMP4 | no |
| LC / HE / LD encode | LC; HE and LD opt-in | no | no | no |
| Output | planar f32, native rate | interleaved f32 | packets | interleaved i16 |
| Duration / RAM caps | yes | no | no | no |
One call decode(&[u8]) | yes | no | no | decode_all |
§Speed
Linux x86_64, AMD Ryzen AI 9 HX 370, rustc 1.97.1, taskset -c 0,
profile.bench (thin LTO), 20 reps after 4 warmup, 2026-10-01. Same
bitstream and the same output shape. Sample checksums differ. A peer
that cannot produce that shape is left blank. Older rows of a different
protocol: BENCH.md.
| Workload | syom | symphonia 0.6.1 | rusty_aac 0.5.0 | oxideav-aac 0.1.7 |
|---|---|---|---|---|
| LC ADTS, 48 kHz mono, 13312 samples | 72.2 µs | 123 µs | 179 ms | 175 ms |
| HE-AAC v2 ADTS, 48 kHz stereo, 53248 samples | 4.61 ms | 369 ms | ||
| LC 5.1 ADTS, 48 kHz, 20480 samples | 640 µs | 1.67 s |
§Install
[dependencies]
syom = "0.8"rustc 1.89.
No resample. MIT. AAC patents: NOTICE.
Modules§
- guide
- Task-oriented guide (TASK-107). Every example here is self-contained — it makes its own PCM — so it runs from the published crate as well as from the repository.
Structs§
- Budget
Exceeded - Exact-boundary / overflow failure. TASK-24 maps this to a public error.
- Decode
Options - Options for
decode_with. - Decoded
Aac - Decoded AAC at native sample rate (planar f32, mono-mixed or split).
- Decoder
- Resumable push decoder for ADTS and LATM/LOAS byte streams, bounded
fragmented MP4 (init
moov+moof/mdatfragments, TASK-125), or raw access units afterDecoder::from_asc. Peak PCM RAM is O(frame). Flat M4A/ISOBMFF input is rejected onfeed(moovneeds random access; usedecode_streamingon the full slice). - Encode
Info - Tallies from a finished streaming encode.
- Encode
Options - Options for
encode_with/write_with. - Encoded
Frame - One encoded AAC frame.
- Encoder
- Resumable push encoder: planar f32 chunks in, ADTS, LOAS, or raw units out.
- Frame
- One decoded AAC frame: planar f32 in [-1, 1], one plane per channel.
- Frame
Meta - Copy metadata for one decoded frame or a finished stream.
- M4aSeek
- Seekable M4A source: presentation-sample
seekwith codec preroll. - Memory
Budgets - Named memory limits.
Defaultis the speech-ingest set. - Probe
- Read-only AAC metadata.
Copy; no PCM. - Stream
Info - Tallies from a finished stream decode.
Enums§
- AacError
- Errors produced while sniffing, decoding, or encoding AAC.
- Budget
Kind - Which budget a
BudgetExceededrefers to. - Channel
- One output plane’s identity.
- Channel
Mode - Whether decoded channels are mixed to mono or kept separate.
- Encode
Container #[non_exhaustive]: further transports may follow.- Input
Scope - Which compressed-byte policy applies.
- Layout
- How the plane vector was produced.
- Lifecycle
State - Push
Decoder/Encoderused after finish or fail. - Malformed
Kind - Malformed syntax (CRC, Huffman, lengths). Truncation is
AacError::Truncated. - PcmReject
- Encoder rejected the PCM domain (not a bitrate/container knob).
- Probe
Container - Advertised transport the probe locked onto.
- Probe
Duration - How sure we are about presentation length.
- Probe
Profile - Codec profile from the header or ASC. ADTS implicit HE stays
Self::Lc(SBR is in-band, not in the 7-byte header).Self::Ldis ER AAC LD (AOT 23, 512- or 480-sample frames). Both grids probe asSelf::Ld. - Probe
Trim - Encoder delay and unplayed tail. An ADTS
iTunSMPBtag reportsSelf::Exactonly when its counts match the buffered frames (1024 or 2048 samples each). An M4A edit list reportsSelf::Exact. A stale tag, untagged ADTS, and LATM staySelf::Unknown. - Unsupported
Feature - Unsupported AOT / transport / tool (not malformed syntax).
Constants§
- DEFAULT_
MAX_ BOX_ DEPTH - ISOBMFF box nesting depth.
- DEFAULT_
MAX_ BUFFERED_ INPUT_ BYTES - Streaming
Decodercompressed bytes after compact (partial AU + unconsumed). - DEFAULT_
MAX_ CHANNELS - Output / PCE plane ceiling: product layouts are 1–8 (7.1 since TASK-62); a PCE beyond eight planes is a Format error, not a memory bomb.
- DEFAULT_
MAX_ DECLARED_ AU_ BYTES - Declared access-unit length (ADTS 13-bit max is 8191; LATM/M4A
stsz). - DEFAULT_
MAX_ DECODE_ SAMPLE_ RATE - Ceiling when converting duration → a per-channel sample budget.
- DEFAULT_
MAX_ DURATION_ SECS - Duration / rate caps for a lecture-length MP4.
- DEFAULT_
MAX_ INDEX_ ENTRIES - Sample-table entry count (
stszsamples,stts/stscrows, chunks). - DEFAULT_
MAX_ INPUT_ BYTES - Hard upper bound on a finite compressed buffer or file (1 GiB).
- DEFAULT_
MAX_ METADATA_ BYTES - M4A table / box-payload bytes (
stsz/stts/stsc/stcoplus names). - DEFAULT_
MAX_ OUTPUT_ BYTES - Collected planar f32 cap (4 GiB). Covers 2 h stereo @ 48 kHz; 2 h 5.1
split does not fit (F07). Same default as ryf
max_output_bytes. - DEFAULT_
MAX_ SAMPLE_ RATE - DEFAULT_
MAX_ WORKSPACE_ BYTES - Resident codec + compressed-buffer workspace, independent of duration.
- MAX_
PLANES - Product plane ceiling (
crate::DEFAULT_MAX_CHANNELS).
Functions§
- allocable_
bytes Veccapacity isisize::MAXbytes.Noneifplannedcannot be ausizelength on this target.- check_
planned - Exact-boundary:
planned == maxis allowed;planned == max + 1is not. Overflow and 32-bitisize::MAXclip are the same class as over-budget. TASK-24/25 map a failedtry_reservetoBudgetExceeded— never panic. - decode
- Decode ADTS, M4A, LATM/LOAS, or bounded fragmented MP4 with speech defaults.
- decode_
bytes - Alias of
decode. - decode_
read - Decode ADTS, LOAS, or bounded fMP4 from a generic
Readwith speech defaults. Does not materialize the whole stream. Flat M4A isAacError::Unsupported(it needsstd::io::Seek). - decode_
read_ streaming - Stream-decode ADTS, LOAS or bounded fMP4 from
reader. Same lifecycle as a pushDecoder:feed_readthenDecoder::finish. Flat M4A isAacError::Unsupported. - decode_
read_ with - Decode ADTS, LOAS, or bounded fMP4 from
readerunderopts. Peak compressed RAM is the streaming resident cap, not the stream duration. - decode_
seek - Decode seekable M4A with speech-ingest defaults. Peak compressed RAM is
moovplus one access unit. - decode_
seek_ streaming - Stream-decode M4A from a seekable reader. Peak compressed RAM is the
moovbox plus one access unit (mdatis skipped, then sampled). - decode_
seek_ with - Decode seekable M4A under
opts. - decode_
streaming - Stream-decode a complete in-memory buffer (any container, incl. M4A).
- decode_
with - Decode ADTS, M4A, LATM/LOAS, or bounded fragmented MP4 under
opts. - encode
- Encode planar f32 PCM in
[-1, 1]to an ADTS stream: AAC-LC at 128 kbps. - encode_
with - Encode planar f32 PCM under
opts(container, bitrate, lookahead, quality VBR, HE v1/v2, AAC-LD, or the opt-in LC tools). - encode_
write - Encode planar PCM incrementally into any
std::io::Writesink (TASK-59): ADTS, LOAS, or raw access units are written as they are produced, so the codec workspace stays one frame and no compressed buffer is collected. ADTS and LOAS match [encode_with]. Raw is push-only (crate::UnsupportedFeature::EncodeRawOneShotfrom one-shot). M4A iscrate::UnsupportedFeature::EncodeM4aStreaming. - encode_
write_ m4a - Encode planar PCM into an M4A written incrementally to a
Write + Seeksink (TASK-60):ftypand anmdatheader go out first, every access unit is written as it is produced, andfinishseeks back to patch themdatsize and appendsmoov(sample tables,elstpriming, exact presentation duration). Retained memory is one frame of codec state plus 4 bytes per access unit (thestszindex, at most 2^20 units); compressed media is never accumulated.opts.containeris ignored — the output is always M4A. Bytes are identical to [encode_with] withEncodeContainer::M4a; offsets are relative to the sink position at entry, so the M4A must start at that position. - input_
cap_ applies - Finite complete buffers use
DEFAULT_MAX_INPUT_BYTES. Streamingfeedmust not: a lifetime sum is F07 and is a no-go. - mpeg_
channels - MPEG default-layout labels (lavc / Table 4.1 order). Empty if
cfgis not 1–7. Config 7 (7.1) is FL FR FC LFE BL BR SL SR: ISO calls the third CPE “left/right outside front”; lavc presents it as the side pair, and so does syom. - mux_
raw_ he_ m4a - Mux raw HE v1 access units (push
crate::EncoderwithEncodeOptions::with_he+EncodeContainer::Raw) into M4A: explicit two-rate ASC, output-rate timeline,elstpriming as reported bycrate::EncodeInfo::priming. - mux_
raw_ lc_ m4a - Mux raw LC access units into M4A (
elstpriming 1024, presentationvalid_samples). - pcm_
bytes n_ch * n_samples * size_of::<f32>();Noneon overflow.- preroll_
aus - Extra LC mono/stereo overlap AUs (measured). HE/PS and any
channel_configurationabove stereo replay from AU 0 (seeM4aSeek::seek). - probe
- Inspect
dataunder default memory budgets. Does not decode PCM. - probe_
with probewith explicit metadata / AU / channel fences.- read
- Read a file and
decodeit. M4A uses seekable sample access (no whole-mdatload); ADTS/LOAS still cap the file atDEFAULT_MAX_INPUT_BYTES. - read_
with - Read a file and
decode_withit. - sniff_
aac - True if the buffer looks like ADTS, LATM/LOAS, or ISO-BMFF M4A.
- sniff_
is_ adts - Sniff ADTS: 12-bit sync, layer 0, plausible frame length.
- sniff_
is_ isobmff - sniff_
is_ latm - LOAS syncword
0x2B7in the first 11 bits. - workspace_
lower_ bound_ bytes - Lower-bound resident workspace (codec state + streaming input buffer). Collected PCM is the output budget, not this.
- wrap_
adts_ au - Wrap one
raw_data_blockin an ADTS frame (no CRC, fullness0x7FF). For HE v1 access units pass the core rate (half the input rate): ADTS signals SBR implicitly and the header carries the core rate. - wrap_
loas_ au - Wrap one raw access unit in a LOAS
AudioSyncStreamframe whoseStreamMuxConfigcarriesasc(crate::Encoder::asc: 2-byte LC, 3-byte AAC-LD, or 4-byte HE). Every frame is a sync point. AnAudioMuxElementabove 8191 bytes is an error (LC and HE v1 frames never reach it). - write
- Encode to ADTS and write the file.
- write_
with - Encode under
optsand write the file.
Type Aliases§
- Result
- Result alias for this crate.