はじめに
前回、Color画像のサンプルや FaceTracking のサンプルを移植しましたが、今回は Audioのサンプルを移植してみます。
AudioBasics-D2D
このサンプルは、Kinectのマイクをつかって、1つの音源の方向を特定し、方向とその信頼度を可視化しつつ、リアルタイムに音波の強さを描画します。
音源の方向特定はかなりの精度で出来ていますが、今のところ一方向のみの特定に制約されています。しかし、APIは複数の AudioBeamFrame を取り出せる作りになっているので、将来的にはもしかしたら、同時に複数方向から鳴っているときも、それぞれ識別できるようになるかもしれません。楽しみです。
本記事の完成品
事前に
ポイントになりそうなところを調べておきます。
マルチスレッド
本家サンプルコードをざーっと読んでみたところ
- 受け取った音声データの解析・可視化を行うメインスレッド
- 音声データの受け取りを行うスレッド
の2つがあることがわかりました。SDK本家サンプルではこれを CreateThread
関数や EnterCriticalSection
関数を使ったWindowsらしいマルチスレッドプログラミングで実現していますが、せっかくoFなのでoFのマルチスレッドで実現したいです。
リングバッファの取り扱いと逐次描画
Kinecet SDKを oF に移植するぜ!という主旨からは若干離れるので本記事では特段言及しませんが、
- 「40byte分のボリュームの平均値」 を、可視化する量より少し多い 1000 個分ためて先頭を記録するリングバッファ
- その中で可視化する領域の先頭を指すポインタ
- 前回の描画フレーム後入ってきたデータの量の算出
- 上記の情報を使って、数秒分を少しずつ進めながらリアルタイムにボリューム可視化する
あたりのプログラムのサンプルとして非常に勉強になりました。
取りかかる
oFでKinect for WindowsSDK 2.0 を使う方法の概要については、前回の記事を参考になさってください。
openFrameworks で Kinect for Windows SDK 2.0 をまるっと利用する - 自習室
ブロッキング
ofThread のサンプルを参考に、音声データの受け取りを行うスレッドを作ります。受け取る瞬間と、データを絵を描くのに使う配列に移動する瞬間がぶつかるとまずいので、スレッドを立てて、ロックアンロックを行います。
本家では EnterCriticalSection()
LeaveCriticalSection()
でクリティカルセクションを作っているところは、 if(lock())
+ unlock()
で同様の処理になります。
WINAPIのクリティカルセクションについてはこちらの記事が分かりやすかったです
ofThread は startThread()
引数無しだとブロッキングなスレッドになって、 lock()
時に他でロックしてたら、それがアンロックされるのを待機します。一方 startThread(false)
でスレッドをスタートさせるとノンブロッキングになり、if(lock()){}
は即時に false を返すので、 if文ブロック内の処理はいったん飛ばされることになります。今回はWINAPIのクリティカルセクションの手法に習って、ブロッキングなスレッドとします。
ofThreadのブロッキングについてはこちらのフォーラムが勉強になりました。 Theo先生直々返答。
ofApp::exit()
での処理 WaitForThread()
ofThreadのサンプルは stopThread()
関数で終了させていますが、今回のような音のプログラミングの終了時にこれをやると、データのコピー中等に処理を殺そうとしてしまい、多くの場合エラーを起こします。(エラーが起きない場合もあるようです) ちゃんとスレッド内の処理が終わるまで待ってあげるために、アプリ終了時は WaitForThread()
を使いましょう。
それ以外
は、ほぼ本家サンプルのコピーです。すみません。ありがとうございます。
できあがり
描画は、適宜良い感じにしてください!