Skip to content

Commit 163f1a0

Browse files
committed
Add helpers for hog mode
1 parent b3e605f commit 163f1a0

File tree

5 files changed

+122
-13
lines changed

5 files changed

+122
-13
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "coreaudio-rs"
3-
version = "0.11.0"
3+
version = "0.11.1"
44
authors = ["mitchmindtree <mitchell.nordine@gmail.com>", "yupferris <jake@fusetools.com>"]
55
description = "A friendly rust interface for Apple's CoreAudio API."
66
keywords = ["core", "audio", "unit", "osx", "ios"]

examples/feedback.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ use std::collections::VecDeque;
66
use std::sync::{Arc, Mutex};
77

88
use coreaudio::audio_unit::audio_format::LinearPcmFlags;
9+
use coreaudio::audio_unit::macos_helpers::{audio_unit_from_device_id, get_default_device_id};
910
use coreaudio::audio_unit::render_callback::{self, data};
1011
use coreaudio::audio_unit::{Element, SampleFormat, Scope, StreamFormat};
11-
use coreaudio::audio_unit::macos_helpers::{audio_unit_from_device_id, get_default_device_id};
1212
use coreaudio::sys::*;
1313

1414
const SAMPLE_RATE: f64 = 44100.0;
@@ -20,8 +20,10 @@ const SAMPLE_FORMAT: SampleFormat = SampleFormat::F32;
2020
// type S = i8; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I8;
2121

