Skip to content

Commit

Permalink
[macos] Implement Endpoint and Format Enumeration
Browse files Browse the repository at this point in the history
Based on RustAudio#195.

Also implements proper handling of the given `Endpoint` in the
macos implementation of the `build_voice` method.

Updates to the latest coreaudio-sys and coreaudio-rs which include the
additional necessary frameworks.

Also adds a line that prints the name of the default device in the
`enumeration.rs` example.

Updates the CHANGELOG for this PR.

Closes RustAudio#194.
Related to RustAudio#180.

Related external issues:

- RustAudio/coreaudio-sys#4
- RustAudio/coreaudio-rs#57
  • Loading branch information
mitchmindtree committed Jan 28, 2018
1 parent 091798a commit 3952c44
Show file tree
Hide file tree
Showing 5 changed files with 294 additions and 31 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Unreleased

- Implement Endpoint and Format Enumeration for macos.
- Implement format handling for macos `build_voice` method.

# Version 0.6.0 (2017-12-11)
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ alsa-sys = { version = "0.1", path = "alsa-sys" }
libc = "0.2"

[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
coreaudio-rs = "0.7.0"
coreaudio-rs = "0.8.0"
core-foundation-sys = "0.5.1" # For linking to CoreFoundation.framework and handling device name `CFString`s.

[target.'cfg(target_os = "emscripten")'.dependencies]
stdweb = { version = "0.1.3", default-features = false }
3 changes: 2 additions & 1 deletion examples/enumerate.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
extern crate cpal;

fn main() {
let endpoints = cpal::endpoints();
println!("Default Endpoint:\n {:?}", cpal::default_endpoint().map(|e| e.name()));

let endpoints = cpal::endpoints();
println!("Endpoints: ");
for (endpoint_index, endpoint) in endpoints.enumerate() {
println!("{}. Endpoint \"{}\" Audio formats: ",
Expand Down
111 changes: 100 additions & 11 deletions src/coreaudio/enumerate.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,76 @@
use SupportedFormat;
use std::mem;
use std::ptr::null;
use std::vec::IntoIter as VecIntoIter;
use super::coreaudio::sys::{
AudioDeviceID,
AudioObjectPropertyAddress,
AudioObjectGetPropertyData,
AudioObjectGetPropertyDataSize,
kAudioHardwareNoError,
kAudioHardwarePropertyDefaultOutputDevice,
kAudioHardwarePropertyDevices,
kAudioObjectPropertyElementMaster,
kAudioObjectPropertyScopeGlobal,
kAudioObjectSystemObject,
OSStatus,
};
use super::Endpoint;

use SupportedFormat;
unsafe fn audio_output_devices() -> Result<Vec<AudioDeviceID>, OSStatus> {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioHardwarePropertyDevices,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};

use std::vec::IntoIter as VecIntoIter;
macro_rules! try_status_or_return {
($status:expr) => {
if $status != kAudioHardwareNoError as i32 {
return Err($status);
}
};
}

pub struct EndpointsIterator(bool);
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);

// Only keep the devices that have some supported output format.
audio_devices.retain(|&id| {
let e = Endpoint { audio_device_id: id };
match e.supported_formats() {
Err(_) => false,
Ok(mut fmts) => fmts.next().is_some(),
}
});

Ok(audio_devices)
}

pub struct EndpointsIterator(VecIntoIter<AudioDeviceID>);

unsafe impl Send for EndpointsIterator {
}
Expand All @@ -13,24 +79,47 @@ unsafe impl Sync for EndpointsIterator {

impl Default for EndpointsIterator {
fn default() -> Self {
EndpointsIterator(false)
let devices = unsafe {
audio_output_devices().expect("failed to get audio output devices")
};
EndpointsIterator(devices.into_iter())
}
}

impl Iterator for EndpointsIterator {
type Item = Endpoint;
fn next(&mut self) -> Option<Endpoint> {
if self.0 {
None
} else {
self.0 = true;
Some(Endpoint)
}
self.0.next().map(|id| Endpoint { audio_device_id: id })
}
}

pub fn default_endpoint() -> Option<Endpoint> {
Some(Endpoint)
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 endpoint = Endpoint {
audio_device_id: audio_device_id,
};
Some(endpoint)
}

pub type SupportedFormatsIterator = VecIntoIter<SupportedFormat>;
Loading

0 comments on commit 3952c44

Please sign in to comment.