2017-10-23 14:41:38 +00:00
|
|
|
//! # How to use cpal
|
|
|
|
//!
|
|
|
|
//! Here are some concepts cpal exposes:
|
|
|
|
//!
|
|
|
|
//! - An endpoint is a target where the data of the audio channel will be played.
|
|
|
|
//! - A voice is an open audio channel which you can stream audio data to. You have to choose which
|
|
|
|
//! endpoint your voice targets before you create one.
|
|
|
|
//! - An event loop is a collection of voices. Each voice must belong to an event loop, and all the
|
|
|
|
//! voices that belong to an event loop are managed together.
|
|
|
|
//!
|
|
|
|
//! In order to play a sound, you first need to create an event loop:
|
|
|
|
//!
|
|
|
|
//! ```
|
|
|
|
//! use cpal::EventLoop;
|
|
|
|
//! let event_loop = EventLoop::new();
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! Then choose an endpoint. You can either use the default endpoint with the `default_endpoint()`
|
|
|
|
//! function, or enumerate all the available endpoints with the `endpoints()` function. Beware that
|
|
|
|
//! `default_endpoint()` returns an `Option` in case no endpoint is available on the system.
|
|
|
|
//!
|
|
|
|
//! ```
|
|
|
|
//! // Note: we call `unwrap()` because it is convenient, but you should avoid doing that in a real
|
|
|
|
//! // code.
|
|
|
|
//! let endpoint = cpal::default_endpoint().expect("no endpoint is available");
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! Before we can create a voice, we must decide what the format of the audio samples is going to
|
|
|
|
//! be. You can query all the supported formats with the `supported_formats()` method, which
|
|
|
|
//! produces 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 voice if the format ends up not
|
|
|
|
//! being supported.
|
|
|
|
//!
|
|
|
|
//! > **Note**: the `supported_formats()` method could return an error for example if the device
|
|
|
|
//! > has been disconnected.
|
|
|
|
//!
|
|
|
|
//! ```no_run
|
|
|
|
//! # let endpoint = cpal::default_endpoint().unwrap();
|
|
|
|
//! let mut supported_formats_range = endpoint.supported_formats()
|
|
|
|
//! .expect("error while querying formats");
|
|
|
|
//! let format = supported_formats_range.next().expect("no supported format?!")
|
|
|
|
//! .with_max_samples_rate();
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! Now that we have everything, we can create a voice from that event loop:
|
|
|
|
//!
|
|
|
|
//! ```no_run
|
|
|
|
//! # let endpoint = cpal::default_endpoint().unwrap();
|
|
|
|
//! # let format = endpoint.supported_formats().unwrap().next().unwrap().with_max_samples_rate();
|
|
|
|
//! # let event_loop = cpal::EventLoop::new();
|
|
|
|
//! let voice_id = event_loop.build_voice(&endpoint, &format).unwrap();
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! The value returned by `build_voice()` is of type `VoiceId` and is an identifier that will
|
|
|
|
//! allow you to control the voice.
|
|
|
|
//!
|
|
|
|
//! There is a last step to perform before going forward, which is to start the voice. This is done
|
|
|
|
//! with the `play()` method on the event loop.
|
|
|
|
//!
|
|
|
|
//! ```
|
|
|
|
//! # let event_loop: cpal::EventLoop = return;
|
|
|
|
//! # let voice_id: cpal::VoiceId = return;
|
|
|
|
//! event_loop.play(voice_id);
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! Once everything is done, you must call `run()` on the `event_loop`.
|
|
|
|
//!
|
|
|
|
//! ```no_run
|
|
|
|
//! # let event_loop = cpal::EventLoop::new();
|
|
|
|
//! event_loop.run(move |_voice_id, _buffer| {
|
|
|
|
//! // write data to `buffer` here
|
|
|
|
//! });
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! > **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
|
|
|
|
//! that you passed to this function. The callback gets passed the voice ID, and a struct of type
|
|
|
|
//! `UnknownTypeBuffer` that represents the buffer that must be filled with audio samples. The
|
|
|
|
//! `UnknownTypeBuffer` can be one of `I16`, `U16` or `F32` depending on the format that was passed
|
|
|
|
//! to `build_voice`.
|
|
|
|
//!
|
|
|
|
//! In this example, we simply simply fill the buffer with zeroes.
|
|
|
|
//!
|
|
|
|
//! ```no_run
|
|
|
|
//! use cpal::UnknownTypeBuffer;
|
|
|
|
//!
|
|
|
|
//! # let event_loop = cpal::EventLoop::new();
|
|
|
|
//! event_loop.run(move |_voice_id, mut buffer| {
|
|
|
|
//! match buffer {
|
|
|
|
//! UnknownTypeBuffer::U16(mut buffer) => {
|
|
|
|
//! for elem in buffer.iter_mut() {
|
|
|
|
//! *elem = u16::max_value() / 2;
|
|
|
|
//! }
|
|
|
|
//! },
|
|
|
|
//! UnknownTypeBuffer::I16(mut buffer) => {
|
|
|
|
//! for elem in buffer.iter_mut() {
|
|
|
|
//! *elem = 0;
|
|
|
|
//! }
|
|
|
|
//! },
|
|
|
|
//! UnknownTypeBuffer::F32(mut buffer) => {
|
|
|
|
//! for elem in buffer.iter_mut() {
|
|
|
|
//! *elem = 0.0;
|
|
|
|
//! }
|
|
|
|
//! },
|
|
|
|
//! }
|
|
|
|
//! });
|
|
|
|
//! ```
|
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
|
|
|
|
2017-10-11 11:24:49 +00:00
|
|
|
#[cfg(all(not(windows), not(target_os = "linux"), not(target_os = "freebsd"),
|
2017-10-18 18:24:05 +00:00
|
|
|
not(target_os = "macos"), not(target_os = "ios"), not(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;
|
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;
|
|
|
|
|
2015-09-01 09:23:41 +00:00
|
|
|
/// An iterator for the list of formats that are supported by the backend.
|
2017-10-23 14:41:38 +00:00
|
|
|
///
|
|
|
|
/// See [`endpoints()`](fn.endpoints.html).
|
2015-09-01 09:23:41 +00:00
|
|
|
pub struct EndpointsIterator(cpal_impl::EndpointsIterator);
|
|
|
|
|
|
|
|
impl Iterator for EndpointsIterator {
|
|
|
|
type Item = Endpoint;
|
|
|
|
|
2015-09-01 11:53:54 +00:00
|
|
|
#[inline]
|
2015-09-01 09:23:41 +00:00
|
|
|
fn next(&mut self) -> Option<Endpoint> {
|
|
|
|
self.0.next().map(Endpoint)
|
|
|
|
}
|
|
|
|
|
2015-09-01 11:53:54 +00:00
|
|
|
#[inline]
|
2015-09-01 09:23:41 +00:00
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
self.0.size_hint()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return an iterator to the list of formats that are supported by the system.
|
2017-10-23 14:41:38 +00:00
|
|
|
///
|
|
|
|
/// Can be empty if the system doesn't support audio in general.
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2017-10-11 08:39:44 +00:00
|
|
|
pub fn endpoints() -> EndpointsIterator {
|
|
|
|
EndpointsIterator(Default::default())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Deprecated. Use `endpoints()` instead.
|
|
|
|
#[inline]
|
|
|
|
#[deprecated]
|
2015-09-01 09:23:41 +00:00
|
|
|
pub fn get_endpoints_list() -> EndpointsIterator {
|
|
|
|
EndpointsIterator(Default::default())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the default endpoint, or `None` if no device is available.
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2017-10-11 08:39:44 +00:00
|
|
|
pub fn default_endpoint() -> Option<Endpoint> {
|
2017-10-12 09:54:09 +00:00
|
|
|
cpal_impl::default_endpoint().map(Endpoint)
|
2015-09-01 09:23:41 +00:00
|
|
|
}
|
|
|
|
|
2017-10-11 08:39:44 +00:00
|
|
|
/// Deprecated. Use `default_endpoint()` instead.
|
|
|
|
#[inline]
|
|
|
|
#[deprecated]
|
|
|
|
pub fn get_default_endpoint() -> Option<Endpoint> {
|
|
|
|
default_endpoint()
|
|
|
|
}
|
|
|
|
|
2017-10-23 14:41:38 +00:00
|
|
|
/// An opaque type that identifies an endpoint that is capable of playing audio.
|
|
|
|
///
|
|
|
|
/// Please note that endpoints may become invalid if they get disconnected. Therefore all the
|
|
|
|
/// methods that involve an endpoint return a `Result`.
|
2015-09-01 09:23:41 +00:00
|
|
|
#[derive(Clone, PartialEq, Eq)]
|
|
|
|
pub struct Endpoint(cpal_impl::Endpoint);
|
|
|
|
|
2015-09-01 11:53:54 +00:00
|
|
|
impl Endpoint {
|
|
|
|
/// Returns an iterator that produces the list of formats that are supported by the backend.
|
2017-10-23 14:41:38 +00:00
|
|
|
///
|
|
|
|
/// Can return an error if the endpoint is no longer valid (eg. it has been disconnected).
|
|
|
|
/// The returned iterator should never be empty.
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2017-10-11 11:24:49 +00:00
|
|
|
pub fn supported_formats(&self) -> Result<SupportedFormatsIterator, FormatsEnumerationError> {
|
2017-10-12 09:54:09 +00:00
|
|
|
Ok(SupportedFormatsIterator(self.0.supported_formats()?))
|
2017-10-11 08:39:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Deprecated. Use `supported_formats` instead.
|
|
|
|
#[inline]
|
|
|
|
#[deprecated]
|
2017-10-11 11:24:49 +00:00
|
|
|
pub fn get_supported_formats_list(
|
|
|
|
&self)
|
|
|
|
-> Result<SupportedFormatsIterator, FormatsEnumerationError> {
|
2017-10-11 08:39:44 +00:00
|
|
|
self.supported_formats()
|
2015-09-01 11:53:54 +00:00
|
|
|
}
|
2015-09-22 12:46:27 +00:00
|
|
|
|
|
|
|
/// Returns the name of the endpoint.
|
2017-10-23 14:41:38 +00:00
|
|
|
// TODO: human-readable or system name?
|
2015-09-22 12:46:27 +00:00
|
|
|
#[inline]
|
2017-10-11 08:39:44 +00:00
|
|
|
pub fn name(&self) -> String {
|
2017-10-12 09:54:09 +00:00
|
|
|
self.0.name()
|
2015-09-22 12:46:27 +00:00
|
|
|
}
|
2017-10-11 08:39:44 +00:00
|
|
|
|
|
|
|
/// Deprecated. Use `name()` instead.
|
|
|
|
#[deprecated]
|
|
|
|
#[inline]
|
|
|
|
pub fn get_name(&self) -> String {
|
|
|
|
self.name()
|
|
|
|
}
|
2015-09-01 11:53:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Number of channels.
|
|
|
|
pub type ChannelsCount = u16;
|
|
|
|
|
2015-09-10 09:44:19 +00:00
|
|
|
/// Possible position of a channel.
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
|
|
pub enum ChannelPosition {
|
|
|
|
FrontLeft,
|
|
|
|
FrontRight,
|
|
|
|
FrontCenter,
|
|
|
|
LowFrequency,
|
|
|
|
BackLeft,
|
|
|
|
BackRight,
|
|
|
|
FrontLeftOfCenter,
|
|
|
|
FrontRightOfCenter,
|
|
|
|
BackCenter,
|
|
|
|
SideLeft,
|
|
|
|
SideRight,
|
|
|
|
TopCenter,
|
|
|
|
TopFrontLeft,
|
|
|
|
TopFrontCenter,
|
|
|
|
TopFrontRight,
|
|
|
|
TopBackLeft,
|
|
|
|
TopBackCenter,
|
|
|
|
TopBackRight,
|
|
|
|
}
|
|
|
|
|
2015-09-01 11:53:54 +00:00
|
|
|
///
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
|
|
pub struct SamplesRate(pub u32);
|
|
|
|
|
|
|
|
/// Describes a format.
|
2015-09-10 09:44:19 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
2015-09-01 11:53:54 +00:00
|
|
|
pub struct Format {
|
2015-09-10 09:44:19 +00:00
|
|
|
pub channels: Vec<ChannelPosition>,
|
2015-09-01 11:53:54 +00:00
|
|
|
pub samples_rate: SamplesRate,
|
|
|
|
pub data_type: SampleFormat,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// An iterator that produces a list of formats supported by the endpoint.
|
2017-10-23 14:41:38 +00:00
|
|
|
///
|
|
|
|
/// See [`Endpoint::supported_formats()`](struct.Endpoint.html#method.supported_formats).
|
2015-09-01 11:53:54 +00:00
|
|
|
pub struct SupportedFormatsIterator(cpal_impl::SupportedFormatsIterator);
|
|
|
|
|
|
|
|
impl Iterator for SupportedFormatsIterator {
|
2017-10-20 19:18:40 +00:00
|
|
|
type Item = SupportedFormat;
|
2015-09-01 11:53:54 +00:00
|
|
|
|
|
|
|
#[inline]
|
2017-10-20 19:18:40 +00:00
|
|
|
fn next(&mut self) -> Option<SupportedFormat> {
|
2015-09-01 11:53:54 +00:00
|
|
|
self.0.next()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
self.0.size_hint()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-23 14:41:38 +00:00
|
|
|
/// Describes a range of supported formats.
|
2017-10-20 19:18:40 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
pub struct SupportedFormat {
|
|
|
|
pub channels: Vec<ChannelPosition>,
|
2017-10-23 14:41:38 +00:00
|
|
|
/// Minimum value for the samples rate of the supported formats.
|
2017-10-20 19:18:40 +00:00
|
|
|
pub min_samples_rate: SamplesRate,
|
2017-10-23 14:41:38 +00:00
|
|
|
/// Maximum value for the samples rate of the supported formats.
|
2017-10-20 19:18:40 +00:00
|
|
|
pub max_samples_rate: SamplesRate,
|
2017-10-23 14:41:38 +00:00
|
|
|
/// Type of data expected by the endpoint.
|
2017-10-20 19:18:40 +00:00
|
|
|
pub data_type: SampleFormat,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SupportedFormat {
|
2017-10-23 14:41:38 +00:00
|
|
|
/// Turns this `SupportedFormat` into a `Format` corresponding to the maximum samples rate.
|
2017-10-20 19:18:40 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn with_max_samples_rate(self) -> Format {
|
|
|
|
Format {
|
|
|
|
channels: self.channels,
|
|
|
|
samples_rate: self.max_samples_rate,
|
|
|
|
data_type: self.data_type,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Format> for SupportedFormat {
|
|
|
|
#[inline]
|
|
|
|
fn from(format: Format) -> SupportedFormat {
|
|
|
|
SupportedFormat {
|
|
|
|
channels: format.channels,
|
|
|
|
min_samples_rate: format.samples_rate,
|
|
|
|
max_samples_rate: format.samples_rate,
|
|
|
|
data_type: format.data_type,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-23 14:41:38 +00:00
|
|
|
/// Collection of voices managed together.
|
|
|
|
///
|
|
|
|
/// Created with the [`new`](struct.EventLoop.html#method.new) method.
|
2016-08-02 14:13:59 +00:00
|
|
|
pub struct EventLoop(cpal_impl::EventLoop);
|
|
|
|
|
|
|
|
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())
|
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
/// Creates a new voice that will play on the given endpoint and with the given format.
|
|
|
|
///
|
|
|
|
/// On success, returns an identifier for the voice.
|
2017-10-23 14:41:38 +00:00
|
|
|
///
|
|
|
|
/// Can return an error if the endpoint is no longer valid, or if the format is not supported
|
|
|
|
/// by the endpoint.
|
2016-08-02 14:13:59 +00:00
|
|
|
#[inline]
|
2017-10-18 18:24:05 +00:00
|
|
|
pub fn build_voice(&self, endpoint: &Endpoint, format: &Format)
|
2017-10-23 14:41:38 +00:00
|
|
|
-> Result<VoiceId, CreationError> {
|
2017-10-18 18:24:05 +00:00
|
|
|
self.0.build_voice(&endpoint.0, format).map(VoiceId)
|
2016-08-02 14:13:59 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
/// Destroys an existing voice.
|
|
|
|
///
|
|
|
|
/// # Panic
|
|
|
|
///
|
|
|
|
/// If the voice doesn't exist, this function can either panic or be a no-op.
|
|
|
|
///
|
|
|
|
#[inline]
|
|
|
|
pub fn destroy_voice(&self, voice_id: VoiceId) {
|
|
|
|
self.0.destroy_voice(voice_id.0)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Takes control of the current thread and processes the sounds.
|
|
|
|
///
|
2017-10-23 14:41:38 +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
|
|
|
/// Whenever a voice needs to be fed some data, the closure passed as parameter is called.
|
2017-10-23 14:41:38 +00:00
|
|
|
/// You can call the other methods of `EventLoop` without getting a deadlock.
|
2017-10-18 18:24:05 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn run<F>(&self, mut callback: F) -> !
|
2017-12-06 11:13:36 +00:00
|
|
|
where F: FnMut(VoiceId, UnknownTypeBuffer) + Send
|
2017-10-18 18:24:05 +00:00
|
|
|
{
|
|
|
|
self.0.run(move |id, buf| callback(VoiceId(id), buf))
|
|
|
|
}
|
|
|
|
|
2017-10-23 14:41:38 +00:00
|
|
|
/// Instructs the audio device that it should start playing.
|
2017-10-18 18:24:05 +00:00
|
|
|
///
|
|
|
|
/// Has no effect is the voice was already playing.
|
|
|
|
///
|
|
|
|
/// Only call this after you have submitted some data, otherwise you may hear
|
|
|
|
/// some glitches.
|
|
|
|
///
|
|
|
|
/// # Panic
|
|
|
|
///
|
|
|
|
/// If the voice doesn't exist, this function can either panic or be a no-op.
|
|
|
|
///
|
|
|
|
#[inline]
|
|
|
|
pub fn play(&self, voice: VoiceId) {
|
|
|
|
self.0.play(voice.0)
|
|
|
|
}
|
|
|
|
|
2017-10-23 14:41:38 +00:00
|
|
|
/// Instructs the audio device that it should stop playing.
|
2017-10-18 18:24:05 +00:00
|
|
|
///
|
|
|
|
/// Has no effect is the voice was already paused.
|
|
|
|
///
|
2017-10-23 14:41:38 +00:00
|
|
|
/// If you call `play` afterwards, the playback will resume where it was.
|
2017-10-18 18:24:05 +00:00
|
|
|
///
|
|
|
|
/// # Panic
|
|
|
|
///
|
|
|
|
/// If the voice doesn't exist, this function can either panic or be a no-op.
|
|
|
|
///
|
|
|
|
#[inline]
|
|
|
|
pub fn pause(&self, voice: VoiceId) {
|
|
|
|
self.0.pause(voice.0)
|
|
|
|
}
|
2014-12-15 14:29:59 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
/// Identifier of a voice in an events loop.
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
pub struct VoiceId(cpal_impl::VoiceId);
|
|
|
|
|
2015-08-20 12:38:25 +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.
|
2017-10-18 18:24:05 +00:00
|
|
|
pub enum UnknownTypeBuffer<'a> {
|
2015-08-20 12:38:25 +00:00
|
|
|
/// Samples whose format is `u16`.
|
2017-10-18 18:24:05 +00:00
|
|
|
U16(Buffer<'a, u16>),
|
2015-08-20 12:38:25 +00:00
|
|
|
/// Samples whose format is `i16`.
|
2017-10-18 18:24:05 +00:00
|
|
|
I16(Buffer<'a, i16>),
|
2015-08-20 12:38:25 +00:00
|
|
|
/// Samples whose format is `f32`.
|
2017-10-18 18:24:05 +00:00
|
|
|
F32(Buffer<'a, f32>),
|
2014-12-15 10:45:38 +00:00
|
|
|
}
|
2014-12-11 16:23:33 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
impl<'a> UnknownTypeBuffer<'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 {
|
|
|
|
&UnknownTypeBuffer::U16(ref buf) => buf.target.as_ref().unwrap().len(),
|
|
|
|
&UnknownTypeBuffer::I16(ref buf) => buf.target.as_ref().unwrap().len(),
|
|
|
|
&UnknownTypeBuffer::F32(ref buf) => buf.target.as_ref().unwrap().len(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-01 12:17:57 +00:00
|
|
|
/// 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,
|
|
|
|
}
|
|
|
|
|
|
|
|
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)."
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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,
|
|
|
|
}
|
|
|
|
|
|
|
|
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."
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
/// Represents a buffer that must be filled with audio data.
|
2015-08-20 12:38:25 +00:00
|
|
|
///
|
2017-10-23 14:41:38 +00:00
|
|
|
/// 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
|
2017-10-18 18:24:05 +00:00
|
|
|
#[must_use]
|
|
|
|
pub struct Buffer<'a, T: 'a>
|
|
|
|
where T: Sample
|
|
|
|
{
|
|
|
|
// Always contains something, taken by `Drop`
|
|
|
|
// TODO: change that
|
|
|
|
target: Option<cpal_impl::Buffer<'a, T>>,
|
2014-12-11 16:23:33 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
impl<'a, T> Deref for Buffer<'a, T>
|
2017-10-11 11:24:49 +00:00
|
|
|
where T: Sample
|
|
|
|
{
|
2015-01-05 09:52:59 +00:00
|
|
|
type Target = [T];
|
|
|
|
|
2015-09-01 11:53:54 +00:00
|
|
|
#[inline]
|
2014-12-15 15:32:13 +00:00
|
|
|
fn deref(&self) -> &[T] {
|
|
|
|
panic!("It is forbidden to read from the audio buffer");
|
2014-12-11 16:23:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
impl<'a, T> DerefMut for Buffer<'a, T>
|
2017-10-11 11:24:49 +00:00
|
|
|
where T: Sample
|
|
|
|
{
|
2015-09-01 11:53:54 +00:00
|
|
|
#[inline]
|
2014-12-15 15:32:13 +00:00
|
|
|
fn deref_mut(&mut self) -> &mut [T] {
|
2017-10-12 09:54:09 +00:00
|
|
|
self.target.as_mut().unwrap().buffer()
|
2014-12-15 10:45:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
impl<'a, T> Drop for Buffer<'a, T>
|
2017-10-11 11:24:49 +00:00
|
|
|
where T: Sample
|
|
|
|
{
|
2015-09-01 11:53:54 +00:00
|
|
|
#[inline]
|
2014-12-15 10:45:38 +00:00
|
|
|
fn drop(&mut self) {
|
2014-12-15 15:32:13 +00:00
|
|
|
self.target.take().unwrap().finish();
|
2014-12-11 16:23:33 +00:00
|
|
|
}
|
|
|
|
}
|