From 188ce74eb5cd41dbb850f23c982e04b856824605 Mon Sep 17 00:00:00 2001 From: Eagisa Date: Mon, 25 May 2026 07:48:42 +0530 Subject: [PATCH 1/3] Upgrade symphonia from 0.5.4 to 0.6.0 --- crates/kira/Cargo.toml | 4 +- .../src/sound/static_sound/data/from_file.rs | 39 ++-- .../src/sound/streaming/decoder/symphonia.rs | 178 ++++++++++-------- crates/kira/src/sound/symphonia.rs | 38 ++-- 4 files changed, 146 insertions(+), 113 deletions(-) diff --git a/crates/kira/Cargo.toml b/crates/kira/Cargo.toml index 99bc51a1..3f8f660f 100644 --- a/crates/kira/Cargo.toml +++ b/crates/kira/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kira" -version = "0.12.0" +version = "0.12.1" authors = ["Andrew Minnich "] edition.workspace = true license = "MIT OR Apache-2.0" @@ -18,7 +18,7 @@ mint = "0.5.9" pastey = "0.2.1" rtrb = "0.3.2" serde = { version = "1.0.219", features = ["derive"], optional = true } -symphonia = { version = "0.5.4", optional = true, default-features = false } +symphonia = { version = "0.6.0", optional = true, default-features = false } triple_buffer = "8.1.1" [target.'cfg(not(target_arch = "wasm32"))'.dependencies.cpal] diff --git a/crates/kira/src/sound/static_sound/data/from_file.rs b/crates/kira/src/sound/static_sound/data/from_file.rs index cf232072..75ca9b6e 100644 --- a/crates/kira/src/sound/static_sound/data/from_file.rs +++ b/crates/kira/src/sound/static_sound/data/from_file.rs @@ -1,6 +1,7 @@ use std::io::Cursor; use symphonia::core::io::{MediaSource, MediaSourceStream}; +use symphonia::core::{codecs::CodecParameters, formats::TrackType}; use crate::sound::{ FromFileError, static_sound::StaticSoundSettings, symphonia::load_frames_from_buffer_ref, @@ -37,32 +38,41 @@ impl StaticSoundData { let codecs = symphonia::default::get_codecs(); let probe = symphonia::default::get_probe(); let mss = MediaSourceStream::new(media_source, Default::default()); - let mut format_reader = probe - .format( - &Default::default(), - mss, - &Default::default(), - &Default::default(), - )? - .format; + let mut format_reader = probe.probe( + &Default::default(), + mss, + Default::default(), + Default::default(), + )?; + let default_track = format_reader - .default_track() + .default_track(TrackType::Audio) .ok_or(FromFileError::NoDefaultTrack)?; + let default_track_id = default_track.id; - let codec_params = &default_track.codec_params; - let sample_rate = codec_params + + let audio_params = match default_track.codec_params.as_ref() { + Some(CodecParameters::Audio(p)) => p, + _ => return Err(FromFileError::NoDefaultTrack), + }; + + let sample_rate = audio_params .sample_rate .ok_or(FromFileError::UnknownSampleRate)?; - let mut decoder = codecs.make(codec_params, &Default::default())?; + + let mut decoder = codecs.make_audio_decoder(audio_params, &Default::default())?; + let mut frames = vec![]; + loop { match format_reader.next_packet() { - Ok(packet) => { - if default_track_id == packet.track_id() { + Ok(Some(packet)) => { + if default_track_id == packet.track_id { let buffer = decoder.decode(&packet)?; frames.append(&mut load_frames_from_buffer_ref(&buffer)?); } } + Ok(None) => break, Err(error) => match error { symphonia::core::errors::Error::IoError(error) => { if error.kind() == std::io::ErrorKind::UnexpectedEof { @@ -74,6 +84,7 @@ impl StaticSoundData { }, } } + Ok(Self { sample_rate, frames: frames.into(), diff --git a/crates/kira/src/sound/streaming/decoder/symphonia.rs b/crates/kira/src/sound/streaming/decoder/symphonia.rs index 129539d7..5aaad6fe 100644 --- a/crates/kira/src/sound/streaming/decoder/symphonia.rs +++ b/crates/kira/src/sound/streaming/decoder/symphonia.rs @@ -1,95 +1,113 @@ use std::convert::TryInto; use crate::{ - frame::Frame, - sound::{FromFileError, symphonia::load_frames_from_buffer_ref}, + frame::Frame, + sound::{symphonia::load_frames_from_buffer_ref, FromFileError}, }; use symphonia::core::{ - codecs::Decoder, - formats::{FormatReader, SeekMode, SeekTo}, - io::{MediaSource, MediaSourceStream}, - probe::Hint, + codecs::{audio::AudioDecoder, CodecParameters}, + formats::{probe::Hint, FormatReader, SeekMode, SeekTo, TrackType}, + io::{MediaSource, MediaSourceStream}, + units::Timestamp, }; pub(crate) struct SymphoniaDecoder { - format_reader: Box, - decoder: Box, - sample_rate: u32, - num_frames: usize, - track_id: u32, + format_reader: Box, + decoder: Box, + sample_rate: u32, + num_frames: usize, + track_id: u32, } impl SymphoniaDecoder { - pub(crate) fn new(media_source: Box) -> Result { - let codecs = symphonia::default::get_codecs(); - let probe = symphonia::default::get_probe(); - let mss = MediaSourceStream::new(media_source, Default::default()); - let format_reader = probe - .format( - &Hint::default(), - mss, - &Default::default(), - &Default::default(), - )? - .format; - let default_track = format_reader - .default_track() - .ok_or(FromFileError::NoDefaultTrack)?; - let sample_rate = default_track - .codec_params - .sample_rate - .ok_or(FromFileError::UnknownSampleRate)?; - let num_frames = default_track - .codec_params - .n_frames - .ok_or(FromFileError::UnknownSampleRate)? - .try_into() - .expect("could not convert u64 into usize"); - let decoder = codecs.make(&default_track.codec_params, &Default::default())?; - let track_id = default_track.id; - Ok(Self { - format_reader, - decoder, - sample_rate, - num_frames, - track_id, - }) - } + pub(crate) fn new(media_source: Box) -> Result { + let codecs = symphonia::default::get_codecs(); + let probe = symphonia::default::get_probe(); + let mss = MediaSourceStream::new(media_source, Default::default()); + + let format_reader = probe.probe( + &Hint::default(), + mss, + Default::default(), + Default::default(), + )?; + + let default_track = format_reader + .default_track(TrackType::Audio) + .ok_or(FromFileError::NoDefaultTrack)?; + + let audio_params = match default_track.codec_params.as_ref() { + Some(CodecParameters::Audio(p)) => p, + _ => return Err(FromFileError::NoDefaultTrack), + }; + + let sample_rate = audio_params + .sample_rate + .ok_or(FromFileError::UnknownSampleRate)?; + + let num_frames = default_track + .num_frames + .ok_or(FromFileError::UnknownSampleRate)? + .try_into() + .expect("could not convert u64 into usize"); + + let decoder = codecs.make_audio_decoder(audio_params, &Default::default())?; + + let track_id = default_track.id; + + Ok(Self { + format_reader, + decoder, + sample_rate, + num_frames, + track_id, + }) + } } impl super::Decoder for SymphoniaDecoder { - type Error = FromFileError; - - fn sample_rate(&self) -> u32 { - self.sample_rate - } - - fn num_frames(&self) -> usize { - self.num_frames - } - - fn decode(&mut self) -> Result, Self::Error> { - let packet = loop { - let packet = self.format_reader.next_packet()?; - if self.track_id == packet.track_id() { - break packet; - } - }; - let buffer = self.decoder.decode(&packet)?; - load_frames_from_buffer_ref(&buffer) - } - - fn seek(&mut self, index: usize) -> Result { - let seeked_to = self.format_reader.seek( - SeekMode::Accurate, - SeekTo::TimeStamp { - ts: index.try_into().expect("could not convert usize into u64"), - track_id: self.track_id, - }, - )?; - Ok(seeked_to - .actual_ts - .try_into() - .expect("could not convert u64 into usize")) - } + type Error = FromFileError; + + fn sample_rate(&self) -> u32 { + self.sample_rate + } + + fn num_frames(&self) -> usize { + self.num_frames + } + + fn decode(&mut self) -> Result, Self::Error> { + let packet = loop { + match self.format_reader.next_packet()? { + Some(packet) if packet.track_id == self.track_id => break packet, + Some(_) => continue, + None => { + return Err(FromFileError::SymphoniaError( + symphonia::core::errors::Error::IoError(std::io::Error::new( + std::io::ErrorKind::UnexpectedEof, + "end of stream", + )), + )); + } + } + }; + let buffer = self.decoder.decode(&packet)?; + load_frames_from_buffer_ref(&buffer) + } + + fn seek(&mut self, index: usize) -> Result { + let seeked_to = self.format_reader.seek( + SeekMode::Accurate, + SeekTo::Timestamp { + ts: Timestamp::new(index as i64), + track_id: self.track_id, + }, + )?; + let actual = seeked_to.actual_ts.get(); + Ok(if actual < 0 { + 0 + } else { + (actual as usize).min(self.num_frames) + }) + } } diff --git a/crates/kira/src/sound/symphonia.rs b/crates/kira/src/sound/symphonia.rs index d707a546..15954b46 100644 --- a/crates/kira/src/sound/symphonia.rs +++ b/crates/kira/src/sound/symphonia.rs @@ -1,5 +1,5 @@ -use symphonia::core::{ - audio::{AudioBuffer, AudioBufferRef, Signal}, +use symphonia::core::audio::{ + Audio, AudioBuffer, GenericAudioBufferRef, conv::{FromSample, IntoSample}, sample::Sample, }; @@ -8,18 +8,20 @@ use crate::frame::Frame; use super::FromFileError; -pub fn load_frames_from_buffer_ref(buffer: &AudioBufferRef) -> Result, FromFileError> { +pub fn load_frames_from_buffer_ref( + buffer: &GenericAudioBufferRef, +) -> Result, FromFileError> { match buffer { - AudioBufferRef::U8(buffer) => load_frames_from_buffer(buffer), - AudioBufferRef::U16(buffer) => load_frames_from_buffer(buffer), - AudioBufferRef::U24(buffer) => load_frames_from_buffer(buffer), - AudioBufferRef::U32(buffer) => load_frames_from_buffer(buffer), - AudioBufferRef::S8(buffer) => load_frames_from_buffer(buffer), - AudioBufferRef::S16(buffer) => load_frames_from_buffer(buffer), - AudioBufferRef::S24(buffer) => load_frames_from_buffer(buffer), - AudioBufferRef::S32(buffer) => load_frames_from_buffer(buffer), - AudioBufferRef::F32(buffer) => load_frames_from_buffer(buffer), - AudioBufferRef::F64(buffer) => load_frames_from_buffer(buffer), + GenericAudioBufferRef::U8(buffer) => load_frames_from_buffer(buffer), + GenericAudioBufferRef::U16(buffer) => load_frames_from_buffer(buffer), + GenericAudioBufferRef::U24(buffer) => load_frames_from_buffer(buffer), + GenericAudioBufferRef::U32(buffer) => load_frames_from_buffer(buffer), + GenericAudioBufferRef::S8(buffer) => load_frames_from_buffer(buffer), + GenericAudioBufferRef::S16(buffer) => load_frames_from_buffer(buffer), + GenericAudioBufferRef::S24(buffer) => load_frames_from_buffer(buffer), + GenericAudioBufferRef::S32(buffer) => load_frames_from_buffer(buffer), + GenericAudioBufferRef::F32(buffer) => load_frames_from_buffer(buffer), + GenericAudioBufferRef::F64(buffer) => load_frames_from_buffer(buffer), } } @@ -29,16 +31,18 @@ pub fn load_frames_from_buffer( where f32: FromSample, { - match buffer.spec().channels.count() { + match buffer.num_planes() { 1 => Ok(buffer - .chan(0) + .plane(0) + .unwrap() .iter() .map(|sample| Frame::from_mono((*sample).into_sample())) .collect()), 2 => Ok(buffer - .chan(0) + .plane(0) + .unwrap() .iter() - .zip(buffer.chan(1).iter()) + .zip(buffer.plane(1).unwrap().iter()) .map(|(left, right)| Frame::new((*left).into_sample(), (*right).into_sample())) .collect()), _ => Err(FromFileError::UnsupportedChannelConfiguration), From 3dc311a2c5223bb8c54e1f11ba48a06c7ccb0825 Mon Sep 17 00:00:00 2001 From: Eagisa Date: Wed, 27 May 2026 09:16:16 +0530 Subject: [PATCH 2/3] Sync dependency updates from upstream --- crates/kira/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/kira/Cargo.toml b/crates/kira/Cargo.toml index 3f8f660f..3f3a5e6f 100644 --- a/crates/kira/Cargo.toml +++ b/crates/kira/Cargo.toml @@ -13,13 +13,13 @@ readme = "../../README.md" [dependencies] assert_no_alloc = { version = "1.1.2", optional = true } atomic-arena = "0.1.2" -glam = { version = "0.32.0", features = ["mint"] } +glam = { version = "0.33.0", features = ["mint"] } mint = "0.5.9" pastey = "0.2.1" rtrb = "0.3.2" serde = { version = "1.0.219", features = ["derive"], optional = true } symphonia = { version = "0.6.0", optional = true, default-features = false } -triple_buffer = "8.1.1" +triple_buffer = "9.0.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies.cpal] version = "0.17.0" From d52c10fb7ed79998c103509ac57f51f669648b58 Mon Sep 17 00:00:00 2001 From: Eagisa Date: Mon, 8 Jun 2026 10:00:07 +0530 Subject: [PATCH 3/3] Fix UnexpectedEof error handling for Symphonia 0.6.0 --- .../src/sound/static_sound/data/from_file.rs | 18 +- .../src/sound/streaming/decoder/symphonia.rs | 181 ++++++++---------- 2 files changed, 84 insertions(+), 115 deletions(-) diff --git a/crates/kira/src/sound/static_sound/data/from_file.rs b/crates/kira/src/sound/static_sound/data/from_file.rs index 75ca9b6e..e2736df8 100644 --- a/crates/kira/src/sound/static_sound/data/from_file.rs +++ b/crates/kira/src/sound/static_sound/data/from_file.rs @@ -44,26 +44,19 @@ impl StaticSoundData { Default::default(), Default::default(), )?; - let default_track = format_reader .default_track(TrackType::Audio) .ok_or(FromFileError::NoDefaultTrack)?; - let default_track_id = default_track.id; - let audio_params = match default_track.codec_params.as_ref() { Some(CodecParameters::Audio(p)) => p, _ => return Err(FromFileError::NoDefaultTrack), }; - let sample_rate = audio_params .sample_rate .ok_or(FromFileError::UnknownSampleRate)?; - let mut decoder = codecs.make_audio_decoder(audio_params, &Default::default())?; - let mut frames = vec![]; - loop { match format_reader.next_packet() { Ok(Some(packet)) => { @@ -73,18 +66,9 @@ impl StaticSoundData { } } Ok(None) => break, - Err(error) => match error { - symphonia::core::errors::Error::IoError(error) => { - if error.kind() == std::io::ErrorKind::UnexpectedEof { - break; - } - return Err(symphonia::core::errors::Error::IoError(error).into()); - } - error => return Err(error.into()), - }, + Err(error) => return Err(error.into()), } } - Ok(Self { sample_rate, frames: frames.into(), diff --git a/crates/kira/src/sound/streaming/decoder/symphonia.rs b/crates/kira/src/sound/streaming/decoder/symphonia.rs index 5aaad6fe..618dde20 100644 --- a/crates/kira/src/sound/streaming/decoder/symphonia.rs +++ b/crates/kira/src/sound/streaming/decoder/symphonia.rs @@ -1,113 +1,98 @@ use std::convert::TryInto; use crate::{ - frame::Frame, - sound::{symphonia::load_frames_from_buffer_ref, FromFileError}, + frame::Frame, + sound::{FromFileError, symphonia::load_frames_from_buffer_ref}, }; use symphonia::core::{ - codecs::{audio::AudioDecoder, CodecParameters}, - formats::{probe::Hint, FormatReader, SeekMode, SeekTo, TrackType}, - io::{MediaSource, MediaSourceStream}, - units::Timestamp, + codecs::{CodecParameters, audio::AudioDecoder}, + formats::{FormatReader, SeekMode, SeekTo, TrackType, probe::Hint}, + io::{MediaSource, MediaSourceStream}, + units::Timestamp, }; pub(crate) struct SymphoniaDecoder { - format_reader: Box, - decoder: Box, - sample_rate: u32, - num_frames: usize, - track_id: u32, + format_reader: Box, + decoder: Box, + sample_rate: u32, + num_frames: usize, + track_id: u32, } impl SymphoniaDecoder { - pub(crate) fn new(media_source: Box) -> Result { - let codecs = symphonia::default::get_codecs(); - let probe = symphonia::default::get_probe(); - let mss = MediaSourceStream::new(media_source, Default::default()); - - let format_reader = probe.probe( - &Hint::default(), - mss, - Default::default(), - Default::default(), - )?; - - let default_track = format_reader - .default_track(TrackType::Audio) - .ok_or(FromFileError::NoDefaultTrack)?; - - let audio_params = match default_track.codec_params.as_ref() { - Some(CodecParameters::Audio(p)) => p, - _ => return Err(FromFileError::NoDefaultTrack), - }; - - let sample_rate = audio_params - .sample_rate - .ok_or(FromFileError::UnknownSampleRate)?; - - let num_frames = default_track - .num_frames - .ok_or(FromFileError::UnknownSampleRate)? - .try_into() - .expect("could not convert u64 into usize"); - - let decoder = codecs.make_audio_decoder(audio_params, &Default::default())?; - - let track_id = default_track.id; - - Ok(Self { - format_reader, - decoder, - sample_rate, - num_frames, - track_id, - }) - } + pub(crate) fn new(media_source: Box) -> Result { + let codecs = symphonia::default::get_codecs(); + let probe = symphonia::default::get_probe(); + let mss = MediaSourceStream::new(media_source, Default::default()); + let format_reader = probe.probe( + &Hint::default(), + mss, + Default::default(), + Default::default(), + )?; + let default_track = format_reader + .default_track(TrackType::Audio) + .ok_or(FromFileError::NoDefaultTrack)?; + let audio_params = match default_track.codec_params.as_ref() { + Some(CodecParameters::Audio(p)) => p, + _ => return Err(FromFileError::NoDefaultTrack), + }; + let sample_rate = audio_params + .sample_rate + .ok_or(FromFileError::UnknownSampleRate)?; + let num_frames = default_track + .num_frames + .ok_or(FromFileError::UnknownSampleRate)? + .try_into() + .expect("could not convert u64 into usize"); + let decoder = codecs.make_audio_decoder(audio_params, &Default::default())?; + let track_id = default_track.id; + Ok(Self { + format_reader, + decoder, + sample_rate, + num_frames, + track_id, + }) + } } impl super::Decoder for SymphoniaDecoder { - type Error = FromFileError; - - fn sample_rate(&self) -> u32 { - self.sample_rate - } - - fn num_frames(&self) -> usize { - self.num_frames - } - - fn decode(&mut self) -> Result, Self::Error> { - let packet = loop { - match self.format_reader.next_packet()? { - Some(packet) if packet.track_id == self.track_id => break packet, - Some(_) => continue, - None => { - return Err(FromFileError::SymphoniaError( - symphonia::core::errors::Error::IoError(std::io::Error::new( - std::io::ErrorKind::UnexpectedEof, - "end of stream", - )), - )); - } - } - }; - let buffer = self.decoder.decode(&packet)?; - load_frames_from_buffer_ref(&buffer) - } - - fn seek(&mut self, index: usize) -> Result { - let seeked_to = self.format_reader.seek( - SeekMode::Accurate, - SeekTo::Timestamp { - ts: Timestamp::new(index as i64), - track_id: self.track_id, - }, - )?; - let actual = seeked_to.actual_ts.get(); - Ok(if actual < 0 { - 0 - } else { - (actual as usize).min(self.num_frames) - }) - } + type Error = FromFileError; + + fn sample_rate(&self) -> u32 { + self.sample_rate + } + + fn num_frames(&self) -> usize { + self.num_frames + } + + fn decode(&mut self) -> Result, Self::Error> { + let packet = loop { + match self.format_reader.next_packet()? { + Some(packet) if packet.track_id == self.track_id => break packet, + Some(_) => continue, + None => return Ok(vec![]), + } + }; + let buffer = self.decoder.decode(&packet)?; + load_frames_from_buffer_ref(&buffer) + } + + fn seek(&mut self, index: usize) -> Result { + let seeked_to = self.format_reader.seek( + SeekMode::Accurate, + SeekTo::Timestamp { + ts: Timestamp::new(index as i64), + track_id: self.track_id, + }, + )?; + let actual = seeked_to.actual_ts.get(); + Ok(if actual < 0 { + 0 + } else { + (actual as usize).min(self.num_frames) + }) + } }