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

Add support for scope related queries #96

Merged
merged 3 commits into from
Oct 13, 2023
Merged
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ core_midi = ["coreaudio-sys/core_midi"]
[dependencies]
bitflags = "1.0"
coreaudio-sys = { version = "0.2", default-features = false }
core-foundation-sys = "0.6.2"
core-foundation-sys = "0.8.3"

[package.metadata.docs.rs]
all-features = true
Expand Down
4 changes: 2 additions & 2 deletions examples/sine_advanced.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ extern crate coreaudio;

use coreaudio::audio_unit::audio_format::LinearPcmFlags;
use coreaudio::audio_unit::macos_helpers::{
audio_unit_from_device_id, find_matching_physical_format, get_default_device_id, get_hogging_pid,
get_supported_physical_stream_formats, set_device_physical_stream_format,
audio_unit_from_device_id, find_matching_physical_format, get_default_device_id,
get_hogging_pid, get_supported_physical_stream_formats, set_device_physical_stream_format,
set_device_sample_rate, toggle_hog_mode, AliveListener, RateListener,
};
use coreaudio::audio_unit::render_callback::{self, data};
Expand Down
82 changes: 76 additions & 6 deletions src/audio_unit/macos_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,19 @@ use sys::pid_t;
use sys::{
kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyDeviceIsAlive,
kAudioDevicePropertyDeviceNameCFString, kAudioDevicePropertyHogMode,
kAudioDevicePropertyNominalSampleRate, kAudioDevicePropertyScopeOutput, kAudioHardwareNoError,
kAudioDevicePropertyNominalSampleRate, kAudioDevicePropertyScopeOutput,
kAudioDevicePropertyStreamConfiguration, kAudioHardwareNoError,
kAudioHardwarePropertyDefaultInputDevice, kAudioHardwarePropertyDefaultOutputDevice,
kAudioHardwarePropertyDevices, kAudioObjectPropertyElementMaster,
kAudioObjectPropertyScopeGlobal, kAudioObjectSystemObject,
kAudioObjectPropertyElementWildcard, kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyScopeInput, kAudioObjectPropertyScopeOutput, kAudioObjectSystemObject,
kAudioOutputUnitProperty_CurrentDevice, kAudioOutputUnitProperty_EnableIO,
kAudioStreamPropertyAvailablePhysicalFormats, kAudioStreamPropertyPhysicalFormat,
kCFStringEncodingUTF8, AudioDeviceID, AudioObjectAddPropertyListener,
AudioObjectGetPropertyData, AudioObjectGetPropertyDataSize, AudioObjectID,
AudioObjectPropertyAddress, AudioObjectRemovePropertyListener, AudioObjectSetPropertyData,
AudioStreamBasicDescription, AudioStreamRangedDescription, AudioValueRange, OSStatus,
AudioObjectPropertyAddress, AudioObjectPropertyScope, AudioObjectRemovePropertyListener,
AudioObjectSetPropertyData, AudioStreamBasicDescription, AudioStreamRangedDescription,
AudioValueRange, OSStatus,
};

use crate::audio_unit::audio_format::{AudioFormat, LinearPcmFlags};
Expand Down Expand Up @@ -115,10 +118,15 @@ 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,
mScope: dev_scope,
mElement: kAudioObjectPropertyElementMaster,
};

Expand Down Expand Up @@ -161,6 +169,68 @@ pub fn get_audio_device_ids() -> Result<Vec<AudioDeviceID>, Error> {
Ok(audio_devices)
}

pub fn get_audio_device_ids() -> Result<Vec<AudioDeviceID>, Error> {
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 {
($status:expr) => {
if $status != kAudioHardwareNoError as i32 {
return Err(Error::Unknown($status));
}
};
}
Comment on lines +189 to +195
Copy link
Member

Choose a reason for hiding this comment

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

Given that you're only using this macro once, declaration and use makes it a bit harder to understand. Let's just use the if status != ... { return Err(...)} What do you think? oh I see, this was here before.


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);

let mut bfrs: Vec<u8> = Vec::with_capacity(data_size as usize);
let buffers = bfrs.as_mut_ptr() as *mut sys::AudioBufferList;
unsafe {
let status = AudioObjectGetPropertyData(
devid,
&property_address as *const _,
0,
null(),
&data_size as *const _ as *mut _,
buffers as *mut _,
);
if status != kAudioHardwareNoError as i32 {
return Err(Error::Unknown(status));
}

for i in 0..(*buffers).mNumberBuffers {
let buf = (*buffers).mBuffers[i as usize];
if buf.mNumberChannels > 0 {
return Ok(true);
}
}
}
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 {
Expand Down
10 changes: 10 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ pub mod audio {
}
}

impl std::error::Error for Error {}

impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
let description = match *self {
Expand Down Expand Up @@ -93,6 +95,8 @@ pub mod audio_codec {
}
}

impl std::error::Error for Error {}

impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
let description = match *self {
Expand Down Expand Up @@ -139,6 +143,8 @@ pub mod audio_format {
}
}

impl std::error::Error for Error {}

impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
let description = match *self {
Expand Down Expand Up @@ -209,6 +215,8 @@ pub mod audio_unit {
}
}

impl std::error::Error for Error {}

impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
let description = match *self {
Expand Down Expand Up @@ -302,6 +310,8 @@ impl Error {
}
}

impl std::error::Error for Error {}

impl ::std::fmt::Display for Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
match *self {
Expand Down