CoreAudio AudioObjectRemovePropertyListener 在 Swift 中不工作
CoreAudio AudioObjectRemovePropertyListener not working in Swift
我在 swift 中使用 CoreAudio,需要查找用户何时更改系统音量。
我可以正确获取音量,甚至可以添加一个 属性 侦听器来查找用户何时更改音量。但我需要在某个时候停止收听(如果用户更改默认输出设备)但我无法删除 属性 侦听器。
我为游乐场创建了一个基本测试,并在命令行 obj-c 项目中进行了相同的测试。该测试在 obj-c 中运行良好,但在 swift
中不起作用
代码只是添加了侦听器然后将其删除,因此在 运行 之后更改音量代码应该不会打印任何内容,但在 swift 中它会继续打印
swift代码:
import CoreAudio
//first get default output device
var outputDeviceAOPA:AudioObjectPropertyAddress = AudioObjectPropertyAddress(
mSelector: kAudioHardwarePropertyDefaultOutputDevice,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster)
var outputDeviceID = kAudioObjectUnknown
var propertySize = UInt32(sizeof(AudioDeviceID))
AudioObjectGetPropertyData(UInt32(kAudioObjectSystemObject), &outputDeviceAOPA,
0, nil, &propertySize, &outputDeviceID)
// get volume from device
var volumeAOPA:AudioObjectPropertyAddress = AudioObjectPropertyAddress(
mSelector: kAudioDevicePropertyVolumeScalar,
mScope: kAudioObjectPropertyScopeOutput,
mElement: kAudioObjectPropertyElementMaster
)
var volume:Float32 = 0.5
var volSize = UInt32(sizeof(Float32))
AudioObjectGetPropertyData(outputDeviceID, &volumeAOPA, 0, nil, &volSize, &volume)
print(volume)
var queue = dispatch_queue_create("testqueue", nil)
var listener:AudioObjectPropertyListenerBlock = {
_, _ in
AudioObjectGetPropertyData(outputDeviceID, &volumeAOPA, 0, nil, &volSize, &volume)
print(volume)
}
AudioObjectAddPropertyListenerBlock(outputDeviceID, &volumeAOPA, queue, listener)
AudioObjectRemovePropertyListenerBlock(outputDeviceID, &volumeAOPA, queue, listener)
while true{
//keep playground running
}
这是 objective-c 代码:
//objective-c code working
// main.m
// objccatest
#import <Foundation/Foundation.h>
#import <CoreAudio/CoreAudio.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
//first get default output device
AudioObjectPropertyAddress outputDeviceAOPA;
outputDeviceAOPA.mSelector= kAudioHardwarePropertyDefaultOutputDevice;
outputDeviceAOPA.mScope= kAudioObjectPropertyScopeGlobal;
outputDeviceAOPA.mElement= kAudioObjectPropertyElementMaster;
AudioObjectID outputDeviceID = kAudioObjectUnknown;
UInt32 propertySize = sizeof(AudioDeviceID);
AudioObjectGetPropertyData(kAudioObjectSystemObject, &outputDeviceAOPA,
0, nil, &propertySize, &outputDeviceID);
// get volume from device
AudioObjectPropertyAddress volumeAOPA;
volumeAOPA.mSelector= kAudioDevicePropertyVolumeScalar;
volumeAOPA.mScope= kAudioObjectPropertyScopeOutput;
volumeAOPA.mElement= kAudioObjectPropertyElementMaster;
Float32 volume = 0.5;
UInt32 volSize = sizeof(Float32);
AudioObjectGetPropertyData(outputDeviceID, &volumeAOPA, 0, nil, &volSize, &volume);
NSLog(@"%f", volume);
dispatch_queue_t queue = dispatch_queue_create("testqueue", nil);
AudioObjectPropertyListenerBlock listener = ^(UInt32 a, const AudioObjectPropertyAddress* arst){
AudioObjectGetPropertyData(outputDeviceID, &volumeAOPA, 0, nil, &volSize, &volume);
NSLog(@"%f", volume);
};
AudioObjectAddPropertyListenerBlock(outputDeviceID, &volumeAOPA, queue, listener);
AudioObjectRemovePropertyListenerBlock(outputDeviceID, &volumeAOPA, queue, listener);
while (true){
//keep app running
}
}
return 0;
}
我认为这是 Core Audio API 中的错误,但也许有解决方法或 obj-c 块的工作方式不同于 swift 闭包。
我遇到了同样的问题。
似乎是将 Swift 闭包传递给 Objective-C API 的问题。
相同的闭包被块复制两次,每个副本都有不同的地址。因此,为侦听器注册提供的块与注销期间使用的块不匹配。
我发现的解决方法是使用 Objective-C 帮助程序注册和取消注册侦听器,该程序还将存储用于取消注册的块地址。
是的,这实际上可能是一个错误,因为 AudioObjectRemovePropertyListenerBlock
无法删除侦听器块。但是,我发现用 AudioObject
注册 AudioObjectPropertyListenerProc
可以作为 Swift.
的解决方法
//var queue = dispatch_queue_create("testqueue", nil)
//var listener:AudioObjectPropertyListenerBlock = {
// _, _ in
// AudioObjectGetPropertyData(outputDeviceID, &volumeAOPA, 0, nil, &volSize, &volume)
// print(volume)
//}
//
//AudioObjectAddPropertyListenerBlock(outputDeviceID, &volumeAOPA, queue, listener)
//AudioObjectRemovePropertyListenerBlock(outputDeviceID, &volumeAOPA, queue, listener)
var data: UInt32 = 0
func listenerProc() -> AudioObjectPropertyListenerProc {
return { _, _, _, _ in
AudioObjectGetPropertyData(outputDeviceID, &volumeAOPA, 0, nil, &volSize, &volume)
print(volume)
return 0
}
}
AudioObjectAddPropertyListener(outputDeviceID, &volumeAOPA, listenerProc(), &data)
AudioObjectRemovePropertyListener(outputDeviceID, &volumeAOPA, listenerProc(), &data)
我在 swift 中使用 CoreAudio,需要查找用户何时更改系统音量。
我可以正确获取音量,甚至可以添加一个 属性 侦听器来查找用户何时更改音量。但我需要在某个时候停止收听(如果用户更改默认输出设备)但我无法删除 属性 侦听器。
我为游乐场创建了一个基本测试,并在命令行 obj-c 项目中进行了相同的测试。该测试在 obj-c 中运行良好,但在 swift
中不起作用代码只是添加了侦听器然后将其删除,因此在 运行 之后更改音量代码应该不会打印任何内容,但在 swift 中它会继续打印
swift代码:
import CoreAudio
//first get default output device
var outputDeviceAOPA:AudioObjectPropertyAddress = AudioObjectPropertyAddress(
mSelector: kAudioHardwarePropertyDefaultOutputDevice,
mScope: kAudioObjectPropertyScopeGlobal,
mElement: kAudioObjectPropertyElementMaster)
var outputDeviceID = kAudioObjectUnknown
var propertySize = UInt32(sizeof(AudioDeviceID))
AudioObjectGetPropertyData(UInt32(kAudioObjectSystemObject), &outputDeviceAOPA,
0, nil, &propertySize, &outputDeviceID)
// get volume from device
var volumeAOPA:AudioObjectPropertyAddress = AudioObjectPropertyAddress(
mSelector: kAudioDevicePropertyVolumeScalar,
mScope: kAudioObjectPropertyScopeOutput,
mElement: kAudioObjectPropertyElementMaster
)
var volume:Float32 = 0.5
var volSize = UInt32(sizeof(Float32))
AudioObjectGetPropertyData(outputDeviceID, &volumeAOPA, 0, nil, &volSize, &volume)
print(volume)
var queue = dispatch_queue_create("testqueue", nil)
var listener:AudioObjectPropertyListenerBlock = {
_, _ in
AudioObjectGetPropertyData(outputDeviceID, &volumeAOPA, 0, nil, &volSize, &volume)
print(volume)
}
AudioObjectAddPropertyListenerBlock(outputDeviceID, &volumeAOPA, queue, listener)
AudioObjectRemovePropertyListenerBlock(outputDeviceID, &volumeAOPA, queue, listener)
while true{
//keep playground running
}
这是 objective-c 代码:
//objective-c code working
// main.m
// objccatest
#import <Foundation/Foundation.h>
#import <CoreAudio/CoreAudio.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
//first get default output device
AudioObjectPropertyAddress outputDeviceAOPA;
outputDeviceAOPA.mSelector= kAudioHardwarePropertyDefaultOutputDevice;
outputDeviceAOPA.mScope= kAudioObjectPropertyScopeGlobal;
outputDeviceAOPA.mElement= kAudioObjectPropertyElementMaster;
AudioObjectID outputDeviceID = kAudioObjectUnknown;
UInt32 propertySize = sizeof(AudioDeviceID);
AudioObjectGetPropertyData(kAudioObjectSystemObject, &outputDeviceAOPA,
0, nil, &propertySize, &outputDeviceID);
// get volume from device
AudioObjectPropertyAddress volumeAOPA;
volumeAOPA.mSelector= kAudioDevicePropertyVolumeScalar;
volumeAOPA.mScope= kAudioObjectPropertyScopeOutput;
volumeAOPA.mElement= kAudioObjectPropertyElementMaster;
Float32 volume = 0.5;
UInt32 volSize = sizeof(Float32);
AudioObjectGetPropertyData(outputDeviceID, &volumeAOPA, 0, nil, &volSize, &volume);
NSLog(@"%f", volume);
dispatch_queue_t queue = dispatch_queue_create("testqueue", nil);
AudioObjectPropertyListenerBlock listener = ^(UInt32 a, const AudioObjectPropertyAddress* arst){
AudioObjectGetPropertyData(outputDeviceID, &volumeAOPA, 0, nil, &volSize, &volume);
NSLog(@"%f", volume);
};
AudioObjectAddPropertyListenerBlock(outputDeviceID, &volumeAOPA, queue, listener);
AudioObjectRemovePropertyListenerBlock(outputDeviceID, &volumeAOPA, queue, listener);
while (true){
//keep app running
}
}
return 0;
}
我认为这是 Core Audio API 中的错误,但也许有解决方法或 obj-c 块的工作方式不同于 swift 闭包。
我遇到了同样的问题。
似乎是将 Swift 闭包传递给 Objective-C API 的问题。 相同的闭包被块复制两次,每个副本都有不同的地址。因此,为侦听器注册提供的块与注销期间使用的块不匹配。
我发现的解决方法是使用 Objective-C 帮助程序注册和取消注册侦听器,该程序还将存储用于取消注册的块地址。
是的,这实际上可能是一个错误,因为 AudioObjectRemovePropertyListenerBlock
无法删除侦听器块。但是,我发现用 AudioObject
注册 AudioObjectPropertyListenerProc
可以作为 Swift.
//var queue = dispatch_queue_create("testqueue", nil)
//var listener:AudioObjectPropertyListenerBlock = {
// _, _ in
// AudioObjectGetPropertyData(outputDeviceID, &volumeAOPA, 0, nil, &volSize, &volume)
// print(volume)
//}
//
//AudioObjectAddPropertyListenerBlock(outputDeviceID, &volumeAOPA, queue, listener)
//AudioObjectRemovePropertyListenerBlock(outputDeviceID, &volumeAOPA, queue, listener)
var data: UInt32 = 0
func listenerProc() -> AudioObjectPropertyListenerProc {
return { _, _, _, _ in
AudioObjectGetPropertyData(outputDeviceID, &volumeAOPA, 0, nil, &volSize, &volume)
print(volume)
return 0
}
}
AudioObjectAddPropertyListener(outputDeviceID, &volumeAOPA, listenerProc(), &data)
AudioObjectRemovePropertyListener(outputDeviceID, &volumeAOPA, listenerProc(), &data)