Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Audio data model

Linear: AUT-99

Every audio buffer in the recorder flows through one type: AudioChunk — a timestamped slice of normalized f32 samples with its AudioFormat. The GStreamer capture path (M-MEDIA.5), the deterministic mock sources (M-MEDIA.4), the histogram quantizer (M-MEDIA.8), and the live microphone path (M-MEDIA.15) all produce / consume this type.

api

Why normalized f32

  • It's what every downstream visualization wants — AudioHistogram's RMS / peak math runs cleaner on floats than on integers.
  • It's what GStreamer's audioconvert ! audio/x-raw,format=F32LE produces natively — capture pipelines don't have to re-quantize.
  • Future device-capture backends (cpal, coreaudio-rs) emit f32 as their preferred shape too — no buffer layout churn at the seam.

The SampleFormat enum exists so capture-side code can declare its input layout (F32 / I16 / U8) before normalization. Internally, AudioChunk::samples is always &[f32].

Interleave order — planar-per-frame

Stereo: [L₀, R₀, L₁, R₁, …]. Mono: [s₀, s₁, …]. Matches GStreamer raw-audio, cpal, coreaudio-rs. No re-layout needed at the capture seam.

Validation

[AudioChunk::new] rejects:

  • samples.len() % channels != 0 — each frame must carry exactly one sample per channel.
  • channels == 0.
  • sample_rate == 0.

These are the three "is this a meaningful chunk?" checks. The remaining shape questions (clipping, NaN, DC offset) are visualization concerns, not data-model concerns.

Derived metrics

AudioChunk::peak() and AudioChunk::rms() are pre-computed shortcuts used by M-MEDIA.8 (histogram quantization) and capture-side regression checks. They run in O(n) over the buffer; cache the result if you need it more than once per chunk.

Quick start

#![allow(unused)]
fn main() {
use media::audio::{AudioChunk, AudioFormat};
use media::clock::MediaTime;

let fmt = AudioFormat::mono_f32(48_000);
let samples = vec![0.0_f32; 48_000]; // 1.0 s of silence at 48 kHz.
let chunk = AudioChunk::new(fmt, samples, MediaTime::ZERO).expect("valid");
assert_eq!(chunk.frame_count(), 48_000);
assert!((chunk.duration().as_seconds() - 1.0).abs() < 1e-9);
}