diff --git a/crates/kira/Cargo.toml b/crates/kira/Cargo.toml index 4be18d7c..863d0b84 100644 --- a/crates/kira/Cargo.toml +++ b/crates/kira/Cargo.toml @@ -18,15 +18,15 @@ 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 = "9.0.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies.cpal] -version = "0.17.0" +version = "0.18.1" optional = true [target.'cfg(target_arch = "wasm32")'.dependencies.cpal] -version = "0.17.0" +version = "0.18.1" optional = true features = ["wasm-bindgen"] diff --git a/crates/kira/src/backend/cpal/desktop.rs b/crates/kira/src/backend/cpal/desktop.rs index 1e7bdb9a..6d1e69d0 100644 --- a/crates/kira/src/backend/cpal/desktop.rs +++ b/crates/kira/src/backend/cpal/desktop.rs @@ -9,7 +9,7 @@ use stream_manager::{StreamManager, StreamManagerController}; use crate::backend::{Backend, Renderer}; use cpal::{ - BufferSize, Device, StreamConfig, StreamError, + BufferSize, Device, StreamConfig, traits::{DeviceTrait, HostTrait}, }; @@ -60,7 +60,7 @@ impl CpalBackend { /// Returns the oldest available stream error in the queue. #[cfg_attr(docsrs, doc(cfg(not(wasm32))))] - pub fn pop_error(&mut self) -> Option { + pub fn pop_error(&mut self) -> Option { if let State::Initialized { stream_manager_controller, } = &mut self.state diff --git a/crates/kira/src/backend/cpal/desktop/stream_manager.rs b/crates/kira/src/backend/cpal/desktop/stream_manager.rs index 14ca03f1..4616ea1d 100644 --- a/crates/kira/src/backend/cpal/desktop/stream_manager.rs +++ b/crates/kira/src/backend/cpal/desktop/stream_manager.rs @@ -10,7 +10,7 @@ use std::{ use super::renderer_with_cpu_usage::RendererWithCpuUsage; use cpal::{ - BufferSize, Device, Stream, StreamConfig, StreamError, + BufferSize, Device, Stream, StreamConfig, traits::{DeviceTrait, HostTrait, StreamTrait}, }; use rtrb::{Consumer, Producer, PushError, RingBuffer}; @@ -35,7 +35,7 @@ enum State { pub(super) struct StreamManagerController { should_drop: Arc, num_stream_errors_discarded: Arc, - handled_stream_error_consumer: Mutex>, + handled_stream_error_consumer: Mutex>, } impl StreamManagerController { @@ -48,7 +48,7 @@ impl StreamManagerController { self.num_stream_errors_discarded.load(Ordering::Acquire) } - pub fn pop_handled_error(&mut self) -> Option { + pub fn pop_handled_error(&mut self) -> Option { self.handled_stream_error_consumer .get_mut() .unwrap() @@ -139,15 +139,15 @@ impl StreamManager { /// Restarts the stream if the audio device gets disconnected. fn check_stream( &mut self, - unhandled_stream_error_consumer: &mut Consumer, - handled_stream_error_producer: &mut Producer, + unhandled_stream_error_consumer: &mut Consumer, + handled_stream_error_producer: &mut Producer, num_stream_errors_discarded: &Arc, ) { if let State::Running { .. } = &self.state { while let Ok(error) = unhandled_stream_error_consumer.pop() { - match error { + match error.kind() { // check for device disconnection - StreamError::DeviceNotAvailable | StreamError::StreamInvalidated => { + cpal::ErrorKind::DeviceNotAvailable | cpal::ErrorKind::StreamInvalidated => { self.stop_stream(); if let Ok((device, mut config)) = default_device_and_config() { // TODO: gracefully handle errors that occur in this function @@ -160,7 +160,8 @@ impl StreamManager { .unwrap(); } } - StreamError::BackendSpecific { err: _ } | StreamError::BufferUnderrun => {} + // cpal::ErrorKind::BackendSpecific { err: _ } | StreamError::BufferUnderrun => {} + _ => {} } match handled_stream_error_producer.push(error) { Ok(()) => {} @@ -194,7 +195,7 @@ impl StreamManager { device: &Device, config: &mut StreamConfig, num_stream_errors_discarded: Arc, - ) -> Result, Error> { + ) -> Result, Error> { let mut renderer = if let State::Idle { renderer } = std::mem::replace(&mut self.state, State::Empty) { renderer @@ -214,7 +215,7 @@ impl StreamManager { RingBuffer::new(64); let channels = config.channels; let stream = device.build_output_stream( - config, + *config, move |data: &mut [f32], _| { #[cfg(feature = "assert_no_alloc")] assert_no_alloc::assert_no_alloc(|| { diff --git a/crates/kira/src/backend/cpal/error.rs b/crates/kira/src/backend/cpal/error.rs index 84cbb027..a2b728c4 100644 --- a/crates/kira/src/backend/cpal/error.rs +++ b/crates/kira/src/backend/cpal/error.rs @@ -1,58 +1,32 @@ use std::fmt::{Display, Formatter}; - -use cpal::{BuildStreamError, DefaultStreamConfigError, PlayStreamError}; - /// Errors that can occur when using the cpal backend. #[derive(Debug)] pub enum Error { /// A default audio output device could not be determined. NoDefaultOutputDevice, - /// An error occurred when getting the default output configuration. - DefaultStreamConfigError(DefaultStreamConfigError), - /// An error occurred when building the audio stream. - BuildStreamError(BuildStreamError), - /// An error occurred when starting the audio stream. - PlayStreamError(PlayStreamError), + /// An error occurred in the cpal backend. + CpalError(cpal::Error), } - impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Error::NoDefaultOutputDevice => { f.write_str("Cannot find the default audio output device") } - Error::DefaultStreamConfigError(error) => error.fmt(f), - Error::BuildStreamError(error) => error.fmt(f), - Error::PlayStreamError(error) => error.fmt(f), + Error::CpalError(error) => error.fmt(f), } } } - impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { - Error::DefaultStreamConfigError(error) => Some(error), - Error::BuildStreamError(error) => Some(error), - Error::PlayStreamError(error) => Some(error), + Error::CpalError(error) => Some(error), _ => None, } } } - -impl From for Error { - fn from(v: DefaultStreamConfigError) -> Self { - Self::DefaultStreamConfigError(v) - } -} - -impl From for Error { - fn from(v: BuildStreamError) -> Self { - Self::BuildStreamError(v) - } -} - -impl From for Error { - fn from(v: PlayStreamError) -> Self { - Self::PlayStreamError(v) +impl From for Error { + fn from(v: cpal::Error) -> Self { + Self::CpalError(v) } } 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..e2736df8 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,41 +38,35 @@ 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)?); } } - 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()), - }, + Ok(None) => break, + Err(error) => return Err(error.into()), } } Ok(Self { diff --git a/crates/kira/src/sound/streaming/decoder/symphonia.rs b/crates/kira/src/sound/streaming/decoder/symphonia.rs index 129539d7..618dde20 100644 --- a/crates/kira/src/sound/streaming/decoder/symphonia.rs +++ b/crates/kira/src/sound/streaming/decoder/symphonia.rs @@ -5,15 +5,15 @@ use crate::{ sound::{FromFileError, symphonia::load_frames_from_buffer_ref}, }; use symphonia::core::{ - codecs::Decoder, - formats::{FormatReader, SeekMode, SeekTo}, + codecs::{CodecParameters, audio::AudioDecoder}, + formats::{FormatReader, SeekMode, SeekTo, TrackType, probe::Hint}, io::{MediaSource, MediaSourceStream}, - probe::Hint, + units::Timestamp, }; pub(crate) struct SymphoniaDecoder { format_reader: Box, - decoder: Box, + decoder: Box, sample_rate: u32, num_frames: usize, track_id: u32, @@ -24,28 +24,28 @@ impl SymphoniaDecoder { 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 format_reader = probe.probe( + &Hint::default(), + mss, + Default::default(), + Default::default(), + )?; let default_track = format_reader - .default_track() + .default_track(TrackType::Audio) .ok_or(FromFileError::NoDefaultTrack)?; - let sample_rate = default_track - .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 num_frames = default_track - .codec_params - .n_frames + .num_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 decoder = codecs.make_audio_decoder(audio_params, &Default::default())?; let track_id = default_track.id; Ok(Self { format_reader, @@ -70,9 +70,10 @@ impl super::Decoder for SymphoniaDecoder { 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; + 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)?; @@ -82,14 +83,16 @@ impl super::Decoder for SymphoniaDecoder { 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"), + SeekTo::Timestamp { + ts: Timestamp::new(index as i64), track_id: self.track_id, }, )?; - Ok(seeked_to - .actual_ts - .try_into() - .expect("could not convert u64 into usize")) + 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),