diff --git a/examples/beep.rs b/examples/beep.rs index ee3e125..82e154d 100644 --- a/examples/beep.rs +++ b/examples/beep.rs @@ -5,7 +5,7 @@ fn main() { let format = device.default_output_format().expect("Failed to get default output format"); let event_loop = cpal::EventLoop::new(); let stream_id = event_loop.build_output_stream(&device, &format).unwrap(); - event_loop.play_stream(stream_id.clone()); + event_loop.play_stream(stream_id.clone()).unwrap(); let sample_rate = format.sample_rate.0 as f32; let mut sample_clock = 0f32; diff --git a/examples/feedback.rs b/examples/feedback.rs index 3807b53..eaa22a9 100644 --- a/examples/feedback.rs +++ b/examples/feedback.rs @@ -43,8 +43,8 @@ fn main() { // Play the streams. println!("Starting the input and output streams with `{}` milliseconds of latency.", LATENCY_MS); - event_loop.play_stream(input_stream_id.clone()); - event_loop.play_stream(output_stream_id.clone()); + event_loop.play_stream(input_stream_id.clone()).unwrap(); + event_loop.play_stream(output_stream_id.clone()).unwrap(); // Run the event loop on a separate thread. std::thread::spawn(move || { diff --git a/examples/record_wav.rs b/examples/record_wav.rs index ad5fd5f..f7aa019 100644 --- a/examples/record_wav.rs +++ b/examples/record_wav.rs @@ -14,7 +14,7 @@ fn main() { let event_loop = cpal::EventLoop::new(); let stream_id = event_loop.build_input_stream(&device, &format) .expect("Failed to build input stream"); - event_loop.play_stream(stream_id); + event_loop.play_stream(stream_id).unwrap(); // The WAV file we're recording to. const PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/recorded.wav"); diff --git a/src/alsa/mod.rs b/src/alsa/mod.rs index de5e691..43a3191 100644 --- a/src/alsa/mod.rs +++ b/src/alsa/mod.rs @@ -9,6 +9,8 @@ use BuildStreamError; use DefaultFormatError; use DeviceNameError; use Format; +use PauseStreamError; +use PlayStreamError; use SupportedFormatsError; use SampleFormat; use SampleRate; @@ -837,13 +839,15 @@ impl EventLoop { } #[inline] - pub fn play_stream(&self, stream_id: StreamId) { + pub fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> { self.push_command(Command::PlayStream(stream_id)); + Ok(()) } #[inline] - pub fn pause_stream(&self, stream_id: StreamId) { + pub fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> { self.push_command(Command::PauseStream(stream_id)); + Ok(()) } } diff --git a/src/coreaudio/mod.rs b/src/coreaudio/mod.rs index a80d001..40ccf17 100644 --- a/src/coreaudio/mod.rs +++ b/src/coreaudio/mod.rs @@ -7,6 +7,8 @@ use BuildStreamError; use DefaultFormatError; use DeviceNameError; use Format; +use PauseStreamError; +use PlayStreamError; use SupportedFormatsError; use Sample; use SampleFormat; @@ -782,24 +784,34 @@ impl EventLoop { streams[stream_id.0] = None; } - pub fn play_stream(&self, stream: StreamId) { + pub fn play_stream(&self, stream: StreamId) -> Result<(), PlayStreamError> { let mut streams = self.streams.lock().unwrap(); let stream = streams[stream.0].as_mut().unwrap(); if !stream.playing { - stream.audio_unit.start().unwrap(); + if let Err(e) = stream.audio_unit.start() { + let description = format!("{}", std::error::Error::description(e)); + let err = BackendSpecificError { description }; + return Err(err.into()); + } stream.playing = true; } + Ok(()) } - pub fn pause_stream(&self, stream: StreamId) { + pub fn pause_stream(&self, stream: StreamId) -> Result<(), PauseStreamError> { let mut streams = self.streams.lock().unwrap(); let stream = streams[stream.0].as_mut().unwrap(); if stream.playing { - stream.audio_unit.stop().unwrap(); + if let Err(e) = stream.audio_unit.stop() { + let description = format!("{}", std::error::Error::description(e)); + let err = BackendSpecificError { description }; + return Err(err.into()); + } stream.playing = false; } + Ok(()) } } diff --git a/src/emscripten/mod.rs b/src/emscripten/mod.rs index 1138a39..b828f7f 100644 --- a/src/emscripten/mod.rs +++ b/src/emscripten/mod.rs @@ -13,6 +13,8 @@ use DefaultFormatError; use DeviceNameError; use DevicesError; use Format; +use PauseStreamError; +use PlayStreamError; use SupportedFormatsError; use StreamData; use SupportedFormat; @@ -147,23 +149,25 @@ impl EventLoop { } #[inline] - pub fn play_stream(&self, stream_id: StreamId) { + pub fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> { let streams = self.streams.lock().unwrap(); let stream = streams .get(stream_id.0) .and_then(|v| v.as_ref()) .expect("invalid stream ID"); js!(@{stream}.resume()); + Ok(()) } #[inline] - pub fn pause_stream(&self, stream_id: StreamId) { + pub fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> { let streams = self.streams.lock().unwrap(); let stream = streams .get(stream_id.0) .and_then(|v| v.as_ref()) .expect("invalid stream ID"); js!(@{stream}.suspend()); + Ok(()) } } diff --git a/src/lib.rs b/src/lib.rs index 6b1f6cd..2a273bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -382,6 +382,36 @@ pub enum BuildStreamError { } } +/// Errors that might occur when calling `play_stream`. +/// +/// As of writing this, only macOS may immediately return an error while calling this method. This +/// is because both the alsa and wasapi backends only enqueue these commands and do not process +/// them immediately. +#[derive(Debug, Fail)] +pub enum PlayStreamError { + /// See the `BackendSpecificError` docs for more information about this error variant. + #[fail(display = "{}", err)] + BackendSpecific { + #[fail(cause)] + err: BackendSpecificError, + } +} + +/// Errors that might occur when calling `pause_stream`. +/// +/// As of writing this, only macOS may immediately return an error while calling this method. This +/// is because both the alsa and wasapi backends only enqueue these commands and do not process +/// them immediately. +#[derive(Debug, Fail)] +pub enum PauseStreamError { + /// See the `BackendSpecificError` docs for more information about this error variant. + #[fail(display = "{}", err)] + BackendSpecific { + #[fail(cause)] + err: BackendSpecificError, + } +} + /// An iterator yielding all `Device`s currently available to the system. /// /// Can be empty if the system does not support audio in general. @@ -522,7 +552,7 @@ impl EventLoop { /// If the stream does not exist, this function can either panic or be a no-op. /// #[inline] - pub fn play_stream(&self, stream: StreamId) { + pub fn play_stream(&self, stream: StreamId) -> Result<(), PlayStreamError> { self.0.play_stream(stream.0) } @@ -537,7 +567,7 @@ impl EventLoop { /// If the stream does not exist, this function can either panic or be a no-op. /// #[inline] - pub fn pause_stream(&self, stream: StreamId) { + pub fn pause_stream(&self, stream: StreamId) -> Result<(), PauseStreamError> { self.0.pause_stream(stream.0) } @@ -789,6 +819,18 @@ impl From for BuildStreamError { } } +impl From for PlayStreamError { + fn from(err: BackendSpecificError) -> Self { + PlayStreamError::BackendSpecific { err } + } +} + +impl From for PauseStreamError { + fn from(err: BackendSpecificError) -> Self { + PauseStreamError::BackendSpecific { err } + } +} + // If a backend does not provide an API for retrieving supported formats, we query it with a bunch // of commonly used rates. This is always the case for wasapi and is sometimes the case for alsa. // diff --git a/src/null/mod.rs b/src/null/mod.rs index dc41f77..c7e4cb7 100644 --- a/src/null/mod.rs +++ b/src/null/mod.rs @@ -7,6 +7,8 @@ use DefaultFormatError; use DevicesError; use DeviceNameError; use Format; +use PauseStreamError; +use PlayStreamError; use SupportedFormatsError; use StreamData; use SupportedFormat; @@ -42,12 +44,12 @@ impl EventLoop { } #[inline] - pub fn play_stream(&self, _: StreamId) { + pub fn play_stream(&self, _: StreamId) -> Result<(), PlayStreamError> { panic!() } #[inline] - pub fn pause_stream(&self, _: StreamId) { + pub fn pause_stream(&self, _: StreamId) -> Result<(), PauseStreamError> { panic!() } } diff --git a/src/wasapi/stream.rs b/src/wasapi/stream.rs index ae13c26..b8070a4 100644 --- a/src/wasapi/stream.rs +++ b/src/wasapi/stream.rs @@ -23,6 +23,8 @@ use std::sync::atomic::Ordering; use BackendSpecificError; use BuildStreamError; use Format; +use PauseStreamError; +use PlayStreamError; use SampleFormat; use StreamData; use UnknownTypeOutputBuffer; @@ -642,13 +644,15 @@ impl EventLoop { } #[inline] - pub fn play_stream(&self, stream: StreamId) { + pub fn play_stream(&self, stream: StreamId) -> Result<(), PlayStreamError> { self.push_command(Command::PlayStream(stream)); + Ok(()) } #[inline] - pub fn pause_stream(&self, stream: StreamId) { + pub fn pause_stream(&self, stream: StreamId) -> Result<(), PauseStreamError> { self.push_command(Command::PauseStream(stream)); + Ok(()) } #[inline]