use std::fs::File; use std::path::Path; use symphonia::core::audio::SampleBuffer; use symphonia::core::codecs::DecoderOptions; use symphonia::core::formats::FormatOptions; use symphonia::core::io::MediaSourceStream; use symphonia::core::meta::MetadataOptions; use symphonia::core::probe::Hint; use kon_core::error::{KonError, Result}; use kon_core::types::AudioSamples; /// Decode an audio file to mono f32 PCM samples. /// Supports all formats symphonia handles: mp3, aac, flac, wav, ogg, etc. pub fn decode_audio_file(path: &Path) -> Result { let file = File::open(path) .map_err(|e| KonError::AudioDecodeFailed(format!("Cannot open file: {e}")))?; let mss = MediaSourceStream::new(Box::new(file), Default::default()); let mut hint = Hint::new(); if let Some(ext) = path.extension().and_then(|e| e.to_str()) { hint.with_extension(ext); } let probed = symphonia::default::get_probe() .format(&hint, mss, &FormatOptions::default(), &MetadataOptions::default()) .map_err(|e| KonError::AudioDecodeFailed(format!("Unsupported format: {e}")))?; let mut format = probed.format; let track = format .default_track() .ok_or_else(|| KonError::AudioDecodeFailed("No audio track found".into()))?; let sample_rate = track .codec_params .sample_rate .ok_or_else(|| KonError::AudioDecodeFailed("Unknown sample rate".into()))?; if sample_rate == 0 { return Err(KonError::AudioDecodeFailed("Invalid sample rate: 0".into())); } let track_id = track.id; let mut decoder = symphonia::default::get_codecs() .make(&track.codec_params, &DecoderOptions::default()) .map_err(|e| KonError::AudioDecodeFailed(format!("Codec error: {e}")))?; let mut samples: Vec = Vec::new(); let mut decode_errors = 0u32; loop { let packet = match format.next_packet() { Ok(p) => p, Err(symphonia::core::errors::Error::IoError(ref e)) if e.kind() == std::io::ErrorKind::UnexpectedEof => { break; } Err(symphonia::core::errors::Error::ResetRequired) => break, Err(_) => break, }; if packet.track_id() != track_id { continue; } let decoded = match decoder.decode(&packet) { Ok(d) => d, Err(_) => { decode_errors += 1; continue; } }; let spec = *decoded.spec(); let channels = spec.channels.count(); let mut sample_buf = SampleBuffer::::new(decoded.capacity() as u64, spec); sample_buf.copy_interleaved_ref(decoded); let buf = sample_buf.samples(); if channels == 1 { samples.extend_from_slice(buf); } else { for chunk in buf.chunks(channels) { let sum: f32 = chunk.iter().sum(); samples.push(sum / channels as f32); } } } if samples.is_empty() { if decode_errors > 0 { return Err(KonError::AudioDecodeFailed(format!( "No audio decoded ({decode_errors} packets failed — file may be corrupt)" ))); } return Err(KonError::AudioDecodeFailed("No audio data decoded".into())); } Ok(AudioSamples::new(samples, sample_rate, 1)) }