2017-10-23 14:41:38 +00:00
|
|
|
//! # How to use cpal
|
|
|
|
//!
|
|
|
|
//! Here are some concepts cpal exposes:
|
|
|
|
//!
|
2018-02-12 13:10:24 +00:00
|
|
|
//! - A `Device` is an audio device that may have any number of input and output streams.
|
|
|
|
//! - A stream is an open audio channel. Input streams allow you to receive audio data, output
|
|
|
|
//! streams allow you to play audio data. You must choose which `Device` runs your stream before
|
|
|
|
//! you create one.
|
|
|
|
//! - An `EventLoop` is a collection of streams being run by one or more `Device`. Each stream must
|
|
|
|
//! belong to an `EventLoop`, and all the streams that belong to an `EventLoop` are managed
|
|
|
|
//! together.
|
2017-10-23 14:41:38 +00:00
|
|
|
//!
|
2018-02-12 13:10:24 +00:00
|
|
|
//! The first step is to create an `EventLoop`:
|
2017-10-23 14:41:38 +00:00
|
|
|
//!
|
|
|
|
//! ```
|
|
|
|
//! use cpal::EventLoop;
|
|
|
|
//! let event_loop = EventLoop::new();
|
|
|
|
//! ```
|
|
|
|
//!
|
2018-02-12 13:10:24 +00:00
|
|
|
//! Then choose a `Device`. The easiest way is to use the default input or output `Device` via the
|
|
|
|
//! `default_input_device()` or `default_output_device()` functions. Alternatively you can
|
|
|
|
//! enumerate all the available devices with the `devices()` function. Beware that the
|
|
|
|
//! `default_*_device()` functions return an `Option` in case no device is available for that
|
|
|
|
//! stream type on the system.
|
2017-10-23 14:41:38 +00:00
|
|
|
//!
|
|
|
|
//! ```
|
2018-02-12 13:10:24 +00:00
|
|
|
//! let device = cpal::default_output_device().expect("no output device available");
|
2017-10-23 14:41:38 +00:00
|
|
|
//! ```
|
|
|
|
//!
|
2018-02-12 13:10:24 +00:00
|
|
|
//! Before we can create a stream, we must decide what the format of the audio samples is going to
|
|
|
|
//! be. You can query all the supported formats with the `supported_input_formats()` and
|
|
|
|
//! `supported_output_formats()` methods. These produce a list of `SupportedFormat` structs which
|
|
|
|
//! can later be turned into actual `Format` structs. If you don't want to query the list of
|
|
|
|
//! formats, you can also build your own `Format` manually, but doing so could lead to an error
|
|
|
|
//! when building the stream if the format is not supported by the device.
|
2017-10-23 14:41:38 +00:00
|
|
|
//!
|
|
|
|
//! > **Note**: the `supported_formats()` method could return an error for example if the device
|
|
|
|
//! > has been disconnected.
|
|
|
|
//!
|
|
|
|
//! ```no_run
|
2018-02-12 13:10:24 +00:00
|
|
|
//! # let device = cpal::default_output_device().unwrap();
|
|
|
|
//! let mut supported_formats_range = device.supported_output_formats()
|
|
|
|
//! .expect("error while querying formats");
|
|
|
|
//! let format = supported_formats_range.next()
|
|
|
|
//! .expect("no supported format?!")
|
|
|
|
//! .with_max_sample_rate();
|
2017-10-23 14:41:38 +00:00
|
|
|
//! ```
|
|
|
|
//!
|
2018-02-12 13:10:24 +00:00
|
|
|
//! Now that we have everything, we can create a stream from our event loop:
|
2017-10-23 14:41:38 +00:00
|
|
|
//!
|
|
|
|
//! ```no_run
|
2018-02-12 13:10:24 +00:00
|
|
|
//! # let device = cpal::default_output_device().unwrap();
|
|
|
|
//! # let format = device.supported_output_formats().unwrap().next().unwrap().with_max_sample_rate();
|
2017-10-23 14:41:38 +00:00
|
|
|
//! # let event_loop = cpal::EventLoop::new();
|
2018-02-12 13:10:24 +00:00
|
|
|
//! let stream_id = event_loop.build_output_stream(&device, &format).unwrap();
|
2017-10-23 14:41:38 +00:00
|
|
|
//! ```
|
|
|
|
//!
|
2018-02-12 13:10:24 +00:00
|
|
|
//! The value returned by `build_output_stream()` is of type `StreamId` and is an identifier that
|
|
|
|
//! will allow you to control the stream.
|
2017-10-23 14:41:38 +00:00
|
|
|
//!
|
2018-02-12 13:10:24 +00:00
|
|
|
//! Now we must start the stream. This is done with the `play_stream()` method on the event loop.
|
2017-10-23 14:41:38 +00:00
|
|
|
//!
|
|
|
|
//! ```
|
|
|
|
//! # let event_loop: cpal::EventLoop = return;
|
2018-02-12 13:10:24 +00:00
|
|
|
//! # let stream_id: cpal::StreamId = return;
|
|
|
|
//! event_loop.play_stream(stream_id);
|
2017-10-23 14:41:38 +00:00
|
|
|
//! ```
|
|
|
|
//!
|
2018-02-12 13:10:24 +00:00
|
|
|
//! Once everything is ready! Now we call `run()` on the `event_loop` to begin processing.
|
2017-10-23 14:41:38 +00:00
|
|
|
//!
|
|
|
|
//! ```no_run
|
|
|
|
//! # let event_loop = cpal::EventLoop::new();
|
2018-02-12 13:10:24 +00:00
|
|
|
//! event_loop.run(move |_stream_id, _stream_data| {
|
|
|
|
//! // read or write stream data here
|
2017-10-23 14:41:38 +00:00
|
|
|
//! });
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! > **Note**: Calling `run()` will block the thread forever, so it's usually best done in a
|
|
|
|
//! > separate thread.
|
|
|
|
//!
|
|
|
|
//! While `run()` is running, the audio device of the user will from time to time call the callback
|
2018-02-12 13:10:24 +00:00
|
|
|
//! that you passed to this function. The callback gets passed the stream ID an instance of type
|
|
|
|
//! `StreamData` that represents the data that must be read from or written to. The inner
|
|
|
|
//! `UnknownTypeOutputBuffer` can be one of `I16`, `U16` or `F32` depending on the format that was
|
|
|
|
//! passed to `build_output_stream`.
|
2017-10-23 14:41:38 +00:00
|
|
|
//!
|
2018-02-12 13:10:24 +00:00
|
|
|
//! In this example, we simply simply fill the given output buffer with zeroes.
|
2017-10-23 14:41:38 +00:00
|
|
|
//!
|
|
|
|
//! ```no_run
|
2018-02-12 13:10:24 +00:00
|
|
|
//! use cpal::{StreamData, UnknownTypeOutputBuffer};
|
2017-10-23 14:41:38 +00:00
|
|
|
//!
|
|
|
|
//! # let event_loop = cpal::EventLoop::new();
|
2018-02-12 13:10:24 +00:00
|
|
|
//! event_loop.run(move |_stream_id, mut stream_data| {
|
|
|
|
//! match stream_data {
|
|
|
|
//! StreamData::Output { buffer: UnknownTypeOutputBuffer::U16(mut buffer) } => {
|
2017-10-23 14:41:38 +00:00
|
|
|
//! for elem in buffer.iter_mut() {
|
|
|
|
//! *elem = u16::max_value() / 2;
|
|
|
|
//! }
|
|
|
|
//! },
|
2018-02-12 13:10:24 +00:00
|
|
|
//! StreamData::Output { buffer: UnknownTypeOutputBuffer::I16(mut buffer) } => {
|
2017-10-23 14:41:38 +00:00
|
|
|
//! for elem in buffer.iter_mut() {
|
|
|
|
//! *elem = 0;
|
|
|
|
//! }
|
|
|
|
//! },
|
2018-02-12 13:10:24 +00:00
|
|
|
//! StreamData::Output { buffer: UnknownTypeOutputBuffer::F32(mut buffer) } => {
|
2017-10-23 14:41:38 +00:00
|
|
|
//! for elem in buffer.iter_mut() {
|
|
|
|
//! *elem = 0.0;
|
|
|
|
//! }
|
|
|
|
//! },
|
2018-02-12 13:10:24 +00:00
|
|
|
//! _ => (),
|
2017-10-23 14:41:38 +00:00
|
|
|
//! }
|
|
|
|
//! });
|
|
|
|
//! ```
|
2016-08-02 14:13:59 +00:00
|
|
|
|
2017-10-22 12:17:25 +00:00
|
|
|
#![recursion_limit = "512"]
|
|
|
|
|
2017-11-03 09:51:02 +00:00
|
|
|
#[cfg(target_os = "windows")]
|
2015-09-01 09:23:41 +00:00
|
|
|
#[macro_use]
|
|
|
|
extern crate lazy_static;
|
|
|
|
|
2017-10-22 12:17:25 +00:00
|
|
|
// Extern crate declarations with `#[macro_use]` must unfortunately be at crate root.
|
|
|
|
#[cfg(target_os = "emscripten")]
|
|
|
|
#[macro_use]
|
|
|
|
extern crate stdweb;
|
|
|
|
|
2017-10-11 11:24:49 +00:00
|
|
|
pub use samples_formats::{Sample, SampleFormat};
|
2014-12-17 07:47:19 +00:00
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
#[cfg(not(any(windows, target_os = "linux", target_os = "freebsd",
|
|
|
|
target_os = "macos", target_os = "ios", target_os = "emscripten")))]
|
2015-09-10 16:37:37 +00:00
|
|
|
use null as cpal_impl;
|
|
|
|
|
2015-09-01 09:23:41 +00:00
|
|
|
use std::error::Error;
|
2017-10-11 11:24:49 +00:00
|
|
|
use std::fmt;
|
2018-02-12 13:10:24 +00:00
|
|
|
use std::iter;
|
2015-01-05 09:52:59 +00:00
|
|
|
use std::ops::{Deref, DerefMut};
|
|
|
|
|
2015-09-10 16:37:37 +00:00
|
|
|
mod null;
|
2014-12-17 07:47:19 +00:00
|
|
|
mod samples_formats;
|
2014-12-15 10:45:38 +00:00
|
|
|
|
2017-07-13 11:58:01 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
2017-10-11 11:24:49 +00:00
|
|
|
#[path = "alsa/mod.rs"]
|
2014-12-17 08:13:58 +00:00
|
|
|
mod cpal_impl;
|
2015-02-22 13:50:06 +00:00
|
|
|
|
2014-12-11 13:22:55 +00:00
|
|
|
#[cfg(windows)]
|
2017-10-11 11:24:49 +00:00
|
|
|
#[path = "wasapi/mod.rs"]
|
2014-12-17 08:13:58 +00:00
|
|
|
mod cpal_impl;
|
2014-12-11 16:23:33 +00:00
|
|
|
|
2017-10-10 16:24:53 +00:00
|
|
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
2017-10-11 11:24:49 +00:00
|
|
|
#[path = "coreaudio/mod.rs"]
|
2015-02-22 13:50:06 +00:00
|
|
|
mod cpal_impl;
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
#[cfg(target_os = "emscripten")]
|
|
|
|
#[path = "emscripten/mod.rs"]
|
|
|
|
mod cpal_impl;
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// An opaque type that identifies a device that is capable of either audio input or output.
|
2017-10-23 14:41:38 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// Please note that `Device`s may become invalid if they get disconnected. Therefore all the
|
|
|
|
/// methods that involve a device return a `Result`.
|
|
|
|
#[derive(Clone, PartialEq, Eq)]
|
|
|
|
pub struct Device(cpal_impl::Device);
|
2015-09-01 09:23:41 +00:00
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// Collection of voices managed together.
|
|
|
|
///
|
|
|
|
/// Created with the [`new`](struct.EventLoop.html#method.new) method.
|
|
|
|
pub struct EventLoop(cpal_impl::EventLoop);
|
2015-09-01 09:23:41 +00:00
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// Identifier of a stream within the `EventLoop`.
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
pub struct StreamId(cpal_impl::StreamId);
|
2015-09-01 09:23:41 +00:00
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// Number of channels.
|
|
|
|
pub type ChannelCount = u16;
|
|
|
|
|
|
|
|
/// The number of samples processed per second for a single channel of audio.
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
|
|
pub struct SampleRate(pub u32);
|
|
|
|
|
|
|
|
/// The format of an input or output audio stream.
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
pub struct Format {
|
|
|
|
pub channels: ChannelCount,
|
|
|
|
pub sample_rate: SampleRate,
|
|
|
|
pub data_type: SampleFormat,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Describes a range of supported stream formats.
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
pub struct SupportedFormat {
|
|
|
|
pub channels: ChannelCount,
|
|
|
|
/// Minimum value for the samples rate of the supported formats.
|
|
|
|
pub min_sample_rate: SampleRate,
|
|
|
|
/// Maximum value for the samples rate of the supported formats.
|
|
|
|
pub max_sample_rate: SampleRate,
|
|
|
|
/// Type of data expected by the device.
|
|
|
|
pub data_type: SampleFormat,
|
2015-09-01 09:23:41 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// Stream data passed to the `EventLoop::run` callback.
|
|
|
|
pub enum StreamData<'a> {
|
|
|
|
Input {
|
|
|
|
buffer: UnknownTypeInputBuffer<'a>,
|
|
|
|
},
|
|
|
|
Output {
|
|
|
|
buffer: UnknownTypeOutputBuffer<'a>,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Represents a buffer containing audio data that may be read.
|
2017-10-23 14:41:38 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// This struct implements the `Deref` trait targeting `[T]`. Therefore this buffer can be read the
|
|
|
|
/// same way as reading from a `Vec` or any other kind of Rust array.
|
|
|
|
// TODO: explain audio stuff in general
|
|
|
|
pub struct InputBuffer<'a, T: 'a>
|
|
|
|
where
|
|
|
|
T: Sample,
|
|
|
|
{
|
|
|
|
// Always contains something, taken by `Drop`
|
|
|
|
// TODO: change that
|
|
|
|
buffer: Option<cpal_impl::InputBuffer<'a, T>>,
|
2017-10-11 08:39:44 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// Represents a buffer that must be filled with audio data.
|
|
|
|
///
|
|
|
|
/// You should destroy this object as soon as possible. Data is only sent to the audio device when
|
|
|
|
/// this object is destroyed.
|
|
|
|
///
|
|
|
|
/// This struct implements the `Deref` and `DerefMut` traits to `[T]`. Therefore writing to this
|
|
|
|
/// buffer is done in the same way as writing to a `Vec` or any other kind of Rust array.
|
|
|
|
// TODO: explain audio stuff in general
|
|
|
|
#[must_use]
|
|
|
|
pub struct OutputBuffer<'a, T: 'a>
|
|
|
|
where
|
|
|
|
T: Sample,
|
|
|
|
{
|
|
|
|
// Always contains something, taken by `Drop`
|
|
|
|
// TODO: change that
|
|
|
|
target: Option<cpal_impl::OutputBuffer<'a, T>>,
|
2015-09-01 09:23:41 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// This is the struct that is provided to you by cpal when you want to read samples from a buffer.
|
|
|
|
///
|
|
|
|
/// Since the type of data is only known at runtime, you have to read the right buffer.
|
|
|
|
pub enum UnknownTypeInputBuffer<'a> {
|
|
|
|
/// Samples whose format is `u16`.
|
|
|
|
U16(InputBuffer<'a, u16>),
|
|
|
|
/// Samples whose format is `i16`.
|
|
|
|
I16(InputBuffer<'a, i16>),
|
|
|
|
/// Samples whose format is `f32`.
|
|
|
|
F32(InputBuffer<'a, f32>),
|
2015-09-01 09:23:41 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// This is the struct that is provided to you by cpal when you want to write samples to a buffer.
|
|
|
|
///
|
|
|
|
/// Since the type of data is only known at runtime, you have to fill the right buffer.
|
|
|
|
pub enum UnknownTypeOutputBuffer<'a> {
|
|
|
|
/// Samples whose format is `u16`.
|
|
|
|
U16(OutputBuffer<'a, u16>),
|
|
|
|
/// Samples whose format is `i16`.
|
|
|
|
I16(OutputBuffer<'a, i16>),
|
|
|
|
/// Samples whose format is `f32`.
|
|
|
|
F32(OutputBuffer<'a, f32>),
|
2017-10-11 08:39:44 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// An iterator yielding all `Device`s currently available to the system.
|
2017-10-23 14:41:38 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// See [`devices()`](fn.devices.html).
|
|
|
|
pub struct Devices(cpal_impl::Devices);
|
2015-09-01 09:23:41 +00:00
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// A `Devices` yielding only *input* devices.
|
|
|
|
pub type InputDevices = iter::Filter<Devices, fn(&Device) -> bool>;
|
2017-10-11 08:39:44 +00:00
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// A `Devices` yielding only *output* devices.
|
|
|
|
pub type OutputDevices = iter::Filter<Devices, fn(&Device) -> bool>;
|
2015-09-22 12:46:27 +00:00
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// An iterator that produces a list of input stream formats supported by the device.
|
|
|
|
///
|
|
|
|
/// See [`Device::supported_input_formats()`](struct.Device.html#method.supported_input_formats).
|
|
|
|
pub struct SupportedInputFormats(cpal_impl::SupportedInputFormats);
|
2017-10-11 08:39:44 +00:00
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// An iterator that produces a list of output stream formats supported by the device.
|
|
|
|
///
|
|
|
|
/// See [`Device::supported_output_formats()`](struct.Device.html#method.supported_output_formats).
|
|
|
|
pub struct SupportedOutputFormats(cpal_impl::SupportedOutputFormats);
|
|
|
|
|
|
|
|
/// Error that can happen when enumerating the list of supported formats.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum FormatsEnumerationError {
|
|
|
|
/// The device no longer exists. This can happen if the device is disconnected while the
|
|
|
|
/// program is running.
|
|
|
|
DeviceNotAvailable,
|
2015-09-01 11:53:54 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// May occur when attempting to request the default input or output stream format from a `Device`.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum DefaultFormatError {
|
|
|
|
/// The device no longer exists. This can happen if the device is disconnected while the
|
|
|
|
/// program is running.
|
|
|
|
DeviceNotAvailable,
|
|
|
|
/// Returned if e.g. the default input format was requested on an output-only audio device.
|
|
|
|
StreamTypeNotSupported,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Error that can happen when creating a `Voice`.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum CreationError {
|
|
|
|
/// The device no longer exists. This can happen if the device is disconnected while the
|
|
|
|
/// program is running.
|
|
|
|
DeviceNotAvailable,
|
|
|
|
/// The required format is not supported.
|
|
|
|
FormatNotSupported,
|
|
|
|
}
|
2015-09-01 11:53:54 +00:00
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// An iterator yielding all `Device`s currently available to the system.
|
2015-09-01 11:53:54 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// Can be empty if the system does not support audio in general.
|
|
|
|
#[inline]
|
|
|
|
pub fn devices() -> Devices {
|
|
|
|
Devices(Default::default())
|
|
|
|
}
|
2015-09-01 11:53:54 +00:00
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// An iterator yielding all `Device`s currently available to the system that support one or more
|
|
|
|
/// input stream formats.
|
|
|
|
///
|
|
|
|
/// Can be empty if the system does not support audio input.
|
|
|
|
pub fn input_devices() -> InputDevices {
|
|
|
|
fn supports_input(device: &Device) -> bool {
|
|
|
|
device.supported_input_formats()
|
|
|
|
.map(|mut iter| iter.next().is_some())
|
|
|
|
.unwrap_or(false)
|
|
|
|
}
|
|
|
|
devices().filter(supports_input)
|
2015-09-01 11:53:54 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// An iterator yielding all `Device`s currently available to the system that support one or more
|
|
|
|
/// output stream formats.
|
2017-10-23 14:41:38 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// Can be empty if the system does not support audio output.
|
|
|
|
pub fn output_devices() -> OutputDevices {
|
|
|
|
fn supports_output(device: &Device) -> bool {
|
|
|
|
device.supported_output_formats()
|
|
|
|
.map(|mut iter| iter.next().is_some())
|
|
|
|
.unwrap_or(false)
|
|
|
|
}
|
|
|
|
devices().filter(supports_output)
|
|
|
|
}
|
2015-09-01 11:53:54 +00:00
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// The default input audio device on the system.
|
|
|
|
///
|
|
|
|
/// Returns `None` if no input device is available.
|
|
|
|
pub fn default_input_device() -> Option<Device> {
|
|
|
|
cpal_impl::default_input_device().map(Device)
|
|
|
|
}
|
2015-09-01 11:53:54 +00:00
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// The default output audio device on the system.
|
|
|
|
///
|
|
|
|
/// Returns `None` if no output device is available.
|
|
|
|
pub fn default_output_device() -> Option<Device> {
|
|
|
|
cpal_impl::default_output_device().map(Device)
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Device {
|
|
|
|
/// The human-readable name of the device.
|
2015-09-01 11:53:54 +00:00
|
|
|
#[inline]
|
2018-02-12 13:10:24 +00:00
|
|
|
pub fn name(&self) -> String {
|
|
|
|
self.0.name()
|
2015-09-01 11:53:54 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// An iterator yielding formats that are supported by the backend.
|
|
|
|
///
|
|
|
|
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
|
2015-09-01 11:53:54 +00:00
|
|
|
#[inline]
|
2018-02-12 13:10:24 +00:00
|
|
|
pub fn supported_input_formats(&self) -> Result<SupportedInputFormats, FormatsEnumerationError> {
|
|
|
|
Ok(SupportedInputFormats(self.0.supported_input_formats()?))
|
2015-09-01 11:53:54 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// An iterator yielding output stream formats that are supported by the device.
|
|
|
|
///
|
|
|
|
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
|
|
|
|
#[inline]
|
|
|
|
pub fn supported_output_formats(&self) -> Result<SupportedOutputFormats, FormatsEnumerationError> {
|
|
|
|
Ok(SupportedOutputFormats(self.0.supported_output_formats()?))
|
|
|
|
}
|
2017-10-20 19:18:40 +00:00
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// The default input stream format for the device.
|
2017-10-20 19:18:40 +00:00
|
|
|
#[inline]
|
2018-02-12 13:10:24 +00:00
|
|
|
pub fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
|
|
|
self.0.default_input_format()
|
2017-10-20 19:18:40 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// The default output stream format for the device.
|
2017-10-20 19:18:40 +00:00
|
|
|
#[inline]
|
2018-02-12 13:10:24 +00:00
|
|
|
pub fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
|
|
|
self.0.default_output_format()
|
2017-10-20 19:18:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-02 14:13:59 +00:00
|
|
|
impl EventLoop {
|
2017-10-18 18:24:05 +00:00
|
|
|
/// Initializes a new events loop.
|
2016-08-02 14:13:59 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn new() -> EventLoop {
|
|
|
|
EventLoop(cpal_impl::EventLoop::new())
|
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// Creates a new input stream that will run from the given device and with the given format.
|
2017-10-18 18:24:05 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// On success, returns an identifier for the stream.
|
2017-10-23 14:41:38 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// Can return an error if the device is no longer valid, or if the input stream format is not
|
|
|
|
/// supported by the device.
|
2016-08-02 14:13:59 +00:00
|
|
|
#[inline]
|
2018-02-12 13:10:24 +00:00
|
|
|
pub fn build_input_stream(
|
|
|
|
&self,
|
|
|
|
device: &Device,
|
|
|
|
format: &Format,
|
|
|
|
) -> Result<StreamId, CreationError>
|
|
|
|
{
|
|
|
|
self.0.build_input_stream(&device.0, format).map(StreamId)
|
2016-08-02 14:13:59 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// Creates a new output stream that will play on the given device and with the given format.
|
|
|
|
///
|
|
|
|
/// On success, returns an identifier for the stream.
|
|
|
|
///
|
|
|
|
/// Can return an error if the device is no longer valid, or if the output stream format is not
|
|
|
|
/// supported by the device.
|
|
|
|
#[inline]
|
|
|
|
pub fn build_output_stream(
|
|
|
|
&self,
|
|
|
|
device: &Device,
|
|
|
|
format: &Format,
|
|
|
|
) -> Result<StreamId, CreationError>
|
|
|
|
{
|
|
|
|
self.0.build_output_stream(&device.0, format).map(StreamId)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Instructs the audio device that it should start playing the stream with the given ID.
|
|
|
|
///
|
|
|
|
/// Has no effect is the stream was already playing.
|
|
|
|
///
|
|
|
|
/// Only call this after you have submitted some data, otherwise you may hear some glitches.
|
2017-10-18 18:24:05 +00:00
|
|
|
///
|
|
|
|
/// # Panic
|
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// If the stream does not exist, this function can either panic or be a no-op.
|
2017-10-18 18:24:05 +00:00
|
|
|
///
|
|
|
|
#[inline]
|
2018-02-12 13:10:24 +00:00
|
|
|
pub fn play_stream(&self, stream: StreamId) {
|
|
|
|
self.0.play_stream(stream.0)
|
2017-10-18 18:24:05 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// Instructs the audio device that it should stop playing the stream with the given ID.
|
2017-10-18 18:24:05 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// Has no effect is the stream was already paused.
|
|
|
|
///
|
|
|
|
/// If you call `play` afterwards, the playback will resume where it was.
|
|
|
|
///
|
|
|
|
/// # Panic
|
|
|
|
///
|
|
|
|
/// If the stream does not exist, this function can either panic or be a no-op.
|
2017-10-23 14:41:38 +00:00
|
|
|
///
|
2017-10-18 18:24:05 +00:00
|
|
|
#[inline]
|
2018-02-12 13:10:24 +00:00
|
|
|
pub fn pause_stream(&self, stream: StreamId) {
|
|
|
|
self.0.pause_stream(stream.0)
|
2017-10-18 18:24:05 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// Destroys an existing stream.
|
2017-10-18 18:24:05 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// # Panic
|
2017-10-18 18:24:05 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// If the stream does not exist, this function can either panic or be a no-op.
|
2017-10-18 18:24:05 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn destroy_stream(&self, stream_id: StreamId) {
|
|
|
|
self.0.destroy_stream(stream_id.0)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Takes control of the current thread and begins the stream processing.
|
2017-10-18 18:24:05 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// > **Note**: Since it takes control of the thread, this method is best called on a separate
|
|
|
|
/// > thread.
|
2017-10-18 18:24:05 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// Whenever a stream needs to be fed some data, the closure passed as parameter is called.
|
|
|
|
/// You can call the other methods of `EventLoop` without getting a deadlock.
|
|
|
|
#[inline]
|
|
|
|
pub fn run<F>(&self, mut callback: F) -> !
|
|
|
|
where F: FnMut(StreamId, StreamData) + Send
|
|
|
|
{
|
|
|
|
self.0.run(move |id, data| callback(StreamId(id), data))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SupportedFormat {
|
|
|
|
/// Turns this `SupportedFormat` into a `Format` corresponding to the maximum samples rate.
|
2017-10-18 18:24:05 +00:00
|
|
|
#[inline]
|
2018-02-12 13:10:24 +00:00
|
|
|
pub fn with_max_sample_rate(self) -> Format {
|
|
|
|
Format {
|
|
|
|
channels: self.channels,
|
|
|
|
sample_rate: self.max_sample_rate,
|
|
|
|
data_type: self.data_type,
|
|
|
|
}
|
2017-10-18 18:24:05 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
/// A comparison function which compares two `SupportedFormat`s in terms of their priority of
|
|
|
|
/// use as a default stream format.
|
2017-10-18 18:24:05 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// Some backends do not provide a default stream format for their audio devices. In these
|
|
|
|
/// cases, CPAL attempts to decide on a reasonable default format for the user. To do this we
|
|
|
|
/// use the "greatest" of all supported stream formats when compared with this method.
|
2017-10-18 18:24:05 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// Formats are prioritised by the following heuristics:
|
2017-10-18 18:24:05 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// **Channels**:
|
|
|
|
///
|
|
|
|
/// - Stereo
|
|
|
|
/// - Mono
|
|
|
|
/// - Max available channels
|
2017-10-18 18:24:05 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// **Sample format**:
|
|
|
|
/// - f32
|
|
|
|
/// - i16
|
|
|
|
/// - u16
|
2017-10-18 18:24:05 +00:00
|
|
|
///
|
2018-02-12 13:10:24 +00:00
|
|
|
/// **Sample rate**:
|
|
|
|
///
|
|
|
|
/// - 44100 (cd quality)
|
|
|
|
/// - Max sample rate
|
|
|
|
pub fn cmp_default_heuristics(&self, other: &Self) -> std::cmp::Ordering {
|
|
|
|
use std::cmp::Ordering::Equal;
|
|
|
|
use SampleFormat::{F32, I16, U16};
|
|
|
|
|
|
|
|
let cmp_stereo = (self.channels == 2).cmp(&(other.channels == 2));
|
|
|
|
if cmp_stereo != Equal {
|
|
|
|
return cmp_stereo;
|
|
|
|
}
|
|
|
|
|
|
|
|
let cmp_mono = (self.channels == 1).cmp(&(other.channels == 1));
|
|
|
|
if cmp_mono != Equal {
|
|
|
|
return cmp_mono;
|
|
|
|
}
|
|
|
|
|
|
|
|
let cmp_channels = self.channels.cmp(&other.channels);
|
|
|
|
if cmp_channels != Equal {
|
|
|
|
return cmp_channels;
|
|
|
|
}
|
|
|
|
|
|
|
|
let cmp_f32 = (self.data_type == F32).cmp(&(other.data_type == F32));
|
|
|
|
if cmp_f32 != Equal {
|
|
|
|
return cmp_f32;
|
|
|
|
}
|
|
|
|
|
|
|
|
let cmp_i16 = (self.data_type == I16).cmp(&(other.data_type == I16));
|
|
|
|
if cmp_i16 != Equal {
|
|
|
|
return cmp_i16;
|
|
|
|
}
|
|
|
|
|
|
|
|
let cmp_u16 = (self.data_type == U16).cmp(&(other.data_type == U16));
|
|
|
|
if cmp_u16 != Equal {
|
|
|
|
return cmp_u16;
|
|
|
|
}
|
|
|
|
|
|
|
|
const HZ_44100: SampleRate = SampleRate(44_100);
|
|
|
|
let r44100_in_self = self.min_sample_rate <= HZ_44100
|
|
|
|
&& HZ_44100 <= self.max_sample_rate;
|
|
|
|
let r44100_in_other = other.min_sample_rate <= HZ_44100
|
|
|
|
&& HZ_44100 <= other.max_sample_rate;
|
|
|
|
let cmp_r44100 = r44100_in_self.cmp(&r44100_in_other);
|
|
|
|
if cmp_r44100 != Equal {
|
|
|
|
return cmp_r44100;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.max_sample_rate.cmp(&other.max_sample_rate)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, T> Deref for InputBuffer<'a, T>
|
|
|
|
where T: Sample
|
|
|
|
{
|
|
|
|
type Target = [T];
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
#[inline]
|
2018-02-12 13:10:24 +00:00
|
|
|
fn deref(&self) -> &[T] {
|
|
|
|
self.buffer.as_ref().unwrap().buffer()
|
2017-10-18 18:24:05 +00:00
|
|
|
}
|
2014-12-15 14:29:59 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
impl<'a, T> Drop for InputBuffer<'a, T>
|
|
|
|
where T: Sample
|
|
|
|
{
|
|
|
|
#[inline]
|
|
|
|
fn drop(&mut self) {
|
|
|
|
self.buffer.take().unwrap().finish();
|
|
|
|
}
|
|
|
|
}
|
2017-10-18 18:24:05 +00:00
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
impl<'a, T> Deref for OutputBuffer<'a, T>
|
|
|
|
where T: Sample
|
|
|
|
{
|
|
|
|
type Target = [T];
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn deref(&self) -> &[T] {
|
|
|
|
panic!("It is forbidden to read from the audio buffer");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, T> DerefMut for OutputBuffer<'a, T>
|
|
|
|
where T: Sample
|
|
|
|
{
|
|
|
|
#[inline]
|
|
|
|
fn deref_mut(&mut self) -> &mut [T] {
|
|
|
|
self.target.as_mut().unwrap().buffer()
|
|
|
|
}
|
2014-12-15 10:45:38 +00:00
|
|
|
}
|
2014-12-11 16:23:33 +00:00
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
impl<'a, T> Drop for OutputBuffer<'a, T>
|
|
|
|
where T: Sample
|
|
|
|
{
|
|
|
|
#[inline]
|
|
|
|
fn drop(&mut self) {
|
|
|
|
self.target.take().unwrap().finish();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> UnknownTypeInputBuffer<'a> {
|
2015-09-10 19:21:46 +00:00
|
|
|
/// Returns the length of the buffer in number of samples.
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2015-09-10 19:21:46 +00:00
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
match self {
|
2018-02-12 13:10:24 +00:00
|
|
|
&UnknownTypeInputBuffer::U16(ref buf) => buf.len(),
|
|
|
|
&UnknownTypeInputBuffer::I16(ref buf) => buf.len(),
|
|
|
|
&UnknownTypeInputBuffer::F32(ref buf) => buf.len(),
|
2015-09-10 19:21:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
impl<'a> UnknownTypeOutputBuffer<'a> {
|
|
|
|
/// Returns the length of the buffer in number of samples.
|
|
|
|
#[inline]
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
match self {
|
|
|
|
&UnknownTypeOutputBuffer::U16(ref buf) => buf.target.as_ref().unwrap().len(),
|
|
|
|
&UnknownTypeOutputBuffer::I16(ref buf) => buf.target.as_ref().unwrap().len(),
|
|
|
|
&UnknownTypeOutputBuffer::F32(ref buf) => buf.target.as_ref().unwrap().len(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Format> for SupportedFormat {
|
|
|
|
#[inline]
|
|
|
|
fn from(format: Format) -> SupportedFormat {
|
|
|
|
SupportedFormat {
|
|
|
|
channels: format.channels,
|
|
|
|
min_sample_rate: format.sample_rate,
|
|
|
|
max_sample_rate: format.sample_rate,
|
|
|
|
data_type: format.data_type,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Iterator for Devices {
|
|
|
|
type Item = Device;
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
self.0.next().map(Device)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
self.0.size_hint()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Iterator for SupportedInputFormats {
|
|
|
|
type Item = SupportedFormat;
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn next(&mut self) -> Option<SupportedFormat> {
|
|
|
|
self.0.next()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
self.0.size_hint()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Iterator for SupportedOutputFormats {
|
|
|
|
type Item = SupportedFormat;
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn next(&mut self) -> Option<SupportedFormat> {
|
|
|
|
self.0.next()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
self.0.size_hint()
|
|
|
|
}
|
2015-09-01 12:17:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for FormatsEnumerationError {
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2015-09-01 12:17:57 +00:00
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
|
|
|
write!(fmt, "{}", self.description())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Error for FormatsEnumerationError {
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2015-09-01 12:17:57 +00:00
|
|
|
fn description(&self) -> &str {
|
|
|
|
match self {
|
|
|
|
&FormatsEnumerationError::DeviceNotAvailable => {
|
|
|
|
"The requested device is no longer available (for example, it has been unplugged)."
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for CreationError {
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2015-09-01 12:17:57 +00:00
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
|
|
|
write!(fmt, "{}", self.description())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Error for CreationError {
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2015-09-01 12:17:57 +00:00
|
|
|
fn description(&self) -> &str {
|
|
|
|
match self {
|
|
|
|
&CreationError::DeviceNotAvailable => {
|
|
|
|
"The requested device is no longer available (for example, it has been unplugged)."
|
|
|
|
},
|
|
|
|
|
|
|
|
&CreationError::FormatNotSupported => {
|
|
|
|
"The requested samples format is not supported by the device."
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-12 13:10:24 +00:00
|
|
|
// If a backend does not provide an API for retrieving supported formats, we query it with a bunch
|
|
|
|
// of commonly used rates. This is always the case for wasapi and is sometimes the case for alsa.
|
|
|
|
//
|
|
|
|
// If a rate you desire is missing from this list, feel free to add it!
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
const COMMON_SAMPLE_RATES: &'static [SampleRate] = &[
|
|
|
|
SampleRate(5512),
|
|
|
|
SampleRate(8000),
|
|
|
|
SampleRate(11025),
|
|
|
|
SampleRate(16000),
|
|
|
|
SampleRate(22050),
|
|
|
|
SampleRate(32000),
|
|
|
|
SampleRate(44100),
|
|
|
|
SampleRate(48000),
|
|
|
|
SampleRate(64000),
|
|
|
|
SampleRate(88200),
|
|
|
|
SampleRate(96000),
|
|
|
|
SampleRate(176400),
|
|
|
|
SampleRate(192000),
|
|
|
|
];
|