Last active
September 4, 2018 19:56
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | |
) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
all: | |
rustc audio_device_utils.rs | |
clean: | |
rm audio_device_utils |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![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