2222
fn main() -> Result<(), coreaudio::Error> {
23-
let mut input_audio_unit = audio_unit_from_device_id(get_default_device_id(true).unwrap(), true)?;
24-
let mut output_audio_unit = audio_unit_from_device_id(get_default_device_id(false).unwrap(), false)?;
23+
let mut input_audio_unit =
24+
audio_unit_from_device_id(get_default_device_id(true).unwrap(), true)?;
25+
let mut output_audio_unit =
26+
audio_unit_from_device_id(get_default_device_id(false).unwrap(), false)?;
2527

2628
let format_flag = match SAMPLE_FORMAT {
2729
SampleFormat::F32 => LinearPcmFlags::IS_FLOAT,

examples/feedback_interleaved.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ use std::collections::VecDeque;
66
use std::sync::{Arc, Mutex};
77

88
use coreaudio::audio_unit::audio_format::LinearPcmFlags;
9+
use coreaudio::audio_unit::macos_helpers::{
10+
audio_unit_from_device_id, get_default_device_id, get_device_name, RateListener,
11+
};
912
use coreaudio::audio_unit::render_callback::{self, data};
10-
use coreaudio::audio_unit::macos_helpers::{audio_unit_from_device_id, get_default_device_id, get_device_name, RateListener};
1113
use coreaudio::audio_unit::{Element, SampleFormat, Scope, StreamFormat};
1214
use coreaudio::sys::*;
1315

examples/sine_advanced.rs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
//! A basic output stream example, using an Output AudioUnit to generate a sine wave.
1+
//! An output stream example showing more advanced usage.
2+
//! Tries to use hog mode to get exclusive access to the device.
23
34
extern crate coreaudio;
45

56
use coreaudio::audio_unit::audio_format::LinearPcmFlags;
6-
use coreaudio::audio_unit::render_callback::{self, data};
77
use coreaudio::audio_unit::macos_helpers::{
8-
audio_unit_from_device_id, find_matching_physical_format, get_default_device_id,
8+
audio_unit_from_device_id, find_matching_physical_format, get_default_device_id, get_owner_pid,
99
get_supported_physical_stream_formats, set_device_physical_stream_format,
10-
set_device_sample_rate, AliveListener, RateListener,
10+
set_device_sample_rate, switch_ownership, AliveListener, RateListener,
1111
};
12+
use coreaudio::audio_unit::render_callback::{self, data};
1213
use coreaudio::audio_unit::{Element, SampleFormat, Scope, StreamFormat};
1314
use coreaudio::sys::kAudioUnitProperty_StreamFormat;
1415
use std::f64::consts::PI;
16+
use std::process;
1517

1618
const SAMPLE_FORMAT: SampleFormat = SampleFormat::F32;
1719
// type S = i32; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I32;
@@ -60,6 +62,23 @@ fn main() -> Result<(), coreaudio::Error> {
6062
let audio_unit_id = get_default_device_id(false).unwrap();
6163
let mut audio_unit = audio_unit_from_device_id(audio_unit_id, false)?;
6264

65+
let pid = get_owner_pid(audio_unit_id)?;
66+
if pid != -1 {
67+
println!("Device is owned by another process with pid {}!", pid);
68+
} else {
69+
println!("Device is free, trying to get exclusive access..");
70+
let new_pid = switch_ownership(audio_unit_id)?;
71+
let process_id = process::id();
72+
if new_pid == process_id as i32 {
73+
println!("We have exclusive access.");
74+
} else {
75+
println!(
76+
"Could not get exclusive access. Process pid: {}, new pid value: {}",
77+
process_id, new_pid
78+
);
79+
}
80+
}
81+
6382
let mut format_flag = match SAMPLE_FORMAT {
6483
SampleFormat::F32 => LinearPcmFlags::IS_FLOAT | LinearPcmFlags::IS_PACKED,
6584
SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => {
@@ -101,7 +120,7 @@ fn main() -> Result<(), coreaudio::Error> {
101120
println!("setting hardware (physical) format");
102121
let hw_stream_format = StreamFormat {
103122
sample_rate: SAMPLE_RATE,
104-
sample_format: SampleFormat::I24,
123+
sample_format: SampleFormat::I16,
105124
flags: LinearPcmFlags::empty(),
106125
channels: 2,
107126
};
@@ -178,5 +197,21 @@ fn main() -> Result<(), coreaudio::Error> {
178197
println!("rate events: {:?}", rate_listener.copy_values());
179198
println!("alive state: {}", alive_listener.is_alive());
180199
}
200+
201+
// Release exclusive access, not really needed as the process anyway exits after this.
202+
let owner_pid = get_owner_pid(audio_unit_id)?;
203+
let process_id = process::id();
204+
if owner_pid == process_id as i32 {
205+
println!("Releasing exclusive access");
206+
let new_pid = switch_ownership(audio_unit_id)?;
207+
if new_pid == -1 {
208+
println!("Exclusive access released.");
209+
} else {
210+
println!(
211+
"Could not release exclusive access. Process pid: {}, new pid value: {}",
212+
process_id, new_pid
213+
);
214+
}
215+
}
181216
Ok(())
182217
}

src/audio_unit/macos_helpers.rs

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ use std::{mem, thread};
1313

1414
use core_foundation_sys::string::{CFStringGetCString, CFStringGetCStringPtr, CFStringRef};
1515
use sys;
16+
use sys::pid_t;
1617
use sys::{
1718
kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyDeviceIsAlive,
18-
kAudioDevicePropertyDeviceNameCFString, kAudioDevicePropertyNominalSampleRate,
19-
kAudioDevicePropertyScopeOutput, kAudioHardwareNoError,
19+
kAudioDevicePropertyDeviceNameCFString, kAudioDevicePropertyHogMode,
20+
kAudioDevicePropertyNominalSampleRate, kAudioDevicePropertyScopeOutput, kAudioHardwareNoError,
2021
kAudioHardwarePropertyDefaultInputDevice, kAudioHardwarePropertyDefaultOutputDevice,
2122
kAudioHardwarePropertyDevices, kAudioObjectPropertyElementMaster,
2223
kAudioObjectPropertyScopeGlobal, kAudioObjectSystemObject,
@@ -603,7 +604,7 @@ impl RateListener {
603604
}
604605

605606
/// Get the number of sample rate values received (equals the number of change events).
606-
/// Not used if the RateListener was created with a `std::sync::mpsc::Sender`.
607+
/// Not used if the RateListener was created with a `std::sync::mpsc::Sender`.
607608
pub fn get_nbr_values(&self) -> usize {
608609
self.queue.lock().unwrap().len()
609610
}
@@ -726,3 +727,72 @@ impl AliveListener {
726727
self.alive.load(Ordering::SeqCst)
727728
}
728729
}
730+
731+
/// Hog mode.
732+
/// Get the pid of the process that currently owns
733+
/// exclusive access to a device.
734+
/// A value of -1 means no process owns exclusive access.
735+
pub fn get_owner_pid(device_id: AudioDeviceID) -> Result<pid_t, Error> {
736+
// Get available formats.
737+
let property_address = AudioObjectPropertyAddress {
738+
mSelector: kAudioDevicePropertyHogMode,
739+
mScope: kAudioObjectPropertyScopeGlobal,
740+
mElement: kAudioObjectPropertyElementMaster,
741+
};
742+
let pid = unsafe {
743+
let temp_pid: pid_t = 0;
744+
let data_size = mem::size_of::<pid_t>();
745+
let status = AudioObjectGetPropertyData(
746+
device_id,
747+
&property_address as *const _,
748+
0,
749+
null(),
750+
&data_size as *const _ as *mut _,
751+
&temp_pid as *const _ as *mut _,
752+
);
753+
Error::from_os_status(status)?;
754+
temp_pid
755+
};
756+
Ok(pid)
757+
}
758+
759+
/// Hog mode.
760+
/// Switch the exclusive access to a device.
761+
/// If no process owns exclusive access, then the calling process takes ownership.
762+
/// If the calling process already has ownership, this is released.
763+
/// If another process owns access, then nothing will happen.
764+
/// Returns the pid of the new owning process.
765+
/// A value of -1 means no process owns exclusive access.
766+
pub fn switch_ownership(device_id: AudioDeviceID) -> Result<pid_t, Error> {
767+
// Get available formats.
768+
let property_address = AudioObjectPropertyAddress {
769+
mSelector: kAudioDevicePropertyHogMode,
770+
mScope: kAudioObjectPropertyScopeGlobal,
771+
mElement: kAudioObjectPropertyElementMaster,
772+
};
773+
let pid = unsafe {
774+
//let temp_pid: pid_t = std::process::id() as _;
775+
let temp_pid: pid_t = -1;
776+
let data_size = mem::size_of::<pid_t>();
777+
let status = AudioObjectSetPropertyData(
778+
device_id,
779+
&property_address as *const _,
780+
0,
781+
null(),
782+
data_size as u32,
783+
&temp_pid as *const _ as *mut _,
784+
);
785+
Error::from_os_status(status)?;
786+
let status = AudioObjectGetPropertyData(
787+
device_id,
788+
&property_address as *const _,
789+
0,
790+
null(),
791+
&data_size as *const _ as *mut _,
792+
&temp_pid as *const _ as *mut _,
793+
);
794+
Error::from_os_status(status)?;
795+
temp_pid
796+
};
797+
Ok(pid)
798+
}

0 commit comments

Comments
 (0)