pub use self::platform_impl::*;
#[cfg(all(
feature = "jack",
any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "macos",
target_os = "windows",
)
))]
#[cfg_attr(docsrs, doc(cfg(feature = "jack")))]
pub use crate::host::jack::Host as JackHost;
#[cfg(all(
any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
),
feature = "pipewire",
))]
#[cfg_attr(docsrs, doc(cfg(feature = "pipewire")))]
pub use crate::host::pipewire::Host as PipeWireHost;
#[cfg(feature = "custom")]
pub use crate::host::custom::{Device as CustomDevice, Host as CustomHost, Stream as CustomStream};
macro_rules! impl_platform_host {
($($(#[cfg($feat: meta)])? $HostVariant:ident $($HostName:literal)? => $Host:ty),* $(,)?) => {
pub const ALL_HOSTS: &'static [HostId] = &[
$(
$(#[cfg($feat)])?
HostId::$HostVariant,
)*
];
pub struct Host(HostInner);
#[derive(Clone)]
pub struct Device(DeviceInner);
pub struct Devices(DevicesInner);
#[must_use = "If the stream is not stored it will not play."]
pub struct Stream(StreamInner);
#[derive(Clone)]
pub struct SupportedInputConfigs(SupportedInputConfigsInner);
#[derive(Clone)]
pub struct SupportedOutputConfigs(SupportedOutputConfigsInner);
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum HostId {
$(
$(#[cfg($feat)])?
$(#[cfg_attr(docsrs, doc(cfg($feat)))])?
$HostVariant,
)*
}
#[doc(hidden)]
#[derive(Clone)]
#[allow(clippy::large_enum_variant)]
pub enum DeviceInner {
$(
$(#[cfg($feat)])?
$HostVariant(<$Host as crate::traits::HostTrait>::Device),
)*
}
#[doc(hidden)]
pub enum DevicesInner {
$(
$(#[cfg($feat)])?
$HostVariant(<$Host as crate::traits::HostTrait>::Devices),
)*
}
#[doc(hidden)]
pub enum HostInner {
$(
$(#[cfg($feat)])?
$HostVariant($Host),
)*
}
#[doc(hidden)]
pub enum StreamInner {
$(
$(#[cfg($feat)])?
$HostVariant(<<$Host as crate::traits::HostTrait>::Device as crate::traits::DeviceTrait>::Stream),
)*
}
#[derive(Clone)]
enum SupportedInputConfigsInner {
$(
$(#[cfg($feat)])?
$HostVariant(<<$Host as crate::traits::HostTrait>::Device as crate::traits::DeviceTrait>::SupportedInputConfigs),
)*
}
#[derive(Clone)]
enum SupportedOutputConfigsInner {
$(
$(#[cfg($feat)])?
$HostVariant(<<$Host as crate::traits::HostTrait>::Device as crate::traits::DeviceTrait>::SupportedOutputConfigs),
)*
}
impl HostId {
pub fn name(&self) -> &'static str {
match self {
$(
$(#[cfg($feat)])?
HostId::$HostVariant => __cpal_select_host_name!($HostVariant, $($HostName)?),
)*
}
}
}
impl std::fmt::Display for HostId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name().to_ascii_lowercase())
}
}
impl std::str::FromStr for HostId {
type Err = crate::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
$(
$(#[cfg($feat)])?
if HostId::$HostVariant.name().eq_ignore_ascii_case(s) {
return Ok(HostId::$HostVariant);
}
)*
Err(crate::Error::with_message(
crate::ErrorKind::UnsupportedOperation,
format!("host \"{s}\" is not supported on this platform"),
))
}
}
impl Devices {
pub fn as_inner(&self) -> &DevicesInner { &self.0 }
pub fn as_inner_mut(&mut self) -> &mut DevicesInner { &mut self.0 }
pub fn into_inner(self) -> DevicesInner { self.0 }
}
impl Device {
pub fn as_inner(&self) -> &DeviceInner { &self.0 }
pub fn as_inner_mut(&mut self) -> &mut DeviceInner { &mut self.0 }
pub fn into_inner(self) -> DeviceInner { self.0 }
}
impl Host {
pub fn id(&self) -> HostId {
match self.0 {
$(
$(#[cfg($feat)])?
HostInner::$HostVariant(_) => HostId::$HostVariant,
)*
}
}
pub fn as_inner(&self) -> &HostInner { &self.0 }
pub fn as_inner_mut(&mut self) -> &mut HostInner { &mut self.0 }
pub fn into_inner(self) -> HostInner { self.0 }
}
impl Stream {
pub fn as_inner(&self) -> &StreamInner { &self.0 }
pub fn as_inner_mut(&mut self) -> &mut StreamInner { &mut self.0 }
pub fn into_inner(self) -> StreamInner { self.0 }
}
impl Iterator for Devices {
type Item = Device;
fn next(&mut self) -> Option<Self::Item> {
match self.0 {
$(
$(#[cfg($feat)])?
DevicesInner::$HostVariant(ref mut d) => {
d.next().map(DeviceInner::$HostVariant).map(Device::from)
}
)*
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self.0 {
$(
$(#[cfg($feat)])?
DevicesInner::$HostVariant(ref d) => d.size_hint(),
)*
}
}
}
impl Iterator for SupportedInputConfigs {
type Item = crate::SupportedStreamConfigRange;
fn next(&mut self) -> Option<Self::Item> {
match self.0 {
$(
$(#[cfg($feat)])?
SupportedInputConfigsInner::$HostVariant(ref mut s) => s.next(),
)*
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self.0 {
$(
$(#[cfg($feat)])?
SupportedInputConfigsInner::$HostVariant(ref d) => d.size_hint(),
)*
}
}
}
impl Iterator for SupportedOutputConfigs {
type Item = crate::SupportedStreamConfigRange;
fn next(&mut self) -> Option<Self::Item> {
match self.0 {
$(
$(#[cfg($feat)])?
SupportedOutputConfigsInner::$HostVariant(ref mut s) => s.next(),
)*
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self.0 {
$(
$(#[cfg($feat)])?
SupportedOutputConfigsInner::$HostVariant(ref d) => d.size_hint(),
)*
}
}
}
impl crate::traits::DeviceTrait for Device {
type SupportedInputConfigs = SupportedInputConfigs;
type SupportedOutputConfigs = SupportedOutputConfigs;
type Stream = Stream;
fn description(&self) -> Result<crate::DeviceDescription, crate::Error> {
match self.0 {
$(
$(#[cfg($feat)])?
DeviceInner::$HostVariant(ref d) => d.description(),
)*
}
}
fn id(&self) -> Result<crate::DeviceId, crate::Error> {
match self.0 {
$(
$(#[cfg($feat)])?
DeviceInner::$HostVariant(ref d) => d.id(),
)*
}
}
fn supports_input(&self) -> bool {
match self.0 {
$(
$(#[cfg($feat)])?
DeviceInner::$HostVariant(ref d) => d.supports_input(),
)*
}
}
fn supports_output(&self) -> bool {
match self.0 {
$(
$(#[cfg($feat)])?
DeviceInner::$HostVariant(ref d) => d.supports_output(),
)*
}
}
fn supported_input_configs(&self) -> Result<Self::SupportedInputConfigs, crate::Error> {
match self.0 {
$(
$(#[cfg($feat)])?
DeviceInner::$HostVariant(ref d) => {
d.supported_input_configs()
.map(SupportedInputConfigsInner::$HostVariant)
.map(SupportedInputConfigs)
}
)*
}
}
fn supported_output_configs(&self) -> Result<Self::SupportedOutputConfigs, crate::Error> {
match self.0 {
$(
$(#[cfg($feat)])?
DeviceInner::$HostVariant(ref d) => {
d.supported_output_configs()
.map(SupportedOutputConfigsInner::$HostVariant)
.map(SupportedOutputConfigs)
}
)*
}
}
fn default_input_config(&self) -> Result<crate::SupportedStreamConfig, crate::Error> {
match self.0 {
$(
$(#[cfg($feat)])?
DeviceInner::$HostVariant(ref d) => d.default_input_config(),
)*
}
}
fn default_output_config(&self) -> Result<crate::SupportedStreamConfig, crate::Error> {
match self.0 {
$(
$(#[cfg($feat)])?
DeviceInner::$HostVariant(ref d) => d.default_output_config(),
)*
}
}
fn build_input_stream_raw<D, E>(
&self,
config: crate::StreamConfig,
sample_format: crate::SampleFormat,
data_callback: D,
error_callback: E,
timeout: Option<std::time::Duration>,
) -> Result<Self::Stream, crate::Error>
where
D: FnMut(&crate::Data, &crate::InputCallbackInfo) + Send + 'static,
E: FnMut(crate::Error) + Send + 'static,
{
match self.0 {
$(
$(#[cfg($feat)])?
DeviceInner::$HostVariant(ref d) => d
.build_input_stream_raw(
config,
sample_format,
data_callback,
error_callback,
timeout,
)
.map(StreamInner::$HostVariant)
.map(Stream::from),
)*
}
}
fn build_output_stream_raw<D, E>(
&self,
config: crate::StreamConfig,
sample_format: crate::SampleFormat,
data_callback: D,
error_callback: E,
timeout: Option<std::time::Duration>,
) -> Result<Self::Stream, crate::Error>
where
D: FnMut(&mut crate::Data, &crate::OutputCallbackInfo) + Send + 'static,
E: FnMut(crate::Error) + Send + 'static,
{
match self.0 {
$(
$(#[cfg($feat)])?
DeviceInner::$HostVariant(ref d) => d
.build_output_stream_raw(
config,
sample_format,
data_callback,
error_callback,
timeout,
)
.map(StreamInner::$HostVariant)
.map(Stream::from),
)*
}
}
}
impl crate::traits::HostTrait for Host {
type Devices = Devices;
type Device = Device;
fn is_available() -> bool {
$(
$(#[cfg($feat)])?
if <$Host>::is_available() { return true; }
)*
false
}
fn devices(&self) -> Result<Self::Devices, crate::Error> {
match self.0 {
$(
$(#[cfg($feat)])?
HostInner::$HostVariant(ref h) => {
h.devices().map(DevicesInner::$HostVariant).map(Devices::from)
}
)*
}
}
fn device_by_id(&self, id: &crate::DeviceId) -> Option<Self::Device> {
match self.0 {
$(
$(#[cfg($feat)])?
HostInner::$HostVariant(ref h) => {
h.device_by_id(id).map(DeviceInner::$HostVariant).map(Device::from)
}
)*
}
}
fn default_input_device(&self) -> Option<Self::Device> {
match self.0 {
$(
$(#[cfg($feat)])?
HostInner::$HostVariant(ref h) => {
h.default_input_device().map(DeviceInner::$HostVariant).map(Device::from)
}
)*
}
}
fn default_output_device(&self) -> Option<Self::Device> {
match self.0 {
$(
$(#[cfg($feat)])?
HostInner::$HostVariant(ref h) => {
h.default_output_device().map(DeviceInner::$HostVariant).map(Device::from)
}
)*
}
}
}
impl crate::traits::StreamTrait for Stream {
fn play(&self) -> Result<(), crate::Error> {
match self.0 {
$(
$(#[cfg($feat)])?
StreamInner::$HostVariant(ref s) => {
s.play()
}
)*
}
}
fn pause(&self) -> Result<(), crate::Error> {
match self.0 {
$(
$(#[cfg($feat)])?
StreamInner::$HostVariant(ref s) => {
s.pause()
}
)*
}
}
fn buffer_size(&self) -> Result<crate::FrameCount, crate::Error> {
match self.0 {
$(
$(#[cfg($feat)])?
StreamInner::$HostVariant(ref s) => {
s.buffer_size()
}
)*
}
}
fn now(&self) -> crate::StreamInstant {
match self.0 {
$(
$(#[cfg($feat)])?
StreamInner::$HostVariant(ref s) => {
s.now()
}
)*
}
}
}
use std::fmt;
use std::hash::{Hash, Hasher};
impl PartialEq for DeviceInner {
#[allow(unreachable_patterns)]
fn eq(&self, other: &DeviceInner) -> bool {
match (self, other) {
$(
$(#[cfg($feat)])?
(DeviceInner::$HostVariant(a), DeviceInner::$HostVariant(b)) => a == b,
)*
_ => false,
}
}
}
impl Eq for DeviceInner {}
impl Hash for DeviceInner {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
$(
$(#[cfg($feat)])?
DeviceInner::$HostVariant(d) => d.hash(state),
)*
}
}
}
impl fmt::Debug for DeviceInner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
$(
$(#[cfg($feat)])?
DeviceInner::$HostVariant(d) => d.fmt(f),
)*
}
}
}
impl PartialEq for Device {
fn eq(&self, other: &Device) -> bool {
self.0 == other.0
}
}
impl Eq for Device {}
impl Hash for Device {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl fmt::Debug for Device {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl fmt::Display for Device {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
$(
$(#[cfg($feat)])?
DeviceInner::$HostVariant(ref d) => fmt::Display::fmt(d, f),
)*
}
}
}
impl From<DeviceInner> for Device {
fn from(d: DeviceInner) -> Self {
Device(d)
}
}
impl From<DevicesInner> for Devices {
fn from(d: DevicesInner) -> Self {
Devices(d)
}
}
impl From<HostInner> for Host {
fn from(h: HostInner) -> Self {
Host(h)
}
}
impl From<StreamInner> for Stream {
fn from(s: StreamInner) -> Self {
Stream(s)
}
}
$(
$(#[cfg($feat)])?
impl From<<$Host as crate::traits::HostTrait>::Device> for Device {
fn from(h: <$Host as crate::traits::HostTrait>::Device) -> Self {
DeviceInner::$HostVariant(h).into()
}
}
$(#[cfg($feat)])?
impl From<<$Host as crate::traits::HostTrait>::Devices> for Devices {
fn from(h: <$Host as crate::traits::HostTrait>::Devices) -> Self {
DevicesInner::$HostVariant(h).into()
}
}
$(#[cfg($feat)])?
impl From<$Host> for Host {
fn from(h: $Host) -> Self {
HostInner::$HostVariant(h).into()
}
}
$(#[cfg($feat)])?
impl From<<<$Host as crate::traits::HostTrait>::Device as crate::traits::DeviceTrait>::Stream> for Stream {
fn from(h: <<$Host as crate::traits::HostTrait>::Device as crate::traits::DeviceTrait>::Stream) -> Self {
StreamInner::$HostVariant(h).into()
}
}
)*
pub fn available_hosts() -> Vec<HostId> {
let mut host_ids = vec![];
$(
$(#[cfg($feat)])?
if <$Host as crate::traits::HostTrait>::is_available() {
host_ids.push(HostId::$HostVariant);
}
)*
host_ids
}
pub fn host_from_id(id: HostId) -> Result<Host, crate::Error> {
match id {
$(
$(#[cfg($feat)])?
HostId::$HostVariant => {
<$Host>::new()
.map(HostInner::$HostVariant)
.map(Host::from)
}
)*
}
}
impl Default for Host {
fn default() -> Host {
default_host()
}
}
};
}
macro_rules! __cpal_select_host_name {
($variant:ident, $name:literal) => {
$name
};
($variant:ident,) => {
stringify!($variant)
};
}
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd"
))]
mod platform_impl {
#[cfg(feature = "jack")]
use super::JackHost;
use crate::host::alsa::Host as AlsaHost;
#[cfg(feature = "pipewire")]
use crate::host::pipewire::Host as PipeWireHost;
#[cfg(feature = "pulseaudio")]
use crate::host::pulseaudio::Host as PulseAudioHost;
impl_platform_host!(
#[cfg(feature = "pipewire")] PipeWire => PipeWireHost,
#[cfg(feature = "pulseaudio")] PulseAudio => PulseAudioHost,
#[cfg(feature = "jack")] Jack "JACK" => JackHost,
Alsa "ALSA" => AlsaHost,
#[cfg(feature = "custom")] Custom => super::CustomHost,
);
pub fn default_host() -> Host {
#[cfg(feature = "pipewire")]
if <PipeWireHost as crate::traits::HostTrait>::is_available() {
if let Ok(host) = PipeWireHost::new() {
return host.into();
}
}
#[cfg(feature = "pulseaudio")]
if <PulseAudioHost as crate::traits::HostTrait>::is_available() {
if let Ok(host) = PulseAudioHost::new() {
return host.into();
}
}
AlsaHost::new()
.expect("the default host should always be available")
.into()
}
}
#[cfg(target_vendor = "apple")]
mod platform_impl {
#[cfg(all(feature = "jack", target_os = "macos"))]
use super::JackHost;
use crate::host::coreaudio::Host as CoreAudioHost;
impl_platform_host!(
CoreAudio => CoreAudioHost,
#[cfg(all(feature = "jack", target_os = "macos"))] Jack "JACK" => JackHost,
#[cfg(feature = "custom")] Custom => super::CustomHost
);
pub fn default_host() -> Host {
CoreAudioHost::new()
.expect("the default host should always be available")
.into()
}
}
#[cfg(all(
target_arch = "wasm32",
target_os = "unknown",
feature = "wasm-bindgen"
))]
mod platform_impl {
#[cfg(all(feature = "audioworklet", target_feature = "atomics"))]
use crate::host::audioworklet::Host as AudioWorkletHost;
use crate::host::webaudio::Host as WebAudioHost;
impl_platform_host!(
WebAudio => WebAudioHost,
#[cfg(all(feature = "audioworklet", target_feature = "atomics"))] AudioWorklet => AudioWorkletHost,
#[cfg(feature = "custom")] Custom => super::CustomHost
);
pub fn default_host() -> Host {
WebAudioHost::new()
.expect("the default host should always be available")
.into()
}
}
#[cfg(windows)]
mod platform_impl {
#[cfg(feature = "jack")]
use super::JackHost;
#[cfg(feature = "asio")]
use crate::host::asio::Host as AsioHost;
use crate::host::wasapi::Host as WasapiHost;
impl_platform_host!(
#[cfg(feature = "asio")] Asio "ASIO" => AsioHost,
Wasapi "WASAPI" => WasapiHost,
#[cfg(feature = "jack")] Jack "JACK" => JackHost,
#[cfg(feature = "custom")] Custom => super::CustomHost,
);
pub fn default_host() -> Host {
WasapiHost::new()
.expect("the default host should always be available")
.into()
}
}
#[cfg(target_os = "android")]
mod platform_impl {
use crate::host::aaudio::Host as AAudioHost;
impl_platform_host!(
AAudio => AAudioHost,
#[cfg(feature = "custom")] Custom => super::CustomHost
);
pub fn default_host() -> Host {
AAudioHost::new()
.expect("the default host should always be available")
.into()
}
}
#[cfg(not(any(
windows,
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_vendor = "apple",
target_os = "android",
all(
target_arch = "wasm32",
target_os = "unknown",
feature = "wasm-bindgen"
),
)))]
mod platform_impl {
use crate::host::null::Host as NullHost;
impl_platform_host!(
Null => NullHost,
#[cfg(feature = "custom")] Custom => super::CustomHost,
);
pub fn default_host() -> Host {
NullHost::new()
.expect("the default host should always be available")
.into()
}
}