Maverick Project
 Microsoft Visual C++ 2012 Express
 Direct3D 11.0
 Kinect for Windows SDK v1.7
 DirectXTex texture processing library

■Kinect 音源の取得 Prev Top Next
関連ページ:Direct3D 自作の共通ライブラリ


今回は音源の方向とビームの方向を取得し、角度をテキスト表示します。

UCommon.h 共通で使用するヘッダファイル
UException.h 例外処理クラスのヘッダファイル
UDirect3D11.h Direct3Dクラスのヘッダーファイル
UDirect3D11.cpp Direct3Dクラスのソースファイル
UGraphicsPipeline.h グラフィックパイプラインクラスのヘッダーファイル
UGraphicsPipeline.cpp グラフィックパイプラインクラスのソースファイル
UMesh.h メッシュの抽象クラスのヘッダーファイル
UMesh.cpp メッシュの抽象クラスのソースファイル
UDDSLoader.h DDSファイルをロードしてシェーダーリソースビューを作成するサンプルクラスのヘッダファイル。
R8G8B8A8またはR8G8B8X8のみサポートし、ミップマップ、キューブマップ、ボリュームマップは未対応。
UDDSLoader.cpp DDSファイルをロードしてシェーダーリソースビューを作成するサンプルクラスのソースファイル。
UDebugFont.h デバッグ用のテキストを画面上に表示するためのクラスのヘッダファイル。
UDebugFont.cpp デバッグ用のテキストを画面上に表示するためのクラスのソースファイル。
UStaticMediaBuffer.h IMediaBufferインターフェースを継承する音声入力バッファを保持するクラスのヘッダーファイル。 既出。
UKinect07.h Kinectクラスのヘッダーファイル。既出。
UKinect07.cpp Kinectクラスのソースファイル。既出。
UKinectAudio08.h 音源の取得処理を追加したクラスのヘッダーファイル。
UKinectAudio08.cpp 音源の取得処理を追加したクラスのソースファイル。
main.cpp main関数のソースファイル

---UKinectAudio08.h---  ↑


#ifndef KINECTAUDIO08_H
#define KINECTAUDIO08_H

#include "../../Common/Header/UCommon.h"
#include "../../Common/Header/UException.h"

#include "UStaticMediaBuffer.h"

// INuiAudioBeam
#include <NuiApi.h>

// IPropertyStore
#include <mfapi.h>

// WAVEFORMATEXのGUID
#include <uuids.h>

// MFPKEY_WMAAECMA_SYSTEM_MODE
#include <Wmcodecdsp.h>

#pragma comment( lib, "Kinect10.lib" )
#pragma comment( lib, "dmoguids.lib" )
#pragma comment( lib, "amstrmid.lib" )
#pragma comment( lib, "Msdmo.lib")

class UCKinectAudio08
{
private:
   // 音源の位置と方向を提供する
   INuiAudioBeam* m_pAudioBeam;

   // Microsoft DirectX Media Object (DMO) を操作するためのメソッドを提供する
   IMediaObject* m_pMediaObject;

   // プロパティを設定するための機能を提供する
   IPropertyStore* m_pPropertyStore;

   void GetWaveFormat( WAVEFORMATEX* pFormat );

public:

   // エコーキャンセラ( AEC )
   // 電気信号や音声の出力が入力機器に拾われてエコーやハウリングを起こすのを防止する機器や技術。

   // マイクロホンアレイ
   // 複数のマイクロホンから音声入力を行う。複数のマイクロホンを使用することで音源方向を推定することができる。

   // エコーキャンセラとマイクロホンアレイの設定モード
   enum class AEC_SYSTEM_MODE
   {
      SINGLE_CHANNEL_AEC = 0,        // エコーキャンセラ( AEC )のみ処理
      OPTIBEAM_ARRAY_ONLY = 2,       // マイクロホンアレイのみ処理
      OPTIBEAM_ARRAY_AND_AEC = 4,    // マイクロホンアレー処理とAEC処理
      SINGLE_CHANNEL_NSAGC = 5,      // マイクロホンアレーなしとAECなし
   };

   // IMediaBufferを継承するバッファ
   UCStaticMediaBuffer m_StaticMediaBuffer;

public:
   UCKinectAudio08();
   ~UCKinectAudio08();
   void Invalidate();
   void CreateKinectAudio( INuiSensor* pKinectSensor, AEC_SYSTEM_MODE AECSystemMode );
   void Read(BYTE **ppBuffer, DWORD *pLength, double* pAngle, double* pConfidence, double* pBeamAngle);
};

#endif

---UKinectAudio08.cpp---  ↑


#include "../../Common/Header/UKinectAudio08.h"

