I want to observe Audio Device Parameter changes using Swift 3. I look up some info and came to this code. In my viewmodel, I setup a listener block, and the resulting status is 0 (succeeded):
override func viewDidLoad() {
super.viewDidLoad()
addListenerBlock(listenerBlock: audioObjectPropertyListenerBlock,
onAudioObjectID: AudioObjectID(bitPattern: kAudioObjectSystemObject),
forPropertyAddress: AudioObjectPropertyAddress(
mSelector: kAudioDevicePropertyVolumeScalar,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster))
}
// Utility function to simplify adding listener blocks:
func addListenerBlock( listenerBlock: @escaping AudioObjectPropertyListenerBlock, onAudioObjectID: AudioObjectID, forPropertyAddress: AudioObjectPropertyAddress) {
var forPropertyAddress = forPropertyAddress
let status = AudioObjectAddPropertyListenerBlock(onAudioObjectID, &forPropertyAddress, nil, listenerBlock)
print(status)
}
func audioObjectPropertyListenerBlock (numberAddresses: UInt32, addresses: UnsafePointer<AudioObjectPropertyAddress>) {
var index: UInt32 = 0
while index < numberAddresses {
let address: AudioObjectPropertyAddress = addresses[index]
switch address.mSelector {
case kAudioHardwarePropertyDefaultOutputDevice:
let deviceID = getDefaultAudioOutputDevice()
print("kAudioHardwarePropertyDefaultOutputDevice: \(deviceID)")
default:
print("We didn't expect this!")
}
index += 1
}
}
// Utility function to get default audio output device:
func getDefaultAudioOutputDevice () -> AudioObjectID {
var devicePropertyAddress = AudioObjectPropertyAddress(mSelector: kAudioHardwarePropertyDefaultOutputDevice, mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMaster)
var deviceID: AudioObjectID = 0
var dataSize = UInt32(truncatingBitPattern: MemoryLayout<AudioDeviceID>.size)
let systemObjectID = AudioObjectID(bitPattern: kAudioObjectSystemObject)
if (kAudioHardwareNoError != AudioObjectGetPropertyData(systemObjectID, &devicePropertyAddress, 0, nil, &dataSize, &deviceID)) { return 0 }
return deviceID
}
Yet even though the OSStatus is 0 and the listener is added, it is not called when I change a parameter (in this case, output volume). I set a breakpoint in my audioObjectPropertyListenerBlock but my application never gets there. What did I miss?
The problems lie here:
addListenerBlock(listenerBlock: audioObjectPropertyListenerBlock,
onAudioObjectID: AudioObjectID(bitPattern: kAudioObjectSystemObject),
forPropertyAddress: AudioObjectPropertyAddress(
mSelector: kAudioDevicePropertyVolumeScalar,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster))
onAudioObjectID
should be the default output device: getDefaultAudioOutputDevice()
(these int-ly typed APIs are confusing)mScope
should be kAudioObjectPropertyScopeOutput
, andmElement
should be 1 or 2. (at least my output device doesn't have a master element!)All together:
addListenerBlock(listenerBlock: audioObjectPropertyListenerBlock,
onAudioObjectID: getDefaultAudioOutputDevice(),
forPropertyAddress: AudioObjectPropertyAddress(
mSelector: kAudioDevicePropertyVolumeScalar,
mScope: kAudioObjectPropertyScopeOutput,
mElement: 1))
After these changes, your listener block is called when the left channel output volume is changed.
I'm not sure what you're trying to do in the listener block, however, as mSelector
is always going to be the kAudioDevicePropertyVolumeScalar
you requested, and never kAudioObjectSystemObject
.