Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rkilgore features #94

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ core_midi = ["coreaudio-sys/core_midi"]
bitflags = "1.0"
coreaudio-sys = { version = "0.2", default-features = false }
core-foundation-sys = "0.6.2"
libc = "0.2.135"

[package.metadata.docs.rs]
all-features = true
Expand Down
115 changes: 97 additions & 18 deletions src/audio_unit/macos_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,23 @@ use std::time::Duration;
use std::{mem, thread};

use core_foundation_sys::string::{CFStringGetCString, CFStringGetCStringPtr, CFStringRef};
use libc;
use sys;
use sys::pid_t;
use sys::{
kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyDeviceIsAlive,
kAudioDevicePropertyDeviceNameCFString, kAudioDevicePropertyHogMode,
kAudioDevicePropertyNominalSampleRate, kAudioDevicePropertyScopeOutput, kAudioHardwareNoError,
kAudioHardwarePropertyDefaultInputDevice, kAudioHardwarePropertyDefaultOutputDevice,
kAudioHardwarePropertyDevices, kAudioObjectPropertyElementMaster,
kAudioObjectPropertyScopeGlobal, kAudioObjectSystemObject,
kAudioOutputUnitProperty_CurrentDevice, kAudioOutputUnitProperty_EnableIO,
kAudioHardwarePropertyDevices, kAudioObjectPropertyElementMain,
kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput, kAudioObjectPropertyScopeOutput,
kAudioObjectSystemObject, kAudioOutputUnitProperty_CurrentDevice, kAudioOutputUnitProperty_EnableIO,
kAudioStreamPropertyAvailablePhysicalFormats, kAudioStreamPropertyPhysicalFormat,
kCFStringEncodingUTF8, AudioDeviceID, AudioObjectAddPropertyListener,
AudioObjectGetPropertyData, AudioObjectGetPropertyDataSize, AudioObjectID,
AudioObjectPropertyAddress, AudioObjectRemovePropertyListener, AudioObjectSetPropertyData,
AudioStreamBasicDescription, AudioStreamRangedDescription, AudioValueRange, OSStatus,
AudioObjectPropertyScope, kAudioDevicePropertyStreamConfiguration, kAudioObjectPropertyElementWildcard,
};

