もう、完全に個人ネタ。というより、メモですね(苦笑)
まあ、Googleなんかで検索を掛けた誰かが
何かの参考にして頂ければ幸いということで「公開」で書きます。
----------------------------------------------------------------------------
Visual C++ Express Edition と DirectX Ver.9を使った音声情報処理。
(準備編)
1.まず、それぞれをマイクロソフトのHPからダウンロードしてインストール。
2.DirectXのインクルードファイルとライブラリファイルの収められているフォルダのパスを通す。
VC++を立ち上げて、メニューバーの「ツール」→「オプション」をクリック。
立ち上がるダイアログの左側のツリーの一番上の
(デフォルトでは、スライドバーを動かさないと見えないみたい)
「プロジェクトおよびソリューション」を展開して「VC++ディレクトリ」をクリック。
次いで右上の「ディレクトリを表示するプロジェクト」をクリックして、
「インクルードファイル」をクリック。
そいでから、下のボックスに表示されているインクルードファイルの探索パスの中に
DirectXのインクルードファイルが収められたフォルダのパスを追加。
同じように、
「ライブラリファイル」も、DirectXのライブラリファイルのフォルダのパスを追加する。
(作成)
1.メニュー→新規作成→プロジェクト作成。 とりあえず、簡単のために、今回はコンソールアプリケーションを作成。
2.できたプロジェクトにDSound.libをリンクする。
ソリューション・エクスプローラ(デフォルトで表示されているはず)のプロジェクト名のところで
右クリック→「プロパティ」。
左側のツリーボックスで、「構成プロパティ」→「リンカ」と展開して、「コマンドライン」をクリック。
右側下段のエディットボックスに、DSound.libと追記。
※ なお、今回は、ミリ秒単位でのタイマも使うために、winmm.libも追記。
ちなみに追記のときは、スペースで区切る。
(ソース)
最終的にこんなプログラムを書きました。
--------------------------------------------------------------------------
#include "stdafx.h"
#include "Dsound.h"
#include <time.h>
#include <mmsystem.h>
LPDIRECTSOUNDCAPTURE g_pDSCapture = NULL;//DirectSoundCaptureDeviceオブジェクト
LPDIRECTSOUNDCAPTUREBUFFER g_pDSBCapture = NULL;//DirectSoundCaptureBufferオブジェクト
DSCBUFFERDESC dscbd;//DirectSound Capture Buffer DESC キャプチャ バッファを記述する構造体。
DWORD dwNextCaptureOffset=0; //次の読み込み開始位置を格納する変数。
DWORD dwReadPos; //読み込み可能カーソルのカレントポジ
DWORD dwCapturePos; //キャプチャカーソルのカレントポジ
void* pbCaptureData = NULL;//読み込む一つ目のブロックのポインタ
DWORD dwCaptureLength;//読み込む一つ目のブロックの長さ
void* pbCaptureData2 = NULL;//読み込む2つ目のブロックのポインタ
DWORD dwCaptureLength2;//読み込む2つ目のブロックの長さ
UINT dwDataWorte;
LONG lLockSize; //バッファから読み込むサイズ
short data1, data2;
/*********** 高精度時刻 ********/
LARGE_INTEGER freq;
LARGE_INTEGER prev;
LARGE_INTEGER start;
LARGE_INTEGER end;
LARGE_INTEGER now_time;
double prevtime = 0.0;
double endtime = 0.0;
/******************************/
WAVEFORMATEX wfx = {WAVE_FORMAT_PCM, 1, 44100, 44100, 1, 8, 0};
//単純なPCMのWAVEデータを定義。
//wFormatTag、Waveのフォーマット
//nChannels モノラル1 ステレオ2(データセットの種類)
//nSamplesPerSec 1秒あたりのサンプル数
//mAvgBytesPerSec、1秒あたりのバイト数。nSamplesPerSec*nBlockAlign。
//nBlockAlign 1サンプルのバイト数。nChannels×wBitsPerSample÷8 8・・・8ビット=1バイト
//wBitsPerSample 1サンプルあたりのビット数。8か16
//cbSize 常に0
int _tmain(int argc, _TCHAR* argv[])
{
// Initialize COM
CoInitialize(NULL);
// Create IDirectSoundCapture8 using the default capture device
DirectSoundCaptureCreate8( NULL, &g_pDSCapture, NULL );
dscbd.dwSize = sizeof(DSCBUFFERDESC);
dscbd.dwFlags = 0;
dscbd.dwBufferBytes = wfx.nAvgBytesPerSec*1;//ここでキャプチャバッファのサイズを設定している。
//今回の例では、1秒間分の容量バッファ。
dscbd.dwReserved = 0;
dscbd.lpwfxFormat = &wfx;
dscbd.dwFXCount = 0;
dscbd.lpDSCFXDesc = NULL;
g_pDSCapture->CreateCaptureBuffer(&dscbd,&g_pDSBCapture,NULL);
//バッファの作成まではできました。
//キャプチャバッファスタート。
g_pDSBCapture->Start(DSCBSTART_LOOPING);
//この命令でオーディオデバイス自体は音声入力を
//開始し、メモリに書き込み出す。ただし、現段階では、まだそのメモリにアクセス
//することはできない。
/********************
while(1){//ここでバッファの読み込みが可能になったかどうかのノーティファイをしないといけない。
if(){ //だけど、とりあえず、まだ未実装。。。
break;
}
}
/********************/
timeBeginPeriod(1); //Sleepの安定化
Sleep(1000);
timeEndPeriod(1);//Sleepの安定化の終了
//高精度時間計測
::QueryPerformanceFrequency(&freq);//クロック周波数ゲット
::QueryPerformanceCounter(&start); //スタートカウンタゲット
timeBeginPeriod(1); //Whileループの中でのスリープのための安定化処理
while(1)//データ取得ループ
{
//1サイクルの開始時間取得
::QueryPerformanceCounter(&prev);
//キャプチャカーソルと読み込み可能カーソルのカレントポジションを取る。
g_pDSBCapture->GetCurrentPosition(&dwCapturePos, &dwReadPos);
/************* 以下のコメントアウトした設定では、最後に読み込んだ位置から、
その時に読み込める範囲までを一気に読み込むことができるようなプログラムになっている。**
//バッファの中でロックを掛ける領域のサイズを取得する
lLockSize = dwReadPos-dwNextCaptureOffset;
if(lLockSize < 0){
//もし上記の計算でマイナスになったら.
//すなわち読み込み可能カーソルがバッファを一回りして、最初に戻ってるために
//CaptureOffsetと、ReadPosが入れ替わってたら。
lLockSize += dscbd.dwBufferBytes;
}
//キャプチャバッファの規定領域をロック
g_pDSBCapture->Lock(dwNextCaptureOffset, lLockSize, //ロックする領域の指定(スタート位置とサイズ)
&pbCaptureData, &dwCaptureLength, //一つ目のロック領域へのポインタとサイズ
&pbCaptureData2,&dwCaptureLength2//二つめのロック領域へのポインタとサイズ
NULL);
//バッファの循環性から、場合によってはロックされる範囲が二つに分断されることがあるため。
//つまり、バッファの後ろの方から読み込みを開始する場合には、読み込みたいメモリ領域が、
//バッファの後ろの方と、バッファの前の方で2つに分断される。
//このため、pbCaptureDataと、pbCaptureData2がある。
dwNextCaptureOffset += dwCaptureLength;
dwNextCaptureOffset %= dscbd.dwBufferBytes;//循環性への対応
//データの取得
data1=*((short*)pbCaptureData);//とりあえず試しにpbCaptureDataの直下データを読み込む。
//16ビット/サンプルに設定してるので、void*ポインタを
//short*でキャストする。
//もし、8ビット/サンプルやったら、当然char*でキャスト。
//なお読もうと思えば、*((short*)pbCapture+1)から、
//*((short*)pbCapture+dwCaptureLength))まで読める。
data2 = 0;
//2つに分断されてる場合に、後半を読み込む
if(pbCaptureData2!=NULL){
dwNextCaptureOffset += dwCaptureLength2;
dwNextCaptureOffset %= dscbd.dwBufferBytes;//循環性への対応
data1=*((short*)pbCaptureData2);//同じように、pbCaptureData2の直下データを読む。
}
//ロックの解除
g_pDSBCapture->Unlock(pbCaptureData, dwCaptureLength,
pbCaptureData2,dwCaptureLength2);
*************/
//読み込みカーソルの直下のデータのみを読み込もう。
g_pDSBCapture->Lock(dwReadPos, 1,
&pbCaptureData, &dwCaptureLength,
&pbCaptureData2,&dwCaptureLength2,
NULL);
data1=*((short*)pbCaptureData);
//1サイクルの時間取得
::QueryPerformanceCounter(&now_time);//1サイクルの終了時刻取得
prevtime = (double)((now_time.QuadPart-prev.QuadPart)*1000.0/freq.QuadPart);//時間取得
//検証処理
printf("処理時間:%f,データ:%d,読み込み位置:%d,ポインタ:%x\n",
prevtime,data1,dwReadPos,pbCaptureData);
//スリープ
::Sleep(10);
//while()ループを終了できるかどうかの処理。
::QueryPerformanceCounter(&end);
endtime = (double)((end.QuadPart-start.QuadPart)*1000/freq.QuadPart);
if(endtime > 30000 ){//30秒録音してOut
break;
}
}
//キャプチャバッファストップ
g_pDSBCapture->Stop();
//オブジェクトのリリース。まあ、DirectSoundオブジェクトの後始末。
g_pDSBCapture->Release();
g_pDSBCapture=NULL;
g_pDSCapture->Release();
g_pDSCapture=NULL;
CoUninitialize();
return 0;
}
---------------------------------------------------------
内容は、
パソコンにつないだマイクから、音声を拾って、
その音声データ(数値データ)の中身を0.01秒ごとにコンソールに書き出すプログラム。
音声のサンプリングされた数値データが得られるので、
その数値を使って閾値処理なんかを掛けると簡単な音声認識ができたりします。
また、もう少し組み込めば、高速フーリエ変換なんかも可能でしょう。
詳しくは、DirectX Ver.9のヘルプのDirectSoundの項を参照してください。
まあ、Googleなんかで検索を掛けた誰かが
何かの参考にして頂ければ幸いということで「公開」で書きます。
----------------------------------------------------------------------------
Visual C++ Express Edition と DirectX Ver.9を使った音声情報処理。
(準備編)
1.まず、それぞれをマイクロソフトのHPからダウンロードしてインストール。
2.DirectXのインクルードファイルとライブラリファイルの収められているフォルダのパスを通す。
VC++を立ち上げて、メニューバーの「ツール」→「オプション」をクリック。
立ち上がるダイアログの左側のツリーの一番上の
(デフォルトでは、スライドバーを動かさないと見えないみたい)
「プロジェクトおよびソリューション」を展開して「VC++ディレクトリ」をクリック。
次いで右上の「ディレクトリを表示するプロジェクト」をクリックして、
「インクルードファイル」をクリック。
そいでから、下のボックスに表示されているインクルードファイルの探索パスの中に
DirectXのインクルードファイルが収められたフォルダのパスを追加。
同じように、
「ライブラリファイル」も、DirectXのライブラリファイルのフォルダのパスを追加する。
(作成)
1.メニュー→新規作成→プロジェクト作成。 とりあえず、簡単のために、今回はコンソールアプリケーションを作成。
2.できたプロジェクトにDSound.libをリンクする。
ソリューション・エクスプローラ(デフォルトで表示されているはず)のプロジェクト名のところで
右クリック→「プロパティ」。
左側のツリーボックスで、「構成プロパティ」→「リンカ」と展開して、「コマンドライン」をクリック。
右側下段のエディットボックスに、DSound.libと追記。
※ なお、今回は、ミリ秒単位でのタイマも使うために、winmm.libも追記。
ちなみに追記のときは、スペースで区切る。
(ソース)
最終的にこんなプログラムを書きました。
--------------------------------------------------------------------------
#include "stdafx.h"
#include "Dsound.h"
#include <time.h>
#include <mmsystem.h>
LPDIRECTSOUNDCAPTURE g_pDSCapture = NULL;//DirectSoundCaptureDeviceオブジェクト
LPDIRECTSOUNDCAPTUREBUFFER g_pDSBCapture = NULL;//DirectSoundCaptureBufferオブジェクト
DSCBUFFERDESC dscbd;//DirectSound Capture Buffer DESC キャプチャ バッファを記述する構造体。
DWORD dwNextCaptureOffset=0; //次の読み込み開始位置を格納する変数。
DWORD dwReadPos; //読み込み可能カーソルのカレントポジ
DWORD dwCapturePos; //キャプチャカーソルのカレントポジ
void* pbCaptureData = NULL;//読み込む一つ目のブロックのポインタ
DWORD dwCaptureLength;//読み込む一つ目のブロックの長さ
void* pbCaptureData2 = NULL;//読み込む2つ目のブロックのポインタ
DWORD dwCaptureLength2;//読み込む2つ目のブロックの長さ
UINT dwDataWorte;
LONG lLockSize; //バッファから読み込むサイズ
short data1, data2;
/*********** 高精度時刻 ********/
LARGE_INTEGER freq;
LARGE_INTEGER prev;
LARGE_INTEGER start;
LARGE_INTEGER end;
LARGE_INTEGER now_time;
double prevtime = 0.0;
double endtime = 0.0;
/******************************/
WAVEFORMATEX wfx = {WAVE_FORMAT_PCM, 1, 44100, 44100, 1, 8, 0};
//単純なPCMのWAVEデータを定義。
//wFormatTag、Waveのフォーマット
//nChannels モノラル1 ステレオ2(データセットの種類)
//nSamplesPerSec 1秒あたりのサンプル数
//mAvgBytesPerSec、1秒あたりのバイト数。nSamplesPerSec*nBlockAlign。
//nBlockAlign 1サンプルのバイト数。nChannels×wBitsPerSample÷8 8・・・8ビット=1バイト
//wBitsPerSample 1サンプルあたりのビット数。8か16
//cbSize 常に0
int _tmain(int argc, _TCHAR* argv[])
{
// Initialize COM
CoInitialize(NULL);
// Create IDirectSoundCapture8 using the default capture device
DirectSoundCaptureCreate8( NULL, &g_pDSCapture, NULL );
dscbd.dwSize = sizeof(DSCBUFFERDESC);
dscbd.dwFlags = 0;
dscbd.dwBufferBytes = wfx.nAvgBytesPerSec*1;//ここでキャプチャバッファのサイズを設定している。
//今回の例では、1秒間分の容量バッファ。
dscbd.dwReserved = 0;
dscbd.lpwfxFormat = &wfx;
dscbd.dwFXCount = 0;
dscbd.lpDSCFXDesc = NULL;
g_pDSCapture->CreateCaptureBuffer(&dscbd,&g_pDSBCapture,NULL);
//バッファの作成まではできました。
//キャプチャバッファスタート。
g_pDSBCapture->Start(DSCBSTART_LOOPING);
//この命令でオーディオデバイス自体は音声入力を
//開始し、メモリに書き込み出す。ただし、現段階では、まだそのメモリにアクセス
//することはできない。
/********************
while(1){//ここでバッファの読み込みが可能になったかどうかのノーティファイをしないといけない。
if(){ //だけど、とりあえず、まだ未実装。。。
break;
}
}
/********************/
timeBeginPeriod(1); //Sleepの安定化
Sleep(1000);
timeEndPeriod(1);//Sleepの安定化の終了
//高精度時間計測
::QueryPerformanceFrequency(&freq);//クロック周波数ゲット
::QueryPerformanceCounter(&start); //スタートカウンタゲット
timeBeginPeriod(1); //Whileループの中でのスリープのための安定化処理
while(1)//データ取得ループ
{
//1サイクルの開始時間取得
::QueryPerformanceCounter(&prev);
//キャプチャカーソルと読み込み可能カーソルのカレントポジションを取る。
g_pDSBCapture->GetCurrentPosition(&dwCapturePos, &dwReadPos);
/************* 以下のコメントアウトした設定では、最後に読み込んだ位置から、
その時に読み込める範囲までを一気に読み込むことができるようなプログラムになっている。**
//バッファの中でロックを掛ける領域のサイズを取得する
lLockSize = dwReadPos-dwNextCaptureOffset;
if(lLockSize < 0){
//もし上記の計算でマイナスになったら.
//すなわち読み込み可能カーソルがバッファを一回りして、最初に戻ってるために
//CaptureOffsetと、ReadPosが入れ替わってたら。
lLockSize += dscbd.dwBufferBytes;
}
//キャプチャバッファの規定領域をロック
g_pDSBCapture->Lock(dwNextCaptureOffset, lLockSize, //ロックする領域の指定(スタート位置とサイズ)
&pbCaptureData, &dwCaptureLength, //一つ目のロック領域へのポインタとサイズ
&pbCaptureData2,&dwCaptureLength2//二つめのロック領域へのポインタとサイズ
NULL);
//バッファの循環性から、場合によってはロックされる範囲が二つに分断されることがあるため。
//つまり、バッファの後ろの方から読み込みを開始する場合には、読み込みたいメモリ領域が、
//バッファの後ろの方と、バッファの前の方で2つに分断される。
//このため、pbCaptureDataと、pbCaptureData2がある。
dwNextCaptureOffset += dwCaptureLength;
dwNextCaptureOffset %= dscbd.dwBufferBytes;//循環性への対応
//データの取得
data1=*((short*)pbCaptureData);//とりあえず試しにpbCaptureDataの直下データを読み込む。
//16ビット/サンプルに設定してるので、void*ポインタを
//short*でキャストする。
//もし、8ビット/サンプルやったら、当然char*でキャスト。
//なお読もうと思えば、*((short*)pbCapture+1)から、
//*((short*)pbCapture+dwCaptureLength))まで読める。
data2 = 0;
//2つに分断されてる場合に、後半を読み込む
if(pbCaptureData2!=NULL){
dwNextCaptureOffset += dwCaptureLength2;
dwNextCaptureOffset %= dscbd.dwBufferBytes;//循環性への対応
data1=*((short*)pbCaptureData2);//同じように、pbCaptureData2の直下データを読む。
}
//ロックの解除
g_pDSBCapture->Unlock(pbCaptureData, dwCaptureLength,
pbCaptureData2,dwCaptureLength2);
*************/
//読み込みカーソルの直下のデータのみを読み込もう。
g_pDSBCapture->Lock(dwReadPos, 1,
&pbCaptureData, &dwCaptureLength,
&pbCaptureData2,&dwCaptureLength2,
NULL);
data1=*((short*)pbCaptureData);
//1サイクルの時間取得
::QueryPerformanceCounter(&now_time);//1サイクルの終了時刻取得
prevtime = (double)((now_time.QuadPart-prev.QuadPart)*1000.0/freq.QuadPart);//時間取得
//検証処理
printf("処理時間:%f,データ:%d,読み込み位置:%d,ポインタ:%x\n",
prevtime,data1,dwReadPos,pbCaptureData);
//スリープ
::Sleep(10);
//while()ループを終了できるかどうかの処理。
::QueryPerformanceCounter(&end);
endtime = (double)((end.QuadPart-start.QuadPart)*1000/freq.QuadPart);
if(endtime > 30000 ){//30秒録音してOut
break;
}
}
//キャプチャバッファストップ
g_pDSBCapture->Stop();
//オブジェクトのリリース。まあ、DirectSoundオブジェクトの後始末。
g_pDSBCapture->Release();
g_pDSBCapture=NULL;
g_pDSCapture->Release();
g_pDSCapture=NULL;
CoUninitialize();
return 0;
}
---------------------------------------------------------
内容は、
パソコンにつないだマイクから、音声を拾って、
その音声データ(数値データ)の中身を0.01秒ごとにコンソールに書き出すプログラム。
音声のサンプリングされた数値データが得られるので、
その数値を使って閾値処理なんかを掛けると簡単な音声認識ができたりします。
また、もう少し組み込めば、高速フーリエ変換なんかも可能でしょう。
詳しくは、DirectX Ver.9のヘルプのDirectSoundの項を参照してください。