Skip to main content

cpal/
traits.rs

1//! The suite of traits allowing CPAL to abstract over hosts, devices, event loops and stream IDs.
2//!
3//! # Custom Host Implementations
4//!
5//! When implementing custom hosts with the `custom` feature, use the [`assert_stream_send!`](crate::assert_stream_send)
6//! and [`assert_stream_sync!`](crate::assert_stream_sync) macros to verify your `Stream` type meets CPAL's requirements.
7
8use std::time::Duration;
9
10use crate::{
11    BuildStreamError, Data, DefaultStreamConfigError, DeviceDescription, DeviceId, DeviceIdError,
12    DeviceNameError, DevicesError, InputCallbackInfo, InputDevices, OutputCallbackInfo,
13    OutputDevices, PauseStreamError, PlayStreamError, SampleFormat, SizedSample, StreamConfig,
14    StreamError, SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError,
15};
16
17/// A [`Host`] provides access to the available audio devices on the system.
18///
19/// Each platform may have a number of available hosts depending on the system, each with their own
20/// pros and cons.
21///
22/// For example, WASAPI is the standard audio host API that ships with the Windows operating
23/// system. However, due to historical limitations with respect to performance and flexibility,
24/// Steinberg created the ASIO API providing better audio device support for pro audio and
25/// low-latency applications. As a result, it is common for some devices and device capabilities to
26/// only be available via ASIO, while others are only available via WASAPI.
27///
28/// Another great example is the Linux platform. While the ALSA host API is the lowest-level API
29/// available to almost all distributions of Linux, its flexibility is limited as it requires that
30/// each process have exclusive access to the devices with which they establish streams. PulseAudio
31/// is another popular host API that aims to solve this issue by providing user-space mixing,
32/// however it has its own limitations w.r.t. low-latency and high-performance audio applications.
33/// JACK is yet another host API that is more suitable to pro-audio applications, however it is
34/// less readily available by default in many Linux distributions and is known to be tricky to
35/// set up.
36///
37/// [`Host`]: crate::Host
38pub trait HostTrait {
39    /// The type used for enumerating available devices by the host.
40    type Devices: Iterator<Item = Self::Device>;
41    /// The `Device` type yielded by the host.
42    type Device: DeviceTrait;
43
44    /// Whether or not the host is available on the system.
45    fn is_available() -> bool;
46
47    /// An iterator yielding all [`Device`](DeviceTrait)s currently available to the host on the system.
48    ///
49    /// Can be empty if the system does not support audio in general.
50    fn devices(&self) -> Result<Self::Devices, DevicesError>;
51
52    /// Fetches a [`Device`](DeviceTrait) based on a [`DeviceId`] if available
53    ///
54    /// Returns `None` if no device matching the id is found
55    fn device_by_id(&self, id: &DeviceId) -> Option<Self::Device> {
56        self.devices()
57            .ok()?
58            .find(|device| device.id().ok().as_ref() == Some(id))
59    }
60
61    /// The default input audio device on the system.
62    ///
63    /// Returns `None` if no input device is available.
64    fn default_input_device(&self) -> Option<Self::Device>;
65
66    /// The default output audio device on the system.
67    ///
68    /// Returns `None` if no output device is available.
69    fn default_output_device(&self) -> Option<Self::Device>;
70
71    /// An iterator yielding all `Device`s currently available to the system that support one or more
72    /// input stream formats.
73    ///
74    /// Can be empty if the system does not support audio input.
75    fn input_devices(&self) -> Result<InputDevices<Self::Devices>, DevicesError> {
76        Ok(self.devices()?.filter(DeviceTrait::supports_input))
77    }
78
79    /// An iterator yielding all `Device`s currently available to the system that support one or more
80    /// output stream formats.
81    ///
82    /// Can be empty if the system does not support audio output.
83    fn output_devices(&self) -> Result<OutputDevices<Self::Devices>, DevicesError> {
84        Ok(self.devices()?.filter(DeviceTrait::supports_output))
85    }
86}
87
88/// A device that is capable of audio input and/or output.
89///
90/// Please note that `Device`s may become invalid if they get disconnected. Therefore, all the
91/// methods that involve a device return a `Result` allowing the user to handle this case.
92pub trait DeviceTrait {
93    /// The iterator type yielding supported input stream formats.
94    type SupportedInputConfigs: Iterator<Item = SupportedStreamConfigRange>;
95    /// The iterator type yielding supported output stream formats.
96    type SupportedOutputConfigs: Iterator<Item = SupportedStreamConfigRange>;
97    /// The stream type created by [`build_input_stream_raw`] and [`build_output_stream_raw`].
98    ///
99    /// [`build_input_stream_raw`]: Self::build_input_stream_raw
100    /// [`build_output_stream_raw`]: Self::build_output_stream_raw
101    type Stream: StreamTrait;
102
103    /// The human-readable name of the device.
104    #[deprecated(
105        since = "0.17.0",
106        note = "Use `description()` for comprehensive device information including name, \
107                manufacturer, and device type. Use `id()` for a unique, stable device identifier \
108                that persists across reboots and reconnections."
109    )]
110    fn name(&self) -> Result<String, DeviceNameError> {
111        self.description().map(|desc| desc.name().to_string())
112    }
113
114    /// Structured description of the device with metadata.
115    ///
116    /// This returns a [`DeviceDescription`] containing structured information about the device,
117    /// including name, manufacturer (if available), device type, bus type, and other
118    /// platform-specific metadata.
119    ///
120    /// For simple string representation, use `device.description().to_string()` or
121    /// `device.description().name()`.
122    fn description(&self) -> Result<DeviceDescription, DeviceNameError>;
123
124    /// The ID of the device.
125    ///
126    /// This ID uniquely identifies the device on the host. It should be stable across program
127    /// runs, device disconnections, and system reboots where possible.
128    fn id(&self) -> Result<DeviceId, DeviceIdError>;
129
130    /// True if the device supports audio input, otherwise false
131    fn supports_input(&self) -> bool {
132        self.supported_input_configs()
133            .is_ok_and(|mut iter| iter.next().is_some())
134    }
135
136    /// True if the device supports audio output, otherwise false
137    fn supports_output(&self) -> bool {
138        self.supported_output_configs()
139            .is_ok_and(|mut iter| iter.next().is_some())
140    }
141
142    /// An iterator yielding formats that are supported by the backend.
143    ///
144    /// Can return an error if the device is no longer valid (e.g. it has been disconnected).
145    fn supported_input_configs(
146        &self,
147    ) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError>;
148
149    /// An iterator yielding output stream formats that are supported by the device.
150    ///
151    /// Can return an error if the device is no longer valid (e.g. it has been disconnected).
152    fn supported_output_configs(
153        &self,
154    ) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError>;
155
156    /// The default input stream format for the device.
157    fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError>;
158
159    /// The default output stream format for the device.
160    fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError>;
161
162    /// Create an input stream.
163    ///
164    /// # Parameters
165    ///
166    /// * `config` - The stream configuration including sample rate, channels, and buffer size.
167    /// * `data_callback` - Called periodically with captured audio data. The callback receives
168    ///   a slice of samples in the format `T` and timing information.
169    /// * `error_callback` - Called when a stream error occurs (e.g., device disconnected).
170    /// * `timeout` - Optional timeout for backend operations. `None` indicates blocking behavior,
171    ///   `Some(duration)` sets a maximum wait time. Not all backends support timeouts.
172    fn build_input_stream<T, D, E>(
173        &self,
174        config: &StreamConfig,
175        mut data_callback: D,
176        error_callback: E,
177        timeout: Option<Duration>,
178    ) -> Result<Self::Stream, BuildStreamError>
179    where
180        T: SizedSample,
181        D: FnMut(&[T], &InputCallbackInfo) + Send + 'static,
182        E: FnMut(StreamError) + Send + 'static,
183    {
184        self.build_input_stream_raw(
185            config,
186            T::FORMAT,
187            move |data, info| {
188                data_callback(
189                    data.as_slice()
190                        .expect("host supplied incorrect sample type"),
191                    info,
192                )
193            },
194            error_callback,
195            timeout,
196        )
197    }
198
199    /// Create an output stream.
200    ///
201    /// # Parameters
202    ///
203    /// * `config` - The stream configuration including sample rate, channels, and buffer size.
204    /// * `data_callback` - Called periodically to fill the output buffer. The callback receives
205    ///   a mutable slice of samples in the format `T` to be filled with audio data, along with
206    ///   timing information.
207    /// * `error_callback` - Called when a stream error occurs (e.g., device disconnected).
208    /// * `timeout` - Optional timeout for backend operations. `None` indicates blocking behavior,
209    ///   `Some(duration)` sets a maximum wait time. Not all backends support timeouts.
210    fn build_output_stream<T, D, E>(
211        &self,
212        config: &StreamConfig,
213        mut data_callback: D,
214        error_callback: E,
215        timeout: Option<Duration>,
216    ) -> Result<Self::Stream, BuildStreamError>
217    where
218        T: SizedSample,
219        D: FnMut(&mut [T], &OutputCallbackInfo) + Send + 'static,
220        E: FnMut(StreamError) + Send + 'static,
221    {
222        self.build_output_stream_raw(
223            config,
224            T::FORMAT,
225            move |data, info| {
226                data_callback(
227                    data.as_slice_mut()
228                        .expect("host supplied incorrect sample type"),
229                    info,
230                )
231            },
232            error_callback,
233            timeout,
234        )
235    }
236
237    /// Create a dynamically typed input stream.
238    ///
239    /// This method allows working with sample data as raw bytes, useful when the sample
240    /// format is determined at runtime. For compile-time known formats, prefer
241    /// [`build_input_stream`](Self::build_input_stream).
242    ///
243    /// # Parameters
244    ///
245    /// * `config` - The stream configuration including sample rate, channels, and buffer size.
246    /// * `sample_format` - The sample format of the audio data.
247    /// * `data_callback` - Called periodically with captured audio data as a [`Data`] buffer.
248    /// * `error_callback` - Called when a stream error occurs (e.g., device disconnected).
249    /// * `timeout` - Optional timeout for backend operations. `None` indicates blocking behavior,
250    ///   `Some(duration)` sets a maximum wait time. Not all backends support timeouts.
251    fn build_input_stream_raw<D, E>(
252        &self,
253        config: &StreamConfig,
254        sample_format: SampleFormat,
255        data_callback: D,
256        error_callback: E,
257        timeout: Option<Duration>,
258    ) -> Result<Self::Stream, BuildStreamError>
259    where
260        D: FnMut(&Data, &InputCallbackInfo) + Send + 'static,
261        E: FnMut(StreamError) + Send + 'static;
262
263    /// Create a dynamically typed output stream.
264    ///
265    /// This method allows working with sample data as raw bytes, useful when the sample
266    /// format is determined at runtime. For compile-time known formats, prefer
267    /// [`build_output_stream`](Self::build_output_stream).
268    ///
269    /// # Parameters
270    ///
271    /// * `config` - The stream configuration including sample rate, channels, and buffer size.
272    /// * `sample_format` - The sample format of the audio data.
273    /// * `data_callback` - Called periodically to fill the output buffer with audio data as
274    ///   a mutable [`Data`] buffer.
275    /// * `error_callback` - Called when a stream error occurs (e.g., device disconnected).
276    /// * `timeout` - Optional timeout for backend operations. `None` indicates blocking behavior,
277    ///   `Some(duration)` sets a maximum wait time. Not all backends support timeouts.
278    fn build_output_stream_raw<D, E>(
279        &self,
280        config: &StreamConfig,
281        sample_format: SampleFormat,
282        data_callback: D,
283        error_callback: E,
284        timeout: Option<Duration>,
285    ) -> Result<Self::Stream, BuildStreamError>
286    where
287        D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static,
288        E: FnMut(StreamError) + Send + 'static;
289}
290
291/// A stream created from [`Device`](DeviceTrait), with methods to control playback.
292pub trait StreamTrait {
293    /// Run the stream.
294    ///
295    /// Note: Not all platforms automatically run the stream upon creation, so it is important to
296    /// call `play` after creation if it is expected that the stream should run immediately.
297    fn play(&self) -> Result<(), PlayStreamError>;
298
299    /// Some devices support pausing the audio stream. This can be useful for saving energy in
300    /// moments of silence.
301    ///
302    /// Note: Not all devices support suspending the stream at the hardware level. This method may
303    /// fail in these cases.
304    fn pause(&self) -> Result<(), PauseStreamError>;
305}
306
307/// Compile-time assertion that a stream type implements [`Send`].
308///
309/// Custom host implementations should use this macro to verify their `Stream` type
310/// can be safely transferred between threads, as required by CPAL's API.
311///
312/// # Example
313///
314/// ```
315/// use cpal::assert_stream_send;
316/// struct MyStream { /* ... */ }
317/// assert_stream_send!(MyStream);
318/// ```
319#[macro_export]
320macro_rules! assert_stream_send {
321    ($t:ty) => {
322        const fn _assert_stream_send<T: Send>() {}
323        const _: () = _assert_stream_send::<$t>();
324    };
325}
326
327/// Compile-time assertion that a stream type implements [`Sync`].
328///
329/// Custom host implementations should use this macro to verify their `Stream` type
330/// can be safely shared between threads, as required by CPAL's API.
331///
332/// # Example
333///
334/// ```
335/// use cpal::assert_stream_sync;
336/// struct MyStream { /* ... */ }
337/// assert_stream_sync!(MyStream);
338/// ```
339#[macro_export]
340macro_rules! assert_stream_sync {
341    ($t:ty) => {
342        const fn _assert_stream_sync<T: Sync>() {}
343        const _: () = _assert_stream_sync::<$t>();
344    };
345}