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 メッシュの抽象クラスのソースファイル
UStaticMediaBuffer.h IMediaBufferインターフェースを継承する音声入力バッファを保持するクラスのヘッダーファイル。
Equalizer.hlsl hlslファイル。ライン描画。
UEqualizer.h Kinectから取得したAudioデータを波形データとしてライン描画するためのメッシュクラスのヘッダーファイル
UEqualizer.cpp Kinectから取得したAudioデータを波形データとしてライン描画するためのメッシュクラスのソースファイル
UKinect07.h Kinectクラスのヘッダーファイル
UKinect07.cpp Kinectクラスのソースファイル
UKinectAudio07.h Kinectの音声入力まわりを制御するためのクラスのヘッダーファイル
UKinectAudio07.cpp Kinectの音声入力まわりを制御するためのクラスのソースファイル
main.cpp main関数のソースファイル

---UStaticMediaBuffer.h---  ↑


#ifndef STATICMEDIABUFFER_H
#define STATICMEDIABUFFER_H

// IMediaBuffer
#include <Dmo.h>

#pragma comment( lib, "Dmoguids.lib" )

// デフォルトの DMO ストリーミング モデルでは、バッファの管理は IMediaBuffer インターフェイスを通して行う。
class UCStaticMediaBuffer : public IMediaBuffer
{
private:
   // 音声入力バッファ
   BYTE* m_pBuffer;
   // 音声入力バッファの最大バイト長
   ULONG m_BufferLength;
   // Kinectから音声入力されたデータのバイト長
   ULONG m_dataLength;

public:
   UCStaticMediaBuffer()
   {
      m_BufferLength = 0;
      m_pBuffer = nullptr;
   }
   ~UCStaticMediaBuffer()
   {
      Invalidate();
   }
   STDMETHODIMP_(ULONG) AddRef() { return 2; }
   STDMETHODIMP_(ULONG) Release() { return 1; }
   STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
   {
      if (riid == IID_IUnknown)
      {
          AddRef();
          *ppv = (IUnknown*)this;
          return NOERROR;
      }
      else if (riid == IID_IMediaBuffer)
      {
          AddRef();
          *ppv = (IMediaBuffer*)this;
          return NOERROR;
      }
      else
      {
          return E_NOINTERFACE;
      }
   }
   // バッファ内の有効なデータの長さを指定する。
   STDMETHODIMP SetLength(DWORD length)
   {
      m_dataLength = length;
      return NOERROR;
   }
   // バッファのサイズを返す。
   STDMETHODIMP GetMaxLength(DWORD *pMaxLength)
   {
      *pMaxLength = m_BufferLength;
      return NOERROR;
   }
   // バッファのアドレス (つまり、データを保持する実際のメモリ ブロック) と、バッファ内にある有効なデータのサイズを返す。
   STDMETHODIMP GetBufferAndLength(BYTE **ppBuffer, DWORD *pLength)
   {
      if (ppBuffer)
      {
          *ppBuffer = m_pBuffer;
      }
      if (pLength)
      {
          *pLength = m_dataLength;
      }
      return NOERROR;
   }

   void Invalidate()
   {
      m_BufferLength = 0;
      SAFE_DELETE_ARRAY( m_pBuffer );
   }

   void ResizeBuffer( DWORD Length )
   {
      Invalidate();
      m_BufferLength = Length;
      m_pBuffer = NEW BYTE[m_BufferLength];
   }
};

#endif

UCStaticMediaBufferクラスは、Kinectから入力された音声データが格納される音声入力バッファを保持します。
アプリケーションからはこのバッファを参照して処理を行うことになります。
このクラスは、IMediaBufferインターフェースを継承し、GetBufferAndLength()とGetMaxLength()とSetLength()を実装する必要があります。

---Equalizer.hlsl---  ↑


// サンプラーステート
SamplerState  g_Sampler : register( s0 );

// 頂点シェーダーの入力パラメータ
struct VS_IN
{
   float3 pos    : POSITION;    // 頂点座標
};