use crate::audio_unit::audio_format::{AudioFormat, LinearPcmFlags};
Expand All @@ -44,7 +46,7 @@ pub fn get_default_device_id(input: bool) -> Option<AudioDeviceID> {
let property_address = AudioObjectPropertyAddress {
mSelector: selector,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
mElement: kAudioObjectPropertyElementMain,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh. Interesting. I figured the name change was due to a bindgen change. Looks like kAudioObjectPropertyElementMaster is deprecated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am on macOS 10, and kAudioObjectPropertyElementMain only exists on macOS 12+ (I haven't upgraded because of reports that moving off Catalina for old macbooks can brick them.)

Does this mean that the current code on master won't work on macOS 13? If we wanted to support all macOS versions would we need OS version selectors or something?

};

let audio_device_id: AudioDeviceID = 0;
Expand Down Expand Up @@ -115,11 +117,16 @@ pub fn audio_unit_from_device_id(
}

/// List all audio device ids on the system.
pub fn get_audio_device_ids() -> Result<Vec<AudioDeviceID>, Error> {
pub fn get_audio_device_ids_for_scope(scope: Scope) -> Result<Vec<AudioDeviceID>, Error> {
let dev_scope = match scope {
Scope::Input => kAudioObjectPropertyScopeInput,
Scope::Output => kAudioObjectPropertyScopeOutput,
_ => kAudioObjectPropertyScopeGlobal,
};
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioHardwarePropertyDevices,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
mScope: dev_scope,
mElement: kAudioObjectPropertyElementMain,
};

macro_rules! try_status_or_return {
Expand Down Expand Up @@ -161,12 +168,84 @@ pub fn get_audio_device_ids() -> Result<Vec<AudioDeviceID>, Error> {
Ok(audio_devices)
}

pub fn get_audio_device_ids() -> Result<Vec<AudioDeviceID>, Error> {
return get_audio_device_ids_for_scope(Scope::Global);
}

/// does this device support input / ouptut?
pub fn get_audio_device_supports_scope(devid: AudioDeviceID, scope: Scope) -> Result<bool, Error> {
let dev_scope: AudioObjectPropertyScope = match scope {
Scope::Input => kAudioObjectPropertyScopeInput,
Scope::Output => kAudioObjectPropertyScopeOutput,
_ => kAudioObjectPropertyScopeGlobal,
};
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyStreamConfiguration,
mScope: dev_scope,
mElement: kAudioObjectPropertyElementWildcard,
};

macro_rules! try_status_or_return {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see. This is what #93 was about.

($status:expr) => {
if $status != kAudioHardwareNoError as i32 {
return Err(Error::Unknown($status));
}
};
}

let data_size = 0u32;
let status = unsafe {
AudioObjectGetPropertyDataSize(
devid,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
)
};
try_status_or_return!(status);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this macro invoked only once in this function? inline it?


unsafe {
let buffers: *mut sys::AudioBufferList = libc::malloc(data_size as usize) as *mut sys::AudioBufferList;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other parts of this repo use vec u8 as a way to allocate some bytes (see render_callback.rs let mut data = vec![0u8; data_byte_size as usize]; but there is also a TODO there saying that it is leaking len and capacity fields of the vec.

A few answers here, https://stackoverflow.com/questions/45306575/how-can-you-allocate-a-raw-mutable-pointer-in-stable-rust the first answer suggests std::alloc.

On closer inspection, is it possible to remove libc and malloc/free by using a vec u8 .as_mut_ptr() and then you don't need to free because the vec will drop itself?

// let count = data_size / mem::size_of::<sys::AudioBuffer>() as u32;
// let mut audio_buffers: Vec<sys::AudioBuffer> = vec![];
// audio_buffers.reserve_exact(count as usize);
// unsafe { audio_buffers.set_len(count as usize) };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove if not needed?


let status = unsafe {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have clippy checks on in this repo but I think it'd complain about an unsafe block in an unsafe block.

AudioObjectGetPropertyData(
devid,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
buffers as *mut _,
)
};
if status != kAudioHardwareNoError as i32 {
libc::free(buffers as *mut libc::c_void);
return Err(Error::Unknown(status));
}

for i in 0..(*buffers).mNumberBuffers {
let buf = (*buffers).mBuffers[i as usize];
if buf.mNumberChannels > 0 {
libc::free(buffers as *mut libc::c_void);
return Ok(true);
}
}
libc::free(buffers as *mut libc::c_void);
}
Ok(false)
}


/// Get the device name for a device id.
pub fn get_device_name(device_id: AudioDeviceID) -> Result<String, Error> {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyDeviceNameCFString,
mScope: kAudioDevicePropertyScopeOutput,
mElement: kAudioObjectPropertyElementMaster,
mElement: kAudioObjectPropertyElementMain,
};

macro_rules! try_status_or_return {
Expand Down Expand Up @@ -228,7 +307,7 @@ pub fn set_device_sample_rate(device_id: AudioDeviceID, new_rate: f64) -> Result
let mut property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyNominalSampleRate,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
mElement: kAudioObjectPropertyElementMain,
};
let sample_rate: f64 = 0.0;
let data_size = mem::size_of::<f64>() as u32;
Expand Down Expand Up @@ -382,7 +461,7 @@ pub fn set_device_physical_stream_format(
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioStreamPropertyPhysicalFormat,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
mElement: kAudioObjectPropertyElementMain,
};
let maybe_asbd: mem::MaybeUninit<AudioStreamBasicDescription> = mem::MaybeUninit::zeroed();
let data_size = mem::size_of::<AudioStreamBasicDescription>() as u32;
Expand All @@ -401,7 +480,7 @@ pub fn set_device_physical_stream_format(
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioStreamPropertyPhysicalFormat,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
mElement: kAudioObjectPropertyElementMain,
};

let reported_asbd: mem::MaybeUninit<AudioStreamBasicDescription> =
Expand Down Expand Up @@ -467,7 +546,7 @@ pub fn get_supported_physical_stream_formats(
let mut property_address = AudioObjectPropertyAddress {
mSelector: kAudioStreamPropertyPhysicalFormat,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
mElement: kAudioObjectPropertyElementMain,
};
let allformats = unsafe {
property_address.mSelector = kAudioStreamPropertyAvailablePhysicalFormats;
Expand Down Expand Up @@ -527,7 +606,7 @@ impl RateListener {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyNominalSampleRate,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
mElement: kAudioObjectPropertyElementMain,
};
let queue = Mutex::new(VecDeque::new());
RateListener {
Expand All @@ -553,7 +632,7 @@ impl RateListener {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyNominalSampleRate,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
mElement: kAudioObjectPropertyElementMain,
};
let result = AudioObjectGetPropertyData(
device_id,
Expand Down Expand Up @@ -653,7 +732,7 @@ impl AliveListener {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyDeviceIsAlive,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
mElement: kAudioObjectPropertyElementMain,
};
AliveListener {
alive: Box::new(AtomicBool::new(true)),
Expand All @@ -677,7 +756,7 @@ impl AliveListener {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyDeviceIsAlive,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
mElement: kAudioObjectPropertyElementMain,
};
let result = AudioObjectGetPropertyData(
device_id,
Expand Down Expand Up @@ -735,7 +814,7 @@ pub fn get_hogging_pid(device_id: AudioDeviceID) -> Result<pid_t, Error> {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyHogMode,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
mElement: kAudioObjectPropertyElementMain,
};
let pid = unsafe {
let temp_pid: pid_t = 0;
Expand Down Expand Up @@ -765,7 +844,7 @@ pub fn toggle_hog_mode(device_id: AudioDeviceID) -> Result<pid_t, Error> {
let property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyHogMode,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster,
mElement: kAudioObjectPropertyElementMain,
};
let pid = unsafe {
let temp_pid: pid_t = -1;
Expand Down