object-based meowdio · 9.1.4 · surround sound for nine lives
ADM-BWF spatial audio parsing, playback, and offline rendering that just happens to be covered in cat hair. No HRTFs were harmed. The cat is fine.
Rust crate for ADM-BWF spatial audio parsing, playback, and offline rendering. It reads a Dolby Atmos ADM-BWF master, interprets the 3D object positions and motion, and spatializes everything down to stereo meowdio -- either in real time with a TUI visualizer, or offline to a WAV.
- ADM-BWF metadata extraction (Audio Definition Model)
- Dolby Atmos metadata parsing
- Object motion timeline interpolation (every object lands on its feet)
- Steam Audio spatialization (panning and HRTF)
- Offline rendering to stereo WAV
- Real-time playback with TUI visualization
- EBU R128 loudness metering (now measured in purr-LUFS)
The binary is named catmos, so building drops a catmos you can run directly:
cargo build --release # -> target/release/catmos
cargo run -- input.wav output.wav # build + run the catmos binarycargo run -- input.wav output.wav
# or, after `cargo build --release`:
catmos input.wav output.wavcatmos [OPTIONS] <INPUT> <OUTPUT>
Arguments:
<INPUT> Input ADM-BWF file
<OUTPUT> Output stereo WAV file
Options:
--buffer-size <N> Buffer size [default: 1024]
--ear-level <BOOL> Z=0 is ear level [default: true]
--no-hrtf <BOOL> Use panning only [default: true]
--spatial-blend <F> 0.0=dry, 1.0=full [default: 1.0]
--normalize Enable RMS normalization
--sofa <PATH> Custom HRTF SOFA file
--bit-depth <N> Output: 16, 24, or 32 [default: 32]
# Render an Atmos mix to stereo meowdio with ear-level panning
cargo run --release -- \
"Dolby Atmos Mix.wav" \
"stereo_output.wav" \
--ear-level true \
--no-hrtf true \
--bit-depth 24use catmos::{Renderer, RenderConfig};
let config = RenderConfig {
ear_level: true,
no_hrtf: true,
bit_depth: 24,
..Default::default()
};
let renderer = Renderer::new(config);
let stats = renderer.render("input.wav", "output.wav")?;
println!("{}", stats);use catmos::{Player, PlayerConfig};
let config = PlayerConfig::default(); // --no-hrtf --ear-level --visualize
let player = Player::new(config);
player.play("input.wav")?;use catmos::{SpatialEngine, SpatialConfig, BwfReader};
let reader = BwfReader::open("input.wav")?;
let adm_scene = reader.adm_scene.as_ref().unwrap();
let config = SpatialConfig {
buffer_size: 1024,
sample_rate: reader.sample_rate,
ear_level: true,
no_hrtf: true,
..Default::default()
};
let mut engine = SpatialEngine::new(config, adm_scene, HashMap::new())?;
// Process a channel
let stereo = engine.process_channel(0, &samples, time_seconds)?;use catmos::{BwfReader, MotionTimeline};
let reader = BwfReader::open("input.wav")?;
let scene = reader.adm_scene.as_ref().unwrap();
for channel in &scene.channels {
println!("{}: {:?}", channel.channel_index, channel.position);
}
let timeline = MotionTimeline::from_scene(scene);
let pos = timeline.interpolate_position(channel_idx, time_seconds);| Module | Purpose |
|---|---|
bwf |
BWF/ADM/Dolby metadata parsing |
metadata |
Type definitions |
spatial |
Shared spatialization engine |
player |
Real-time playback with cpal |
renderer |
Offline rendering to WAV |
audio_meter |
EBU R128 metering |
viz |
TUI visualization |
ear-level mode (default):
- Z=0 → horizontal (ear level)
- Z=1 → ceiling (+90° elevation, a.k.a. "on top of the fridge")
standard mode:
- Z=0 → floor
- Z=0.5 → ear level
- Z=1 → ceiling
audionimbus- Steam Audio bindingshound- WAV I/Ocpal- Audio playbackratatui- TUI frameworkebur128- Loudness metering
MIT OR Apache-2.0.