// 頂点シェーダーの出力パラメータ
struct VS_OUT_PS_IN
{
   float4 pos    : SV_POSITION;
};

// 頂点シェーダー
VS_OUT_PS_IN VS_Main( VS_IN In )
{
   VS_OUT_PS_IN Out;

   Out.pos = float4( In.pos, 1.0f );
   return Out;
}

// ピクセルシェーダ
float4 PS_Main( VS_OUT_PS_IN In ) : SV_TARGET
{
   return float4( 1, 1, 1, 1 );
}

ライン描画するだけなので単純です。

---UEqualizer.h---  ↑


#ifndef UEQUALIZER_H
#define UEQUALIZER_H

#include "../../Common/Header/UCommon.h"
#include "../../Common/Header/UException.h."
#include "../../Common/Header/UMesh.h"
#include "../../Common/Header/UDirect3D11.h"
#include <NuiApi.h>

// イコライザーもどき
class UCEqualizer: public UIMesh
{
private:
   // 頂点定義
   typedef struct _VERTEX
   {
      // 頂点座標
      float x, y, z;
   }VERTEX;

   // 頂点バッファの頂点数
   static const int m_AmplitudeMaxCount = 500;

   // 頂点バッファ
   ID3D11Buffer* m_pVertexBuffer;

   // サンプラーステート
   ID3D11SamplerState* m_pSamplerState;

   // 頂点シェーダー用定数バッファ
   ID3D11Buffer* m_pVSConstantBuffers[1];

   // ピクセルシェーダー用定数バッファ
   ID3D11Buffer* m_pPSConstantBuffers[1];

public:
   UCEqualizer();
   ~UCEqualizer();
   void Invalidate();
   void CreateMesh( ID3D11Device* pD3DDevice );
   void NextFrame( ID3D11DeviceContext* pD3DDeviceContext, BYTE* pAmplitudeArray, DWORD AmplitudeCount );
   void Render( ID3D11DeviceContext* pD3DDeviceContext );
};
#endif

---UEqualizer.cpp---  ↑


#include "UEqualizer.h"

UCEqualizer::UCEqualizer()
{
   m_pVertexBuffer = nullptr;
   m_pSamplerState = nullptr;

   for( int i=0; i<_countof( m_pVSConstantBuffers ); i++ )
      m_pVSConstantBuffers[i] = nullptr;

   for( int i=0; i<_countof( m_pPSConstantBuffers ); i++ )
      m_pPSConstantBuffers[i] = nullptr;
}

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

void UCEqualizer::Invalidate()
{
   for( int i=0; i<_countof( m_pPSConstantBuffers ); i++ )
      SAFE_RELEASE( m_pPSConstantBuffers[i] );

   for( int i=0; i<_countof( m_pVSConstantBuffers ); i++ )
      SAFE_RELEASE( m_pVSConstantBuffers[i] );

   SAFE_RELEASE( m_pSamplerState );
   SAFE_RELEASE( m_pVertexBuffer );
}

void UCEqualizer::CreateMesh( ID3D11Device* pD3DDevice )
{
   // 頂点バッファを作成する
   m_pVertexBuffer = CreateVertexBuffer( pD3DDevice, nullptr, sizeof( UCEqualizer::VERTEX ) * m_AmplitudeMaxCount, D3D11_CPU_ACCESS_WRITE );

   // サンプラーステートを作成する
   m_pSamplerState = CreateSamplerState( pD3DDevice, D3D11_TEXTURE_ADDRESS_CLAMP );
}

