以前の記事「CFNetworkフレームワークを用いた入力HTTPストリームの作成」でネットワークからデータを取得する方法について述べました。今回はここから、Audio File Stream ServicesとAudio Queue Servicesを用いてオーディオ再生を行う方法について説明します。なお、詳細はAppleのドキュメント「Audio Queue Services Programming Guide」を参照してください。
概要
ストリーミングオーディオを再生するには次の手順を踏みます。
- CFNetworkを用いてストリームデータを取得
- Audio File Stream Servicesを用いてネットワークパケットを解析
- Audio Queue Servicesを用いてそのオーディオを再生
この流れを次に示します。矩形はオブジェクト、丸四角はコールバックを示し、実線はオブジェクトなどの生成、破線はオーディオデータの流れを示しています。そして、破線上の赤い文字は発生条件、黒い文字は呼び出し関数を示しています。
1. CFReadSream (CFNetwork)
CFNetworkのCFReadSreamを用いてネットワーク越しにデータを取得します。基本的な部分は以前の記事「CFNetworkフレームワークを用いた入力HTTPストリームの作成」と同様なので、ここでは詳細は述べません。
データを取得したときにイベントkCFStreamEventHasBytesAvailableが発生で、AudioFileStreamParseBytesによってAudioFileStreamにデータを渡すようにします。具体的には次のように書きます。
switch(event) {
case kCFStreamEventHasBytesAvailable: {
UInt8 buf[4096];
CFIndex bytesRead = CFReadStreamRead(stream, buf, sizeof(buf));
AudioFileStreamParseBytes(globalAudioFileStream, bytesRead, buf, 0);
} break;
2. Audio File Stream Services
ネットワークから得られたデータはオーディオパケット単位で得られるわけではない上、オーディオパケット以外のデータとしてプロパティが混じっています。次の図はこれを概念化したものです。
オーディオデータは(上図の状態0)のようにオーディオパケット(青)とプロパティ(紫)が混じっています。さらに、ネットワークから得られるデータ場合はオーディオパケットなどの境界で適切に分割されていません(上図の状態1)。そのため、Audio File Stream Servicesによってオーディオデータをパケット単位で取得することと、ストリーム中のプロパティを取得することを行います(上図の状態2)。
Audio File Stream Servicesで行うべき処理について次に示します。
- 関数AudioFileStreamOpenによって新規オーディオストリーム解析器を作成
オーディオデータとメタデータ (プロパティ) のためにコールバック関数AudioFileStreamPacketsProcとAudioFileStreamPropertyListenerProcを渡す。 - 先ほどのCFReadSreamからストリームデータを取得
実際には可能な限りギャップなしでデータを解析器に継続的に渡すようにする必要があります。 - ストリームの解析が終了時に関数AudioFileStreamCloseを呼び出して、解析器のクローズとデアロケートを行う
また、2つのコールバックでは次のような処理を行います。
- プロパティコールバック
プロパティ値を取得することができます (関数AudioFileStreamGetPropertyInfoとAudioFileStreamGetPropertyを呼ぶ) 今回は再生可能な状態になった時点でAudio Queueを作成します。 - オーディオデータコールバック
データの再生、ファイルへの保存といった処理を行うとよいです。
今回はバッファを作成して、それをオーディオキューに追加します。
次の2つのコード片はこれらコールバックの重要な部分だけの抜き出したものです。
// myAudioFileStream_PropertyListenerProc
switch(inPropertyID){
case kAudioFileStreamProperty_ReadyToProducePackets: {
AudioStreamBasicDescription asbd;
UInt32 size = sizeof(asbd);
AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataFormat,
&size, (void*) &asbd);
AudioQueueNewOutput(&asbd, &myAudioQueueOutputCallback,
NULL, CFRunLoopGetCurrent(),
kCFRunLoopCommonModes, 0,
&globalAudioQueue);
AudioQueueSetParameter(globalAudioQueue, kAudioQueueParam_Volume, 1.0);
AudioQueueStart(globalAudioQueue, NULL);
break;
// myAudioFileStream_PacketsProc
AudioQueueBufferRef aqbuf;
OSStatus err = AudioQueueAllocateBufferWithPacketDescriptions(globalAudioQueue, inNumberBytes,
inNumberPackets, &aqbuf);
memcpy(aqbuf->mAudioData, inInputData, inNumberBytes);
aqbuf->mAudioDataByteSize = inNumberBytes;
AudioQueueEnqueueBuffer(globalAudioQueue, aqbuf, inNumberPackets, inPacketDescriptions);
3. Audio Queue Services
通常、オーディオキューコールバック関数ではバッファを再利用するようなコードを書きますが、今回の例では作成したバッファを削除しています。
AudioQueueFreeBuffer(inAQ, inBuffer);
まとめ
Audio FileStream ServicesとAudio Queue Servicesを用いてオーディオ再生を行う方法について説明しました。なお、この例ではコードを簡潔にするためにオーディオバッファを毎回作成、破棄を行っていますが、実際にはメモリ効率のため使い回す必要があります。
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。