//! Bounded offline evaluation: PCM16/24/32 and IEEE float32 RIFF/WAVE input. use patina_core::{Engine, Monitor, Settings}; use std::{ env, fs, io::{Read, Write}, path::Path, time::Instant, }; fn main() { if let Err(e) = run() { eprintln!("{e}"); std::process::exit(1); } } fn run() -> Result<(), Box> { let args: Vec<_> = env::args().skip(1).collect(); if args.is_empty() || args[0] == "--help" { println!("patina-render [settings.json] [--delta]\npatina-render --demo \npatina-render --benchmark\nOutput is float32 WAV, latency-trimmed with original length. Existing files are refused."); return Ok(()); } if args[0] == "--benchmark" { let mut e = Engine::new(48000., 2)?; e.set_settings(Settings::preset(1))?; let frames = 48000 * 5; let start = Instant::now(); for i in 0..frames { let x = (i as f32 * 0.173).sin() * 0.3; std::hint::black_box(e.process_sample([x, -x], Monitor::default())); } println!("Stereo 48 kHz / FFT 2048 / hop 256 / 5 seconds: {:.3}s ({:.1}% of one core, run --release for meaningful results)",start.elapsed().as_secs_f64(),start.elapsed().as_secs_f64()/5.*100.); return Ok(()); } let (rate, channels, input, output) = if args[0] == "--demo" && args.len() == 2 { let samples = (0..48000 * 3) .map(|i| { let t = i as f32 / 48000.; let envelope = if t % 0.5 < 0.3 { 1. } else { 0.2 }; [((t * 440. * std::f32::consts::TAU).sin() * 0.14 + (t * 3000. * std::f32::consts::TAU).sin() * 0.28) * envelope; 2] }) .collect(); (48000., 2, samples, args[1].clone()) } else if args.len() >= 2 { let (r, c, x) = read_wave(Path::new(&args[0]))?; (r, c, x, args[1].clone()) } else { return Err("Use --help for usage".into()); }; let settings = if args.get(2).is_some_and(|s| !s.starts_with("--")) { let bytes = read_limit(Path::new(&args[2]), 64 * 1024)?; serde_json::from_slice::(&bytes)? } else { Settings::default() }; settings.validate()?; let mut engine = Engine::new(rate, channels)?; engine.set_settings(settings)?; let latency = engine.latency_samples() as usize; let start = Instant::now(); let mut rendered = Vec::with_capacity(input.len()); let monitor = Monitor { delta: args.iter().any(|a| a == "--delta"), }; for i in 0..input.len() + latency { let sample = input.get(i).copied().unwrap_or([0.; 2]); let out = engine.process_sample(sample, monitor); if i >= latency { rendered.push(out); } } write_wave(Path::new(&output), rate as u32, channels, &rendered)?; println!( "Rendered {} frames, {} channel(s), {} Hz in {:.3}s; compensated {} samples → {}", rendered.len(), channels, rate, start.elapsed().as_secs_f64(), latency, output ); Ok(()) } fn read_limit(path: &Path, limit: usize) -> Result, Box> { let mut bytes = Vec::new(); fs::File::open(path)? .take(limit as u64 + 1) .read_to_end(&mut bytes)?; if bytes.len() > limit { return Err("Input exceeds size limit".into()); } Ok(bytes) } type Wave = (f32, usize, Vec<[f32; 2]>); fn read_wave(path: &Path) -> Result> { let b = read_limit(path, 256 * 1024 * 1024)?; if b.len() < 12 || &b[..4] != b"RIFF" || &b[8..12] != b"WAVE" { return Err("Expected RIFF/WAVE".into()); } let u16_at = |p| u16::from_le_bytes([b[p], b[p + 1]]); let u32_at = |p| u32::from_le_bytes([b[p], b[p + 1], b[p + 2], b[p + 3]]); let end = (u32_at(4) as usize) .checked_add(8) .ok_or("Invalid RIFF length")?; if end > b.len() { return Err("Truncated RIFF container".into()); } let mut pos = 12; let mut format = None; let mut data = None; while pos + 8 <= end { let len = u32_at(pos + 4) as usize; let start = pos + 8; let stop = start.checked_add(len).ok_or("Invalid chunk length")?; if stop > end { return Err("Truncated WAV chunk".into()); } match &b[pos..pos + 4] { b"fmt " if len >= 16 => { format = Some(( u16_at(start), u16_at(start + 2) as usize, u32_at(start + 4), u16_at(start + 12) as usize, u16_at(start + 14) as usize, )) } b"data" => data = Some((start, stop)), _ => {} } pos = stop + (len % 2); } let (tag, channels, rate, align, bits) = format.ok_or("Missing WAV format")?; let (start, stop) = data.ok_or("Missing WAV data")?; if !(1..=2).contains(&channels) || !matches!((tag, bits), (1, 16) | (1, 24) | (1, 32) | (3, 32)) { return Err("Use mono/stereo PCM16/24/32 or float32 WAV".into()); } let size = bits / 8; if align != channels * size || (stop - start) % align != 0 { return Err("Invalid WAV block alignment".into()); } let mut samples = Vec::with_capacity((stop - start) / align); for p in (start..stop).step_by(align) { let mut frame = [0.; 2]; for (c, value) in frame.iter_mut().enumerate().take(channels) { let q = p + c * size; *value = match (tag, bits) { (3, 32) => f32::from_le_bytes(b[q..q + 4].try_into()?), (1, 16) => i16::from_le_bytes(b[q..q + 2].try_into()?) as f32 / 32768., (1, 24) => { ((b[q] as i32 | ((b[q + 1] as i32) << 8) | ((b[q + 2] as i32) << 16)) << 8 >> 8) as f32 / 8388608. } _ => i32::from_le_bytes(b[q..q + 4].try_into()?) as f32 / 2147483648., }; if !value.is_finite() { return Err("Non-finite WAV samples".into()); } } samples.push(frame); } Ok((rate as f32, channels, samples)) } fn write_wave( path: &Path, rate: u32, channels: usize, samples: &[[f32; 2]], ) -> Result<(), Box> { let bytes = (samples.len() * channels * 4) as u32; let mut f = fs::OpenOptions::new() .write(true) .create_new(true) .open(path)?; f.write_all(b"RIFF")?; f.write_all(&(36 + bytes).to_le_bytes())?; f.write_all(b"WAVEfmt ")?; f.write_all(&16u32.to_le_bytes())?; f.write_all(&3u16.to_le_bytes())?; f.write_all(&(channels as u16).to_le_bytes())?; f.write_all(&rate.to_le_bytes())?; f.write_all(&(rate * channels as u32 * 4).to_le_bytes())?; f.write_all(&(channels as u16 * 4).to_le_bytes())?; f.write_all(&32u16.to_le_bytes())?; f.write_all(b"data")?; f.write_all(&bytes.to_le_bytes())?; for frame in samples { for sample in frame.iter().take(channels) { f.write_all(&sample.to_le_bytes())?; } } Ok(()) }