void UCEqualizer::NextFrame( ID3D11DeviceContext* pD3DDeviceContext, BYTE* pAmplitudeArray, DWORD AmplitudeCount )
{
   HRESULT hr = E_FAIL;

   DWORD index = 0;
   D3D11_MAPPED_SUBRESOURCE mappedResource;

   float xOffset = 2.0f / (float)m_AmplitudeMaxCount;

   if( FAILED( hr = pD3DDeviceContext->Map( m_pVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource ) ) )
      throw( UCException( hr, _T("UCEqualizer::NextFrame()でエラーが発生しました。Mapに失敗しました") ) );

   VERTEX* v = (VERTEX*)mappedResource.pData;

   DWORD cnt = min( AmplitudeCount, m_AmplitudeMaxCount * 2 );

   for( UINT i=0; i<cnt; i+=2 )
   {
      // 1音声サンプルデータを取得
      short audioSample = static_cast(pAmplitudeArray[i] | (pAmplitudeArray[i+1] << 8));

      // 適当に縮小する
      float meanSquare = audioSample * 0.0001f;

      v[index].x = -1.0f + index * xOffset;
      v[index].y = meanSquare;
      v[index].z = 0.0f;
      index++;
   }

   // 残りを0
   for( UINT i=index; i<m_AmplitudeMaxCount; i++ )
   {
      v[i].x = -1.0f + i * xOffset;
      v[i].y = 0;
      v[i].z = 0.0f;
   }

   pD3DDeviceContext->Unmap( m_pVertexBuffer, 0 );
}

void UCEqualizer::Render( ID3D11DeviceContext* pD3DDeviceContext )
{
    // 頂点バッファ設定
    UINT stride = sizeof( UCEqualizer::VERTEX );
    UINT offset = 0;
    pD3DDeviceContext->IASetVertexBuffers( 0, 1, &m_pVertexBuffer, &stride, &offset );

    // プリミティブ タイプおよびデータの順序に関する情報を設定
    pD3DDeviceContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP );

    // ピクセルシェーダーのサンプラーステートを設定する
    pD3DDeviceContext->PSSetSamplers( 0, 1, &m_pSamplerState );

    // 定数バッファを設定
    pD3DDeviceContext->VSSetConstantBuffers( 0, _countof( m_pVSConstantBuffers ), m_pVSConstantBuffers );
    pD3DDeviceContext->PSSetConstantBuffers( 0, _countof( m_pPSConstantBuffers ), m_pPSConstantBuffers );

    // インデックスバッファを使用しない描画
    pD3DDeviceContext->Draw( m_AmplitudeMaxCount, 0 );
}

波形を描画するためのクラスです。イコライザーといってますが、そういっていいのかは分かりません。
なおSDKのサンプルではフーリエ変換やったり複雑でよくわからんことやってますが、ここでは余計な処理は行わず、音声入力バッファの内容をそのまま使って描画するようにしてます。

---UKinect07.h---  ↑


#ifndef KINECT07_H
#define KINECT07_H

#include "../../Common/Header/UCommon.h"
#include <NuiApi.h>
#include "../../Common/Header/UException.h"

// Kinect用ライブラリ
#pragma comment( lib, "Kinect10.lib" )

class UCKinect07
{
private:
   HANDLE m_ImageStreamHandle, m_DepthStreamHandle, m_streamEventHandle[3];
   NUI_LOCKED_RECT m_pColorImageRectData;
   NUI_SKELETON_FRAME m_SkeletonFrame;

   // Nearモードを使用するか
   BOOL m_NearModeEnaled;

   // Kinectが使用可能か
   BOOL m_ReadyKinect;

   static const NUI_IMAGE_RESOLUTION m_ImageResolution = NUI_IMAGE_RESOLUTION_640x480;
   static const NUI_IMAGE_RESOLUTION m_DepthResolution = NUI_IMAGE_RESOLUTION_640x480;

   // INuiSensorインスタンスのステータスが変更された場合、コールバックされるメンバ関数
   static void CALLBACK StatusChanged( HRESULT hrStatus, const OLECHAR* instanceName, const OLECHAR* uniqueDeviceName, void* pUserData );
   void CALLBACK StatusChanged( HRESULT hrStatus, const OLECHAR* instanceName, const OLECHAR* uniqueDeviceName );

public:
   INuiSensor* m_pKinectSensor;

public:
   UCKinect07();
   ~UCKinect07();
   void Invalidate();
   void CreateKinect( BOOL NearModeEnaled );

