nandi/jolt-nativepublic Fork 0
dce285fb5a5ec1f331b8afa7b2bdc4ed5e1bbd46
Commits
Clone
git clone https://git.rickub.com/nandi/jolt-native.git
git clone ssh://git@rickub.com/nandi/jolt-native.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

lib.rs · 1041 lines · 37.0 KBRust Blame HistoryRaw
Lift freeq's AV media plane out of sleek 90f8b89 nandi 20d ago1//! # How to use cpal
2//!
3//! Here are some concepts cpal exposes:
4//!
5//! - A [`Host`] provides access to the available audio devices on the system.
6//! Some platforms have more than one host available, but every platform supported by CPAL has at
7//! least one [default_host] that is guaranteed to be available.
8//! - A [`Device`] is an audio device that may have any number of input and
9//! output streams.
10//! - A [`Stream`] is an open flow of audio data. Input streams allow you to
11//! receive audio data, output streams allow you to play audio data. You must choose which
12//! [Device] will run your stream before you can create one. Often, a default device can be
13//! retrieved via the [Host].
14//!
15//! The first step is to initialise the [`Host`]:
16//!
17//! ```
18//! use cpal::traits::HostTrait;
19//! let host = cpal::default_host();
20//! ```
21//!
22//! Then choose an available [`Device`]. The easiest way is to use the default input or output
23//! `Device` via the [`default_input_device()`] or [`default_output_device()`] methods on `host`.
24//!
25//! Alternatively, you can enumerate all the available devices with the [`devices()`] method.
26//! Beware that the `default_*_device()` functions return an `Option<Device>` in case no device
27//! is available for that stream type on the system.
28//!
29//! ```no_run
30//! # use cpal::traits::HostTrait;
31//! # let host = cpal::default_host();
32//! let device = host.default_output_device().expect("no output device available");
33//! ```
34//!
35//! Before we can create a stream, we must decide what the configuration of the audio stream is
36//! going to be.
37//! You can query all the supported configurations with the
38//! [`supported_input_configs()`] and [`supported_output_configs()`] methods.
39//! These produce a list of [`SupportedStreamConfigRange`] structs which can later be turned into
40//! actual [`SupportedStreamConfig`] structs.
41//!
42//! If you don't want to query the list of configs,
43//! you can also build your own [`StreamConfig`] manually, but doing so could lead to an error when
44//! building the stream if the config is not supported by the device.
45//!
46//! > **Note**: the `supported_input/output_configs()` methods
47//! > could return an error for example if the device has been disconnected.
48//!
49//! ```no_run
50//! use cpal::traits::{DeviceTrait, HostTrait};
51//! # let host = cpal::default_host();
52//! # let device = host.default_output_device().unwrap();
53//! let mut supported_configs_range = device.supported_output_configs()
54//! .expect("error while querying configs");
55//! let supported_config = supported_configs_range.next()
56//! .expect("no supported config?!")
57//! .with_max_sample_rate();
58//! ```
59//!
60//! Now that we have everything for the stream, we are ready to create it from our selected device:
61//!
62//! ```no_run
63//! use cpal::Data;
64//! use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
65//! # let host = cpal::default_host();
66//! # let device = host.default_output_device().unwrap();
67//! # let config = device.default_output_config().unwrap().into();
68//! let stream = device.build_output_stream(
69//! config,
70//! move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
71//! // react to stream events and read or write stream data here.
72//! },
73//! move |err| {
74//! // react to errors here.
75//! },
76//! None // None=blocking, Some(Duration)=timeout
77//! );
78//! ```
79//!
80//! While the stream is running, the selected audio device will periodically call the data callback
81//! that was passed to the function. For input streams, the callback receives `&`[`Data`] containing
82//! captured audio samples. For output streams, the callback receives `&mut`[`Data`] to be filled
83//! with audio samples for playback.
84//!
85//! > **Note**: Creating and running a stream will *not* block the thread. On modern platforms, the
86//! > given callback is called by a dedicated, high-priority thread responsible for delivering
87//! > audio data to the system's audio device in a timely manner. On older platforms that only
88//! > provide a blocking API (e.g. ALSA), CPAL will create a thread in order to consistently
89//! > provide non-blocking behaviour (currently this is a thread per stream, but this may change to
90//! > use a single thread for all streams). *If this is an issue for your platform or design,
91//! > please share your issue and use-case with the CPAL team on the GitHub issue tracker for
92//! > consideration.*
93//!
94//! In this example, we simply fill the given output buffer with silence.
95//!
96//! ```no_run
97//! use cpal::{Data, Sample, SampleFormat, FromSample};
98//! use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
99//! # let host = cpal::default_host();
100//! # let device = host.default_output_device().unwrap();
101//! # let supported_config = device.default_output_config().unwrap();
102//! let err_fn = |err| eprintln!("an error occurred on the output audio stream: {}", err);
103//! let sample_format = supported_config.sample_format();
104//! let config = supported_config.into();
105//! let stream = match sample_format {
106//! SampleFormat::F32 => device.build_output_stream(config, write_silence::<f32>, err_fn, None),
107//! SampleFormat::I16 => device.build_output_stream(config, write_silence::<i16>, err_fn, None),
108//! SampleFormat::U16 => device.build_output_stream(config, write_silence::<u16>, err_fn, None),
109//! sample_format => panic!("Unsupported sample format '{sample_format}'")
110//! }.unwrap();
111//!
112//! fn write_silence<T: Sample>(data: &mut [T], _: &cpal::OutputCallbackInfo) {
113//! for sample in data.iter_mut() {
114//! *sample = Sample::EQUILIBRIUM;
115//! }
116//! }
117//! ```
118//!
119//! Not all platforms automatically run the stream upon creation. To ensure the stream has started,
120//! we can use [`Stream::play`](traits::StreamTrait::play).
121//!
122//! ```no_run
123//! # use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
124//! # let host = cpal::default_host();
125//! # let device = host.default_output_device().unwrap();
126//! # let supported_config = device.default_output_config().unwrap();
127//! # let sample_format = supported_config.sample_format();
128//! # let config = supported_config.into();
129//! # let data_fn = move |_data: &mut cpal::Data, _: &cpal::OutputCallbackInfo| {};
130//! # let err_fn = move |_err| {};
131//! # let stream = device.build_output_stream_raw(config, sample_format, data_fn, err_fn, None).unwrap();
132//! stream.play().unwrap();
133//! ```
134//!
135//! Some devices support pausing the audio stream. This can be useful for saving energy in moments
136//! of silence.
137//!
138//! ```no_run
139//! # use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
140//! # let host = cpal::default_host();
141//! # let device = host.default_output_device().unwrap();
142//! # let supported_config = device.default_output_config().unwrap();
143//! # let sample_format = supported_config.sample_format();
144//! # let config = supported_config.into();
145//! # let data_fn = move |_data: &mut cpal::Data, _: &cpal::OutputCallbackInfo| {};
146//! # let err_fn = move |_err| {};
147//! # let stream = device.build_output_stream_raw(config, sample_format, data_fn, err_fn, None).unwrap();
148//! stream.pause().unwrap();
149//! ```
150//!
151//! [`default_input_device()`]: traits::HostTrait::default_input_device
152//! [`default_output_device()`]: traits::HostTrait::default_output_device
153//! [`devices()`]: traits::HostTrait::devices
154//! [`supported_input_configs()`]: traits::DeviceTrait::supported_input_configs
155//! [`supported_output_configs()`]: traits::DeviceTrait::supported_output_configs
156
157#![cfg_attr(docsrs, feature(doc_cfg))]
158
159// Extern crate declarations with `#[macro_use]` must unfortunately be at crate root.
160#[cfg(all(
161 target_arch = "wasm32",
162 any(target_os = "emscripten", feature = "wasm-bindgen")
163))]
164extern crate js_sys;
165#[cfg(all(
166 target_arch = "wasm32",
167 any(target_os = "emscripten", feature = "wasm-bindgen")
168))]
169extern crate wasm_bindgen;
170#[cfg(all(
171 target_arch = "wasm32",
172 any(target_os = "emscripten", feature = "wasm-bindgen")
173))]
174extern crate web_sys;
175
176#[cfg(all(
177 target_arch = "wasm32",
178 any(target_os = "emscripten", feature = "wasm-bindgen")
179))]
180use wasm_bindgen::prelude::*;
181
182pub use device_description::{
183 DeviceDescription, DeviceDescriptionBuilder, DeviceDirection, DeviceType, InterfaceType,
184};
185pub use error::*;
186pub use platform::{
187 available_hosts, default_host, host_from_id, Device, Devices, Host, HostId, Stream,
188 SupportedInputConfigs, SupportedOutputConfigs, ALL_HOSTS,
189};
190pub use samples_formats::{FromSample, Sample, SampleFormat, SizedSample, I24, U24};
191use std::convert::TryInto;
192use std::time::Duration;
193
194pub mod device_description;
195mod error;
196mod host;
197pub mod platform;
198mod samples_formats;
199pub mod traits;
200
201/// Iterator of devices wrapped in a filter to only include certain device types
202pub type DevicesFiltered<I> = std::iter::Filter<I, fn(&<I as Iterator>::Item) -> bool>;
203
204/// A host's device iterator yielding only *input* devices.
205pub type InputDevices<I> = DevicesFiltered<I>;
206
207/// A host's device iterator yielding only *output* devices.
208pub type OutputDevices<I> = DevicesFiltered<I>;
209
210/// Number of channels.
211pub type ChannelCount = u16;
212
213/// The number of samples processed per second for a single channel of audio.
214pub type SampleRate = u32;
215
216/// A frame represents one sample for each channel. For example, with stereo audio,
217/// one frame contains two samples (left and right channels).
218pub type FrameCount = u32;
219
220/// A stable identifier for an audio device across all supported platforms.
221///
222/// Device IDs should remain stable across application restarts and can be serialized using `Display`/`FromStr`.
223///
224/// A device ID consists of a [`HostId`] identifying the audio backend and a device-specific identifier string.
225///
226/// # Example
227///
228/// ```no_run
229/// use cpal::traits::{HostTrait, DeviceTrait};
230/// use cpal::DeviceId;
231/// use std::str::FromStr;
232///
233/// let host = cpal::default_host();
234/// let device = host.default_output_device().unwrap();
235/// let device_id = device.id().unwrap();
236///
237/// // Serialize to string (e.g., for storage in config file)
238/// let id_string = device_id.to_string();
239/// println!("Device ID: {}", id_string); // e.g., "wasapi:device_identifier"
240///
241/// // Deserialize from string
242/// match DeviceId::from_str(&id_string) {
243/// Ok(parsed_id) => {
244/// // Retrieve the device by its ID
245/// if let Some(device) = host.device_by_id(&parsed_id) {
246/// println!("Found device: {:?}", device.id());
247/// }
248/// }
249/// Err(e) => eprintln!("Failed to parse device ID: {}", e),
250/// }
251/// ```
252#[derive(Clone, Debug, PartialEq, Eq, Hash)]
253pub struct DeviceId(pub crate::platform::HostId, pub String);
254
255impl std::fmt::Display for DeviceId {
256 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
257 write!(f, "{}:{}", self.0, self.1)
258 }
259}
260
261impl std::str::FromStr for DeviceId {
262 type Err = DeviceIdError;
263
264 fn from_str(s: &str) -> Result<Self, Self::Err> {
265 let (host_str, device_str) = s.split_once(':').ok_or(DeviceIdError::BackendSpecific {
266 err: BackendSpecificError {
267 description: format!(
268 "Failed to parse device ID from: {s}\nCheck if format matches \"host:device_id\""
269 ),
270 },
271 })?;
272
273 let host_id = crate::platform::HostId::from_str(host_str)
274 .map_err(|_| DeviceIdError::UnsupportedPlatform)?;
275
276 Ok(DeviceId(host_id, device_str.to_string()))
277 }
278}
279
280/// The buffer size requests the callback size for audio streams.
281///
282/// This controls the approximate size of the audio buffer passed to your callback.
283/// The actual callback size depends on the host/platform implementation and hardware
284/// constraints, and may differ from or vary around the requested size.
285///
286/// ## Callback Size Expectations
287///
288/// When you specify [`BufferSize::Fixed(x)`], you are **requesting** that callbacks
289/// receive approximately `x` frames of audio data. However, **no guarantees can be
290/// made** about the actual callback size:
291///
292/// - The host may round to hardware-supported values
293/// - Different devices have different constraints
294/// - The callback size may vary between calls (especially on mobile platforms)
295/// - The actual size might be larger or smaller than requested
296///
297/// ## Latency Considerations
298///
299/// [`BufferSize::Default`] uses the host's default buffer size, which may be
300/// surprisingly large, leading to higher latency. If low latency is desired,
301/// [`BufferSize::Fixed`] should be used with a small value in accordance with
302/// the [`SupportedBufferSize`] range from [`SupportedStreamConfig`].
303///
304/// Smaller buffer sizes reduce latency but may increase CPU usage and risk audio
305/// dropouts if the callback cannot process audio quickly enough.
306///
307/// # Example
308///
309/// ```no_run
310/// use cpal::traits::{DeviceTrait, HostTrait};
311/// use cpal::{BufferSize, SupportedBufferSize};
312///
313/// let host = cpal::default_host();
314/// let device = host.default_output_device().unwrap();
315/// let config = device.default_output_config().unwrap();
316///
317/// // Check supported buffer size range
318/// match config.buffer_size() {
319/// SupportedBufferSize::Range { min, max } => {
320/// println!("Buffer size range: {} - {}", min, max);
321/// // Request a small buffer for low latency
322/// let mut stream_config = config.config();
323/// stream_config.buffer_size = BufferSize::Fixed(256);
324/// }
325/// SupportedBufferSize::Unknown => {
326/// // Platform doesn't expose buffer size control
327/// println!("Buffer size cannot be queried on this platform");
328/// }
329/// }
330/// ```
331///
332/// [`BufferSize::Default`]: BufferSize::Default
333/// [`BufferSize::Fixed`]: BufferSize::Fixed
334/// [`BufferSize::Fixed(x)`]: BufferSize::Fixed
335/// [`SupportedBufferSize`]: SupportedStreamConfig::buffer_size
336/// [`SupportedStreamConfig`]: SupportedStreamConfig
337#[derive(Clone, Copy, Debug, Eq, PartialEq)]
338pub enum BufferSize {
339 Default,
340 Fixed(FrameCount),
341}
342
343#[cfg(all(
344 target_arch = "wasm32",
345 any(target_os = "emscripten", feature = "wasm-bindgen")
346))]
347impl wasm_bindgen::describe::WasmDescribe for BufferSize {
348 fn describe() {
349 <Option<FrameCount> as wasm_bindgen::describe::WasmDescribe>::describe();
350 }
351}
352
353#[cfg(all(
354 target_arch = "wasm32",
355 any(target_os = "emscripten", feature = "wasm-bindgen")
356))]
357impl wasm_bindgen::convert::IntoWasmAbi for BufferSize {
358 type Abi = <Option<FrameCount> as wasm_bindgen::convert::IntoWasmAbi>::Abi;
359
360 fn into_abi(self) -> Self::Abi {
361 match self {
362 Self::Default => None,
363 Self::Fixed(fc) => Some(fc),
364 }
365 .into_abi()
366 }
367}
368
369#[cfg(all(
370 target_arch = "wasm32",
371 any(target_os = "emscripten", feature = "wasm-bindgen")
372))]
373impl wasm_bindgen::convert::FromWasmAbi for BufferSize {
374 type Abi = <Option<FrameCount> as wasm_bindgen::convert::FromWasmAbi>::Abi;
375
376 unsafe fn from_abi(js: Self::Abi) -> Self {
377 match Option::<FrameCount>::from_abi(js) {
378 None => Self::Default,
379 Some(fc) => Self::Fixed(fc),
380 }
381 }
382}
383
384/// The set of parameters used to describe how to open a stream.
385///
386/// The sample format is omitted in favour of using a sample type.
387///
388/// See also [`BufferSize`] for details on buffer size behavior and latency considerations.
389#[cfg_attr(
390 all(
391 target_arch = "wasm32",
392 any(target_os = "emscripten", feature = "wasm-bindgen")
393 ),
394 wasm_bindgen
395)]
396#[derive(Clone, Debug, Eq, PartialEq, Copy)]
397pub struct StreamConfig {
398 pub channels: ChannelCount,
399 pub sample_rate: SampleRate,
400 pub buffer_size: BufferSize,
401}
402
403/// Describes the minimum and maximum supported buffer size for the device
404#[derive(Clone, Copy, Debug, Eq, PartialEq)]
405pub enum SupportedBufferSize {
406 Range {
407 min: FrameCount,
408 max: FrameCount,
409 },
410 /// In the case that the platform provides no way of getting the default
411 /// buffer size before starting a stream.
412 Unknown,
413}
414
415/// Describes a range of supported stream configurations, retrieved via the
416/// [`Device::supported_input/output_configs`](traits::DeviceTrait#required-methods) method.
417#[derive(Debug, Clone, Copy, PartialEq, Eq)]
418pub struct SupportedStreamConfigRange {
419 pub(crate) channels: ChannelCount,
420 /// Minimum value for the sample rate of the supported formats.
421 pub(crate) min_sample_rate: SampleRate,
422 /// Maximum value for the sample rate of the supported formats.
423 pub(crate) max_sample_rate: SampleRate,
424 /// Buffer size ranges supported by the device
425 pub(crate) buffer_size: SupportedBufferSize,
426 /// Type of data expected by the device.
427 pub(crate) sample_format: SampleFormat,
428}
429
430/// Common iterator types used by backend implementations.
431///
432/// All backends use these same concrete iterator types for supported stream configurations.
433#[allow(dead_code)]
434pub(crate) mod iter {
435 use super::SupportedStreamConfigRange;
436
437 /// Iterator type for supported input stream configurations.
438 ///
439 /// This is the iterator type returned by all backend implementations of
440 /// [`DeviceTrait::supported_input_configs`](crate::traits::DeviceTrait::supported_input_configs).
441 pub type SupportedInputConfigs = std::vec::IntoIter<SupportedStreamConfigRange>;
442
443 /// Iterator type for supported output stream configurations.
444 ///
445 /// This is the iterator type returned by all backend implementations of
446 /// [`DeviceTrait::supported_output_configs`](crate::traits::DeviceTrait::supported_output_configs).
447 pub type SupportedOutputConfigs = std::vec::IntoIter<SupportedStreamConfigRange>;
448}
449
450/// Describes a single supported stream configuration, retrieved via either a
451/// [`SupportedStreamConfigRange`] instance or one of the
452/// [`Device::default_input/output_config`](traits::DeviceTrait#required-methods) methods.
453#[derive(Debug, Clone, PartialEq, Eq)]
454pub struct SupportedStreamConfig {
455 channels: ChannelCount,
456 sample_rate: SampleRate,
457 buffer_size: SupportedBufferSize,
458 sample_format: SampleFormat,
459}
460
461/// A buffer of dynamically typed audio data, passed to raw stream callbacks.
462///
463/// Raw input stream callbacks receive `&Data`, while raw output stream callbacks expect `&mut Data`.
464#[cfg_attr(target_os = "emscripten", wasm_bindgen)]
465#[derive(Debug)]
466pub struct Data {
467 data: *mut (),
468 len: usize,
469 sample_format: SampleFormat,
470}
471
472/// A monotonic time instance associated with a stream, retrieved from either:
473///
474/// 1. A timestamp provided to the stream's underlying audio data callback or
475/// 2. The same time source used to generate timestamps for a stream's underlying audio data
476/// callback.
477///
478/// `StreamInstant` represents a duration since an unspecified origin point. The origin
479/// is guaranteed to occur at or before the stream starts, and remains consistent for the
480/// lifetime of that stream. Different streams may have different origins.
481///
482/// ## Host `StreamInstant` Sources
483///
484/// | Host | Source |
485/// | ---- | ------ |
486/// | alsa | `snd_pcm_status_get_htstamp` |
487/// | asio | `timeGetTime` |
488/// | coreaudio | `mach_absolute_time` |
489/// | emscripten | `AudioContext.getOutputTimestamp` |
490/// | pulseaudio | `std::time::Instant` |
491/// | wasapi | `QueryPerformanceCounter` |
492#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
493pub struct StreamInstant {
494 secs: i64,
495 nanos: u32,
496}
497
498/// A timestamp associated with a call to an input stream's data callback.
499#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
500pub struct InputStreamTimestamp {
501 /// The instant the stream's data callback was invoked.
502 pub callback: StreamInstant,
503 /// The instant that data was captured from the device.
504 ///
505 /// E.g. The instant data was read from an ADC.
506 pub capture: StreamInstant,
507}
508
509/// A timestamp associated with a call to an output stream's data callback.
510#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
511pub struct OutputStreamTimestamp {
512 /// The instant the stream's data callback was invoked.
513 pub callback: StreamInstant,
514 /// The predicted instant that data written will be delivered to the device for playback.
515 ///
516 /// E.g. The instant data will be played by a DAC.
517 pub playback: StreamInstant,
518}
519
520/// Information relevant to a single call to the user's input stream data callback.
521#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
522pub struct InputCallbackInfo {
523 timestamp: InputStreamTimestamp,
524}
525
526/// Information relevant to a single call to the user's output stream data callback.
527#[cfg_attr(target_os = "emscripten", wasm_bindgen)]
528#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
529pub struct OutputCallbackInfo {
530 timestamp: OutputStreamTimestamp,
531}
532
533impl SupportedStreamConfig {
534 pub fn new(
535 channels: ChannelCount,
536 sample_rate: SampleRate,
537 buffer_size: SupportedBufferSize,
538 sample_format: SampleFormat,
539 ) -> Self {
540 Self {
541 channels,
542 sample_rate,
543 buffer_size,
544 sample_format,
545 }
546 }
547
548 pub fn channels(&self) -> ChannelCount {
549 self.channels
550 }
551
552 pub fn sample_rate(&self) -> SampleRate {
553 self.sample_rate
554 }
555
556 pub fn buffer_size(&self) -> &SupportedBufferSize {
557 &self.buffer_size
558 }
559
560 pub fn sample_format(&self) -> SampleFormat {
561 self.sample_format
562 }
563
564 pub fn config(&self) -> StreamConfig {
565 StreamConfig {
566 channels: self.channels,
567 sample_rate: self.sample_rate,
568 buffer_size: BufferSize::Default,
569 }
570 }
571}
572
573impl StreamInstant {
574 /// The amount of time elapsed from another instant to this one.
575 ///
576 /// Returns `None` if `earlier` is later than self.
577 pub fn duration_since(&self, earlier: &Self) -> Option<Duration> {
578 if self < earlier {
579 None
580 } else {
581 (self.as_nanos() - earlier.as_nanos())
582 .try_into()
583 .ok()
584 .map(Duration::from_nanos)
585 }
586 }
587
588 /// Returns the instant in time after the given duration has passed.
589 ///
590 /// Returns `None` if the resulting instant would exceed the bounds of the underlying data
591 /// structure.
592 pub fn add(&self, duration: Duration) -> Option<Self> {
593 self.as_nanos()
594 .checked_add(duration.as_nanos() as i128)
595 .and_then(Self::from_nanos_i128)
596 }
597
598 /// Returns the instant in time one `duration` ago.
599 ///
600 /// Returns `None` if the resulting instant would underflow. As a result, it is important to
601 /// consider that on some platforms the [`StreamInstant`] may begin at `0` from the moment the
602 /// source stream is created.
603 pub fn sub(&self, duration: Duration) -> Option<Self> {
604 self.as_nanos()
605 .checked_sub(duration.as_nanos() as i128)
606 .and_then(Self::from_nanos_i128)
607 }
608
609 fn as_nanos(&self) -> i128 {
610 (self.secs as i128 * 1_000_000_000) + self.nanos as i128
611 }
612
613 #[allow(dead_code)]
614 fn from_nanos(nanos: i64) -> Self {
615 let secs = nanos / 1_000_000_000;
616 let subsec_nanos = nanos - secs * 1_000_000_000;
617 Self::new(secs, subsec_nanos as u32)
618 }
619
620 #[allow(dead_code)]
621 fn from_nanos_i128(nanos: i128) -> Option<Self> {
622 let secs = nanos / 1_000_000_000;
623 if secs > i64::MAX as i128 || secs < i64::MIN as i128 {
624 None
625 } else {
626 let subsec_nanos = nanos - secs * 1_000_000_000;
627 debug_assert!(subsec_nanos < u32::MAX as i128);
628 Some(Self::new(secs as i64, subsec_nanos as u32))
629 }
630 }
631
632 #[allow(dead_code)]
633 fn from_secs_f64(secs: f64) -> crate::StreamInstant {
634 let s = secs.floor() as i64;
635 let ns = ((secs - s as f64) * 1_000_000_000.0) as u32;
636 Self::new(s, ns)
637 }
638
639 pub fn new(secs: i64, nanos: u32) -> Self {
640 StreamInstant { secs, nanos }
641 }
642}
643
644impl InputCallbackInfo {
645 pub fn new(timestamp: InputStreamTimestamp) -> Self {
646 Self { timestamp }
647 }
648
649 /// The timestamp associated with the call to an input stream's data callback.
650 pub fn timestamp(&self) -> InputStreamTimestamp {
651 self.timestamp
652 }
653}
654
655impl OutputCallbackInfo {
656 pub fn new(timestamp: OutputStreamTimestamp) -> Self {
657 Self { timestamp }
658 }
659
660 /// The timestamp associated with the call to an output stream's data callback.
661 pub fn timestamp(&self) -> OutputStreamTimestamp {
662 self.timestamp
663 }
664}
665
666// Note: Data does not implement `is_empty()` because it always contains a valid audio buffer
667// by design. The buffer may contain silence, but it is never structurally empty.
668#[allow(clippy::len_without_is_empty)]
669impl Data {
670 /// Constructor for host implementations to use.
671 ///
672 /// # Safety
673 /// The following requirements must be met in order for the safety of `Data`'s API.
674 /// - The `data` pointer must point to the first sample in the slice containing all samples.
675 /// - The `len` must describe the length of the buffer as a number of samples in the expected
676 /// format specified via the `sample_format` argument.
677 /// - The `sample_format` must correctly represent the underlying sample data delivered/expected
678 /// by the stream.
679 pub unsafe fn from_parts(data: *mut (), len: usize, sample_format: SampleFormat) -> Self {
680 Data {
681 data,
682 len,
683 sample_format,
684 }
685 }
686
687 /// The sample format of the internal audio data.
688 pub fn sample_format(&self) -> SampleFormat {
689 self.sample_format
690 }
691
692 /// The full length of the buffer in samples.
693 ///
694 /// The returned length is the same length as the slice of type `T` that would be returned via
695 /// [`as_slice`](Self::as_slice) given a sample type that matches the inner sample format.
696 pub fn len(&self) -> usize {
697 self.len
698 }
699
700 /// The raw slice of memory representing the underlying audio data as a slice of bytes.
701 ///
702 /// It is up to the user to interpret the slice of memory based on [`Data::sample_format`].
703 pub fn bytes(&self) -> &[u8] {
704 let len = self.len * self.sample_format.sample_size();
705 // The safety of this block relies on correct construction of the `Data` instance.
706 // See the unsafe `from_parts` constructor for these requirements.
707 unsafe { std::slice::from_raw_parts(self.data as *const u8, len) }
708 }
709
710 /// The raw slice of memory representing the underlying audio data as a slice of bytes.
711 ///
712 /// It is up to the user to interpret the slice of memory based on [`Data::sample_format`].
713 pub fn bytes_mut(&mut self) -> &mut [u8] {
714 let len = self.len * self.sample_format.sample_size();
715 // The safety of this block relies on correct construction of the `Data` instance. See
716 // the unsafe `from_parts` constructor for these requirements.
717 unsafe { std::slice::from_raw_parts_mut(self.data as *mut u8, len) }
718 }
719
720 /// Access the data as a slice of sample type `T`.
721 ///
722 /// Returns `None` if the sample type does not match the expected sample format.
723 pub fn as_slice<T>(&self) -> Option<&[T]>
724 where
725 T: SizedSample,
726 {
727 if T::FORMAT == self.sample_format {
728 // The safety of this block relies on correct construction of the `Data` instance. See
729 // the unsafe `from_parts` constructor for these requirements.
730 unsafe { Some(std::slice::from_raw_parts(self.data as *const T, self.len)) }
731 } else {
732 None
733 }
734 }
735
736 /// Access the data as a slice of sample type `T`.
737 ///
738 /// Returns `None` if the sample type does not match the expected sample format.
739 pub fn as_slice_mut<T>(&mut self) -> Option<&mut [T]>
740 where
741 T: SizedSample,
742 {
743 if T::FORMAT == self.sample_format {
744 // The safety of this block relies on correct construction of the `Data` instance. See
745 // the unsafe `from_parts` constructor for these requirements.
746 unsafe {
747 Some(std::slice::from_raw_parts_mut(
748 self.data as *mut T,
749 self.len,
750 ))
751 }
752 } else {
753 None
754 }
755 }
756}
757
758impl SupportedStreamConfigRange {
759 pub fn new(
760 channels: ChannelCount,
761 min_sample_rate: SampleRate,
762 max_sample_rate: SampleRate,
763 buffer_size: SupportedBufferSize,
764 sample_format: SampleFormat,
765 ) -> Self {
766 Self {
767 channels,
768 min_sample_rate,
769 max_sample_rate,
770 buffer_size,
771 sample_format,
772 }
773 }
774
775 pub fn channels(&self) -> ChannelCount {
776 self.channels
777 }
778
779 pub fn min_sample_rate(&self) -> SampleRate {
780 self.min_sample_rate
781 }
782
783 pub fn max_sample_rate(&self) -> SampleRate {
784 self.max_sample_rate
785 }
786
787 pub fn buffer_size(&self) -> &SupportedBufferSize {
788 &self.buffer_size
789 }
790
791 pub fn sample_format(&self) -> SampleFormat {
792 self.sample_format
793 }
794
795 /// Retrieve a [`SupportedStreamConfig`] with the given sample rate and buffer size.
796 ///
797 /// # Panics
798 ///
799 /// Panics if the given `sample_rate` is outside the range specified within
800 /// this [`SupportedStreamConfigRange`] instance. For a non-panicking
801 /// variant, use [`try_with_sample_rate`](#method.try_with_sample_rate).
802 pub fn with_sample_rate(self, sample_rate: SampleRate) -> SupportedStreamConfig {
803 self.try_with_sample_rate(sample_rate)
804 .expect("sample rate out of range")
805 }
806
807 /// Retrieve a [`SupportedStreamConfig`] with the given sample rate and buffer size.
808 ///
809 /// Returns `None` if the given sample rate is outside the range specified
810 /// within this [`SupportedStreamConfigRange`] instance.
811 pub fn try_with_sample_rate(self, sample_rate: SampleRate) -> Option<SupportedStreamConfig> {
812 if self.min_sample_rate <= sample_rate && sample_rate <= self.max_sample_rate {
813 Some(SupportedStreamConfig {
814 channels: self.channels,
815 sample_rate,
816 sample_format: self.sample_format,
817 buffer_size: self.buffer_size,
818 })
819 } else {
820 None
821 }
822 }
823
824 /// Turns this [`SupportedStreamConfigRange`] into a [`SupportedStreamConfig`] corresponding to the maximum sample rate.
825 #[inline]
826 pub fn with_max_sample_rate(self) -> SupportedStreamConfig {
827 SupportedStreamConfig {
828 channels: self.channels,
829 sample_rate: self.max_sample_rate,
830 sample_format: self.sample_format,
831 buffer_size: self.buffer_size,
832 }
833 }
834
835 /// A comparison function which compares two [`SupportedStreamConfigRange`]s in terms of their priority of
836 /// use as a default stream format.
837 ///
838 /// Some backends do not provide a default stream format for their audio devices. In these
839 /// cases, CPAL attempts to decide on a reasonable default format for the user. To do this we
840 /// use the "greatest" of all supported stream formats when compared with this method.
841 ///
842 /// SupportedStreamConfigs are prioritised by the following heuristics:
843 ///
844 /// **Channels**:
845 ///
846 /// - Stereo
847 /// - Mono
848 /// - Max available channels
849 ///
850 /// **Sample format**:
851 /// - f32
852 /// - i16
853 /// - u16
854 ///
855 /// **Sample rate**:
856 ///
857 /// - 44100 (cd quality)
858 /// - Max sample rate
859 pub fn cmp_default_heuristics(&self, other: &Self) -> std::cmp::Ordering {
860 use std::cmp::Ordering::Equal;
861 use SampleFormat::{F32, I16, I24, I32, U16, U24, U32};
862
863 let cmp_stereo = (self.channels == 2).cmp(&(other.channels == 2));
864 if cmp_stereo != Equal {
865 return cmp_stereo;
866 }
867
868 let cmp_mono = (self.channels == 1).cmp(&(other.channels == 1));
869 if cmp_mono != Equal {
870 return cmp_mono;
871 }
872
873 let cmp_channels = self.channels.cmp(&other.channels);
874 if cmp_channels != Equal {
875 return cmp_channels;
876 }
877
878 let cmp_f32 = (self.sample_format == F32).cmp(&(other.sample_format == F32));
879 if cmp_f32 != Equal {
880 return cmp_f32;
881 }
882
883 let cmp_i32 = (self.sample_format == I32).cmp(&(other.sample_format == I32));
884 if cmp_i32 != Equal {
885 return cmp_i32;
886 }
887
888 let cmp_u32 = (self.sample_format == U32).cmp(&(other.sample_format == U32));
889 if cmp_u32 != Equal {
890 return cmp_u32;
891 }
892
893 let cmp_i24 = (self.sample_format == I24).cmp(&(other.sample_format == I24));
894 if cmp_i24 != Equal {
895 return cmp_i24;
896 }
897
898 let cmp_u24 = (self.sample_format == U24).cmp(&(other.sample_format == U24));
899 if cmp_u24 != Equal {
900 return cmp_u24;
901 }
902
903 let cmp_i16 = (self.sample_format == I16).cmp(&(other.sample_format == I16));
904 if cmp_i16 != Equal {
905 return cmp_i16;
906 }
907
908 let cmp_u16 = (self.sample_format == U16).cmp(&(other.sample_format == U16));
909 if cmp_u16 != Equal {
910 return cmp_u16;
911 }
912
913 const HZ_44100: SampleRate = 44_100;
914 let r44100_in_self = self.min_sample_rate <= HZ_44100 && HZ_44100 <= self.max_sample_rate;
915 let r44100_in_other =
916 other.min_sample_rate <= HZ_44100 && HZ_44100 <= other.max_sample_rate;
917 let cmp_r44100 = r44100_in_self.cmp(&r44100_in_other);
918 if cmp_r44100 != Equal {
919 return cmp_r44100;
920 }
921
922 self.max_sample_rate.cmp(&other.max_sample_rate)
923 }
924}
925
926#[test]
927fn test_cmp_default_heuristics() {
928 let mut formats = [
929 SupportedStreamConfigRange {
930 buffer_size: SupportedBufferSize::Range { min: 256, max: 512 },
931 channels: 2,
932 min_sample_rate: 1,
933 max_sample_rate: 96000,
934 sample_format: SampleFormat::F32,
935 },
936 SupportedStreamConfigRange {
937 buffer_size: SupportedBufferSize::Range { min: 256, max: 512 },
938 channels: 1,
939 min_sample_rate: 1,
940 max_sample_rate: 96000,
941 sample_format: SampleFormat::F32,
942 },
943 SupportedStreamConfigRange {
944 buffer_size: SupportedBufferSize::Range { min: 256, max: 512 },
945 channels: 2,
946 min_sample_rate: 1,
947 max_sample_rate: 96000,
948 sample_format: SampleFormat::I16,
949 },
950 SupportedStreamConfigRange {
951 buffer_size: SupportedBufferSize::Range { min: 256, max: 512 },
952 channels: 2,
953 min_sample_rate: 1,
954 max_sample_rate: 96000,
955 sample_format: SampleFormat::U16,
956 },
957 SupportedStreamConfigRange {
958 buffer_size: SupportedBufferSize::Range { min: 256, max: 512 },
959 channels: 2,
960 min_sample_rate: 1,
961 max_sample_rate: 22050,
962 sample_format: SampleFormat::F32,
963 },
964 ];
965
966 formats.sort_by(|a, b| a.cmp_default_heuristics(b));
967
968 // lowest-priority first:
969 assert_eq!(formats[0].sample_format(), SampleFormat::F32);
970 assert_eq!(formats[0].min_sample_rate(), 1);
971 assert_eq!(formats[0].max_sample_rate(), 96000);
972 assert_eq!(formats[0].channels(), 1);
973
974 assert_eq!(formats[1].sample_format(), SampleFormat::U16);
975 assert_eq!(formats[1].min_sample_rate(), 1);
976 assert_eq!(formats[1].max_sample_rate(), 96000);
977 assert_eq!(formats[1].channels(), 2);
978
979 assert_eq!(formats[2].sample_format(), SampleFormat::I16);
980 assert_eq!(formats[2].min_sample_rate(), 1);
981 assert_eq!(formats[2].max_sample_rate(), 96000);
982 assert_eq!(formats[2].channels(), 2);
983
984 assert_eq!(formats[3].sample_format(), SampleFormat::F32);
985 assert_eq!(formats[3].min_sample_rate(), 1);
986 assert_eq!(formats[3].max_sample_rate(), 22050);
987 assert_eq!(formats[3].channels(), 2);
988
989 assert_eq!(formats[4].sample_format(), SampleFormat::F32);
990 assert_eq!(formats[4].min_sample_rate(), 1);
991 assert_eq!(formats[4].max_sample_rate(), 96000);
992 assert_eq!(formats[4].channels(), 2);
993}
994
995impl From<SupportedStreamConfig> for StreamConfig {
996 fn from(conf: SupportedStreamConfig) -> Self {
997 conf.config()
998 }
999}
1000
1001// If a backend does not provide an API for retrieving supported formats, we query it with a bunch
1002// of commonly used rates. This is always the case for WASAPI and is sometimes the case for ALSA.
1003#[allow(dead_code)]
1004pub(crate) const COMMON_SAMPLE_RATES: &[SampleRate] = &[
1005 5512, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000,
1006 176400, 192000, 352800, 384000, 705600, 768000, 1411200, 1536000,
1007];
1008
1009#[test]
1010fn test_stream_instant() {
1011 let a = StreamInstant::new(2, 0);
1012 let b = StreamInstant::new(-2, 0);
1013 let min = StreamInstant::new(i64::MIN, 0);
1014 let max = StreamInstant::new(i64::MAX, 0);
1015 assert_eq!(
1016 a.sub(Duration::from_secs(1)),
1017 Some(StreamInstant::new(1, 0))
1018 );
1019 assert_eq!(
1020 a.sub(Duration::from_secs(2)),
1021 Some(StreamInstant::new(0, 0))
1022 );
1023 assert_eq!(
1024 a.sub(Duration::from_secs(3)),
1025 Some(StreamInstant::new(-1, 0))
1026 );
1027 assert_eq!(min.sub(Duration::from_secs(1)), None);
1028 assert_eq!(
1029 b.add(Duration::from_secs(1)),
1030 Some(StreamInstant::new(-1, 0))
1031 );
1032 assert_eq!(
1033 b.add(Duration::from_secs(2)),
1034 Some(StreamInstant::new(0, 0))
1035 );
1036 assert_eq!(
1037 b.add(Duration::from_secs(3)),
1038 Some(StreamInstant::new(1, 0))
1039 );
1040 assert_eq!(max.add(Duration::from_secs(1)), None);
1041}