以前、RemoteIOでのオーディオ再生というエントリでRemoteIOでオーディオ再生をする方法を書きましたが、それだけだとiPhoneのアプリとしては設定が不足です。オーディオを扱うアプリでは、AudioSessionというAPIを使って、オーディオに関する情報の取得や設定を行います。SystemSoundServiceなんかのお手軽再生系では無用の様ですが、AudioUnitを使う場合には必須といえるでしょう。
ちなみに、AudioSession系の関数はシミュレータではエラーが出て一切受け付けてくれず、実機にしか効果が及びません。また、デフォルトのオーディオIOのバッファサイズは実機では1024サンプルですが、シミュレータではMacのオーディオインターフェースの設定そのままですので、オーディオの動作の検証は必ず実機で行いましょう。
※OS 3.0にはAVFoundationにAVAudioSessionというObjective-CでAudioSessionが扱えるフレームワークが追加されましたが、動作に?な部分がありますので、あえてC言語のフレームワークの使い方そのままで記述しておきます。
AudioSessionで出来る事はいろいろありますが、いくつかあげてみると
・iPodのミュージックの音を鳴らしたままにするか、止めてしまうかの設定
・スリープしたときにアプリの音声を停止するかの設定
・通話などが割り込んできて自分のアプリの音声が停止する時の通知を受ける
・ヘッドホンがささったりなど、オーディオのルーティングが変わったときの通知を受ける
・オーディオハードウェアの設定の変更や、設定されている情報の取得
といったところでしょうか。必ずやっておかなければいけないのは、外部から割り込みが入ってオーディオが停止する時と、その後復活する時の処理です。これを適切にやっておかないと、アプリに戻ってきたときに音声が再生できなくなります。
では、以前のエントリRemoteIOでのオーディオ再生のYKAudioOutput.mのコードに以下の関数とメソッドを追加してください。initは修正になります。
void InterruptionListener(void *inUserData, UInt32 inInterruption) { AudioUnit *remoteIO = (AudioUnit*)inUserData; if (inInterruption == kAudioSessionEndInterruption) { AudioSessionSetActive(true); AudioOutputUnitStart(*remoteIO); printf("EndInterruption\n"); } if (inInterruption == kAudioSessionBeginInterruption) { AudioOutputUnitStop(*remoteIO); printf("BeginInterruption\n"); } } - (void)setupAudioSession { AudioSessionInitialize(NULL, NULL, InterruptionListener, &outputUnit); AudioSessionSetActive(true); } - (id) init { self = [super init]; if (self != nil) { [self setupAudioSession]; [self setupOutputUnit]; } return self; }
setupAudioSessionメソッドでAudioSessionの設定を行っています。AudioSessionInitialize関数ではInterruptionListnerという関数を登録して初期化しています。以下がAudioToolbox/AudioServices.hの中にある宣言です。
extern OSStatus AudioSessionInitialize(CFRunLoopRef inRunLoop, CFStringRef inRunLoopMode, AudioSessionInterruptionListener inInterruptionListener, void *inClientData)
1つ目と2つ目のRunLoopのところはそれぞれNULLを入れておけば、デフォルトで登録されます。4つ目のinClientDataは自由に使えるところなので、今回のコードではAudioUnitのポインタを渡してAudioUnitのスタートとストップを出来るようにしています。
AudioSessionInitializeで初期化が出来たら、AudioSessionSetActive(true)を呼ぶとAudioSessionが開始されます。
AudioSessionが開始されるとInterruptionListener関数がオーディオの割り込み時に呼ばれまして、inInterruptionにkAudioSessionBeginInterruptionがくれば割り込み開始(アプリのオーディオが停止)、kAudioSessionEndInterruptionなら割り込み終了(アプリのオーディオが復活)となりますので、それぞれに応じて処理するようにします。
割り込みが開始された場合にはAudioUnitをストップするだけで良いのですが、終了のときにはAudioSessionSetActiveを先に読んでおかないと、たぶんAudioUnitStartでアプリが落ちます。
※OS 3.0ではアプリが落ちたりする事は無いようですが、AudioSessionSetActiveを呼ばないとAudioUnitが再開しないのは変わらないようです。
最低限これだけやっておけば良いと思いますが、今回のコードだと、アプリ起動時にiPodのミュージックが停止したり、ロック時に音が止まったりとデフォルトの状態のままですので、その設定の仕方は次回やります。