UCKinectAudio08::UCKinectAudio08()
{
   m_pAudioBeam = nullptr;
   m_pMediaObject = nullptr;
   m_pPropertyStore = nullptr;
}

UCKinectAudio08::~UCKinectAudio08()
{
   Invalidate();
}

void UCKinectAudio08::Invalidate()
{
   SAFE_RELEASE( m_pPropertyStore );
   SAFE_RELEASE( m_pMediaObject );
   SAFE_RELEASE( m_pAudioBeam );
}

void UCKinectAudio08::CreateKinectAudio( INuiSensor* pKinectSensor, AEC_SYSTEM_MODE AECSystemMode )
{
   HRESULT hr = E_FAIL;

   if( FAILED( hr = pKinectSensor->NuiGetAudioSource( &m_pAudioBeam ) ) )
      throw( UCException( hr, _T("UCKinectAudio08::CreateKinectAudio()でエラーが発生しました。INuiAudioBeam()インターフェースの取得に失敗しました") ) );

   if( FAILED( hr = m_pAudioBeam->QueryInterface( IID_IMediaObject, (void**)&m_pMediaObject ) ) )
      throw( UCException( hr, _T("UCKinectAudio08::CreateKinectAudio()でエラーが発生しました。IMediaObject()インターフェースの取得に失敗しました") ) );

   if( FAILED( hr = m_pAudioBeam->QueryInterface( IID_IPropertyStore, (void**)&m_pPropertyStore ) ) )
      throw( UCException( hr, _T("UCKinectAudio08::CreateKinectAudio()でエラーが発生しました。IPropertyStore()インターフェースの取得に失敗しました") ) );

    PROPVARIANT pvSysMode;
    PropVariantInit(&pvSysMode);
    pvSysMode.vt = VT_I4;    // 4バイト符号ありで設定
    pvSysMode.lVal = (LONG)AECSystemMode;
    // エコーキャンセラとマイクロホンアレイのモード設定する
    m_pPropertyStore->SetValue(MFPKEY_WMAAECMA_SYSTEM_MODE, pvSysMode);
    PropVariantClear(&pvSysMode);

   WAVEFORMATEX format;
   GetWaveFormat( &format );

   // バッファサイズを設定する。必要となるサイズはPCMの場合、1秒間当たりのデータ転送量。 
   m_StaticMediaBuffer.ResizeBuffer( format.nAvgBytesPerSec );

   DMO_MEDIA_TYPE mt = { 0 };

   // メディア タイプ構造体を初期化する。よくわかってない。
   ::MoInitMediaType( &mt, sizeof( WAVEFORMATEX ) );
   mt.majortype = MEDIATYPE_Audio;
   mt.subtype = MEDIASUBTYPE_PCM;       // PCMについてはウィキ参照で
   mt.bFixedSizeSamples = TRUE;         // オーディオの場合通常 TRUE
   mt.bTemporalCompression = FALSE;     // よくわからんが FALSE の場合、オーディオサンプルが時系列 (フレーム間) 圧縮方式で圧縮されていない
   mt.lSampleSize = 0;                  // サンプルのサイズ (バイト単位)。未使用?
   mt.formattype = FORMAT_WaveFormatEx;
   ::CopyMemory( mt.pbFormat, &format, sizeof( WAVEFORMATEX ) );

   // ストリームのメディア タイプを設定する
   if( FAILED( hr = m_pMediaObject->SetOutputType( 0, &mt, 0 ) ) )
      throw( UCException( hr, _T("UCKinectAudio08::Start()でエラーが発生しました。SetOutputType()が失敗しました") ) );

   ::MoFreeMediaType(&mt);
}

void UCKinectAudio08::GetWaveFormat( WAVEFORMATEX* pFormat )
{
   // この辺はSDKサンプルの通りで
   pFormat->wFormatTag = WAVE_FORMAT_PCM;
   pFormat->nChannels = 1;
   pFormat->nSamplesPerSec = 16000;
   pFormat->nAvgBytesPerSec = 32000;
   pFormat->nBlockAlign = 2;
   pFormat->wBitsPerSample = 16;
   pFormat->cbSize = 0;
}

