From 184b3831c18e6cefc17fe343f9d10330d6ee84ab Mon Sep 17 00:00:00 2001 From: Petr Gladkikh Date: Sun, 6 Nov 2022 23:08:04 +0100 Subject: [PATCH 1/6] Support buffer size selection Incompatible API change. --- src/stream.rs | 60 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/src/stream.rs b/src/stream.rs index 59a4c917..4f0464d0 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -8,7 +8,7 @@ use crate::dynamic_mixer::{self, DynamicMixerController}; use crate::sink::Sink; use crate::source::Source; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; -use cpal::{Sample, SupportedStreamConfig}; +use cpal::{Sample, SampleFormat, StreamConfig, SupportedStreamConfig}; /// `cpal::Stream` container. Also see the more useful `OutputStreamHandle`. /// @@ -42,7 +42,7 @@ impl OutputStream { device: &cpal::Device, config: SupportedStreamConfig, ) -> Result<(Self, OutputStreamHandle), StreamError> { - let (mixer, _stream) = device.try_new_output_stream_config(config)?; + let (mixer, _stream) = device.try_new_output_stream_config(&config)?; _stream.play()?; let out = Self { mixer, _stream }; let handle = OutputStreamHandle { @@ -78,8 +78,8 @@ impl OutputStream { impl OutputStreamHandle { /// Plays a source with a device until it ends. pub fn play_raw(&self, source: S) -> Result<(), PlayError> - where - S: Source + Send + 'static, + where + S: Source + Send + 'static, { let mixer = self.mixer.upgrade().ok_or(PlayError::NoDevice)?; mixer.add(source); @@ -88,8 +88,8 @@ impl OutputStreamHandle { /// Plays a sound once. Returns a `Sink` that can be used to control the sound. pub fn play_once(&self, input: R) -> Result - where - R: Read + Seek + Send + Sync + 'static, + where + R: Read + Seek + Send + Sync + 'static, { let input = decoder::Decoder::new(input)?; let sink = Sink::try_new(self)?; @@ -192,28 +192,36 @@ impl error::Error for StreamError { pub(crate) trait CpalDeviceExt { fn new_output_stream_with_format( &self, - format: cpal::SupportedStreamConfig, + config: &cpal::StreamConfig, + sample_format: &cpal::SampleFormat, ) -> Result<(Arc>, cpal::Stream), cpal::BuildStreamError>; fn try_new_output_stream_config( &self, - config: cpal::SupportedStreamConfig, + config: &cpal::SupportedStreamConfig, + ) -> Result<(Arc>, cpal::Stream), StreamError>; + + fn try_new_output_stream( + &self, + config: &cpal::StreamConfig, + sample_format: &cpal::SampleFormat, ) -> Result<(Arc>, cpal::Stream), StreamError>; } impl CpalDeviceExt for cpal::Device { fn new_output_stream_with_format( &self, - format: cpal::SupportedStreamConfig, + config: &cpal::StreamConfig, + sample_format: &cpal::SampleFormat, ) -> Result<(Arc>, cpal::Stream), cpal::BuildStreamError> { let (mixer_tx, mut mixer_rx) = - dynamic_mixer::mixer::(format.channels(), format.sample_rate().0); + dynamic_mixer::mixer::(config.channels, config.sample_rate.0); let error_callback = |err| eprintln!("an error occurred on output stream: {}", err); - match format.sample_format() { + match sample_format { cpal::SampleFormat::F32 => self.build_output_stream::( - &format.config(), + &config, move |data, _| { data.iter_mut() .for_each(|d| *d = mixer_rx.next().unwrap_or(0f32)) @@ -240,7 +248,7 @@ impl CpalDeviceExt for cpal::Device { None, ), cpal::SampleFormat::I16 => self.build_output_stream::( - &format.config(), + &config, move |data, _| { data.iter_mut() .for_each(|d| *d = mixer_rx.next().map(Sample::from_sample).unwrap_or(0i16)) @@ -280,7 +288,7 @@ impl CpalDeviceExt for cpal::Device { None, ), cpal::SampleFormat::U16 => self.build_output_stream::( - &format.config(), + &config, move |data, _| { data.iter_mut().for_each(|d| { *d = mixer_rx @@ -320,17 +328,31 @@ impl CpalDeviceExt for cpal::Device { ), _ => return Err(cpal::BuildStreamError::StreamConfigNotSupported), } - .map(|stream| (mixer_tx, stream)) + .map(|stream| (mixer_tx, stream)) } fn try_new_output_stream_config( &self, - config: SupportedStreamConfig, + config: &SupportedStreamConfig, + ) -> Result<(Arc>, cpal::Stream), StreamError> { + self.new_output_stream_with_format(&config.config(), &config.sample_format()).or_else(|err| { + // look through all supported formats to see if another works + supported_output_formats(self)? + .find_map(|format| self.new_output_stream_with_format(&format.config(), &config.sample_format()).ok()) + // return original error if nothing works + .ok_or(StreamError::BuildStreamError(err)) + }) + } + + fn try_new_output_stream( + &self, + config: &StreamConfig, + sample_format: &SampleFormat, ) -> Result<(Arc>, cpal::Stream), StreamError> { - self.new_output_stream_with_format(config).or_else(|err| { + self.new_output_stream_with_format(&config, &sample_format).or_else(|err| { // look through all supported formats to see if another works supported_output_formats(self)? - .find_map(|format| self.new_output_stream_with_format(format).ok()) + .find_map(|format| self.new_output_stream_with_format(&format.config(), &sample_format).ok()) // return original error if nothing works .ok_or(StreamError::BuildStreamError(err)) }) @@ -340,7 +362,7 @@ impl CpalDeviceExt for cpal::Device { /// All the supported output formats with sample rates fn supported_output_formats( device: &cpal::Device, -) -> Result, StreamError> { +) -> Result, StreamError> { const HZ_44100: cpal::SampleRate = cpal::SampleRate(44_100); let mut supported: Vec<_> = device.supported_output_configs()?.collect(); From dd483902cb04105e38c261fae48aed71fef91447 Mon Sep 17 00:00:00 2001 From: Petr Gladkikh Date: Mon, 14 Nov 2022 23:06:00 +0100 Subject: [PATCH 2/6] Make configurable buffer API public --- src/stream.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/stream.rs b/src/stream.rs index 4f0464d0..d7681979 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -31,7 +31,9 @@ impl OutputStream { device: &cpal::Device, ) -> Result<(Self, OutputStreamHandle), StreamError> { let default_config = device.default_output_config()?; - OutputStream::try_from_device_config(device, default_config) + OutputStream::try_from_device_config(device, + &default_config.config(), + &default_config.sample_format()) } /// Returns a new stream & handle using the given device and stream config. @@ -39,6 +41,21 @@ impl OutputStream { /// If the supplied `SupportedStreamConfig` is invalid for the device this function will /// fail to create an output stream and instead return a `StreamError` pub fn try_from_device_config( + device: &cpal::Device, + config: &StreamConfig, + sample_format: &SampleFormat, + ) -> Result<(Self, OutputStreamHandle), StreamError> { + let (mixer, _stream) = device.try_new_output_stream(&config, &sample_format)?; + _stream.play()?; + let out = Self { mixer, _stream }; + let handle = OutputStreamHandle { + mixer: Arc::downgrade(&out.mixer), + }; + Ok((out, handle)) + } + + + pub fn try_from_config( device: &cpal::Device, config: SupportedStreamConfig, ) -> Result<(Self, OutputStreamHandle), StreamError> { @@ -51,6 +68,7 @@ impl OutputStream { Ok((out, handle)) } + /// Return a new stream & handle using the default output device. /// /// On failure will fallback to trying any non-default output devices. From 5d42375270f4b56dc4ff49f325451b07332b6abe Mon Sep 17 00:00:00 2001 From: Petr Gladkikh Date: Mon, 14 Nov 2022 23:17:02 +0100 Subject: [PATCH 3/6] Correct API signatures THe idea was to add a new function. --- src/stream.rs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/stream.rs b/src/stream.rs index d7681979..28e48147 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -31,9 +31,7 @@ impl OutputStream { device: &cpal::Device, ) -> Result<(Self, OutputStreamHandle), StreamError> { let default_config = device.default_output_config()?; - OutputStream::try_from_device_config(device, - &default_config.config(), - &default_config.sample_format()) + OutputStream::try_from_device_config(device, &default_config) } /// Returns a new stream & handle using the given device and stream config. @@ -42,10 +40,9 @@ impl OutputStream { /// fail to create an output stream and instead return a `StreamError` pub fn try_from_device_config( device: &cpal::Device, - config: &StreamConfig, - sample_format: &SampleFormat, + config: &SupportedStreamConfig, ) -> Result<(Self, OutputStreamHandle), StreamError> { - let (mixer, _stream) = device.try_new_output_stream(&config, &sample_format)?; + let (mixer, _stream) = device.try_new_output_stream_config(&config)?; _stream.play()?; let out = Self { mixer, _stream }; let handle = OutputStreamHandle { @@ -54,12 +51,12 @@ impl OutputStream { Ok((out, handle)) } - pub fn try_from_config( device: &cpal::Device, - config: SupportedStreamConfig, + config: &StreamConfig, + sample_format: &SampleFormat, ) -> Result<(Self, OutputStreamHandle), StreamError> { - let (mixer, _stream) = device.try_new_output_stream_config(&config)?; + let (mixer, _stream) = device.try_new_output_stream(&config, &sample_format)?; _stream.play()?; let out = Self { mixer, _stream }; let handle = OutputStreamHandle { @@ -68,7 +65,6 @@ impl OutputStream { Ok((out, handle)) } - /// Return a new stream & handle using the default output device. /// /// On failure will fallback to trying any non-default output devices. @@ -248,7 +244,7 @@ impl CpalDeviceExt for cpal::Device { None, ), cpal::SampleFormat::F64 => self.build_output_stream::( - &format.config(), + &config, move |data, _| { data.iter_mut() .for_each(|d| *d = mixer_rx.next().map(Sample::from_sample).unwrap_or(0f64)) @@ -257,7 +253,7 @@ impl CpalDeviceExt for cpal::Device { None, ), cpal::SampleFormat::I8 => self.build_output_stream::( - &format.config(), + &config, move |data, _| { data.iter_mut() .for_each(|d| *d = mixer_rx.next().map(Sample::from_sample).unwrap_or(0i8)) @@ -275,7 +271,7 @@ impl CpalDeviceExt for cpal::Device { None, ), cpal::SampleFormat::I32 => self.build_output_stream::( - &format.config(), + &config, move |data, _| { data.iter_mut() .for_each(|d| *d = mixer_rx.next().map(Sample::from_sample).unwrap_or(0i32)) @@ -284,7 +280,7 @@ impl CpalDeviceExt for cpal::Device { None, ), cpal::SampleFormat::I64 => self.build_output_stream::( - &format.config(), + &config, move |data, _| { data.iter_mut() .for_each(|d| *d = mixer_rx.next().map(Sample::from_sample).unwrap_or(0i64)) @@ -293,7 +289,7 @@ impl CpalDeviceExt for cpal::Device { None, ), cpal::SampleFormat::U8 => self.build_output_stream::( - &format.config(), + &config, move |data, _| { data.iter_mut().for_each(|d| { *d = mixer_rx @@ -319,7 +315,7 @@ impl CpalDeviceExt for cpal::Device { None, ), cpal::SampleFormat::U32 => self.build_output_stream::( - &format.config(), + &config, move |data, _| { data.iter_mut().for_each(|d| { *d = mixer_rx @@ -332,7 +328,7 @@ impl CpalDeviceExt for cpal::Device { None, ), cpal::SampleFormat::U64 => self.build_output_stream::( - &format.config(), + &config, move |data, _| { data.iter_mut().for_each(|d| { *d = mixer_rx From 4c62dd3ed8f66cca2867e87f3d3d5a72cd34691a Mon Sep 17 00:00:00 2001 From: Petr Gladkikh Date: Sun, 1 Oct 2023 18:44:56 +0200 Subject: [PATCH 4/6] Bump version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 921294e7..a2ac3753 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rodio" -version = "0.17.1" +version = "0.18.0" license = "MIT OR Apache-2.0" description = "Audio playback library" keywords = ["audio", "playback", "gamedev"] From 1a8d63ad2993bd29f756767ea3fb5ab44961ab46 Mon Sep 17 00:00:00 2001 From: Petr Gladkikh Date: Sun, 1 Oct 2023 23:17:43 +0200 Subject: [PATCH 5/6] Reduce API changes --- src/stream.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/stream.rs b/src/stream.rs index 28e48147..d01008a7 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -31,7 +31,7 @@ impl OutputStream { device: &cpal::Device, ) -> Result<(Self, OutputStreamHandle), StreamError> { let default_config = device.default_output_config()?; - OutputStream::try_from_device_config(device, &default_config) + OutputStream::try_from_device_config(device, default_config) } /// Returns a new stream & handle using the given device and stream config. @@ -40,9 +40,9 @@ impl OutputStream { /// fail to create an output stream and instead return a `StreamError` pub fn try_from_device_config( device: &cpal::Device, - config: &SupportedStreamConfig, + config: SupportedStreamConfig, ) -> Result<(Self, OutputStreamHandle), StreamError> { - let (mixer, _stream) = device.try_new_output_stream_config(&config)?; + let (mixer, _stream) = device.try_new_output_stream_config(config)?; _stream.play()?; let out = Self { mixer, _stream }; let handle = OutputStreamHandle { @@ -92,8 +92,8 @@ impl OutputStream { impl OutputStreamHandle { /// Plays a source with a device until it ends. pub fn play_raw(&self, source: S) -> Result<(), PlayError> - where - S: Source + Send + 'static, + where + S: Source + Send + 'static, { let mixer = self.mixer.upgrade().ok_or(PlayError::NoDevice)?; mixer.add(source); @@ -102,8 +102,8 @@ impl OutputStreamHandle { /// Plays a sound once. Returns a `Sink` that can be used to control the sound. pub fn play_once(&self, input: R) -> Result - where - R: Read + Seek + Send + Sync + 'static, + where + R: Read + Seek + Send + Sync + 'static, { let input = decoder::Decoder::new(input)?; let sink = Sink::try_new(self)?; @@ -212,7 +212,7 @@ pub(crate) trait CpalDeviceExt { fn try_new_output_stream_config( &self, - config: &cpal::SupportedStreamConfig, + config: cpal::SupportedStreamConfig, ) -> Result<(Arc>, cpal::Stream), StreamError>; fn try_new_output_stream( @@ -347,7 +347,7 @@ impl CpalDeviceExt for cpal::Device { fn try_new_output_stream_config( &self, - config: &SupportedStreamConfig, + config: SupportedStreamConfig, ) -> Result<(Arc>, cpal::Stream), StreamError> { self.new_output_stream_with_format(&config.config(), &config.sample_format()).or_else(|err| { // look through all supported formats to see if another works @@ -376,7 +376,7 @@ impl CpalDeviceExt for cpal::Device { /// All the supported output formats with sample rates fn supported_output_formats( device: &cpal::Device, -) -> Result, StreamError> { +) -> Result, StreamError> { const HZ_44100: cpal::SampleRate = cpal::SampleRate(44_100); let mut supported: Vec<_> = device.supported_output_configs()?.collect(); From c17712f74b1f41036e36580637ceea54650fe2ad Mon Sep 17 00:00:00 2001 From: Petr Gladkikh Date: Sun, 1 Oct 2023 23:48:24 +0200 Subject: [PATCH 6/6] Reformat code --- src/stream.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream.rs b/src/stream.rs index d01008a7..124a5f87 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -342,7 +342,7 @@ impl CpalDeviceExt for cpal::Device { ), _ => return Err(cpal::BuildStreamError::StreamConfigNotSupported), } - .map(|stream| (mixer_tx, stream)) + .map(|stream| (mixer_tx, stream)) } fn try_new_output_stream_config(