| Lift freeq's AV media plane out of sleek 90f8b89 nandi 19d ago | 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 | |
| 8 | use std::time::Duration; |
| 9 | |
| 10 | use 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 |
| 38 | pub 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. |
| 92 | pub 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. |
| 292 | pub 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 | /// Query the stream's buffer size in frames per callback invocation. |
| 307 | /// |
| 308 | /// Returns the platform's best estimate of the number of frames per callback. |
| 309 | /// |
| 310 | /// - [`crate::BufferSize::Fixed`]: the actual callback size after hardware negotiation, which may |
| 311 | /// differ from the requested value due to hardware constraints. |
| 312 | /// - [`crate::BufferSize::Default`]: the system-configured callback size (e.g. ALSA period, |
| 313 | /// JACK buffer size, AAudio burst size). This reflects the typical callback size, not a |
| 314 | /// guaranteed upper bound. |
| 315 | /// |
| 316 | /// Returns `None` if the platform cannot report a meaningful estimate — for example, before |
| 317 | /// the first callback has fired, or on platforms that do not expose this information. |
| 318 | /// |
| 319 | /// Applications should use this value to size pre-allocated buffers or estimate latency, but |
| 320 | /// must always use the actual frame count passed to each individual callback invocation. |
| 321 | fn buffer_size(&self) -> Option<crate::FrameCount> { |
| 322 | None |
| 323 | } |
| 324 | } |
| 325 | |
| 326 | /// Compile-time assertion that a stream type implements [`Send`]. |
| 327 | /// |
| 328 | /// Custom host implementations should use this macro to verify their `Stream` type |
| 329 | /// can be safely transferred between threads, as required by CPAL's API. |
| 330 | /// |
| 331 | /// # Example |
| 332 | /// |
| 333 | /// ``` |
| 334 | /// use cpal::assert_stream_send; |
| 335 | /// struct MyStream { /* ... */ } |
| 336 | /// assert_stream_send!(MyStream); |
| 337 | /// ``` |
| 338 | #[macro_export] |
| 339 | macro_rules! assert_stream_send { |
| 340 | ($t:ty) => { |
| 341 | const fn _assert_stream_send<T: Send>() {} |
| 342 | const _: () = _assert_stream_send::<$t>(); |
| 343 | }; |
| 344 | } |
| 345 | |
| 346 | /// Compile-time assertion that a stream type implements [`Sync`]. |
| 347 | /// |
| 348 | /// Custom host implementations should use this macro to verify their `Stream` type |
| 349 | /// can be safely shared between threads, as required by CPAL's API. |
| 350 | /// |
| 351 | /// # Example |
| 352 | /// |
| 353 | /// ``` |
| 354 | /// use cpal::assert_stream_sync; |
| 355 | /// struct MyStream { /* ... */ } |
| 356 | /// assert_stream_sync!(MyStream); |
| 357 | /// ``` |
| 358 | #[macro_export] |
| 359 | macro_rules! assert_stream_sync { |
| 360 | ($t:ty) => { |
| 361 | const fn _assert_stream_sync<T: Sync>() {} |
| 362 | const _: () = _assert_stream_sync::<$t>(); |
| 363 | }; |
| 364 | } |