void UCKinectAudio08::Read(BYTE **ppBuffer, DWORD *pLength, double* pAngle, double* pConfidence, double* pBeamAngle)
{
   HRESULT hr = E_FAIL;

   m_StaticMediaBuffer.SetLength( 0 );

   DMO_OUTPUT_DATA_BUFFER outputDataBuffer;

   ::ZeroMemory( &outputDataBuffer, sizeof( DMO_OUTPUT_DATA_BUFFER ) );

   // UCStaticMediaBufferのポインタを設定する
   outputDataBuffer.pBuffer = &m_StaticMediaBuffer;

   do
   {
      DWORD dwStatus;
      
      // 現在の入力データから出力を生成する。
      if( FAILED( hr = m_pMediaObject->ProcessOutput( 0, 1, &outputDataBuffer, &dwStatus ) ) )
         throw( UCException( hr, _T("UCKinectAudio08::Read()でエラーが発生しました。ProcessOutput()が失敗しました") ) );
   }while( outputDataBuffer.dwStatus & DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE );

   m_StaticMediaBuffer.GetBufferAndLength( ppBuffer, pLength );

   // 音源の方向と信頼性を取得する
   if( FAILED( hr = m_pAudioBeam->GetPosition( pAngle, pConfidence ) ) )
      throw( UCException( hr, _T("UCKinectAudio08::Read()でエラーが発生しました。GetPosition()が失敗しました") ) );

   // ビームの方向を取得する
   if( FAILED( hr = m_pAudioBeam->GetBeam( pBeamAngle ) ) )
      throw( UCException( hr, _T("UCKinectAudio08::Read()でエラーが発生しました。GetBeam()が失敗しました") ) );
}

x軸とz軸が水平面となるカメラ座標における音源への推定水平角(ラジアン)で角度が取得されます。
誤差が大きいので間違っているかもしれませんが、Kinectの正面( z軸 )に対し、右側を + 左側を - の角度となるようです。

---main.cpp---  ↑


#include "../../Common/Header/UCommon.h"
#include "../../Common/Header/UException.h"
#include "../../Common/Header/UDirect3D11.h"
#include "../../Common/Header/UGraphicsPipeline.h"
#include "../../Common/Header/UKinect07.h"
#include "../../Common/Header/UKinectAudio08.h"
#include "../../Common/Header/UDebugFont.h"
#include <NuiApi.h>

// アプリケーション名
TCHAR* AppName = _T("Kinect_Tutrial08");

// Direct3D関連の自作クラス
UCDirect3D11* g_pDirect3D11 = nullptr;

// Kinectセンサーオブジェクト
UCKinect07* g_pKinect = nullptr;
// Kinect Audio オブジェクト
UCKinectAudio08* g_pKinectAudio = nullptr;

// デバックフォントクラス
UCDebugFont* g_pDebugFont = nullptr;

// メモリ解放
void Invalidate()
{
   SAFE_DELETE( g_pDebugFont );
   SAFE_DELETE( g_pKinectAudio );
   SAFE_DELETE( g_pKinect );
   SAFE_DELETE( g_pDirect3D11 );
}

// ウィンドウプロシージャ
LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, UINT wParam, LONG lParam )
{
   switch( msg )
   {
   case WM_KEYUP:
      // アプリ終了
      if( wParam == VK_ESCAPE )
         ::DestroyWindow( hWnd );
      break;
   case WM_KEYDOWN:
      switch( wParam )
      {
      case VK_UP:
         if( g_pKinect && g_pKinect->IsReadyKinect() )
            g_pKinect->CameraElevationSetAngle( 5 );
         break;
      case VK_DOWN:
         if( g_pKinect && g_pKinect->IsReadyKinect() )
            g_pKinect->CameraElevationSetAngle( -5 );
         break;
      }
      break;
   case WM_DESTROY:
      ::PostQuitMessage(0);
      break;

   default:
      return ::DefWindowProc( hWnd, msg, wParam, lParam );
   }

   return 0L;
}

// Direct3Dの作成
void CreateDirect3D( HINSTANCE hInstance, DWORD Width, DWORD Height )
{
   DXGI_MODE_DESC* pModeDescArray = nullptr;

   __try
   {
      DXGI_MODE_DESC  ModeDesc;
      UINT num;

      // ディスプレイモード一覧の数を取得する
      g_pDirect3D11->GetDisplayMode( nullptr, &num );

      pModeDescArray = NEW DXGI_MODE_DESC[num];
      // ディスプレイモード一覧を取得する
      g_pDirect3D11->GetDisplayMode( pModeDescArray, &num );

      bool find = false;
      for( UINT i=0; i<num; i++ )
      {
         // 適切な解像度のディスプレイモードを検索する
         if( pModeDescArray[i].Width == Width && pModeDescArray[i].Height == Height )
         {
            find = true;
            ::CopyMemory( &ModeDesc, &pModeDescArray[i], sizeof( DXGI_MODE_DESC ) );
            break;
         }
      }

      if( find == false )
         throw( UCException( -1, _T("InitDirect3D()でエラーが発生しました。適切なディスプレイモードの解像度を取得できません。") ) );

      // ウィンドウの作成およびDirect3D の作成
      g_pDirect3D11->CreateDirect3D11( AppName, hInstance, WndProc, &ModeDesc, TRUE, TRUE );
   }
   __finally
   {
      SAFE_DELETE_ARRAY( pModeDescArray );
   }
}