   // カラーデータを取得する
   void NextFrameImageData( NUI_LOCKED_RECT** ppLockedRect );

   // スケルトンデータを取得する
   void NextFrameSkeletonData( NUI_SKELETON_FRAME** ppSkeletonData );

   // イメージフレームの解像度を取得する
   inline void GetImageResolutionToSize( DWORD& pWidth, DWORD& pHeight )
   {
      ::NuiImageResolutionToSize( m_ImageResolution, pWidth, pHeight );
   }

   // カメラを傾ける
   void CameraElevationSetAngle( LONG Angle );

   // フレーム更新待ち
   void WaitForSingleObject();

   // Kinectが使用可能か
   inline BOOL IsReadyKinect(){ return m_ReadyKinect; };
};

#endif

---UKinect07.cpp---  ↑


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

UCKinect07::UCKinect07()
{
   m_pKinectSensor = nullptr;
   m_pColorImageRectData.pBits = nullptr;
   m_ImageStreamHandle = nullptr;
   m_DepthStreamHandle = nullptr;
   for( int i=0; i<_countof( m_streamEventHandle ); i++ )
      m_streamEventHandle[i] = nullptr;
   m_ReadyKinect = FALSE;
}

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

void UCKinect07::Invalidate()
{
   m_ReadyKinect = FALSE;

   SAFE_DELETE_ARRAY( m_pColorImageRectData.pBits );

   if( m_pKinectSensor != nullptr )
   {
      m_pKinectSensor->NuiShutdown();
      SAFE_RELEASE( m_pKinectSensor );
   }

   if( m_streamEventHandle != nullptr )
   {
      for( int i=0; i<_countof( m_streamEventHandle ); i++ )
      {
         // Setしないとフリーズする
         ::SetEvent( m_streamEventHandle[i] );
         ::CloseHandle( m_streamEventHandle[i] );
         m_streamEventHandle[i] = nullptr;
      }
   }
}

