2008年8月4日月曜日

Managed DirectXでのサウンドキャプチャ(2)

前記事でVC++でManaged DirectSoundを使ってサウンドをキャプチャする方法について書いたのだが、アンマネージドでキャプチャされたデータを取得するところが曖昧だったので、ちゃんと書いてみる。

キャプチャバッファからデータを取得する場合、読み取り用のメソッド(CaptureBuffer::Read)でArrayかStreamのいずれかを得ることができるが、最終的にアンマネージドなコードで処理するために、キャプチャされたデータが格納されているバッファへのポインタ(const unsigned char*)を取得したいとする。

マネージドなデータをアンマネージドなコードから参照する手段は、pin_ptr<>を使用するとして、その場合、array<>が必要になるわけだが、メソッドとして、ArrayかStreamのいずれかの選択肢がある中、どれを使ってpin_ptr<>を得るのがよいのかということを試行した結果、MemoryStreamを使って、CaptureBuffer::Readを呼び出す方法が無難であるという結論になった。

以下のコードは前回の最後のコードの続きとなるが、最終的には、16bitデータ(const short*)へのポインタを得ている。

int size;//キャプチャされたデータのサイズ[byte]
array<unsigned char>^ dataBuffer = gcnew array<unsigned char>(size);
MemoryStream^ dataStream = gcnew MemoryStream(dataBuffer);
captureBuffer->Read(lastReadPos, dataStream, size, LockFlag::None);
pin_ptr<unsigned char> ptr = &dataBuffer[0];
const short* sampleBuffer = (const short*) ptr;

ここで注意することとしては、const short*にキャストしているので、実際のデータはsizeの半分(sizeof(short)分の1)になっていることと、キャプチャしているスレッドで時間がかかる処理をすると、データを取りこぼす可能性があるということである。
キャプチャの取りこぼしを避けるための無難な手段としては、取り込んだデータ(array<unsigned char>^)をキュー(Queue)に入れて、別のスレッドで取り込む方法が考えられる。ただし、CLIで用意されているキューは同期化はサポートされているものの、空の状態のブロッキングはしてくれないので、なんだかの同期オブジェクト(AutoResetEventなど)を利用したほうがよいかもしれない。