void CreateGraphicePipeline()
{

}

// メッシュを作成する
void CreateMesh()
{

}

void CreateResource( HINSTANCE hInstance )
{
    // Direct3D 関連自作クラスのインスタンスを作成
    // CreateDirect3D()関数内でインスタンスの作成を行うと、C2712 エラーが発生するのでここで行う。
    g_pDirect3D11 = NEW UCDirect3D11();
    g_pKinect = NEW UCKinect07();
    g_pKinectAudio = NEW UCKinectAudio08();
   g_pDebugFont = NEW UCDebugFont();

    DWORD Width = 0, Height = 0;

    // Kinectセンサーオブジェクトを作成
    g_pKinect->CreateKinect( FALSE );

   // KinectAudioオブジェクトを作成
   g_pKinectAudio->CreateKinectAudio( g_pKinect->m_pKinectSensor, UCKinectAudio08::AEC_SYSTEM_MODE::OPTIBEAM_ARRAY_AND_AEC );

    g_pKinect->GetImageResolutionToSize( Width, Height );

    // Direct3Dの作成
    CreateDirect3D( hInstance, Width, Height );

    // グラフィックパイプラインリソースの作成
    CreateGraphicePipeline();

    // リソースの作成
   CreateMesh();

   g_pDebugFont->CreateMesh( g_pDirect3D11->m_pD3DDevice, 0.025f, 0.1f );
}

void NextFrame()
{
   NUI_LOCKED_RECT* imageDataFromKinect = nullptr;

   // Kinect側のフレームの更新が完了するまで待つ
   g_pKinect->WaitForSingleObject();

   // Kinect側の次のフレームのイメージフレームの作成
   g_pKinect->NextFrameImageData( &imageDataFromKinect );

   if( g_pKinect->IsReadyKinect() == TRUE )
   {
      // オーディオを読み込む
      BYTE* pBuffer = nullptr;
      DWORD BufferLength = 0;
     double angle, confidence, beamAngle;
      g_pKinectAudio->Read( &pBuffer, &BufferLength, &angle, &confidence, &beamAngle );

     TCHAR str[512];
     _stprintf_s( str, _T("angle:%f, confidence:%f, beamAngle:%f"), angle * 180.0f / 3.141592654f, confidence, beamAngle * 180.0f / 3.141592654f );
     XMFLOAT2 pos = XMFLOAT2( -0.98f, 0.95f );
     XMFLOAT4 color = XMFLOAT4( 1, 1, 1, 1 );
     g_pDebugFont->NextFrame( str, &pos, &color );
   }
}

// 描画処理
HRESULT Render()
{
   HRESULT hr = E_FAIL;
   float ClearColor[4] = { 0.3f, 0.3f, 1.0f, 1.0f };

    // バックバッファをクリアする。
   g_pDirect3D11->ClearBackBuffer( ClearColor ); 

   // 深度バッファをクリアする。
   g_pDirect3D11->ClearDepthStencilView( D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0 );

   // レンダリング
   g_pDebugFont->Render( g_pDirect3D11->m_pD3DDeviceContext );

   // レンダリングされたイメージをユーザーに表示。
   if( FAILED( hr = g_pDirect3D11->Present( 0, 0 ) ) )
      throw( UCException( hr, _T("IDXGISwapChain::Present()が失敗しました") ) );

   return hr;
}
 
// メイン関数
int APIENTRY _tWinMain( HINSTANCE hInstance,
                        HINSTANCE,
                        LPTSTR,
                        INT )
{
   // メモリリーク検出
   _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_EVERY_1024_DF);

   MSG msg = {0};

   try
   {
      CreateResource( hInstance );

      ::ShowWindow( g_pDirect3D11->m_hWnd, SW_SHOW );
      ::UpdateWindow( g_pDirect3D11->m_hWnd );

      // メッセージループ
      do
      { 
         if( ::PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
         {
            ::TranslateMessage(&msg); 
            ::DispatchMessage(&msg); 
         }
         else
         {
            NextFrame();
            Render();
         }
      }while( msg.message != WM_QUIT );
   }
   catch( UCException ex )
   {
      ::MessageBox( nullptr, ex.m_pErrorStr, _T("エラー"), MB_OK );
   }

   Invalidate();
   ::UnregisterClass( AppName, hInstance );

   return msg.wParam;
}

Kinecを壁際に置いた状態では音源の方向を取得するのは難しいようです。まあ波である音は壁で反射するわけですから当然そうなるでしょうけどね。


web拍手 by FC2

Prev Top Next

inserted by FC2 system