OpenNIで(たぶん)唯一動作報告のない AudioGenerator が、Xtion Pro LIVE + OpenNI で動きました:-)
環境
- Xtion Pro LIVE
- Windows 7 64bit
- Visual Studio 2010
- OpenNI 1.2.0.7
- Sensor 5.0.2.3
AudioGeneratorの基本
音声の取得が確認できる最小限のコードです
こちらを参考にしました(中国語のサイトです)
簡単にコードの解説をします。
// コンテキストを初期化する xn::Context context; XnStatus nRetVal = context.Init(); // オーディオジェネレータを初期化する xn::AudioGenerator audio; nRetVal = audio.Create(context);
コンテキストの初期化と、オーディオジェネレータの作成です。
OpenNIのサンプル含め、XMLから初期化しノードを探していましたが、これだとデバイスがないとエラーになったので、ユーザージェネレーターと同様に、ジェネレーターから作成したところ、うまくいきました。
// WAVEデータの設定をする XnWaveOutputMode waveMode; waveMode.nSampleRate = 44100; waveMode.nChannels = 2; waveMode.nBitsPerSample = 16; nRetVal = audio.SetWaveOutputMode(waveMode);
次に、入力するWAVEデータの設定です。周波数44.1kHz、2チャネル(ステレオ)、サンプリングレート16のデフォルト設定です。
OutputMapModeと同様に、XnWaveOutputMode と xn::AudioGenerator::SetWaveOutputMode() を使って設定します。
// データの取得を開始する
context.StartGeneratingAll();
データの取得を開始します。これをしないとデータの取得ができません。
// データの更新を待つ nRetVal = context.WaitOneUpdateAll(audio); // データを取得する const XnUChar* pAudioBuf = audio.GetAudioBuffer(); XnUInt32 nBufSize = audio.GetDataSize();
イメージや距離と同様に、データの更新を待ち、データを取得すします。AudioMetaDataもありますが、バイト単位の処理はしないので、ジェネレータから直接取得しています。
入力した音声を、PCのスピーカーから出力する
OpenNIに付属のサンプルが、Windowsで動作させた場合に、WAVEデータとしてスピーカーから出力するようになっていたので、上記を踏まえて動作するようにしてみました。また、わかりやすいように組み直しました。
基本的なところは同じで、WAVE関連の処理が増えています。
実行すると、右のマイクの音が右のスピーカーに、左のマイクの音が左のスピーカーに出るようになりました。
#include <iostream> #include <vector> #include <XnCppWrapper.h> #include <mmsystem.h> #define NUMBER_OF_AUDIO_BUFFERS 100 class AudioOutput { xn::Context context; xn::AudioGenerator audio; XnWaveOutputMode waveMode; HWAVEOUT hWaveOut; std::vector<WAVEHDR> AudioBuffers; public: // OpenNIの初期化 void initOpenNI() { // コンテキストの初期化 XnStatus nRetVal = context.Init(); if (nRetVal != XN_STATUS_OK) { throw std::runtime_error(xnGetStatusString(nRetVal)); } // デバイスを作成する(XMLからの生成だと、デバイスがないといわれる) nRetVal = audio.Create(context); if (nRetVal != XN_STATUS_OK) { throw std::runtime_error(xnGetStatusString(nRetVal)); } // 取得するWAVEデータの設定 waveMode.nSampleRate = 44100; waveMode.nChannels = 2; waveMode.nBitsPerSample = 16; nRetVal = audio.SetWaveOutputMode(waveMode); if (nRetVal != XN_STATUS_OK) { throw std::runtime_error(xnGetStatusString(nRetVal)); } // データの取得を開始する context.StartGeneratingAll(); } // WAVEの初期化 void initWave() { // WAVEデータの設定 WAVEFORMATEX wf; wf.wFormatTag = 0x0001; // PCM wf.nChannels = waveMode.nChannels; wf.nSamplesPerSec = waveMode.nSampleRate; wf.wBitsPerSample = waveMode.nBitsPerSample; wf.nBlockAlign = wf.wBitsPerSample * wf.nChannels / 8; wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; MMRESULT mmRes = waveOutOpen(&hWaveOut, WAVE_MAPPER, &wf, NULL, NULL, CALLBACK_NULL); if (mmRes != MMSYSERR_NOERROR) { throw std::runtime_error( "Warning: Failed opening wave out device. Audio will not be played!\n" ); } // 音声データ用のバッファの作成と初期化 AudioBuffers.resize( NUMBER_OF_AUDIO_BUFFERS ); xnOSMemSet(&AudioBuffers[0], 0, sizeof(WAVEHDR)*AudioBuffers.size()); const XnUInt32 nMaxBufferSize = 2 * 1024 * 1024; for (int i = 0; i < NUMBER_OF_AUDIO_BUFFERS; ++i) { AudioBuffers[i].lpData = new XnChar[nMaxBufferSize]; AudioBuffers[i].dwUser = i; AudioBuffers[i].dwFlags = WHDR_DONE; // mark this buffer as empty (already played) } } // メインループ void run() { int nAudioNextBuffer = 0; printf ("Press any key to exit...\n"); // 今のデータを捨てる audio.WaitAndUpdateData(); while (!xnOSWasKeyboardHit()) { // データの更新 XnStatus nRetVal = audio.WaitAndUpdateData(); if (nRetVal != XN_STATUS_OK) { throw std::runtime_error(xnGetStatusString(nRetVal)); } // バッファの取得 WAVEHDR* pHeader = &AudioBuffers[nAudioNextBuffer]; if ((pHeader->dwFlags & WHDR_DONE) == 0) { printf("No audio buffer is available!. Audio buffer will be lost!\n"); continue; } // WAVEヘッダのクリーンアップ MMRESULT mmRes = waveOutUnprepareHeader(hWaveOut, pHeader, sizeof(WAVEHDR)); if ( mmRes != MMSYSERR_NOERROR ) { OutputErrorText( mmRes ); } // WAVEデータの取得 pHeader->dwBufferLength = audio.GetDataSize(); pHeader->dwFlags = 0; xnOSMemCopy(pHeader->lpData, audio.GetAudioBuffer(), pHeader->dwBufferLength); // WAVEヘッダの初期化 mmRes = waveOutPrepareHeader(hWaveOut, pHeader, sizeof(WAVEHDR)); if ( mmRes != MMSYSERR_NOERROR ) { OutputErrorText( mmRes ); continue; } // WAVEデータを出力キューに入れる mmRes = waveOutWrite(hWaveOut, pHeader, sizeof(WAVEHDR)); if ( mmRes != MMSYSERR_NOERROR ) { OutputErrorText( mmRes ); continue; } // 次のバッファインデックス nAudioNextBuffer = (nAudioNextBuffer + 1) % NUMBER_OF_AUDIO_BUFFERS; } } private: // エラーメッセージの出力 void OutputErrorText( MMRESULT mmRes ) { CHAR msg[250]; waveOutGetErrorText(mmRes, msg, 250); std::cout << msg << std::endl; } }; void main() { try { AudioOutput audio; audio.initOpenNI(); audio.initWave(); audio.run(); } catch ( std::exception& ex ) { std::cout << ex.what() << std::endl; } }