void UCKinect07::CreateKinect( BOOL NearModeEnaled )
{
   HRESULT hr = E_FAIL;
   DWORD Flags = NUI_INITIALIZE_FLAG_USES_COLOR | NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX | NUI_INITIALIZE_FLAG_USES_SKELETON | NUI_INITIALIZE_FLAG_USES_AUDIO;

   m_NearModeEnaled = NearModeEnaled;

   int count = 0;

   if( m_pKinectSensor == nullptr )
   {
      // INuiSensorインスタンスのステータスが変更された場合、コールバックされる関数を設定する
      ::NuiSetDeviceStatusCallback( StatusChanged, this );

      // マシンに接続されている Kinectセンサー 数を取得する。
      if( FAILED( hr = ::NuiGetSensorCount( &count ) ) )
         throw( UCException( hr, _T("UCKinect07::UCKinect07()でエラーが発生しました。接続されているKinectセンサーの数を取得できません。") ) );

      if( count == 0 )
         throw( UCException( -1, _T("UCKinect07::UCKinect07()でエラーが発生しました。Kinectセンサーが接続されていません") ) );

      // 指定したインデックスの INuiSensor のインスタンスを作成する
      // ここでは 0 番目のインスタンスのみ作成する
      if( FAILED( hr = ::NuiCreateSensorByIndex( 0, &m_pKinectSensor ) ) )
         throw( UCException( hr, _T("UCKinect07::UCKinect07()でエラーが発生しました。INuiSensorの作成に失敗しました") ) );

      // Kinectセンサー の接続状態を取得する
      if( ( hr = m_pKinectSensor->NuiStatus() ) != S_OK )
         throw( UCException( hr, _T("UCKinect07::UCKinect07()でエラーが発生しました。使用可能な状態でありません。") ) );
   }

   for( int i=0; i<_countof( m_streamEventHandle ); i++ )
      m_streamEventHandle[i] = ::CreateEvent( 0, TRUE, FALSE, 0 );

   // Kinectを初期化する。
   if( FAILED( hr = m_pKinectSensor->NuiInitialize( Flags ) ) )
      throw( UCException( -1, _T("UCKinect07::CreateKinect()でエラーが発生しました。Kinectの初期化に失敗した") ) );

   // RGBカメラを使用する場合
   if( Flags & NUI_INITIALIZE_FLAG_USES_COLOR )
   {
      // イメージストリームをオープンする
      if( FAILED( hr = m_pKinectSensor->NuiImageStreamOpen( 
                            NUI_IMAGE_TYPE_COLOR,                   // カラーデータを取得する
                            m_ImageResolution,                      // イメージストリームの解像度を指定する
                            0, 2, m_streamEventHandle[0], 
                            &m_ImageStreamHandle                    // 開始されたイメージストリームのハンドル
                            ) ) )
        throw( UCException( hr, _T("UCKinect07::CreateKinect()でエラーが発生しました。イメージストリームをオープンできなかった") ) );
   }
   
   // 深度カメラを使用し、プレイヤーIDを使用しない場合
   if( Flags & NUI_INITIALIZE_FLAG_USES_DEPTH )
   {
      // イメージストリームをオープンする
      if( FAILED( hr = m_pKinectSensor->NuiImageStreamOpen( 
                            NUI_IMAGE_TYPE_DEPTH,                   // 深度データを取得する
                            m_DepthResolution,                      // 深度ストリームの解像度を指定する
                            0, 2, m_streamEventHandle[1], 
                            &m_DepthStreamHandle                    // 開始された深度ストリームのハンドル
                            ) ) )
        throw( UCException( hr, _T("UCKinect07::CreateKinect()でエラーが発生しました。深度ストリームをオープンできなかった") ) );
   }

   // 深度カメラを使用し、プレイヤーIDを使用する場合
   else if( Flags & NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX )
   {
      // イメージストリームをオープンする
      if( FAILED( hr = m_pKinectSensor->NuiImageStreamOpen( 
                            NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX,  // 深度データとプレイヤーIDを取得する
                            m_DepthResolution,                      // 深度ストリームの解像度を指定する
                            0, 2, m_streamEventHandle[1], 
                            &m_DepthStreamHandle                    // 開始された深度ストリーム + プレイヤーIDのハンドル
                            ) ) )
        throw( UCException( hr, _T("UCKinect07::CreateKinect()でエラーが発生しました。深度ストリーム + プレイヤーIDをオープンできなかった") ) );
   }

   // Nearモードを有効にする?
   if( m_NearModeEnaled )
   {
      // Kinect for XBOX は Nearモード は対応していない!
      if( FAILED( hr = m_pKinectSensor->NuiImageStreamSetImageFrameFlags( m_DepthStreamHandle, NUI_IMAGE_STREAM_FLAG_ENABLE_NEAR_MODE ) ) )
         throw( UCException( hr, _T("UCKinect07::CreateKinect()でエラーが発生しました。イメージフレームの設定に失敗した") ) );
   }

   // スケルトントラッキングを有効にする?
   if( Flags & NUI_INITIALIZE_FLAG_USES_SKELETON )
   {
      if( FAILED( hr = m_pKinectSensor->NuiSkeletonTrackingEnable( m_streamEventHandle[2], 0 ) ) )
          throw( UCException( hr, _T("UCKinect07::CreateKinect()でエラーが発生しました。スケルトントラッキングを有効にできなかった") ) );
   }
   
   DWORD Width, Height;
   ::NuiImageResolutionToSize( m_ImageResolution, Width, Height );

   // カラーイメージの色配列の領域確保
   m_pColorImageRectData.pBits = NEW BYTE[ Width * Height * 4 ];

   // カメラ位置を初期位置に
   m_pKinectSensor->NuiCameraElevationSetAngle( 0 );

   m_ReadyKinect = TRUE;
}

void UCKinect07::NextFrameImageData( NUI_LOCKED_RECT** ppLockedRect )
{
   if( IsReadyKinect() == FALSE )
      return;

   HRESULT hr = E_FAIL;
   NUI_IMAGE_FRAME ImageFrame;
   NUI_LOCKED_RECT rect;
   DWORD dwMillisecondsToWait = 0;

   // フレームの更新待ちをイベント処理にしていない場合はNuiImageStreamGetNextFrame()で待つ
   if( m_streamEventHandle == nullptr )
      dwMillisecondsToWait = INFINITE;

   // イメージストリームから次のイメージフレームを取得する
   if( FAILED( hr = m_pKinectSensor->NuiImageStreamGetNextFrame( m_ImageStreamHandle, dwMillisecondsToWait, &ImageFrame ) ) )
      throw( UCException( hr, _T("UCKinect07::NextFrameImageData()でエラーが発生しました。次のイメージフレームを取得できなかった") ) );

   // イメージフレームからカラーデータを取得する
   if( FAILED( hr = ImageFrame.pFrameTexture->LockRect( 0, &rect, nullptr, 0 ) ) )
      throw( UCException( hr, _T("UCKinect07::NextFrameImageData()でエラーが発生しました。イメージフレームからイメージデータを取得できなかった") ) );

   // ロックを解除すると参照できなくなるので別変数に格納してからコピーする
   m_pColorImageRectData.Pitch = rect.Pitch;
   m_pColorImageRectData.size = rect.size;
   ::CopyMemory( m_pColorImageRectData.pBits, rect.pBits, sizeof( BYTE ) * rect.size );

   // ロックを解除する
   if( FAILED( hr = ImageFrame.pFrameTexture->UnlockRect( 0 ) ) )
      throw( UCException( hr, _T("UCKinect07::NextFrameImageData()でエラーが発生しました。ロックを解除できなかった") ) );

   // イメージストリームから取得したイメージフレームを解放する
   if( FAILED( hr = m_pKinectSensor->NuiImageStreamReleaseFrame( m_ImageStreamHandle, &ImageFrame ) ) )
      throw( UCException( hr, _T("UCKinect07::NextFrameImageData()でエラーが発生しました。イメージストリームから取得したイメージフレームを解放できなかった") ) );

   // 線形合成した場合でも正しくレンダリングできるようにRGBカメラの結果のアルファ値は常に255にする
   for( INT i=3; i<m_pColorImageRectData.size; i+=4 )
      m_pColorImageRectData.pBits[i] = 255;

   *ppLockedRect = &m_pColorImageRectData;
}

// スケルトンデータを取得する
void UCKinect07::NextFrameSkeletonData( NUI_SKELETON_FRAME** ppSkeletonData )
{
   if( IsReadyKinect() == FALSE )
      return;

   HRESULT hr = E_FAIL;
   DWORD dwMillisecondsToWait = 0;

   // フレームの更新待ちをイベント処理にしていない場合はNuiSkeletonGetNextFrame()で待つ
   if( m_streamEventHandle == nullptr )
      dwMillisecondsToWait = INFINITE;

   // スケルトンストリームから次のスケルトンフレームを取得する
   if( FAILED( hr = m_pKinectSensor->NuiSkeletonGetNextFrame( dwMillisecondsToWait, &m_SkeletonFrame ) ) )
      throw( UCException( hr, _T("UCKinect07::NextFrameSkeletonData()でエラーが発生しました。スケルトンの次のフレームのデータを取得できなかった") ) );

   *ppSkeletonData = &m_SkeletonFrame;
}

// カメラを傾ける
void UCKinect07::CameraElevationSetAngle( LONG Angle )
{
   if( IsReadyKinect() == FALSE )
      return;

   LONG angle;
    m_pKinectSensor->NuiCameraElevationGetAngle( &angle );

    angle += Angle;

    if( angle < NUI_CAMERA_ELEVATION_MINIMUM )
        angle = NUI_CAMERA_ELEVATION_MINIMUM;
    else if( angle > NUI_CAMERA_ELEVATION_MAXIMUM )
        angle = NUI_CAMERA_ELEVATION_MAXIMUM;

    m_pKinectSensor->NuiCameraElevationSetAngle( angle );
}

// フレーム更新待ち
void UCKinect07::WaitForSingleObject()
{
   if( m_streamEventHandle != nullptr )
   {
      DWORD ret = ::WaitForMultipleObjects( _countof( m_streamEventHandle ), m_streamEventHandle, TRUE, INFINITE );

      // INFINITE 以外の値に変更した場合、タイムアウトを検出できるようにするためにチェックしておく
      if( ret == WAIT_TIMEOUT )
         throw( UCException( -1, _T("WaitForSingleObject()でエラーが発生しました。タイムアウトした。") ) );

      // 指定されたイベントオブジェクトを非シグナル状態に設定します。
      ::ResetEvent( m_streamEventHandle );
   }
}

// INuiSensorインスタンスのステータスが変更された場合、コールバックされるメンバ関数
void UCKinect07::StatusChanged( HRESULT hrStatus, const OLECHAR* instanceName, const OLECHAR* uniqueDeviceName, void* pUserData )
{
   ((UCKinect07*)pUserData)->StatusChanged( hrStatus, instanceName, uniqueDeviceName );
}

void UCKinect07::StatusChanged( HRESULT hrStatus, const OLECHAR* instanceName, const OLECHAR* )
{
   HRESULT hr = E_FAIL;

   if( hrStatus == S_OK )
   {
      if( FAILED( hr = ::NuiCreateSensorById( instanceName, &m_pKinectSensor ) ) )
         throw( UCException( -1, _T("UCKinect07::StatusChanged()でエラーが発生しました。INuiSensorのインスタンスを作成できなかった") ) );

      CreateKinect(m_NearModeEnaled);
   }
   else
   {
      Invalidate();
   }
}

音声入力のためにNUI_INITIALIZE_FLAG_USES_AUDIOを追加します。

---UKinectAudio07.h---  ↑


#ifndef KINECTAUDIO07_H
#define KINECTAUDIO07_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>

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

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

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

   // プロパティを設定するための機能を提供する( 今回は未使用 )
   IPropertyStore* m_pPropertyStore;

   void GetWaveFormat( WAVEFORMATEX* pFormat );

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

public:
   UCKinectAudio07();
   ~UCKinectAudio07();
   void Invalidate();
   void CreateKinectAudio( INuiSensor* pKinectSensor );
   void Read(BYTE **ppBuffer, DWORD *pLength);
};

#endif

音声入力は、必須機能ではないのでKinect本体と分けました。

---UKinectAudio07.cpp---  ↑


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

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

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

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

void UCKinectAudio07::CreateKinectAudio( INuiSensor* pKinectSensor )
{
   HRESULT hr = E_FAIL;

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

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

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

   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("UCKinectAudio07::Start()でエラーが発生しました。SetOutputType()が失敗しました") ) );

   ::MoFreeMediaType(&mt);
}

void UCKinectAudio07::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 UCKinectAudio07::Read(BYTE **ppBuffer, DWORD *pLength)
{
   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("UCKinectAudio07::Read()でエラーが発生しました。ProcessOutput()が失敗しました") ) );
   }while( outputDataBuffer.dwStatus & DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE );

   m_StaticMediaBuffer.GetBufferAndLength( ppBuffer, pLength );

}

よくわからんので説明できん。

---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/UKinectAudio07.h"
#include "UEqualizer.h"
#include <NuiApi.h>

// シェーダーオブジェクトを作成するとき、ファイルから読むか、メモリから読むかを切り替える
#if defined(DEBUG) || defined(_DEBUG)
   #define UNCOMPILED_SHADER     // ファイルを読み込んでコンパイルする
#else
   #include "../../Common/HLSL/Equalizer_VS_Main.h"
   #include "../../Common/HLSL/Equalizer_PS_Main.h"
#endif

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

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

// グラフィックパイプライン関連の自作クラス
UCGraphicsPipeline* g_pGraphicsPipeline = nullptr;

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

