Skip to content

Instantly share code, notes, and snippets.

@ChunMinChang
Last active September 4, 2018 19:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ChunMinChang/07b806cb6a9ea1136cb3cbd8cda6c806 to your computer and use it in GitHub Desktop.
Save ChunMinChang/07b806cb6a9ea1136cb3cbd8cda6c806 to your computer and use it in GitHub Desktop.
Access data from CoreAudio APIs with a single-element tuple structs wrapping native CoreAudio types
#[path = ""]
mod utils {
#[path = "audio_object.rs"]
mod audio_object;
use self::audio_object::{get_property_data, sys::*};
use std::fmt; // For fmt::{Display, Formatter, Formatter}
#[derive(PartialEq)]
pub enum Scope {
Input,
Output,
}
#[derive(Debug)]
pub enum Error {
AudioObject(audio_object::Error),
NoDevice,
}
impl From<audio_object::Error> for Error {
fn from(e: audio_object::Error) -> Error {
Error::AudioObject(e)
}
}
const DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS: AudioObjectPropertyAddress =
AudioObjectPropertyAddress {
mSelector: kAudioHardwarePropertyDefaultInputDevice,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
const DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS: AudioObjectPropertyAddress =
AudioObjectPropertyAddress {
mSelector: kAudioHardwarePropertyDefaultOutputDevice,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
};
pub struct AudioObject(AudioObjectID);
impl AudioObject {
pub fn is_unknown(&self) -> bool {
self.0 == kAudioObjectUnknown
}
}
impl fmt::Display for AudioObject {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
pub struct AudioSystemObject(AudioObjectID);
impl AudioSystemObject {
pub fn new() -> AudioSystemObject {
AudioSystemObject(kAudioObjectSystemObject)
}
pub fn get_default_device(&self, scope: &Scope) -> Result<AudioObject, Error> {
let address: &AudioObjectPropertyAddress = if scope == &Scope::Input {
&DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS
} else {
&DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS
};
let device = get_property_data::<AudioObject>(self.0, address)?;
if device.is_unknown() { Err(Error::NoDevice) } else { Ok(device) }
}
}
}
fn main() {
let system_device = utils::AudioSystemObject::new();
let default_input_device = system_device.get_default_device(&utils::Scope::Input).unwrap();
println!("default input device: {}", default_input_device);
let default_output_device = system_device.get_default_device(&utils::Scope::Output).unwrap();
println!("default output device: {}", default_output_device);
}
use std::mem; // For mem::{uninitialized(), size_of_val()}
use std::os::raw::c_void;
use std::ptr; // For ptr::null()
#[path = "sys.rs"]
pub mod sys;
#[derive(Debug, PartialEq)]
pub enum Error {
BadObject,
}
impl From<sys::OSStatus> for Error {
fn from(status: sys::OSStatus) -> Error {
match status {
sys::kAudioHardwareBadObjectError => Error::BadObject,
s => panic!("Unknown status: {}", s),
}
}
}
pub fn get_property_data<T>(
id: sys::AudioObjectID,
address: &sys::AudioObjectPropertyAddress,
) -> Result<T, Error> {
let mut data: T = unsafe { mem::uninitialized() };
let mut size = mem::size_of_val(&data);
let status = audio_object_get_property_data(
id,
address,
&mut size,
&mut data,
);
convert_to_result(status)?;
Ok(data)
}
fn convert_to_result(status: sys::OSStatus) -> Result<(), Error> {
match status {
sys::kAudioHardwareNoError => Ok(()),
e => Err(e.into()),
}
}
fn audio_object_get_property_data<T>(
id: sys::AudioObjectID,
address: &sys::AudioObjectPropertyAddress,
size: *mut usize,
data: *mut T,
) -> sys::OSStatus {
unsafe {
sys::AudioObjectGetPropertyData(
id,
address, // as `*const sys::AudioObjectPropertyAddress` automatically.
0,
ptr::null(),
size as *mut u32, // Cast raw usize pointer to raw u32 pointer.
data as *mut c_void, // Cast raw T pointer to void pointer.
)
}
}
all:
rustc audio_device_utils.rs
clean:
rm audio_device_utils
#![allow(non_snake_case, non_upper_case_globals)]
use super::c_void;
pub type OSStatus = i32;
pub const kAudioHardwareNoError: OSStatus = 0;
pub const kAudioHardwareBadObjectError: OSStatus = 560947818;
pub type AudioObjectID = u32;
pub const kAudioObjectUnknown: AudioObjectID = 0;
pub const kAudioObjectSystemObject: AudioObjectID = 1;
pub type AudioObjectPropertySelector = u32;
pub const kAudioHardwarePropertyDefaultInputDevice: AudioObjectPropertySelector = 1682533920;
pub const kAudioHardwarePropertyDefaultOutputDevice: AudioObjectPropertySelector = 1682929012;
pub type AudioObjectPropertyScope = u32;
pub const kAudioObjectPropertyScopeGlobal: AudioObjectPropertyScope = 1735159650;
pub type AudioObjectPropertyElement = u32;
pub const kAudioObjectPropertyElementMaster: AudioObjectPropertyElement = 0;
#[repr(C)]
pub struct AudioObjectPropertyAddress {
pub mSelector: AudioObjectPropertySelector,
pub mScope: AudioObjectPropertyScope,
pub mElement: AudioObjectPropertyElement,
}
#[link(name = "CoreAudio", kind = "framework")] // Link to a dynamic library in CoreAudio framework.
extern "C" {
// https://developer.apple.com/documentation/coreaudio/1422524-audioobjectgetpropertydata?language=objc
pub fn AudioObjectGetPropertyData(
inObjectID: AudioObjectID,
inAddress: *const AudioObjectPropertyAddress,
inQualifierDataSize: u32,
inQualifierData: *const c_void,
ioDataSize: *mut u32,
outData: *mut c_void,
) -> OSStatus;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment