いままで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とかで丸ごとコピーできるように、うまくフォーマットを指定した方が良いです。全然パフォーマンスが違います。