diff --git a/src/stream.rs b/src/stream.rs index 59a4c917..124a5f87 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`. /// @@ -51,6 +51,20 @@ impl OutputStream { Ok((out, handle)) } + pub fn try_from_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)) + } + /// Return a new stream & handle using the default output device. /// /// On failure will fallback to trying any non-default output devices. @@ -192,28 +206,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, ) -> 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)) @@ -222,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)) @@ -231,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)) @@ -240,7 +262,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)) @@ -249,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)) @@ -258,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)) @@ -267,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 @@ -280,7 +302,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 @@ -293,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 @@ -306,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 @@ -327,10 +349,24 @@ impl CpalDeviceExt for cpal::Device { &self, config: SupportedStreamConfig, ) -> Result<(Arc>, cpal::Stream), StreamError> { - self.new_output_stream_with_format(config).or_else(|err| { + 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, &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)) })