Skip to content

Commit da4f2ec

Browse files
authored
Merge pull request #83 from HEnquist/hogmode
Add helpers for hog mode
2 parents 7c54987 + 218d1c7 commit da4f2ec

File tree

5 files changed

+118
-13
lines changed

5 files changed

+118
-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_hogging_pid,
99
get_supported_physical_stream_formats, set_device_physical_stream_format,
10-
set_device_sample_rate, AliveListener, RateListener,
10+
set_device_sample_rate, toggle_hog_mode, 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_hogging_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 = toggle_hog_mode(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 exits anyway after this.
202+
let owner_pid = get_hogging_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 = toggle_hog_mode(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: 69 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,68 @@ impl AliveListener {
726727
self.alive.load(Ordering::SeqCst)
727728
}
728729
}
730+
731+
/// Helper for hog mode (exclusive access).
732+
/// Get the pid of the process that currently owns exclusive access to a device.
733+
/// A pid value of -1 means no process owns exclusive access.
734+
pub fn get_hogging_pid(device_id: AudioDeviceID) -> Result<pid_t, Error> {
735+
let property_address = AudioObjectPropertyAddress {
736+
mSelector: kAudioDevicePropertyHogMode,
737+
mScope: kAudioObjectPropertyScopeGlobal,
738+
mElement: kAudioObjectPropertyElementMaster,
739+
};
740+
let pid = unsafe {
741+
let temp_pid: pid_t = 0;
742+
let data_size = mem::size_of::<pid_t>();
743+
let status = AudioObjectGetPropertyData(
744+
device_id,
745+
&property_address as *const _,
746+
0,
747+
null(),
748+
&data_size as *const _ as *mut _,
749+
&temp_pid as *const _ as *mut _,
750+
);
751+
Error::from_os_status(status)?;
752+
temp_pid
753+
};
754+
Ok(pid)
755+
}
756+
757+
/// Helper for hog mode (exclusive access).
758+
/// Toggle hog mode for a device.
759+
/// If no process owns exclusive access, then the calling process takes ownership.
760+
/// If the calling process already has ownership, this is released.
761+
/// If another process owns access, then nothing will happen.
762+
/// Returns the pid of the new owning process.
763+
/// A pid value of -1 means no process owns exclusive access.
764+
pub fn toggle_hog_mode(device_id: AudioDeviceID) -> Result<pid_t, Error> {
765+
let property_address = AudioObjectPropertyAddress {
766+
mSelector: kAudioDevicePropertyHogMode,
767+
mScope: kAudioObjectPropertyScopeGlobal,
768+
mElement: kAudioObjectPropertyElementMaster,
769+
};
770+
let pid = unsafe {
771+
let temp_pid: pid_t = -1;
772+
let data_size = mem::size_of::<pid_t>();
773+
let status = AudioObjectSetPropertyData(
774+
device_id,
775+
&property_address as *const _,
776+
0,
777+
null(),
778+
data_size as u32,
779+
&temp_pid as *const _ as *mut _,
780+
);
781+
Error::from_os_status(status)?;
782+
let status = AudioObjectGetPropertyData(
783+
device_id,
784+
&property_address as *const _,
785+
0,
786+
null(),
787+
&data_size as *const _ as *mut _,
788+
&temp_pid as *const _ as *mut _,
789+
);
790+
Error::from_os_status(status)?;
791+
temp_pid
792+
};
793+
Ok(pid)
794+
}

0 commit comments

Comments
 (0)