6

現在、一連のUDPパケットで受信したオーディオを再生しようとしています。これらは、次のプロパティを持つPCMフレームにデコードされます。

  • 2チャンネル
  • インターリーブ
  • 単一チャネルのサンプルあたり2バイト(つまり、フレームあたり4バイト)
  • サンプルレートは48000です。

すべてのUDPパケットには480フレームが含まれているため、バッファのサイズは480 * 2(チャネル)* 2(チャネルあたりのバイト数)です。

これらのパケットを再生するには、AudioUnitをセットアップする必要があります。だから、私の最初の質問は、AudioUnitのAudioStreamBasicDescription構造体をどのように設定する必要があるかということです。ドキュメントを見ると、インターリーブPCMが許容可能な形式であるかどうかさえわかりません。

これは私がこれまでに得たものです:

struct AudioStreamBasicDescription {
   Float64 mSampleRate;                 //48000
   UInt32  mFormatID;                   //?????
   UInt32  mFormatFlags;                //?????
   UInt32  mBytesPerPacket;             //Not sure what "packet" means here
   UInt32  mFramesPerPacket;            //Same as above
   UInt32  mBytesPerFrame;              //Same
   UInt32  mChannelsPerFrame;           //2?
   UInt32  mBitsPerChannel;             //16?
   UInt32  mReserved;                   //???
};
typedef struct AudioStreamBasicDescription  AudioStreamBasicDescription;

次に、設定後、UDPコールバックから実際のAudioUnitレンダリング関数にフレームを取得する方法がわかりません。

現在、ソケットリスナーからのコールバック関数があり、再生したいオーディオを含むint16*バッファーを生成します。私が理解しているように、次の形式のオーディオユニットのレンダリングコールバックも実装する必要があります。

OSStatus RenderFrames(
    void                        *inRefCon,
    AudioUnitRenderActionFlags  *ioActionFlags,
    const AudioTimeStamp        *inTimeStamp,
    UInt32                      inBusNumber,
    UInt32                      inNumberFrames,
    AudioBufferList             *ioData)
{
    //No idea what I should do here.
    return noErr;
}

すべてをまとめると、ソケット受信コールバックが行うべきことは、フレームをデコードし、それらをバッファー構造に入れて、RenderFramesコールバックがそのバッファーからフレームをフェッチして再生できるようにすることだと思います。これは正しいです?もしそうなら、RenderFrames関数で次のフレームをフェッチしたら、実際に再生のために「送信」するにはどうすればよいですか?

4

1 に答える 1

11

これを一度にセクションを取る

AudioStreamBasicDescriptor

ASBDに関するAppleのドキュメントはこちらです。明確にするために:

  • オーディオのフレームは、時間的に一致するオーディオサンプルのセットです。つまり、チャネルごとに1つのサンプルです。したがって、ステレオの場合、これはです2
  • PCM形式の場合、パケット化はありません。おそらく、、しかしmBytesPerPacket = mBytesPerFramemFramesPerPacket=1これが実際に使用されたかどうかはわかりません。
  • mReserved使用されていないため、0
  • およびのドキュメントを参照してください。CoreAudioTypes.hには、これらの後者を​​計算するための便利なヘルパー関数があります。mFormatIDmFormatFlagsCalculateLPCMFlagsCoreAudioTypes.h
  • マルチチャンネルオーディオは一般的にインターリーブされます(mFormatFlags本当に望まない場合は、少し設定することができます)。
  • ASBD全体を埋めることができる別のヘルパー関数があります-FillOutASBDForLPCM()線形PCMの一般的なケースのために。
  • remoteIOユニットの多くの組み合わせとmFormatIDサポートmFormatFlagsされていません-iOSでは実験が必要であることがわかりました。

これが私のプロジェクトの1つからのいくつかの実用的なコードです:

AudioStreamBasicDescription inputASBL = {0}; 

inputASBL.mSampleRate =          static_cast<Float64>(sampleRate);
inputASBL.mFormatID =            kAudioFormatLinearPCM;
inputASBL.mFormatFlags =         kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger,
inputASBL.mFramesPerPacket =     1;
inputASBL.mChannelsPerFrame =    2;
inputASBL.mBitsPerChannel =      sizeof(short) * 8;
inputASBL.mBytesPerPacket =      sizeof(short) * 2;
inputASBL.mBytesPerFrame =       sizeof(short) * 2;
inputASBL.mReserved =            0;

コールバックをレンダリングする

CoreAudioは、Appleがプルモデルとして説明しているものを操作します。つまり、CoreAudioがバッファを埋める必要がある場合、レンダリングコールバックはリアルタイムスレッドから呼び出されます。あなたの質問から、あなたは反対のことを期待しているようです-データをオーディオ出力にプッシュします。

基本的に2つの実装の選択肢があります。

  1. レンダーコールバックでUDPソケットから非ブロッキング読み取りを実行します(原則として、ここで行うことはすべて高速で非ブロッキングである必要があります)。
  2. レンダーコールバックによって受信および消費されるときにサンプルが挿入されるオーディオFIFOを維持します。

2番目の方がおそらくより良い選択ですが、バッファのオーバーランとアンダーランを自分で管理する必要があります。

引数はioData、スキャッターギャザー制御構造を指しています。最も単純なケースでは、すべてのフレームを含む1つのバッファーを指しますが、それらの間に、を満たすのに十分なフレームを持つ複数のバッファーを含めることができますinNumberFrames。通常、十分な大きさのバッファを事前に割り当て、inNumberFramesサンプルをそのバッファにコピーしてAudioBufferListから、購入するように指定されたオブジェクトを変更して、それioDataを指すようにします。

アプリケーションでは、デコードされたオーディオパケットにスキャッターギャザーアプローチを使用して、デコード時にバッファーを割り当てることができます。ただし、必要なレイテンシーが常に得られるとは限らずinNumberFrames、デコードされたオーディオのUDPフレームと同じになるように調整できない場合があります。

于 2013-01-23T01:13:37.763 に答える