AudioUnit MultiChannelMixerを使う

いままでiPhoneのAudioUnitはRemoteIOしか使っていなくて、新規アプリ開発に向けてMixerのUnitを使ってみようと思ったら、なかなかうまくいかなくてハマってしまったのでメモしておきます。

以下が、MultiChannelMixerの初期化のコードになります。

- (void)setupMixerUnit:(AudioUnit *)fMixerUnit busCount:(NSUInteger)fBusCount
{
    AudioComponent component;
    AudioComponentDescription description;
    description.componentType = kAudioUnitType_Mixer;
    description.componentSubType = kAudioUnitSubType_MultiChannelMixer;
    description.componentManufacturer = kAudioUnitManufacturer_Apple;
    description.componentFlags = 0;
    description.componentFlagsMask = 0;
    AURenderCallbackStruct callback;
    callback.inputProc = MixerCallback;
    callback.inputProcRefCon = self;
    
    AudioStreamBasicDescription format;
    format.mSampleRate = 44100;
    format.mFormatID = kAudioFormatLinearPCM;
    format.mFormatFlags = kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
    format.mBitsPerChannel = 16;
    format.mChannelsPerFrame = 2;
    format.mFramesPerPacket = 1;
    format.mBytesPerFrame = format.mBitsPerChannel / 8 * format.mChannelsPerFrame;
    format.mBytesPerPacket = format.mBytesPerFrame * format.mFramesPerPacket;
    
    component = AudioComponentFindNext(NULL, &description);
    AudioComponentInstanceNew(component, fMixerUnit);
    
    UInt32 elementCount = fBusCount;
    AudioUnitSetProperty(*fMixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &elementCount, sizeof(UInt32));
    
    for (AudioUnitElement i = 0; i < elementCount; i++) {
        AudioUnitSetProperty(*fMixerUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, i, &callback, sizeof(AURenderCallbackStruct));
        AudioUnitSetProperty(*fMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, i, &format, sizeof(AudioStreamBasicDescription));
    }
    
    AudioUnitInitialize(*fMixerUnit);
    
    //ボリュームを変更してみる
    for (AudioUnitElement i = 0; i < elementCount; i++) {
        AudioUnitParameterValue volume = 0.8;
        AudioUnitSetParameter(*fMixerUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Input, i, volume, 0);
    }
}

設定の順番が重要です。
1 - MultiChannelMixerのAudioUnitをインスタンス化
2 - インプットバスを追加
3 - インプットごとにコールバックやフォーマットをセット
4 - AudioUnitを初期化
5 - 初期化後に、プロパティの取得やパラメータの取得・設定

あとは、AUGraphでつなぐなり、RemoteIOのコールバック内でAudioUnitRenderするなりって感じですね。

ミキサーユニットをMacで使う場合のコードが、アップルのQ&Aのページにのっていたりするんですが、そこにはインプットバスを設定するプロパティがElementCountじゃなくてBusCountになっていて、iPhoneでは使えないのがハマった原因でした。よくヘッダを見ると、MacではkAudioUnitProperty_BusCount = kAudioUnitProperty_ElementCountって定義されてます。

ミキサーユニットにはオーディオフォーマットのコンバーターが組み込まれているので便利ですね。今回のコードだと、インプットに16bit・44.1kHz・ステレオのデータをそのまま渡せば8.24の固定小数点に勝手に変換してくれます。ただOS3.0だとRemoteIOのデフォルトのフォーマットが8.24ではなくなっているので、アウト側のフォーマットを設定して合わせる必要があります。

ちなみに、オーディオのデータをコールバックで渡すときは、for文で回してコピーするのではなく、出来るだけmemcpyとかで丸ごとコピーできるように、うまくフォーマットを指定した方が良いです。全然パフォーマンスが違います。

AudioUnit MultiChannelMixerを使う」への3件のフィードバック

  1. 通りすがり

    一部だけなので最初から最後まで説明していただけるとありがたいです。
    サンプルなどあれば是非。

    返信
    1. yaso_san 投稿作成者

      コメントありがとうございます。
      だいぶ昔に書いたエントリですし「最初から最後まで」というのが丸投げで明確では無いので、ちょっと対応しかねます。
      ミキサーユニットを使って動くサンプルが欲しいのであれば、Appleの公式のサンプルコードを「kAudioUnitSubType_MultiChannelMixer」で検索するといくつかヒットしますので、そちらを参考にするのが良いと思います。

      返信

コメントを残す

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