Skip to main content

proteus_lib/tools/
tools.rs

1//! Symphonia helpers for opening and decoding audio files.
2
3use symphonia::core::codecs::{Decoder, DecoderOptions, CODEC_TYPE_NULL};
4use symphonia::core::formats::{FormatOptions, FormatReader};
5use symphonia::core::io::MediaSourceStream;
6use symphonia::core::meta::MetadataOptions;
7use symphonia::core::probe::Hint;
8
9/// Open a file and return a decoder plus format reader.
10///
11/// This is a convenience wrapper around [`get_reader`] and [`get_decoder`].
12pub fn open_file(file_path: &str) -> (Box<dyn Decoder>, Box<dyn FormatReader>) {
13    let format = get_reader(file_path);
14    let decoder = get_decoder(&format);
15
16    (decoder, format)
17}
18
19/// Build a Symphonia `FormatReader` for the given file path.
20///
21/// `.prot` files are treated as `.mka` for probe hinting.
22pub fn get_reader(file_path: &str) -> Box<dyn FormatReader> {
23    // Open the media source.
24    let src = std::fs::File::open(file_path).expect("failed to open media");
25
26    // Create the media source stream.
27    let mss = MediaSourceStream::new(Box::new(src), Default::default());
28
29    // Create a probe hint using the file's extension. [Optional]
30    let mut hint = Hint::new();
31    let mut hint_extension = std::path::Path::new(file_path)
32        .extension()
33        .unwrap()
34        .to_str()
35        .unwrap();
36    // if hint_extension == "prot" replace with "mka"
37    if hint_extension == "prot" {
38        hint_extension = "mka";
39    }
40    hint.with_extension(hint_extension);
41
42    // Use the default options for metadata and format readers.
43    let meta_opts: MetadataOptions = Default::default();
44    let fmt_opts: FormatOptions = Default::default();
45
46    // Probe the media source.
47    let probed = symphonia::default::get_probe()
48        .format(&hint, mss, &fmt_opts, &meta_opts)
49        .expect("unsupported format");
50
51    // Get the instantiated format reader.
52    let format = probed.format;
53
54    // Find the first audio track with a known (decodeable) codec.
55    format
56        .tracks()
57        .iter()
58        .find(|t| t.codec_params.codec != CODEC_TYPE_NULL)
59        .expect("no supported audio tracks");
60
61    format
62}
63
64/// Build a decoder for the first audio track in a `FormatReader`.
65pub fn get_decoder(format: &Box<dyn FormatReader>) -> Box<dyn Decoder> {
66    // Use the default options for the decoder.
67    let dec_opts: DecoderOptions = Default::default();
68
69    // Create a decoder for the track.
70    let decoder = symphonia::default::get_codecs()
71        .make(&format.tracks()[0].codec_params, &dec_opts)
72        .expect("unsupported codec");
73
74    decoder
75}