// イコライザもどきオブジェクト
UCEqualizer* g_pEqualizer = nullptr;

// メモリ解放
void Invalidate()
{
   SAFE_DELETE( g_pKinectAudio );
   SAFE_DELETE( g_pKinect );
   SAFE_DELETE( g_pEqualizer );
   SAFE_DELETE( g_pGraphicsPipeline );
   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()
{
   D3D11_INPUT_ELEMENT_DESC layout[] = {
         { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,  0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
   };
   
   // シェーダーを作成する
#if defined(DEBUG) || defined(_DEBUG)
   g_pGraphicsPipeline->CreateVertexShaderFromFile( g_pDirect3D11->m_pD3DDevice, _T("../../Common/HLSL/Equalizer.hlsl"), "VS_Main", layout, _countof( layout ) );
   g_pGraphicsPipeline->CreatePixelShaderFromFile(  g_pDirect3D11->m_pD3DDevice, _T("../../Common/HLSL/Equalizer.hlsl"), "PS_Main" );
#else
   g_pGraphicsPipeline->CreateVertexShaderFromMemory( g_pDirect3D11->m_pD3DDevice, (LPBYTE)g_VS_Main, sizeof( g_VS_Main ), layout, _countof( layout ) );
   g_pGraphicsPipeline->CreatePixelShaderFromMemory(  g_pDirect3D11->m_pD3DDevice, (LPBYTE)g_PS_Main, sizeof( g_PS_Main ) );
#endif

   // ラスタライザーステートを作成する
   g_pGraphicsPipeline->CreateRasterizerState( g_pDirect3D11->m_pD3DDevice, D3D11_CULL_MODE::D3D11_CULL_BACK );

   // 深度ステンシルステートを作成する
   g_pGraphicsPipeline->CreateDepthStencilState( g_pDirect3D11->m_pD3DDevice, FALSE, D3D11_DEPTH_WRITE_MASK::D3D11_DEPTH_WRITE_MASK_ALL );

   // ブレンドステートを線形合成で作成する
   UCGraphicsPipeline::UEBLEND_STATE BlendStateType[1] = { UCGraphicsPipeline::UEBLEND_STATE::NONE };
   g_pGraphicsPipeline->CreateBlendState( g_pDirect3D11->m_pD3DDevice, BlendStateType, 1 );
}

// メッシュを作成する
void CreateMesh()
{
   g_pEqualizer->CreateMesh( g_pDirect3D11->m_pD3DDevice );
}

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

    DWORD Width = 0, Height = 0;

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

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

    g_pKinect->GetImageResolutionToSize( Width, Height );

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

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

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

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;
      g_pKinectAudio->Read( &pBuffer, &BufferLength );

      // イコライザもどきの頂点座標更新
      g_pEqualizer->NextFrame( g_pDirect3D11->m_pD3DDeviceContext, pBuffer, BufferLength );
   }
}

// 描画処理
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_pGraphicsPipeline->SetVertexShader( g_pDirect3D11->m_pD3DDeviceContext );
   g_pGraphicsPipeline->SetPixelShader( g_pDirect3D11->m_pD3DDeviceContext );
   g_pGraphicsPipeline->SetRasterizerState( g_pDirect3D11->m_pD3DDeviceContext );
   g_pGraphicsPipeline->SetDepthStencilState( g_pDirect3D11->m_pD3DDeviceContext );
   g_pGraphicsPipeline->SetBlendState( g_pDirect3D11->m_pD3DDeviceContext );

   // レンダリング
   g_pEqualizer->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;
}

サウンド系のプログラミングにはフーリエ変換が必要なようですが、まあできる人はやってください。自分には無理です。
一応フーリエ変換を行うライブラリはあります。
Microsoft XDSP アプリケーション プログラミング インターフェイス (API) や ID3DX11FFT インターフェイス です。
Direct3D エクステンション は廃止されたので使うなら XDSP ですかね。


web拍手 by FC2

Prev Top Next

inserted by FC2 system