Core Audio その6 プロパティリスナー

オーディオデバイスの設定が外部から変更された時や、前回のようにオーディオデバイスの設定を変更し、その変更がちゃんと適用された後に処理を行いたい場合、プロパティが変更されたという通知を受け取るように設定します。

変更された事を知りたいプロパティを、〜AddPropertyListenerと名前のついた関数で登録します。必要がなくなったら〜RemovePropertyListerで登録を解除します。以下が<CoreAudio/AudioHardware.h>で宣言されている関数です。

//AudioHardware用

extern OSStatus
AudioHardwareAddPropertyListener(AudioHardwarePropertyID inPropertyID,
                                 AudioHardwarePropertyListenerProc inProc,
                                 void* inClientData)

extern OSStatus
AudioHardwareRemovePropertyListener(AudioHardwarePropertyID inPropertyID,
                                    AudioHardwarePropertyListenerProc inProc) 


//AudioDevice用

extern OSStatus
AudioDeviceAddPropertyListener(AudioDeviceID                   inDevice,
                               UInt32                          inChannel,
                               Boolean                         isInput,
                               AudioDevicePropertyID           inPropertyID,
                               AudioDevicePropertyListenerProc inProc,
                               void*                           inClientData)

extern OSStatus
AudioDeviceRemovePropertyListener(AudioDeviceID                   inDevice,
                                  UInt32                          inChannel,
                                  Boolean                         isInput,
                                  AudioDevicePropertyID           inPropertyID,
                                  AudioDevicePropertyListenerProc inProc)


//AudioStream用

extern OSStatus
AudioStreamAddPropertyListener(AudioStreamID                   inStream,
                               UInt32                          inChannel,
                               AudioDevicePropertyID           inPropertyID,
                               AudioStreamPropertyListenerProc inProc,
                               void*                           inClientData)

extern OSStatus
AudioStreamRemovePropertyListener(AudioStreamID                   inStream,
                                  UInt32                          inChannel,
                                  AudioDevicePropertyID           inPropertyID,
                                  AudioStreamPropertyListenerProc inProc)

だいたいの引数は〜GetPropertyや〜SetPropertyと一緒ですが、inProcというところにプロパティの変更を受け取る関数名を指定します。その関数は引数の構成がすでに決められていて、以下のように宣言されています。

typedef OSStatus
(*AudioHardwarePropertyListenerProc)( AudioHardwarePropertyID inPropertyID,
                                      void*                   inClientData);

typedef OSStatus
(*AudioDevicePropertyListenerProc)( AudioDeviceID         inDevice,
                                    UInt32                inChannel,
                                    Boolean               isInput,
                                    AudioDevicePropertyID inPropertyID,
                                    void*                 inClientData);

typedef OSStatus
(*AudioStreamPropertyListenerProc)( AudioStreamID         inStream,
                                    UInt32                inChannel,
                                    AudioDevicePropertyID inPropertyID,
                                    void*                 inClientData);

一つの関数に対して一つのプロパティを登録した場合は気にしなくても良いですが、いくつかのプロパティを同じ関数に登録したときなどは、これらの引数で渡ってくる情報をもとに条件分岐して処理を行うという感じになると思います。また、この関数には変更されたプロパティの種類が来るだけなので、変更された値自体は別途〜GetPropertyで取得しなければいけません。

では例として、オーディオデバイスのサンプリング周波数の変更を受け取ってみたいと思います。今回はアプリケーションを起動した状態にしておかないといけないので、Cocoaアプリケーションを新規プロジェクトで作成して、CoreAudio.Frameworkをインポートし、以下のクラスをnibファイル(最近ではxib?)でインスタンス化しておきます。

#import <Cocoa/Cocoa.h>
#import <CoreAudio/CoreAudio.h>

@interface SampleRateListner : NSObject {

}

@end


@implementation SampleRateListner

static OSStatus PropertyListener(AudioDeviceID inDevice,
                                 UInt32 inChannel,
                                 Boolean isInput,
                                 AudioDevicePropertyID inPropertyID,
                                 void *inClientData)
{
    OSStatus err = noErr;
    UInt32 size;
    
    //サンプリング周波数を取得してログに表示
    Float64 sampleRate;
    size = sizeof(sampleRate);
    err = AudioDeviceGetProperty(
        inDevice, 0, false, kAudioDevicePropertyNominalSampleRate, 
        &size, &sampleRate);
    if (err == noErr) {
        printf("SampleRate = %f\n", sampleRate);
    }

    return noErr;
}

- (void)awakeFromNib
{
    OSStatus err = noErr;
	
    //デフォルトのアウトプットに設定されているオーディオデバイスを取得する
    AudioDeviceID devID;
    UInt32 size = sizeof(devID);
    err = AudioHardwareGetProperty(
        kAudioHardwarePropertyDefaultOutputDevice, &size, &devID);
    if (err != noErr) goto catchErr;
	
    //プロパティリスナーを設定する
    err = AudioDeviceAddPropertyListener(
        devID, 0, false, kAudioDevicePropertyNominalSampleRate, 
        PropertyListener, self);
    if (err != noErr) goto catchErr;
	
    return;
	
catchErr:
	
    NSLog(@"catch err");
    exit(0);
}

@end

実行して起動したら、Audio MIDI 設定でデフォルトの出力に割り当てられているデバイスのサンプリング周波数を変更すると、ログにその周波数が表示されると思います。

このサンプルでは周波数を表示するだけですが、実際はプロパティの変更を受けてあれこれと処理をしなくてはいけないと思うので、独自のデータなどをinClientDataで渡して、それをもとに何か処理を行うという感じでしょうか。

ちなみに、今回のプロパティリスナーのようなCore Audioでのコールバック系の関数は基本的にメインではないスレッドで呼ばれるので、インスタンス変数へのアクセスなどするならマルチスレッドを意識してコードを記述していかなくてはいけないと思います。

といったところで、次回はオーディオデバイスから音を出したり、取り込んだりするところを見ていきたいと思います。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です