This is an implementation of the API described at #204. Please see that issue for more details on the motivation. ----- A **Host** provides access to the available audio devices on the system. Some platforms have more than one host available, e.g. wasapi/asio/dsound on windows, alsa/pulse/jack on linux and so on. As a result, some audio devices are only available on certain hosts, while others are only available on other hosts. Every platform supported by CPAL has at least one **DefaultHost** that is guaranteed to be available (alsa, wasapi and coreaudio). Currently, the default hosts are the only hosts supported by CPAL, however this will change as of landing #221 (cc @freesig). These changes should also accommodate support for other hosts such as jack #250 (cc @derekdreery) and pulseaudio (cc @knappador) #259. This introduces a suite of traits allowing for both compile time and runtime dispatch of different hosts and their uniquely associated device and event loop types. A new private **host** module has been added containing the individual host implementations, each in their own submodule gated to the platforms on which they are available. A new **platform** module has been added containing platform-specific items, including a dynamically dispatched host type that allows for easily switching between hosts at runtime. The **ALL_HOSTS** slice contains a **HostId** for each host supported on the current platform. The **available_hosts** function produces a **HostId** for each host that is currently *available* on the platform. The **host_from_id** function allows for initialising a host from its associated ID, failing with a **HostUnavailable** error. The **default_host** function returns the default host and should never fail. Please see the examples for a demonstration of the change in usage. For the most part, things look the same at the surface level, however the role of device enumeration and creating the event loop have been moved from global functions to host methods. The enumerate.rs example has been updated to enumerate all devices for each host, not just the default. **TODO** - [x] Add the new **Host** API - [x] Update examples for the new API. - [x] ALSA host - [ ] WASAPI host - [ ] CoreAudio host - [ ] Emscripten host **Follow-up PR** - [ ] ASIO host #221 cc @ishitatsuyuki more to review for you if you're interested, but it might be easier after #288 lands and this gets rebased.
155 lines
4.3 KiB
Rust
155 lines
4.3 KiB
Rust
use {BackendSpecificError, DevicesError, SupportedFormat};
|
|
use std::mem;
|
|
use std::ptr::null;
|
|
use std::vec::IntoIter as VecIntoIter;
|
|
use super::coreaudio::sys::{
|
|
AudioDeviceID,
|
|
AudioObjectPropertyAddress,
|
|
AudioObjectGetPropertyData,
|
|
AudioObjectGetPropertyDataSize,
|
|
kAudioHardwareNoError,
|
|
kAudioHardwarePropertyDefaultInputDevice,
|
|
kAudioHardwarePropertyDefaultOutputDevice,
|
|
kAudioHardwarePropertyDevices,
|
|
kAudioObjectPropertyElementMaster,
|
|
kAudioObjectPropertyScopeGlobal,
|
|
kAudioObjectSystemObject,
|
|
OSStatus,
|
|
};
|
|
use super::Device;
|
|
|
|
unsafe fn audio_devices() -> Result<Vec<AudioDeviceID>, OSStatus> {
|
|
let property_address = AudioObjectPropertyAddress {
|
|
mSelector: kAudioHardwarePropertyDevices,
|
|
mScope: kAudioObjectPropertyScopeGlobal,
|
|
mElement: kAudioObjectPropertyElementMaster,
|
|
};
|
|
|
|
macro_rules! try_status_or_return {
|
|
($status:expr) => {
|
|
if $status != kAudioHardwareNoError as i32 {
|
|
return Err($status);
|
|
}
|
|
};
|
|
}
|
|
|
|
let data_size = 0u32;
|
|
let status = AudioObjectGetPropertyDataSize(
|
|
kAudioObjectSystemObject,
|
|
&property_address as *const _,
|
|
0,
|
|
null(),
|
|
&data_size as *const _ as *mut _,
|
|
);
|
|
try_status_or_return!(status);
|
|
|
|
let device_count = data_size / mem::size_of::<AudioDeviceID>() as u32;
|
|
let mut audio_devices = vec![];
|
|
audio_devices.reserve_exact(device_count as usize);
|
|
|
|
let status = AudioObjectGetPropertyData(
|
|
kAudioObjectSystemObject,
|
|
&property_address as *const _,
|
|
0,
|
|
null(),
|
|
&data_size as *const _ as *mut _,
|
|
audio_devices.as_mut_ptr() as *mut _,
|
|
);
|
|
try_status_or_return!(status);
|
|
|
|
audio_devices.set_len(device_count as usize);
|
|
|
|
Ok(audio_devices)
|
|
}
|
|
|
|
pub struct Devices(VecIntoIter<AudioDeviceID>);
|
|
|
|
impl Devices {
|
|
pub fn new() -> Result<Self, DevicesError> {
|
|
let devices = unsafe {
|
|
match audio_devices() {
|
|
Ok(devices) => devices,
|
|
Err(os_status) => {
|
|
let description = format!("{}", os_status);
|
|
let err = BackendSpecificError { description };
|
|
return Err(err.into());
|
|
}
|
|
}
|
|
};
|
|
Ok(Devices(devices.into_iter()))
|
|
}
|
|
}
|
|
|
|
unsafe impl Send for Devices {
|
|
}
|
|
unsafe impl Sync for Devices {
|
|
}
|
|
|
|
impl Iterator for Devices {
|
|
type Item = Device;
|
|
fn next(&mut self) -> Option<Device> {
|
|
self.0.next().map(|id| Device { audio_device_id: id })
|
|
}
|
|
}
|
|
|
|
pub fn default_input_device() -> Option<Device> {
|
|
let property_address = AudioObjectPropertyAddress {
|
|
mSelector: kAudioHardwarePropertyDefaultInputDevice,
|
|
mScope: kAudioObjectPropertyScopeGlobal,
|
|
mElement: kAudioObjectPropertyElementMaster,
|
|
};
|
|
|
|
let audio_device_id: AudioDeviceID = 0;
|
|
let data_size = mem::size_of::<AudioDeviceID>();;
|
|
let status = unsafe {
|
|
AudioObjectGetPropertyData(
|
|
kAudioObjectSystemObject,
|
|
&property_address as *const _,
|
|
0,
|
|
null(),
|
|
&data_size as *const _ as *mut _,
|
|
&audio_device_id as *const _ as *mut _,
|
|
)
|
|
};
|
|
if status != kAudioHardwareNoError as i32 {
|
|
return None;
|
|
}
|
|
|
|
let device = Device {
|
|
audio_device_id: audio_device_id,
|
|
};
|
|
Some(device)
|
|
}
|
|
|
|
pub fn default_output_device() -> Option<Device> {
|
|
let property_address = AudioObjectPropertyAddress {
|
|
mSelector: kAudioHardwarePropertyDefaultOutputDevice,
|
|
mScope: kAudioObjectPropertyScopeGlobal,
|
|
mElement: kAudioObjectPropertyElementMaster,
|
|
};
|
|
|
|
let audio_device_id: AudioDeviceID = 0;
|
|
let data_size = mem::size_of::<AudioDeviceID>();;
|
|
let status = unsafe {
|
|
AudioObjectGetPropertyData(
|
|
kAudioObjectSystemObject,
|
|
&property_address as *const _,
|
|
0,
|
|
null(),
|
|
&data_size as *const _ as *mut _,
|
|
&audio_device_id as *const _ as *mut _,
|
|
)
|
|
};
|
|
if status != kAudioHardwareNoError as i32 {
|
|
return None;
|
|
}
|
|
|
|
let device = Device {
|
|
audio_device_id: audio_device_id,
|
|
};
|
|
Some(device)
|
|
}
|
|
|
|
pub type SupportedInputFormats = VecIntoIter<SupportedFormat>;
|
|
pub type SupportedOutputFormats = VecIntoIter<SupportedFormat>;
|