#[path = "common/wav.rs"]
mod wav;
use opus_pure::{MAX_PACKET_SAMPLES, OggOpusReader, Trim};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = std::env::args().collect();
if args.len() < 3 {
eprintln!("usage: {} <input.opus> <output.wav> [output_rate]", args[0]);
std::process::exit(2);
}
let file = std::fs::File::open(&args[1])?;
let mut reader = OggOpusReader::new(std::io::BufReader::new(file))?;
let head = reader.head().clone();
let channels = head.channel_count as usize;
let requested: i32 = args
.get(3)
.map(|s| s.parse())
.transpose()?
.unwrap_or(head.input_sample_rate as i32);
let rate = match requested {
8_000 | 12_000 | 16_000 | 24_000 | 48_000 => requested,
other => {
eprintln!("note: {other} Hz is not an Opus decode rate; using 48000 Hz");
48_000
}
};
println!(
"{}: {channels} ch, pre-skip {} samples, gain {:+.2} dB, vendor {:?}",
args[1],
head.pre_skip,
head.output_gain_db(),
reader.tags().vendor,
);
for comment in &reader.tags().comments {
println!(" {comment}");
}
let mut decoder = head.decoder(rate)?;
let mut trim = Trim::new(&head, rate, channels)?;
let mut block = vec![0.0f32; MAX_PACKET_SAMPLES * channels];
let mut samples = Vec::new();
let mut packets = 0usize;
for packet in reader.packets() {
let packet = packet?;
let n = decoder.decode(&packet.data, MAX_PACKET_SAMPLES, &mut block)?;
samples.extend_from_slice(trim.keep(&packet, &block[..n * channels]));
packets += 1;
}
let per_channel = trim.samples_emitted();
wav::write(
&args[2],
&wav::Wav {
sample_rate: rate as u32,
channels: head.channel_count as u16,
samples,
},
)?;
println!(
" {packets} packets -> {} ({rate} Hz, {per_channel} samples/ch = {:.2} s of audio)",
args[2],
per_channel as f64 / rate as f64,
);
Ok(())
}