Skip to content

Commit 95b9943

Browse files
committed
Add listener for if device is still alive
1 parent dacf71e commit 95b9943

File tree

2 files changed

+112
-36
lines changed

2 files changed

+112
-36
lines changed

src/audio_unit/macos_helpers.rs

Lines changed: 112 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::collections::VecDeque;
44
use std::ffi::CStr;
55
use std::os::raw::{c_char, c_void};
66
use std::ptr::null;
7+
use std::sync::atomic::{AtomicBool, Ordering};
78
use std::sync::mpsc::{channel, Sender};
89
use std::sync::Mutex;
910
use std::time::Duration;
@@ -12,12 +13,12 @@ use std::{mem, slice, thread};
1213
use core_foundation_sys::string::{CFStringGetCString, CFStringGetCStringPtr, CFStringRef};
1314
use sys;
1415
use sys::{
15-
kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyDeviceNameCFString,
16-
kAudioDevicePropertyNominalSampleRate, kAudioDevicePropertyScopeOutput, kAudioHardwareNoError,
16+
kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyDeviceIsAlive,
17+
kAudioDevicePropertyDeviceNameCFString, kAudioDevicePropertyNominalSampleRate,
18+
kAudioDevicePropertyScopeOutput, kAudioHardwareNoError,
1719
kAudioHardwarePropertyDefaultInputDevice, kAudioHardwarePropertyDefaultOutputDevice,
1820
kAudioHardwarePropertyDevices, kAudioObjectPropertyElementMaster,
19-
kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput,
20-
kAudioObjectPropertyScopeOutput, kAudioObjectSystemObject,
21+
kAudioObjectPropertyScopeGlobal, kAudioObjectSystemObject,
2122
kAudioOutputUnitProperty_CurrentDevice, kAudioOutputUnitProperty_EnableIO,
2223
kAudioStreamPropertyAvailablePhysicalFormats, kAudioStreamPropertyPhysicalFormat,
2324
kCFStringEncodingUTF8, AudioDeviceID, AudioObjectAddPropertyListener,
@@ -67,11 +68,10 @@ pub fn get_device_id_from_name(name: &str) -> Option<AudioDeviceID> {
6768
if let Ok(all_ids) = get_audio_device_ids() {
6869
return all_ids
6970
.iter()
70-
.find(|id| get_device_name(**id).unwrap_or("".to_string()) == name)
71-
.map(|id| *id);
72-
} else {
73-
return None;
71+
.find(|id| get_device_name(**id).unwrap_or_else(|_| "".to_string()) == name)
72+
.copied();
7473
}
74+
None
7575
}
7676

7777
/// Create an AudioUnit instance from a device id.
@@ -305,31 +305,17 @@ pub fn set_device_sample_rate(device_id: AudioDeviceID, new_rate: f64) -> Result
305305

306306
// Wait for the reported_rate to change.
307307
//
308-
// This should not take longer than a few ms, but we timeout after 1 sec just in case.
308+
// This sometimes takes up to half a second, timeout after 2 sec to have a little margin.
309309
let timer = ::std::time::Instant::now();
310310
loop {
311-
println!("waiting for rate change");
312311
if let Ok(reported_rate) = receiver.recv_timeout(Duration::from_millis(100)) {
313-
println!("got rate change event");
314312
if new_rate as usize == reported_rate as usize {
315-
println!("rate was updated!");
316313
break;
317314
}
318315
}
319-
/*
320-
if listener.get_nbr_values() > 0 {
321-
if let Some(reported_rate) = listener.copy_values().last() {
322-
if new_rate as usize == *reported_rate as usize {
323-
println!("rate was updated!");
324-
break;
325-
}
326-
}
327-
}
328-
*/
329-
if timer.elapsed() > Duration::from_secs(1) {
316+
if timer.elapsed() > Duration::from_secs(2) {
330317
return Err(Error::UnsupportedSampleRate);
331318
}
332-
//thread::sleep(Duration::from_millis(5));
333319
}
334320
};
335321
Ok(())
@@ -348,9 +334,6 @@ pub fn set_device_sample_format(
348334
let property_address = AudioObjectPropertyAddress {
349335
mSelector: kAudioStreamPropertyPhysicalFormat,
350336
mScope: kAudioObjectPropertyScopeGlobal,
351-
//mScope: kAudioDevicePropertyScopeInput,
352-
//mScope: kAudioObjectPropertyScopeOutput,
353-
//mScope: kAudioDevicePropertyScopeOutput,
354337
mElement: kAudioObjectPropertyElementMaster,
355338
};
356339
let maybe_asbd: mem::MaybeUninit<AudioStreamBasicDescription> = mem::MaybeUninit::zeroed();
@@ -365,9 +348,6 @@ pub fn set_device_sample_format(
365348
);
366349
Error::from_os_status(status)?;
367350
let asbd = maybe_asbd.assume_init();
368-
println!("---- Current format ----");
369-
println!("{:#?}", asbd);
370-
//println!("{:#?}", StreamFormat::from_asbd(asbd).unwrap());
371351

372352
// If the requested sample rate and/or format is different to the device sample rate, update the device.
373353
if !asbds_are_equal(&asbd, &new_asbd) {
@@ -394,7 +374,6 @@ pub fn set_device_sample_format(
394374
// Wait for the reported format to change.
395375
//
396376
// This should not take longer than a few ms, but we timeout after 1 sec just in case.
397-
println!("{:#?}", reported_asbd);
398377
let timer = ::std::time::Instant::now();
399378
loop {
400379
let status = AudioObjectGetPropertyData(
@@ -409,13 +388,11 @@ pub fn set_device_sample_format(
409388
if asbds_are_equal(&reported_asbd, &new_asbd) {
410389
break;
411390
}
412-
println!("spinning");
413391
thread::sleep(Duration::from_millis(5));
414392
if timer.elapsed() > Duration::from_secs(1) {
415393
return Err(Error::UnsupportedSampleRate);
416394
}
417395
}
418-
println!("{:#?}", reported_asbd);
419396
}
420397
Ok(())
421398
}
@@ -503,7 +480,6 @@ pub struct RateListener {
503480

504481
impl Drop for RateListener {
505482
fn drop(&mut self) {
506-
println!("Dropping RateListener!");
507483
let _ = self.unregister();
508484
}
509485
}
@@ -619,3 +595,105 @@ impl RateListener {
619595
self.queue.lock().unwrap().drain(..).collect::<Vec<f64>>()
620596
}
621597
}
598+
599+
/// Use an AliveListener to get notified when a device is disconnected.
600+
/// Only implemented for macOS, not iOS.
601+
pub struct AliveListener {
602+
alive: AtomicBool,
603+
device_id: AudioDeviceID,
604+
property_address: AudioObjectPropertyAddress,
605+
alive_listener: Option<
606+
unsafe extern "C" fn(u32, u32, *const AudioObjectPropertyAddress, *mut c_void) -> i32,
607+
>,
608+
}
609+
610+
impl Drop for AliveListener {
611+
fn drop(&mut self) {
612+
let _ = self.unregister();
613+
}
614+
}
615+
616+
impl AliveListener {
617+
/// Create a new ErrorListener for the given AudioDeviceID.
618+
/// If a sync Sender is provided, then events will be pushed to that channel.
619+
/// If not, they will be stored in an internal queue that will need to be polled.
620+
pub fn new(device_id: AudioDeviceID) -> Result<AliveListener, Error> {
621+
// Add our error listener callback.
622+
let property_address = AudioObjectPropertyAddress {
623+
mSelector: kAudioDevicePropertyDeviceIsAlive,
624+
mScope: kAudioObjectPropertyScopeGlobal,
625+
mElement: kAudioObjectPropertyElementMaster,
626+
};
627+
Ok(AliveListener {
628+
alive: AtomicBool::new(true),
629+
device_id,
630+
property_address,
631+
alive_listener: None,
632+
})
633+
}
634+
635+
/// Register this listener to receive notifications.
636+
pub fn register(&mut self) -> Result<(), Error> {
637+
unsafe extern "C" fn alive_listener(
638+
device_id: AudioObjectID,
639+
_n_addresses: u32,
640+
_properties: *const AudioObjectPropertyAddress,
641+
self_ptr: *mut ::std::os::raw::c_void,
642+
) -> OSStatus {
643+
let self_ptr: &mut AliveListener = &mut *(self_ptr as *mut AliveListener);
644+
let alive: u32 = 0;
645+
let data_size = mem::size_of::<u32>();
646+
let property_address = AudioObjectPropertyAddress {
647+
mSelector: kAudioDevicePropertyDeviceIsAlive,
648+
mScope: kAudioObjectPropertyScopeGlobal,
649+
mElement: kAudioObjectPropertyElementMaster,
650+
};
651+
let result = AudioObjectGetPropertyData(
652+
device_id,
653+
&property_address as *const _,
654+
0,
655+
null(),
656+
&data_size as *const _ as *mut _,
657+
&alive as *const _ as *mut _,
658+
);
659+
self_ptr.alive.store(alive > 0, Ordering::Relaxed);
660+
result
661+
}
662+
663+
// Add our listener callback.
664+
let status = unsafe {
665+
AudioObjectAddPropertyListener(
666+
self.device_id,
667+
&self.property_address as *const _,
668+
Some(alive_listener),
669+
self as *const _ as *mut _,
670+
)
671+
};
672+
Error::from_os_status(status)?;
673+
self.alive_listener = Some(alive_listener);
674+
Ok(())
675+
}
676+
677+
/// Unregister this listener to stop receiving notifications
678+
pub fn unregister(&mut self) -> Result<(), Error> {
679+
// Add our sample rate change listener callback.
680+
if self.alive_listener.is_some() {
681+
let status = unsafe {
682+
AudioObjectRemovePropertyListener(
683+
self.device_id,
684+
&self.property_address as *const _,
685+
self.alive_listener,
686+
self as *const _ as *mut _,
687+
)
688+
};
689+
Error::from_os_status(status)?;
690+
self.alive_listener = None;
691+
}
692+
Ok(())
693+
}
694+
695+
/// Get the number of sample rate values received (equals the number of change events).
696+
pub fn is_alive(&self) -> bool {
697+
self.alive.load(Ordering::Relaxed)
698+
}
699+
}

src/audio_unit/render_callback.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,6 @@ pub mod data {
228228
mDataByteSize,
229229
mData,
230230
} = (*io_data).mBuffers[0];
231-
// println!("{}, {}",mNumberChannels, mDataByteSize);
232231
// // Ensure that the size of the data matches the size of the sample format
233232
// // multiplied by the number of frames.
234233
// //
@@ -270,7 +269,6 @@ pub mod data {
270269
mDataByteSize,
271270
mData,
272271
} = (*io_data).mBuffers[0];
273-
// println!("{}, {}",mNumberChannels, mDataByteSize);
274272
// // Ensure that the size of the data matches the size of the sample format
275273
// // multiplied by the number of frames.
276274
// //

0 commit comments

Comments
 (0)