Objective c CoreAudio AudioObjectRemovePropertyListener未在Swift中工作
我正在swift中使用CoreAudio,需要查找用户何时更改系统音量 我可以正确地获取卷,甚至可以添加一个属性侦听器,以便在用户更改卷时查找。但是我需要在某个时刻停止侦听(如果用户更改了默认输出设备),但我无法删除属性侦听器 我已经为Playerd创建了一个基本测试,并在命令行obj-c项目中进行了相同的测试。该测试在obj-c中运行良好,但在swift中不起作用 代码只是添加侦听器,然后将其删除,因此在运行代码后更改卷应该不会打印任何内容,但在swift中它会继续打印 swift代码:Objective c CoreAudio AudioObjectRemovePropertyListener未在Swift中工作,objective-c,swift,closures,core-audio,Objective C,Swift,Closures,Core Audio,我正在swift中使用CoreAudio,需要查找用户何时更改系统音量 我可以正确地获取卷,甚至可以添加一个属性侦听器,以便在用户更改卷时查找。但是我需要在某个时刻停止侦听(如果用户更改了默认输出设备),但我无法删除属性侦听器 我已经为Playerd创建了一个基本测试,并在命令行obj-c项目中进行了相同的测试。该测试在obj-c中运行良好,但在swift中不起作用 代码只是添加侦听器,然后将其删除,因此在运行代码后更改卷应该不会打印任何内容,但在swift中它会继续打印 swift代码: im
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;
}
//objective-c代码工作
//main.m
//对象
#进口
#进口
int main(int argc,const char*argv[]{
@自动释放池{
//首先获取默认输出设备
AudioObjectPropertyAddressOutputDeviceAOPA;
OutputDeviceOpa.mSelector=kAudioHardwarePropertyDefaultOutputDevice;
outputDeviceAOPA.mScope=kAudioObjectPropertyScopeGlobal;
OutputDeviceOpa.mElement=kAudioObjectPropertyElementMaster;
AudioObjectID outputDeviceID=KaudioObjectInKnown;
UInt32 propertySize=sizeof(AudioDeviceID);
AudioObjectGetPropertyData(kAudioObjectSystemObject和outputDeviceAOPA,
0、nil和propertySize以及outputDeviceID);
//从设备获取卷
AudioObjectPropertyAddressVolumeAopa;
volumeAOPA.mSelector=kAudioDevicePropertyVolumeScalar;
volumeAOPA.mScope=kAudioObjectPropertyScopeOutput;
volumeAOPA.mElement=kAudioObjectPropertyElementMaster;
体积=0.5;
UInt32 volSize=sizeof(Float32);
AudioObjectGetPropertyData(outputDeviceID和volumeAOPA、0、nil和volSize和volume);
NSLog(@“%f”,卷);
调度队列=调度队列创建(“测试队列”,无);
AudioObjectPropertyListenerBlock侦听器=^(UInt32 a,常量AudioObjectPropertyAddress*arst){
AudioObjectGetPropertyData(outputDeviceID和volumeAOPA、0、nil和volSize和volume);
NSLog(@“%f”,卷);
};
AudioObjectAddPropertyListenerBlock(outputDeviceID和volumeAOPA、队列、侦听器);
AudioObjectRemovePropertyListenerBlock(outputDeviceID和volumeAOPA、队列、侦听器);
while(true){
//保持应用程序运行
}
}
返回0;
}
我认为这是核心音频API中的一个缺陷,但可能有一个变通方法,或者obj-c块的工作方式与swift闭包不同。我也有同样的问题 向Objective-CAPI传递Swift闭包似乎是一个问题。 相同的闭包被块复制两次,每个副本有不同的地址。因此,为侦听器注册提供的块与注销期间使用的块不正确匹配
我发现的解决方法是使用Objective-C helper注册和注销侦听器,Objective-C helper还将存储用于注销的块地址 是的,实际上这可能是一个bug,因为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)
您甚至可以使用第四个参数来传递您自己的用户数据,这可以是一个快速关闭。因此,您可以制作一个API,将Swift闭包毫无保留地包装起来。我会调查的。