main
rs 103 lines 3.73 KB
Raw
1 //! Streaming decode to interleaved `f32` with symphonia 0.6.
2
3 use std::path::Path;
4
5 use anyhow::{Context, Result, anyhow, bail};
6 use symphonia::core::codecs::audio::AudioDecoderOptions;
7 use symphonia::core::errors::Error;
8 use symphonia::core::formats::probe::Hint;
9 use symphonia::core::formats::{FormatOptions, TrackType};
10 use symphonia::core::io::MediaSourceStream;
11 use symphonia::core::meta::MetadataOptions;
12
13 #[derive(Debug, Clone, Default)]
14 pub struct StreamInfo {
15 pub extension: String,
16 pub codec: String,
17 pub sample_rate: u32,
18 pub channels: usize,
19 pub bits_per_sample: Option<u32>,
20 pub frames: u64,
21 }
22
23 impl StreamInfo {
24 pub fn duration_secs(&self) -> f64 {
25 self.frames as f64 / self.sample_rate.max(1) as f64
26 }
27 }
28
29 /// Decode `path`, handing every decoded packet to `sink` as interleaved samples.
30 /// `sink` receives `(info_so_far, interleaved_samples)`; the rate and channel count are
31 /// fixed from the first packet onward.
32 pub fn decode_file(path: &Path, mut sink: impl FnMut(&StreamInfo, &[f32])) -> Result<StreamInfo> {
33 let file = std::fs::File::open(path).with_context(|| format!("opening {}", path.display()))?;
34 let mss = MediaSourceStream::new(Box::new(file), Default::default());
35 let mut hint = Hint::new();
36 let extension = path
37 .extension()
38 .and_then(|e| e.to_str())
39 .unwrap_or("")
40 .to_lowercase();
41 if !extension.is_empty() {
42 hint.with_extension(&extension);
43 }
44 let mut format = symphonia::default::get_probe()
45 .probe(
46 &hint,
47 mss,
48 FormatOptions::default(),
49 MetadataOptions::default(),
50 )
51 .map_err(|e| anyhow!("unsupported or unreadable audio file ({e})"))?;
52 let track = format
53 .default_track(TrackType::Audio)
54 .ok_or_else(|| anyhow!("no audio track in {}", path.display()))?;
55 let track_id = track.id;
56 let params = track
57 .codec_params
58 .as_ref()
59 .and_then(|p| p.audio())
60 .ok_or_else(|| anyhow!("no decodable audio track in {}", path.display()))?;
61 let mut decoder = symphonia::default::get_codecs()
62 .make_audio_decoder(params, &AudioDecoderOptions::default())
63 .map_err(|e| anyhow!("no decoder for this audio codec ({e})"))?;
64
65 let mut info = StreamInfo {
66 extension,
67 codec: decoder.codec_info().long_name.to_string(),
68 sample_rate: params.sample_rate.unwrap_or(0),
69 channels: params.channels.as_ref().map_or(0, |c| c.count()),
70 bits_per_sample: params.bits_per_sample.or(params.bits_per_coded_sample),
71 frames: 0,
72 };
73 let mut buf: Vec<f32> = Vec::new();
74 loop {
75 let packet = match format.next_packet() {
76 Ok(Some(p)) => p,
77 Ok(None) => break,
78 Err(Error::ResetRequired) => break,
79 Err(e) => return Err(anyhow!("reading audio failed: {e}")),
80 };
81 if packet.track_id != track_id {
82 continue;
83 }
84 match decoder.decode(&packet) {
85 Ok(audio) => {
86 let spec = audio.spec();
87 info.sample_rate = spec.rate();
88 info.channels = spec.channels().count();
89 buf.resize(audio.samples_interleaved(), 0.0);
90 audio.copy_to_slice_interleaved(&mut buf);
91 info.frames += audio.frames() as u64;
92 sink(&info, &buf);
93 }
94 // A corrupt packet is skipped; the rest of the file is still analysable.
95 Err(Error::DecodeError(_)) => {}
96 Err(e) => return Err(anyhow!("decoding audio failed: {e}")),
97 }
98 }
99 if info.frames == 0 || info.sample_rate == 0 || info.channels == 0 {
100 bail!("no audio could be decoded from {}", path.display());
101 }
102 Ok(info)
103 }