khora_io/asset/decoders/audio/
symphonia.rs1use anyhow::{anyhow, Result};
18use khora_data::assets::SoundData;
19use std::{error::Error, io::Cursor};
20use symphonia::core::{
21 audio::SampleBuffer, codecs::DecoderOptions, formats::FormatOptions, io::MediaSourceStream,
22 meta::MetadataOptions, probe::Hint,
23};
24
25use crate::asset::AssetDecoder;
26
27#[derive(Default)]
29pub struct SymphoniaDecoder;
30
31impl SymphoniaDecoder {
32 pub fn new() -> Self {
34 Self
35 }
36}
37
38impl AssetDecoder<SoundData> for SymphoniaDecoder {
39 fn load(&self, bytes: &[u8]) -> Result<SoundData, Box<dyn Error + Send + Sync>> {
40 let mss = MediaSourceStream::new(Box::new(Cursor::new(bytes.to_vec())), Default::default());
41
42 let hint = Hint::new();
43 let meta_opts: MetadataOptions = Default::default();
44 let fmt_opts: FormatOptions = Default::default();
45 let probed = symphonia::default::get_probe().format(&hint, mss, &fmt_opts, &meta_opts)?;
46 let mut format_reader = probed.format;
47
48 let track = format_reader
49 .default_track()
50 .ok_or_else(|| anyhow!("No default audio track found"))?;
51
52 let track_id = track.id;
53 let sample_rate = track
54 .codec_params
55 .sample_rate
56 .ok_or_else(|| anyhow!("Unknown sample rate"))?;
57 let channels = track
58 .codec_params
59 .channels
60 .ok_or_else(|| anyhow!("Unknown channel count"))?;
61
62 let dec_opts: DecoderOptions = Default::default();
63 let mut decoder = symphonia::default::get_codecs().make(&track.codec_params, &dec_opts)?;
64
65 let mut all_samples = Vec::<f32>::new();
66 let mut decode_errors: u64 = 0;
70
71 loop {
72 match format_reader.next_packet() {
73 Ok(packet) => {
74 if packet.track_id() != track_id {
75 continue;
76 }
77
78 match decoder.decode(&packet) {
79 Ok(decoded) => {
80 let mut sample_buf = SampleBuffer::<f32>::new(
81 decoded.capacity() as u64,
82 *decoded.spec(),
83 );
84 sample_buf.copy_interleaved_ref(decoded);
85 all_samples.extend_from_slice(sample_buf.samples());
86 }
87 Err(e) => {
88 if decode_errors == 0 {
89 log::warn!("Audio packet decode error: {e}");
90 } else {
91 log::debug!("Audio packet decode error: {e}");
92 }
93 decode_errors += 1;
94 }
95 }
96 }
97 Err(symphonia::core::errors::Error::IoError(_)) => {
98 break;
99 }
100 Err(e) => {
101 return Err(Box::new(e));
102 }
103 }
104 }
105
106 if decode_errors > 1 {
107 log::warn!("Audio decoding skipped {decode_errors} corrupt packets in total");
108 }
109
110 Ok(SoundData {
111 samples: all_samples,
112 channels: channels.count() as u16,
113 sample_rate,
114 })
115 }
116}