use std::sync::atomic::{AtomicU64, Ordering::Relaxed};
use rusty_mp3::Mp3Decoder;
#[global_allocator]
static GLOBAL_ALLOC: rusty_alloc_api::RustyAlloc = rusty_alloc_api::RustyAlloc;
static FRAMES: AtomicU64 = AtomicU64::new(0);
static SAMPLES: AtomicU64 = AtomicU64::new(0);
fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
let Some(path) = args.first() else {
eprintln!("usage: decprof <input.mp3> [repeats]");
std::process::exit(2);
};
let repeats: usize = args.get(1).and_then(|s| s.parse().ok()).unwrap_or(1);
let pipelined = std::env::var("MP3_PIPELINE").as_deref() == Ok("1");
let nohash = std::env::var("MP3_NOHASH").as_deref() == Ok("1");
let bytes = std::fs::read(path).expect("read input");
println!("input: {path} ({} KiB)", bytes.len() / 1024);
let mut pcm_hash: u64 = 0;
let t0 = std::time::Instant::now();
for _ in 0..repeats {
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
let mut eat = |f: rusty_mp3::DecodedAudio| {
FRAMES.fetch_add(1, Relaxed);
SAMPLES.fetch_add(
(f.samples.len() / f.channels.max(1) as usize) as u64,
Relaxed,
);
if !nohash {
for s in &f.samples {
for b in s.to_bits().to_le_bytes() {
h ^= u64::from(b);
h = h.wrapping_mul(0x0000_0100_0000_01b3);
}
}
}
};
if pipelined {
for f in rusty_mp3::decode_pipelined(&bytes) {
eat(f);
}
} else {
let mut dec = Mp3Decoder::new();
for chunk in bytes.chunks(1024) {
dec.push(chunk);
while let Ok(f) = dec.next_frame() {
eat(f);
}
}
dec.flush();
while let Ok(f) = dec.next_frame() {
eat(f);
}
}
pcm_hash = h;
}
let wall = t0.elapsed().as_secs_f64();
let frames = FRAMES.load(Relaxed);
let samples = SAMPLES.load(Relaxed);
println!(
"decoded {frames} frames, {samples} samples/ch over {repeats} pass(es) in {wall:.3} s"
);
println!("WORK-COUNT frames={frames} samples_per_ch={samples}");
println!("PCM fnv1a: {pcm_hash:#018x} <- decode byte-identity gate");
println!(
"throughput: {:.1}x realtime (44.1 kHz assumed)\n",
samples as f64 / 44_100.0 / wall
);
rusty_mp3::decode::prof::dump();
rusty_mp3::decode::prof::census();
}