如果系统首选项默认声音更改,如何获取通知

时间:2014-09-27 00:20:16

标签: macos cocoa notifications core-audio

这很简单(我认为)。每当用户更改系统偏好设置 - 声音中的默认声音输入或声音输出设备时,我只想在我的应用程序中收到通知。但是,如果我能够将它从Apple文档中删除,我会感到很沮丧。

作为旁注,这是针对OSX,而​​不是IOS。

谢谢你们!

2 个答案:

答案 0 :(得分:4)

以下是如何在Swift中执行此操作:

  1. 通过添加侦听器块来注册通知,例如,当View Controller加载其视图时。 addListenerBlock函数简化了添加属性侦听器块的过程。 Swift允许参数为变量,这对forPropertyAddress参数非常方便。调用addListenerBlock时,只需插入属性地址参数。

    import Cocoa
    import CoreAudio
    
    class ViewController: NSViewController {
    
       // Utility function to simplify adding listener blocks:
       func addListenerBlock( listenerBlock: AudioObjectPropertyListenerBlock, onAudioObjectID: AudioObjectID, var forPropertyAddress: AudioObjectPropertyAddress) {
          if (kAudioHardwareNoError != AudioObjectAddPropertyListenerBlock(onAudioObjectID, &forPropertyAddress, nil, listenerBlock)) {
              print("Error calling: AudioObjectAddPropertyListenerBlock") }
       }
    
    
       override func viewDidLoad() { super.viewDidLoad()
    
          addListenerBlock(audioObjectPropertyListenerBlock, 
             onAudioObjectID: AudioObjectID(bitPattern: kAudioObjectSystemObject),
             forPropertyAddress: AudioObjectPropertyAddress(
                mSelector: kAudioHardwarePropertyDefaultOutputDevice,
                mScope: kAudioObjectPropertyScopeGlobal,
                mElement: kAudioObjectPropertyElementMaster))
       }
    
    ...
    
  2. 提供侦听器阻止功能以接收通知。你得到一个可能有多个属性地址的数组,所以循环查找你想要的那个:

       func audioObjectPropertyListenerBlock (numberAddresses: UInt32, addresses: UnsafePointer<AudioObjectPropertyAddress>) {
    
          var index: UInt32 = 0
          while index < numberAddresses {
              let address: AudioObjectPropertyAddress = addresses[0]
              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: sizeof(AudioDeviceID))
             let systemObjectID = AudioObjectID(bitPattern: kAudioObjectSystemObject)
             if (kAudioHardwareNoError != AudioObjectGetPropertyData(systemObjectID, &devicePropertyAddress, 0, nil, &dataSize, &deviceID)) { return 0 }
             return deviceID
         }
    
    }
    
  3. 当然,如果要监控其他音频设备属性,只需向cases语句添加更多switch即可。致电addListenerBlock以添加您感兴趣的任何设备和属性地址。

答案 1 :(得分:3)

为默认输出设备设置AudioObjectPropertyAddress

AudioObjectPropertyAddress outputDeviceAddress = {
    kAudioHardwarePropertyDefaultOutputDevice,
    kAudioObjectPropertyScopeGlobal,
    kAudioObjectPropertyElementMaster
};

然后,使用AudioObjectSetPropertyData为默认设备中的更改注册一个侦听器:

AudioObjectAddPropertyListener(kAudioObjectSystemObject, 
                                 &outputDeviceAddress,
                                 &callbackFunction, nil);

回调函数如下所示:

OSStatus callbackFunction(AudioObjectID inObjectID, 
                            UInt32 inNumberAddresses,
                            const AudioObjectPropertyAddress inAddresses[],             
                            void *inClientData)

作为旁注,您还应该使用AudioObjectPropertyAddress告诉HAL管理自己的通知线程。您可以通过将运行循环选择器设置为NULL来执行此操作。我实际上是在设置输出设备监听器之前执行此步骤。

AudioObjectPropertyAddress runLoopAddress = {
    kAudioHardwarePropertyRunLoop, 
    kAudioObjectPropertyScopeGlobal,
    kAudioObjectPropertyElementMaster
};

CFRunLoopRef runLoop = NULL;
UInt32 size = sizeof(CFRunLoopRef);
AudioObjectSetPropertyData(kAudioObjectSystemObject, 
                            &runLoopAddress, 0, NULL, size, &runLoop);