1#[path = "common/wav.rs"]
16mod wav;
17
18use opus_pure::{MAX_PACKET_SAMPLES, OggOpusReader, Trim};
19
20fn main() -> Result<(), Box<dyn std::error::Error>> {
21 let args: Vec<String> = std::env::args().collect();
22 if args.len() < 3 {
23 eprintln!("usage: {} <input.opus> <output.wav> [output_rate]", args[0]);
24 std::process::exit(2);
25 }
26
27 let file = std::fs::File::open(&args[1])?;
28 let mut reader = OggOpusReader::new(std::io::BufReader::new(file))?;
29 let head = reader.head().clone();
30 let channels = head.channel_count as usize;
31
32 let requested: i32 = args
35 .get(3)
36 .map(|s| s.parse())
37 .transpose()?
38 .unwrap_or(head.input_sample_rate as i32);
39 let rate = match requested {
40 8_000 | 12_000 | 16_000 | 24_000 | 48_000 => requested,
41 other => {
42 eprintln!("note: {other} Hz is not an Opus decode rate; using 48000 Hz");
43 48_000
44 }
45 };
46
47 println!(
48 "{}: {channels} ch, pre-skip {} samples, gain {:+.2} dB, vendor {:?}",
49 args[1],
50 head.pre_skip,
51 head.output_gain_db(),
52 reader.tags().vendor,
53 );
54 for comment in &reader.tags().comments {
55 println!(" {comment}");
56 }
57
58 let mut decoder = head.decoder(rate)?;
62 let mut trim = Trim::new(&head, rate, channels)?;
63
64 let mut block = vec![0.0f32; MAX_PACKET_SAMPLES * channels];
67 let mut samples = Vec::new();
68 let mut packets = 0usize;
69
70 for packet in reader.packets() {
71 let packet = packet?;
72 let n = decoder.decode(&packet.data, MAX_PACKET_SAMPLES, &mut block)?;
73 samples.extend_from_slice(trim.keep(&packet, &block[..n * channels]));
74 packets += 1;
75 }
76
77 let per_channel = trim.samples_emitted();
78 wav::write(
79 &args[2],
80 &wav::Wav {
81 sample_rate: rate as u32,
82 channels: head.channel_count as u16,
83 samples,
84 },
85 )?;
86 println!(
87 " {packets} packets -> {} ({rate} Hz, {per_channel} samples/ch = {:.2} s of audio)",
88 args[2],
89 per_channel as f64 / rate as f64,
90 );
91 Ok(())
92}