1use std::fs::File;
6use std::path::Path;
7
8use symphonia::core::audio::{ SampleBuffer, SignalSpec };
9use symphonia::core::codecs::{ Decoder as SymphoniaDecoder, DecoderOptions, CODEC_TYPE_NULL };
10use symphonia::core::formats::{ FormatOptions, FormatReader, SeekMode, SeekTo };
11use symphonia::core::io::{ MediaSourceStream, MediaSourceStreamOptions };
12use symphonia::core::meta::{ MetadataOptions, StandardTagKey };
13use symphonia::core::probe::ProbedMetadata;
14use symphonia::core::probe::Hint;
15use symphonia::core::units::Time;
16use thiserror::Error;
17
18
19#[derive( Debug, Clone, Default )]
21pub struct AudioMetadata {
22 pub title: Option<String>,
23 pub artist: Option<String>,
24 pub album: Option<String>,
25 pub album_artist: Option<String>,
26 pub track_number: Option<u32>,
27 pub genre: Option<String>,
28 pub year: Option<u32>,
29 pub codec: Option<String>,
30 pub bitrate: Option<u32>,
31 pub sample_rate: Option<u32>,
32 pub channels: Option<u32>,
33}
34
35
36#[derive( Debug, Error )]
38pub enum DecoderError {
39 #[error( "Failed to open file: {0}" )]
40 FileOpen( #[from] std::io::Error ),
41
42 #[error( "Unsupported format" )]
43 UnsupportedFormat,
44
45 #[error( "No audio tracks found" )]
46 NoAudioTrack,
47
48 #[error( "Decoder creation failed: {0}" )]
49 DecoderCreation( String ),
50
51 #[error( "Decode error: {0}" )]
52 Decode( String ),
53
54 #[error( "Seek error: {0}" )]
55 Seek( String ),
56}
57
58
59pub struct Decoder {
61 format_reader: Box<dyn FormatReader>,
62 decoder: Box<dyn SymphoniaDecoder>,
63 track_id: u32,
64 sample_rate: u32,
65 channels: usize,
66 sample_buf: Option<SampleBuffer<f32>>,
67 duration: Option<f64>,
68 probe_metadata: ProbedMetadata,
70}
71
72
73impl Decoder {
74 pub fn open( path: &Path ) -> Result<Self, DecoderError> {
78 let buffer_len = if path.starts_with( r"\\" ) {
80 256 * 1024 } else {
82 64 * 1024 };
84
85 let file = File::open( path )?;
86 let mss_opts = MediaSourceStreamOptions { buffer_len };
87 let mss = MediaSourceStream::new( Box::new( file ), mss_opts );
88
89 let mut hint = Hint::new();
91 if let Some( ext ) = path.extension().and_then( |e| e.to_str() ) {
92 hint.with_extension( ext );
93 }
94
95 let format_opts = FormatOptions::default();
96 let metadata_opts = MetadataOptions::default();
97
98 let probed = symphonia::default::get_probe()
100 .format( &hint, mss, &format_opts, &metadata_opts )
101 .map_err( |_| DecoderError::UnsupportedFormat )?;
102
103 let probe_metadata = probed.metadata;
105 let format_reader = probed.format;
106
107 let track = format_reader
109 .tracks()
110 .iter()
111 .find( |t| t.codec_params.codec != CODEC_TYPE_NULL )
112 .ok_or( DecoderError::NoAudioTrack )?;
113
114 let track_id = track.id;
115 let codec_params = &track.codec_params;
116
117 let sample_rate = codec_params.sample_rate.unwrap_or( 44100 );
118 let channels = codec_params.channels.map( |c| c.count() ).unwrap_or( 2 );
119
120 let duration = codec_params.n_frames.map( |frames| {
122 frames as f64 / sample_rate as f64
123 });
124
125 tracing::info!(
126 "Opened audio: {} Hz, {} channels, duration: {:?}s",
127 sample_rate,
128 channels,
129 duration
130 );
131
132 let decoder_opts = DecoderOptions::default();
134 let decoder = symphonia::default::get_codecs()
135 .make( codec_params, &decoder_opts )
136 .map_err( |e| DecoderError::DecoderCreation( e.to_string() ) )?;
137
138 Ok( Self {
139 format_reader,
140 decoder,
141 track_id,
142 sample_rate,
143 channels,
144 sample_buf: None,
145 duration,
146 probe_metadata,
147 })
148 }
149
150
151 pub fn sample_rate( &self ) -> u32 {
153 self.sample_rate
154 }
155
156
157 pub fn channels( &self ) -> usize {
159 self.channels
160 }
161
162
163 pub fn duration( &self ) -> Option<f64> {
165 self.duration
166 }
167
168
169 pub fn metadata( &mut self ) -> AudioMetadata {
171 let mut meta = AudioMetadata::default();
172
173 let extract_tags = |meta: &mut AudioMetadata, tags: &[symphonia::core::meta::Tag]| {
175 for tag in tags {
176 if let Some( std_key ) = tag.std_key {
177 let value = tag.value.to_string();
178 match std_key {
179 StandardTagKey::TrackTitle => {
180 if meta.title.is_none() {
181 meta.title = Some( value );
182 }
183 }
184 StandardTagKey::Artist => {
185 if meta.artist.is_none() {
186 meta.artist = Some( value );
187 }
188 }
189 StandardTagKey::Album => {
190 if meta.album.is_none() {
191 meta.album = Some( value );
192 }
193 }
194 StandardTagKey::AlbumArtist => {
195 if meta.album_artist.is_none() {
196 meta.album_artist = Some( value );
197 }
198 }
199 StandardTagKey::TrackNumber => {
200 if meta.track_number.is_none() {
201 meta.track_number = value.parse().ok();
202 }
203 }
204 StandardTagKey::Genre => {
205 if meta.genre.is_none() {
206 meta.genre = Some( value );
207 }
208 }
209 StandardTagKey::Date | StandardTagKey::ReleaseDate => {
210 if meta.year.is_none() {
211 if let Some( year_str ) = value.split( '-' ).next() {
213 meta.year = year_str.parse().ok();
214 }
215 }
216 }
217 _ => {}
218 }
219 }
220 }
221 };
222
223 if let Some( metadata_log ) = self.probe_metadata.get() {
225 if let Some( metadata_rev ) = metadata_log.current() {
226 extract_tags( &mut meta, metadata_rev.tags() );
227 }
228 }
229
230 if let Some( metadata_rev ) = self.format_reader.metadata().current() {
232 extract_tags( &mut meta, metadata_rev.tags() );
233 }
234
235 meta.sample_rate = Some( self.sample_rate );
237 meta.channels = Some( self.channels as u32 );
238
239 if let Some( track ) = self.format_reader.tracks().iter().find( |t| t.id == self.track_id ) {
241 let codec_type = track.codec_params.codec;
243 meta.codec = Some( format!( "{:?}", codec_type ).replace( "CODEC_TYPE_", "" ) );
244
245 if let Some( bit_rate ) = track.codec_params.bits_per_sample {
247 let bitrate = bit_rate * self.sample_rate * self.channels as u32 / 1000;
249 meta.bitrate = Some( bitrate );
250 }
251 }
252
253 meta
254 }
255
256
257 pub fn signal_spec( &self ) -> SignalSpec {
259 SignalSpec::new( self.sample_rate, symphonia::core::audio::Channels::FRONT_LEFT | symphonia::core::audio::Channels::FRONT_RIGHT )
260 }
261
262
263 pub fn decode_next( &mut self ) -> Result<Option<Vec<f32>>, DecoderError> {
267 loop {
268 let packet = match self.format_reader.next_packet() {
270 Ok( packet ) => packet,
271 Err( symphonia::core::errors::Error::IoError( ref e ) )
272 if e.kind() == std::io::ErrorKind::UnexpectedEof =>
273 {
274 return Ok( None ); }
276 Err( e ) => {
277 return Err( DecoderError::Decode( e.to_string() ) );
278 }
279 };
280
281 if packet.track_id() != self.track_id {
283 continue;
284 }
285
286 let decoded = match self.decoder.decode( &packet ) {
288 Ok( decoded ) => decoded,
289 Err( symphonia::core::errors::Error::DecodeError( _ ) ) => {
290 continue;
292 }
293 Err( e ) => {
294 return Err( DecoderError::Decode( e.to_string() ) );
295 }
296 };
297
298 let spec = *decoded.spec();
300 let num_frames = decoded.frames();
301
302 if self.sample_buf.is_none() || self.sample_buf.as_ref().unwrap().capacity() < num_frames {
304 self.sample_buf = Some( SampleBuffer::new( num_frames as u64, spec ) );
305 }
306
307 let sample_buf = self.sample_buf.as_mut().unwrap();
308 sample_buf.copy_interleaved_ref( decoded );
309
310 return Ok( Some( sample_buf.samples().to_vec() ) );
311 }
312 }
313
314
315 pub fn seek( &mut self, position_secs: f64 ) -> Result<(), DecoderError> {
317 let seek_to = SeekTo::Time {
318 time: Time::from( position_secs ),
319 track_id: Some( self.track_id ),
320 };
321
322 self.format_reader
323 .seek( SeekMode::Accurate, seek_to )
324 .map_err( |e| DecoderError::Seek( e.to_string() ) )?;
325
326 self.decoder.reset();
328
329 Ok(())
330 }
331}