現在、インターネット経由で mp3 オーディオをストリーミングしています。AudioFileStream を使用して、CFReadStreamRef からの mp3 ストリームを解析し、AudioConverterFillComplexBuffer を使用して mp3 をデコードし、変換された PCM データをリング バッファーにコピーし、最後に RemoteIO を使用して PCM を再生します。
私が現在直面している問題は、AudioConverterFillComplexBuffer が常に 0 (エラーなし) を返すことですが、変換結果が正しくないようです。細かいところ、気づけますが、
A. UInt32 *ioOutputDataPacketSize は、送信した値と同じ値を保持します。
B. convertData.mBuffers[0].mDataByteSize は、常に出力バッファのサイズに設定されています (バッファの大きさは関係ありません)。
C. 出力データでクリック ノイズしか聞こえません。
以下は、オーディオをレンダリングするための私の手順です。私の Audio キューの実装でも同じ手順が機能するので、AudioConverterFillComplexBuffer を呼び出す場所または AudioConverterFillComplexBuffer のコールバックのどちらにも問題はなかったと思います。
私は長い間この問題に悩まされてきました。どんな助けでも大歓迎です。
AudioFileStream を開きます。
// オーディオ ファイル ストリーム パーサーを作成します AudioFileTypeID fileTypeHint = kAudioFileMP3Type;
AudioFileStreamOpen(self, MyPropertyListenerProc, MyPacketsProc, fileTypeHint, &audioFileStream);解析されたデータをコールバック関数 ("MyPacketsProc") で処理します。
void MyPacketsProc(void * inClientData, UInt32 inNumberBytes, UInt32 inNumberPackets, const void * inInputData, AudioStreamPacketDescription *inPacketDescriptions) { @synchronized(self) { // オーディオ コンバーターを初期化します。if (!audioConverter) AudioConverterNew(&asbd, &asbd_out, &audioConverter);
struct mp3Data mSettings; memset(&mSettings, 0, sizeof(mSettings)); UInt32 packetsPerBuffer = 0; UInt32 outputBufferSize = 1024 * 32; // 32 KB is a good starting point. UInt32 sizePerPacket = asbd.mBytesPerPacket; // Calculate the size per buffer. // Variable Bit Rate Data. if (sizePerPacket == 0) { UInt32 size = sizeof(sizePerPacket); AudioConverterGetProperty(audioConverter, kAudioConverterPropertyMaximumOutputPacketSize, &size, &sizePerPacket); if (sizePerPacket > outputBufferSize) outputBufferSize = sizePerPacket; packetsPerBuffer = outputBufferSize / sizePerPacket; } //CBR else packetsPerBuffer = outputBufferSize / sizePerPacket; // Prepare the input data for the callback. mSettings.inputBuffer.mDataByteSize = inNumberBytes; mSettings.inputBuffer.mData = (void *)inInputData; mSettings.inputBuffer.mNumberChannels = 1; mSettings.numberPackets = inNumberPackets; mSettings.packetDescription = inPacketDescriptions; // Set up our output buffers UInt8 * outputBuffer = (UInt8*)malloc(sizeof(UInt8) * outputBufferSize); memset(outputBuffer, 0, outputBufferSize); // describe output data buffers into which we can receive data. AudioBufferList convertedData; convertedData.mNumberBuffers = 1; convertedData.mBuffers[0].mNumberChannels = 1; convertedData.mBuffers[0].mDataByteSize = outputBufferSize; convertedData.mBuffers[0].mData = outputBuffer; // Convert. UInt32 ioOutputDataPackets = packetsPerBuffer; OSStatus result = AudioConverterFillComplexBuffer(audioConverter, converterComplexInputDataProc, &mSettings, &ioOutputDataPackets, &convertedData, NULL ); // Enqueue the ouput pcm data. TPCircularBufferProduceBytes(&m_pcmBuffer, convertedData.mBuffers[0].mData, convertedData.mBuffers[0].mDataByteSize); free(outputBuffer); }
}
コールバック関数 ("converterComplexInputDataProc") からオーディオ コンバーターにフィードします。
OSStatus converterComplexInputDataProc(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** ioDataPacketDescription, void* inUserData) { struct mp3Data THIS = (struct mp3Data ) inUserData;
if (THIS->inputBuffer.mDataByteSize > 0)
{
*ioNumberDataPackets = THIS->numberPackets;
ioData->mNumberBuffers = 1;
ioData->mBuffers[0].mDataByteSize = THIS->inputBuffer.mDataByteSize;
ioData->mBuffers[0].mData = THIS->inputBuffer.mData;
ioData->mBuffers[0].mNumberChannels = 1;
if (ioDataPacketDescription)
*ioDataPacketDescription = THIS->packetDescription;
}
else
*ioDataPacketDescription = 0;
return 0;
}
RemoteIO コンポーネントを使用して再生します。
入力と出力の AudioStreamBasicDescription。
入力:
サンプルレート: 16000 フォーマット ID: .mp3
フォーマット フラグ: 0
パケットあたりのバイト数: 0
パケットあたりのフレーム数: 576
フレームあたりのバイト数: 0
フレームあたりのチャネル: 1
チャンネルあたりのビット数: 0
出力:
サンプルレート: 44100
フォーマットID:lpcm
フォーマット フラグ: 3116
パケットあたりのバイト数: 4
パケットあたりのフレーム数: 1
フレームあたりのバイト数: 4
フレームあたりのチャネル: 1
チャンネルあたりのビット数: 32