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}