【问题标题】:CoreAudio AudioObjectRemovePropertyListener not working in SwiftCoreAudio AudioObjectRemovePropertyListener 在 Swift 中不起作用
【发布时间】:2016-01-09 23:53:37
【问题描述】:

我正在快速使用 CoreAudio,需要查找用户何时更改系统音量。

我可以正确获取音量,甚至可以添加一个属性监听器来查找用户何时更改音量。但是我需要在某个时候停止监听(如果用户更改了默认输出设备)但我无法删除属性监听器。

我为 Playground 创建了一个基本测试,并在命令行 obj-c 项目中进行了相同的测试。该测试在 obj-c 中运行良好,但在 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 闭包不同。

【问题讨论】:

    标签: objective-c swift closures core-audio


    【解决方案1】:

    是的,这实际上可能是一个错误,因为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 闭包。因此,您可以创建一个无缝包装 Swift 闭包的 API。我会调查的。
    【解决方案2】:

    我遇到了同样的问题。

    将 Swift 闭包传递给 Objective-C API 似乎是一个问题。 同一个闭包被块复制两次,每个副本都有不同的地址。因此,为侦听器注册提供的块与注销期间使用的块不匹配。

    我发现的解决方法是使用 Objective-C 帮助程序注册和取消注册侦听器,该帮助程序还将存储块地址以进行取消注册。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-05
      • 2014-07-27
      • 2014-07-29
      • 2016-02-18
      • 2015-03-19
      • 2015-02-18
      • 2018-10-11
      • 1970-01-01
      相关资源
      最近更新 更多