cpal/lib.rs
1//! # 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. The callback is passed an instance of either [`&Data` or
82//! `&mut Data`](Data) depending on whether the stream is an input stream or output stream respectively.
83//!
84//! > **Note**: Creating and running a stream will *not* block the thread. On modern platforms, the
85//! > given callback is called by a dedicated, high-priority thread responsible for delivering
86//! > audio data to the system's audio device in a timely manner. On older platforms that only
87//! > provide a blocking API (e.g. ALSA), CPAL will create a thread in order to consistently
88//! > provide non-blocking behaviour (currently this is a thread per stream, but this may change to
89//! > use a single thread for all streams). *If this is an issue for your platform or design,
90//! > please share your issue and use-case with the CPAL team on the GitHub issue tracker for
91//! > consideration.*
92//!
93//! In this example, we simply fill the given output buffer with silence.
94//!
95//! ```no_run
96//! use cpal::{Data, Sample, SampleFormat, FromSample};
97//! use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
98//! # let host = cpal::default_host();
99//! # let device = host.default_output_device().unwrap();
100//! # let supported_config = device.default_output_config().unwrap();
101//! let err_fn = |err| eprintln!("an error occurred on the output audio stream: {}", err);
102//! let sample_format = supported_config.sample_format();
103//! let config = supported_config.into();
104//! let stream = match sample_format {
105//! SampleFormat::F32 => device.build_output_stream(&config, write_silence::<f32>, err_fn, None),
106//! SampleFormat::I16 => device.build_output_stream(&config, write_silence::<i16>, err_fn, None),
107//! SampleFormat::U16 => device.build_output_stream(&config, write_silence::<u16>, err_fn, None),
108//! sample_format => panic!("Unsupported sample format '{sample_format}'")
109//! }.unwrap();
110//!
111//! fn write_silence<T: Sample>(data: &mut [T], _: &cpal::OutputCallbackInfo) {
112//! for sample in data.iter_mut() {
113//! *sample = Sample::EQUILIBRIUM;
114//! }
115//! }
116//! ```
117//!
118//! Not all platforms automatically run the stream upon creation. To ensure the stream has started,
119//! we can use [`Stream::play`](traits::StreamTrait::play).
120//!
121//! ```no_run
122//! # use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
123//! # let host = cpal::default_host();
124//! # let device = host.default_output_device().unwrap();
125//! # let supported_config = device.default_output_config().unwrap();
126//! # let sample_format = supported_config.sample_format();
127//! # let config = supported_config.into();
128//! # let data_fn = move |_data: &mut cpal::Data, _: &cpal::OutputCallbackInfo| {};
129//! # let err_fn = move |_err| {};
130//! # let stream = device.build_output_stream_raw(&config, sample_format, data_fn, err_fn, None).unwrap();
131//! stream.play().unwrap();
132//! ```
133//!
134//! Some devices support pausing the audio stream. This can be useful for saving energy in moments
135//! of silence.
136//!
137//! ```no_run
138//! # use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
139//! # let host = cpal::default_host();
140//! # let device = host.default_output_device().unwrap();
141//! # let supported_config = device.default_output_config().unwrap();
142//! # let sample_format = supported_config.sample_format();
143//! # let config = supported_config.into();
144//! # let data_fn = move |_data: &mut cpal::Data, _: &cpal::OutputCallbackInfo| {};
145//! # let err_fn = move |_err| {};
146//! # let stream = device.build_output_stream_raw(&config, sample_format, data_fn, err_fn, None).unwrap();
147//! stream.pause().unwrap();
148//! ```
149//!
150//! [`default_input_device()`]: traits::HostTrait::default_input_device
151//! [`default_output_device()`]: traits::HostTrait::default_output_device
152//! [`devices()`]: traits::HostTrait::devices
153//! [`supported_input_configs()`]: traits::DeviceTrait::supported_input_configs
154//! [`supported_output_configs()`]: traits::DeviceTrait::supported_output_configs
155
156#![recursion_limit = "2048"]
157
158// Extern crate declarations with `#[macro_use]` must unfortunately be at crate root.
159#[cfg(target_os = "emscripten")]
160#[macro_use]
161extern crate wasm_bindgen;
162#[cfg(target_os = "emscripten")]
163extern crate js_sys;
164#[cfg(target_os = "emscripten")]
165extern crate web_sys;
166
167pub use error::*;
168pub use platform::{
169 available_hosts, default_host, host_from_id, Device, Devices, Host, HostId, Stream,
170 SupportedInputConfigs, SupportedOutputConfigs, ALL_HOSTS,
171};
172pub use samples_formats::{FromSample, Sample, SampleFormat, SizedSample, I24, I48, U24, U48};
173use std::convert::TryInto;
174use std::ops::{Div, Mul};
175use std::time::Duration;
176#[cfg(target_os = "emscripten")]
177use wasm_bindgen::prelude::*;
178
179mod error;
180mod host;
181pub mod platform;
182mod samples_formats;
183pub mod traits;
184
185/// A host's device iterator yielding only *input* devices.
186pub type InputDevices<I> = std::iter::Filter<I, fn(&<I as Iterator>::Item) -> bool>;
187
188/// A host's device iterator yielding only *output* devices.
189pub type OutputDevices<I> = std::iter::Filter<I, fn(&<I as Iterator>::Item) -> bool>;
190
191/// Number of channels.
192pub type ChannelCount = u16;
193
194/// The number of samples processed per second for a single channel of audio.
195#[cfg_attr(target_os = "emscripten", wasm_bindgen)]
196#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
197pub struct SampleRate(pub u32);
198
199impl<T> Mul<T> for SampleRate
200where
201 u32: Mul<T, Output = u32>,
202{
203 type Output = Self;
204 fn mul(self, rhs: T) -> Self {
205 SampleRate(self.0 * rhs)
206 }
207}
208
209impl<T> Div<T> for SampleRate
210where
211 u32: Div<T, Output = u32>,
212{
213 type Output = Self;
214 fn div(self, rhs: T) -> Self {
215 SampleRate(self.0 / rhs)
216 }
217}
218
219/// The desired number of frames for the hardware buffer.
220pub type FrameCount = u32;
221
222/// The buffer size used by the device.
223///
224/// [`Default`] is used when no specific buffer size is set and uses the default
225/// behavior of the given host. Note, the default buffer size may be surprisingly
226/// large, leading to latency issues. If low latency is desired, [`Fixed(FrameCount)`]
227/// should be used in accordance with the [`SupportedBufferSize`] range produced by
228/// the [`SupportedStreamConfig`] API.
229///
230/// [`Default`]: BufferSize::Default
231/// [`Fixed(FrameCount)`]: BufferSize::Fixed
232/// [`SupportedStreamConfig`]: SupportedStreamConfig::buffer_size
233#[derive(Clone, Copy, Debug, Eq, PartialEq)]
234pub enum BufferSize {
235 Default,
236 Fixed(FrameCount),
237}
238
239#[cfg(target_os = "emscripten")]
240impl wasm_bindgen::describe::WasmDescribe for BufferSize {
241 fn describe() {}
242}
243
244#[cfg(target_os = "emscripten")]
245impl wasm_bindgen::convert::IntoWasmAbi for BufferSize {
246 type Abi = <Option<FrameCount> as wasm_bindgen::convert::IntoWasmAbi>::Abi;
247
248 fn into_abi(self) -> Self::Abi {
249 match self {
250 Self::Default => None,
251 Self::Fixed(fc) => Some(fc),
252 }
253 .into_abi()
254 }
255}
256
257/// The set of parameters used to describe how to open a stream.
258///
259/// The sample format is omitted in favour of using a sample type.
260#[cfg_attr(target_os = "emscripten", wasm_bindgen)]
261#[derive(Clone, Debug, Eq, PartialEq)]
262pub struct StreamConfig {
263 pub channels: ChannelCount,
264 pub sample_rate: SampleRate,
265 pub buffer_size: BufferSize,
266}
267
268/// Describes the minimum and maximum supported buffer size for the device
269#[derive(Clone, Copy, Debug, Eq, PartialEq)]
270pub enum SupportedBufferSize {
271 Range {
272 min: FrameCount,
273 max: FrameCount,
274 },
275 /// In the case that the platform provides no way of getting the default
276 /// buffersize before starting a stream.
277 Unknown,
278}
279
280/// Describes a range of supported stream configurations, retrieved via the
281/// [`Device::supported_input/output_configs`](traits::DeviceTrait#required-methods) method.
282#[derive(Debug, Clone, Copy, PartialEq, Eq)]
283pub struct SupportedStreamConfigRange {
284 pub(crate) channels: ChannelCount,
285 /// Minimum value for the samples rate of the supported formats.
286 pub(crate) min_sample_rate: SampleRate,
287 /// Maximum value for the samples rate of the supported formats.
288 pub(crate) max_sample_rate: SampleRate,
289 /// Buffersize ranges supported by the device
290 pub(crate) buffer_size: SupportedBufferSize,
291 /// Type of data expected by the device.
292 pub(crate) sample_format: SampleFormat,
293}
294
295/// Describes a single supported stream configuration, retrieved via either a
296/// [`SupportedStreamConfigRange`] instance or one of the
297/// [`Device::default_input/output_config`](traits::DeviceTrait#required-methods) methods.
298#[derive(Debug, Clone, PartialEq, Eq)]
299pub struct SupportedStreamConfig {
300 channels: ChannelCount,
301 sample_rate: SampleRate,
302 buffer_size: SupportedBufferSize,
303 sample_format: SampleFormat,
304}
305
306/// A buffer of dynamically typed audio data, passed to raw stream callbacks.
307///
308/// Raw input stream callbacks receive `&Data`, while raw output stream callbacks expect `&mut
309/// Data`.
310#[cfg_attr(target_os = "emscripten", wasm_bindgen)]
311#[derive(Debug)]
312pub struct Data {
313 data: *mut (),
314 len: usize,
315 sample_format: SampleFormat,
316}
317
318/// A monotonic time instance associated with a stream, retrieved from either:
319///
320/// 1. A timestamp provided to the stream's underlying audio data callback or
321/// 2. The same time source used to generate timestamps for a stream's underlying audio data
322/// callback.
323///
324/// `StreamInstant` represents a duration since some unspecified origin occurring either before
325/// or equal to the moment the stream from which it was created begins.
326///
327/// ## Host `StreamInstant` Sources
328///
329/// | Host | Source |
330/// | ---- | ------ |
331/// | alsa | `snd_pcm_status_get_htstamp` |
332/// | coreaudio | `mach_absolute_time` |
333/// | wasapi | `QueryPerformanceCounter` |
334/// | asio | `timeGetTime` |
335/// | emscripten | `AudioContext.getOutputTimestamp` |
336#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
337pub struct StreamInstant {
338 secs: i64,
339 nanos: u32,
340}
341
342/// A timestamp associated with a call to an input stream's data callback.
343#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
344pub struct InputStreamTimestamp {
345 /// The instant the stream's data callback was invoked.
346 pub callback: StreamInstant,
347 /// The instant that data was captured from the device.
348 ///
349 /// E.g. The instant data was read from an ADC.
350 pub capture: StreamInstant,
351}
352
353/// A timestamp associated with a call to an output stream's data callback.
354#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
355pub struct OutputStreamTimestamp {
356 /// The instant the stream's data callback was invoked.
357 pub callback: StreamInstant,
358 /// The predicted instant that data written will be delivered to the device for playback.
359 ///
360 /// E.g. The instant data will be played by a DAC.
361 pub playback: StreamInstant,
362}
363
364/// Information relevant to a single call to the user's input stream data callback.
365#[derive(Debug, Clone, PartialEq, Eq)]
366pub struct InputCallbackInfo {
367 timestamp: InputStreamTimestamp,
368}
369
370/// Information relevant to a single call to the user's output stream data callback.
371#[cfg_attr(target_os = "emscripten", wasm_bindgen)]
372#[derive(Debug, Clone, PartialEq, Eq)]
373pub struct OutputCallbackInfo {
374 timestamp: OutputStreamTimestamp,
375}
376
377impl SupportedStreamConfig {
378 pub fn new(
379 channels: ChannelCount,
380 sample_rate: SampleRate,
381 buffer_size: SupportedBufferSize,
382 sample_format: SampleFormat,
383 ) -> Self {
384 Self {
385 channels,
386 sample_rate,
387 buffer_size,
388 sample_format,
389 }
390 }
391
392 pub fn channels(&self) -> ChannelCount {
393 self.channels
394 }
395
396 pub fn sample_rate(&self) -> SampleRate {
397 self.sample_rate
398 }
399
400 pub fn buffer_size(&self) -> &SupportedBufferSize {
401 &self.buffer_size
402 }
403
404 pub fn sample_format(&self) -> SampleFormat {
405 self.sample_format
406 }
407
408 pub fn config(&self) -> StreamConfig {
409 StreamConfig {
410 channels: self.channels,
411 sample_rate: self.sample_rate,
412 buffer_size: BufferSize::Default,
413 }
414 }
415}
416
417impl StreamInstant {
418 /// The amount of time elapsed from another instant to this one.
419 ///
420 /// Returns `None` if `earlier` is later than self.
421 pub fn duration_since(&self, earlier: &Self) -> Option<Duration> {
422 if self < earlier {
423 None
424 } else {
425 (self.as_nanos() - earlier.as_nanos())
426 .try_into()
427 .ok()
428 .map(Duration::from_nanos)
429 }
430 }
431
432 /// Returns the instant in time after the given duration has passed.
433 ///
434 /// Returns `None` if the resulting instant would exceed the bounds of the underlying data
435 /// structure.
436 pub fn add(&self, duration: Duration) -> Option<Self> {
437 self.as_nanos()
438 .checked_add(duration.as_nanos() as i128)
439 .and_then(Self::from_nanos_i128)
440 }
441
442 /// Returns the instant in time one `duration` ago.
443 ///
444 /// Returns `None` if the resulting instant would underflow. As a result, it is important to
445 /// consider that on some platforms the [`StreamInstant`] may begin at `0` from the moment the
446 /// source stream is created.
447 pub fn sub(&self, duration: Duration) -> Option<Self> {
448 self.as_nanos()
449 .checked_sub(duration.as_nanos() as i128)
450 .and_then(Self::from_nanos_i128)
451 }
452
453 fn as_nanos(&self) -> i128 {
454 (self.secs as i128 * 1_000_000_000) + self.nanos as i128
455 }
456
457 #[allow(dead_code)]
458 fn from_nanos(nanos: i64) -> Self {
459 let secs = nanos / 1_000_000_000;
460 let subsec_nanos = nanos - secs * 1_000_000_000;
461 Self::new(secs, subsec_nanos as u32)
462 }
463
464 #[allow(dead_code)]
465 fn from_nanos_i128(nanos: i128) -> Option<Self> {
466 let secs = nanos / 1_000_000_000;
467 if secs > i64::MAX as i128 || secs < i64::MIN as i128 {
468 None
469 } else {
470 let subsec_nanos = nanos - secs * 1_000_000_000;
471 debug_assert!(subsec_nanos < u32::MAX as i128);
472 Some(Self::new(secs as i64, subsec_nanos as u32))
473 }
474 }
475
476 #[allow(dead_code)]
477 fn from_secs_f64(secs: f64) -> crate::StreamInstant {
478 let s = secs.floor() as i64;
479 let ns = ((secs - s as f64) * 1_000_000_000.0) as u32;
480 Self::new(s, ns)
481 }
482
483 pub fn new(secs: i64, nanos: u32) -> Self {
484 StreamInstant { secs, nanos }
485 }
486}
487
488impl InputCallbackInfo {
489 pub fn new(timestamp: InputStreamTimestamp) -> Self {
490 Self { timestamp }
491 }
492
493 /// The timestamp associated with the call to an input stream's data callback.
494 pub fn timestamp(&self) -> InputStreamTimestamp {
495 self.timestamp
496 }
497}
498
499impl OutputCallbackInfo {
500 pub fn new(timestamp: OutputStreamTimestamp) -> Self {
501 Self { timestamp }
502 }
503
504 /// The timestamp associated with the call to an output stream's data callback.
505 pub fn timestamp(&self) -> OutputStreamTimestamp {
506 self.timestamp
507 }
508}
509
510#[allow(clippy::len_without_is_empty)]
511impl Data {
512 // Internal constructor for host implementations to use.
513 //
514 // The following requirements must be met in order for the safety of `Data`'s public API.
515 //
516 // - The `data` pointer must point to the first sample in the slice containing all samples.
517 // - The `len` must describe the length of the buffer as a number of samples in the expected
518 // format specified via the `sample_format` argument.
519 // - The `sample_format` must correctly represent the underlying sample data delivered/expected
520 // by the stream.
521 pub(crate) unsafe fn from_parts(
522 data: *mut (),
523 len: usize,
524 sample_format: SampleFormat,
525 ) -> Self {
526 Data {
527 data,
528 len,
529 sample_format,
530 }
531 }
532
533 /// The sample format of the internal audio data.
534 pub fn sample_format(&self) -> SampleFormat {
535 self.sample_format
536 }
537
538 /// The full length of the buffer in samples.
539 ///
540 /// The returned length is the same length as the slice of type `T` that would be returned via
541 /// [`as_slice`](Self::as_slice) given a sample type that matches the inner sample format.
542 pub fn len(&self) -> usize {
543 self.len
544 }
545
546 /// The raw slice of memory representing the underlying audio data as a slice of bytes.
547 ///
548 /// It is up to the user to interpret the slice of memory based on [`Data::sample_format`].
549 pub fn bytes(&self) -> &[u8] {
550 let len = self.len * self.sample_format.sample_size();
551 // The safety of this block relies on correct construction of the `Data` instance.
552 // See the unsafe `from_parts` constructor for these requirements.
553 unsafe { std::slice::from_raw_parts(self.data as *const u8, len) }
554 }
555
556 /// The raw slice of memory representing the underlying audio data as a slice of bytes.
557 ///
558 /// It is up to the user to interpret the slice of memory based on [`Data::sample_format`].
559 pub fn bytes_mut(&mut self) -> &mut [u8] {
560 let len = self.len * self.sample_format.sample_size();
561 // The safety of this block relies on correct construction of the `Data` instance. See
562 // the unsafe `from_parts` constructor for these requirements.
563 unsafe { std::slice::from_raw_parts_mut(self.data as *mut u8, len) }
564 }
565
566 /// Access the data as a slice of sample type `T`.
567 ///
568 /// Returns `None` if the sample type does not match the expected sample format.
569 pub fn as_slice<T>(&self) -> Option<&[T]>
570 where
571 T: SizedSample,
572 {
573 if T::FORMAT == self.sample_format {
574 // The safety of this block relies on correct construction of the `Data` instance. See
575 // the unsafe `from_parts` constructor for these requirements.
576 unsafe { Some(std::slice::from_raw_parts(self.data as *const T, self.len)) }
577 } else {
578 None
579 }
580 }
581
582 /// Access the data as a slice of sample type `T`.
583 ///
584 /// Returns `None` if the sample type does not match the expected sample format.
585 pub fn as_slice_mut<T>(&mut self) -> Option<&mut [T]>
586 where
587 T: SizedSample,
588 {
589 if T::FORMAT == self.sample_format {
590 // The safety of this block relies on correct construction of the `Data` instance. See
591 // the unsafe `from_parts` constructor for these requirements.
592 unsafe {
593 Some(std::slice::from_raw_parts_mut(
594 self.data as *mut T,
595 self.len,
596 ))
597 }
598 } else {
599 None
600 }
601 }
602}
603
604impl SupportedStreamConfigRange {
605 pub fn new(
606 channels: ChannelCount,
607 min_sample_rate: SampleRate,
608 max_sample_rate: SampleRate,
609 buffer_size: SupportedBufferSize,
610 sample_format: SampleFormat,
611 ) -> Self {
612 Self {
613 channels,
614 min_sample_rate,
615 max_sample_rate,
616 buffer_size,
617 sample_format,
618 }
619 }
620
621 pub fn channels(&self) -> ChannelCount {
622 self.channels
623 }
624
625 pub fn min_sample_rate(&self) -> SampleRate {
626 self.min_sample_rate
627 }
628
629 pub fn max_sample_rate(&self) -> SampleRate {
630 self.max_sample_rate
631 }
632
633 pub fn buffer_size(&self) -> &SupportedBufferSize {
634 &self.buffer_size
635 }
636
637 pub fn sample_format(&self) -> SampleFormat {
638 self.sample_format
639 }
640
641 /// Retrieve a [`SupportedStreamConfig`] with the given sample rate and buffer size.
642 ///
643 /// # Panics
644 ///
645 /// Panics if the given `sample_rate` is outside the range specified within
646 /// this [`SupportedStreamConfigRange`] instance. For a non-panicking
647 /// variant, use [`try_with_sample_rate`](#method.try_with_sample_rate).
648 pub fn with_sample_rate(self, sample_rate: SampleRate) -> SupportedStreamConfig {
649 self.try_with_sample_rate(sample_rate)
650 .expect("sample rate out of range")
651 }
652
653 /// Retrieve a [`SupportedStreamConfig`] with the given sample rate and buffer size.
654 ///
655 /// Returns `None` if the given sample rate is outside the range specified
656 /// within this [`SupportedStreamConfigRange`] instance.
657 pub fn try_with_sample_rate(self, sample_rate: SampleRate) -> Option<SupportedStreamConfig> {
658 if self.min_sample_rate <= sample_rate && sample_rate <= self.max_sample_rate {
659 Some(SupportedStreamConfig {
660 channels: self.channels,
661 sample_rate,
662 sample_format: self.sample_format,
663 buffer_size: self.buffer_size,
664 })
665 } else {
666 None
667 }
668 }
669
670 /// Turns this [`SupportedStreamConfigRange`] into a [`SupportedStreamConfig`] corresponding to the maximum samples rate.
671 #[inline]
672 pub fn with_max_sample_rate(self) -> SupportedStreamConfig {
673 SupportedStreamConfig {
674 channels: self.channels,
675 sample_rate: self.max_sample_rate,
676 sample_format: self.sample_format,
677 buffer_size: self.buffer_size,
678 }
679 }
680
681 /// A comparison function which compares two [`SupportedStreamConfigRange`]s in terms of their priority of
682 /// use as a default stream format.
683 ///
684 /// Some backends do not provide a default stream format for their audio devices. In these
685 /// cases, CPAL attempts to decide on a reasonable default format for the user. To do this we
686 /// use the "greatest" of all supported stream formats when compared with this method.
687 ///
688 /// SupportedStreamConfigs are prioritised by the following heuristics:
689 ///
690 /// **Channels**:
691 ///
692 /// - Stereo
693 /// - Mono
694 /// - Max available channels
695 ///
696 /// **Sample format**:
697 /// - f32
698 /// - i16
699 /// - u16
700 ///
701 /// **Sample rate**:
702 ///
703 /// - 44100 (cd quality)
704 /// - Max sample rate
705 pub fn cmp_default_heuristics(&self, other: &Self) -> std::cmp::Ordering {
706 use std::cmp::Ordering::Equal;
707 use SampleFormat::{F32, I16, U16};
708
709 let cmp_stereo = (self.channels == 2).cmp(&(other.channels == 2));
710 if cmp_stereo != Equal {
711 return cmp_stereo;
712 }
713
714 let cmp_mono = (self.channels == 1).cmp(&(other.channels == 1));
715 if cmp_mono != Equal {
716 return cmp_mono;
717 }
718
719 let cmp_channels = self.channels.cmp(&other.channels);
720 if cmp_channels != Equal {
721 return cmp_channels;
722 }
723
724 let cmp_f32 = (self.sample_format == F32).cmp(&(other.sample_format == F32));
725 if cmp_f32 != Equal {
726 return cmp_f32;
727 }
728
729 let cmp_i16 = (self.sample_format == I16).cmp(&(other.sample_format == I16));
730 if cmp_i16 != Equal {
731 return cmp_i16;
732 }
733
734 let cmp_u16 = (self.sample_format == U16).cmp(&(other.sample_format == U16));
735 if cmp_u16 != Equal {
736 return cmp_u16;
737 }
738
739 const HZ_44100: SampleRate = SampleRate(44_100);
740 let r44100_in_self = self.min_sample_rate <= HZ_44100 && HZ_44100 <= self.max_sample_rate;
741 let r44100_in_other =
742 other.min_sample_rate <= HZ_44100 && HZ_44100 <= other.max_sample_rate;
743 let cmp_r44100 = r44100_in_self.cmp(&r44100_in_other);
744 if cmp_r44100 != Equal {
745 return cmp_r44100;
746 }
747
748 self.max_sample_rate.cmp(&other.max_sample_rate)
749 }
750}
751
752#[test]
753fn test_cmp_default_heuristics() {
754 let mut formats = [
755 SupportedStreamConfigRange {
756 buffer_size: SupportedBufferSize::Range { min: 256, max: 512 },
757 channels: 2,
758 min_sample_rate: SampleRate(1),
759 max_sample_rate: SampleRate(96000),
760 sample_format: SampleFormat::F32,
761 },
762 SupportedStreamConfigRange {
763 buffer_size: SupportedBufferSize::Range { min: 256, max: 512 },
764 channels: 1,
765 min_sample_rate: SampleRate(1),
766 max_sample_rate: SampleRate(96000),
767 sample_format: SampleFormat::F32,
768 },
769 SupportedStreamConfigRange {
770 buffer_size: SupportedBufferSize::Range { min: 256, max: 512 },
771 channels: 2,
772 min_sample_rate: SampleRate(1),
773 max_sample_rate: SampleRate(96000),
774 sample_format: SampleFormat::I16,
775 },
776 SupportedStreamConfigRange {
777 buffer_size: SupportedBufferSize::Range { min: 256, max: 512 },
778 channels: 2,
779 min_sample_rate: SampleRate(1),
780 max_sample_rate: SampleRate(96000),
781 sample_format: SampleFormat::U16,
782 },
783 SupportedStreamConfigRange {
784 buffer_size: SupportedBufferSize::Range { min: 256, max: 512 },
785 channels: 2,
786 min_sample_rate: SampleRate(1),
787 max_sample_rate: SampleRate(22050),
788 sample_format: SampleFormat::F32,
789 },
790 ];
791
792 formats.sort_by(|a, b| a.cmp_default_heuristics(b));
793
794 // lowest-priority first:
795 assert_eq!(formats[0].sample_format(), SampleFormat::F32);
796 assert_eq!(formats[0].min_sample_rate(), SampleRate(1));
797 assert_eq!(formats[0].max_sample_rate(), SampleRate(96000));
798 assert_eq!(formats[0].channels(), 1);
799
800 assert_eq!(formats[1].sample_format(), SampleFormat::U16);
801 assert_eq!(formats[1].min_sample_rate(), SampleRate(1));
802 assert_eq!(formats[1].max_sample_rate(), SampleRate(96000));
803 assert_eq!(formats[1].channels(), 2);
804
805 assert_eq!(formats[2].sample_format(), SampleFormat::I16);
806 assert_eq!(formats[2].min_sample_rate(), SampleRate(1));
807 assert_eq!(formats[2].max_sample_rate(), SampleRate(96000));
808 assert_eq!(formats[2].channels(), 2);
809
810 assert_eq!(formats[3].sample_format(), SampleFormat::F32);
811 assert_eq!(formats[3].min_sample_rate(), SampleRate(1));
812 assert_eq!(formats[3].max_sample_rate(), SampleRate(22050));
813 assert_eq!(formats[3].channels(), 2);
814
815 assert_eq!(formats[4].sample_format(), SampleFormat::F32);
816 assert_eq!(formats[4].min_sample_rate(), SampleRate(1));
817 assert_eq!(formats[4].max_sample_rate(), SampleRate(96000));
818 assert_eq!(formats[4].channels(), 2);
819}
820
821impl From<SupportedStreamConfig> for StreamConfig {
822 fn from(conf: SupportedStreamConfig) -> Self {
823 conf.config()
824 }
825}
826
827// If a backend does not provide an API for retrieving supported formats, we query it with a bunch
828// of commonly used rates. This is always the case for wasapi and is sometimes the case for alsa.
829//
830// If a rate you desire is missing from this list, feel free to add it!
831#[cfg(target_os = "windows")]
832const COMMON_SAMPLE_RATES: &[SampleRate] = &[
833 SampleRate(5512),
834 SampleRate(8000),
835 SampleRate(11025),
836 SampleRate(16000),
837 SampleRate(22050),
838 SampleRate(32000),
839 SampleRate(44100),
840 SampleRate(48000),
841 SampleRate(64000),
842 SampleRate(88200),
843 SampleRate(96000),
844 SampleRate(176400),
845 SampleRate(192000),
846 SampleRate(384000),
847];
848
849#[test]
850fn test_stream_instant() {
851 let a = StreamInstant::new(2, 0);
852 let b = StreamInstant::new(-2, 0);
853 let min = StreamInstant::new(i64::MIN, 0);
854 let max = StreamInstant::new(i64::MAX, 0);
855 assert_eq!(
856 a.sub(Duration::from_secs(1)),
857 Some(StreamInstant::new(1, 0))
858 );
859 assert_eq!(
860 a.sub(Duration::from_secs(2)),
861 Some(StreamInstant::new(0, 0))
862 );
863 assert_eq!(
864 a.sub(Duration::from_secs(3)),
865 Some(StreamInstant::new(-1, 0))
866 );
867 assert_eq!(min.sub(Duration::from_secs(1)), None);
868 assert_eq!(
869 b.add(Duration::from_secs(1)),
870 Some(StreamInstant::new(-1, 0))
871 );
872 assert_eq!(
873 b.add(Duration::from_secs(2)),
874 Some(StreamInstant::new(0, 0))
875 );
876 assert_eq!(
877 b.add(Duration::from_secs(3)),
878 Some(StreamInstant::new(1, 0))
879 );
880 assert_eq!(max.add(Duration::from_secs(1)), None);
881}