2007年04月18日

SAPI Windows音声認識

何か、音声認識ネタが/.に出ていたので、昔書いた SAPI周りのやつでも上げとくか。
数年前に書いたやつなので、もう時代遅れになっている個所もあるかもしれんが何か役に立つといいかもね。




マイクから自由単語で認識するには?


USES_CONVERSION;

HRESULT hr;
CComPtr Engine; // 認識エンジンオブジェクト
CComPtr RecoCtxt; // 認識エンジンオブジェクトの作成

// CLSID_SpSharedRecognizer マイクから録音するとき?
// CLSID_SpInprocRecognizer Waveから変換するとき?
hr = Engine.CoCreateInstance(CLSID_SpSharedRecognizer);
if(FAILED(hr)) throw RComException(hr , "CLSID_SpSharedRecognizer 構築 に失敗");

// 認識コンテクストオブジェクトの作成

hr = Engine->CreateRecoContext(&RecoCtxt);

if(FAILED(hr)) throw RComException(hr , "CreateRecoContext に失敗");
hr = RecoCtxt->SetNotifyWin32Event();

if ( FAILED(hr) ) throw RComException(hr , "SetNotifyWin32Event に失敗");
hr = RecoCtxt->SetInterest(SPFEI(SPEI_RECOGNITION), SPFEI(SPEI_RECOGNITION));

if ( FAILED(hr) ) throw RComException(hr , "SetInterest に失敗");
hr = RecoCtxt->SetAudioOptions(SPAO_RETAIN_AUDIO, NULL, NULL);

if ( FAILED(hr) ) throw RComException(hr , "SetAudioOptions に失敗");
CComPtr dictationGrammar;

//メインとなる文法の作成
hr = RecoCtxt->CreateGrammar(0, &dictationGrammar);
if ( FAILED(hr) ) throw RComException(hr , "CreateGrammar に失敗");

hr = dictationGrammar->LoadDictation(NULL, SPLO_STATIC);
if ( FAILED(hr) ) throw RComException(hr , "LoadDictation に失敗");

//録音開始
hr = dictationGrammar->SetDictationState( SPRS_ACTIVE );
if ( FAILED(hr) ) throw RComException(hr , "SetDictationState に失敗");

puts("go!");
CSpEvent event;

//録音が終わるまで大待機
hr = RecoCtxt->WaitForNotifyEvent(INFINITE);
if ( FAILED(hr) ) throw RComException(hr , "WaitForNotifyEvent に失敗");

hr = event.GetFrom( RecoCtxt );
if ( FAILED(hr) ) throw RComException(hr , "GetFrom に失敗");

//認識した結果
ISpRecoResult* result;
result = event.RecoResult();

//認識した文字列の取得
CSpDynamicString dstrText;

hr = result->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE, &dstrText, NULL);
if ( FAILED(hr) ) throw RComException(hr , "録音したテキストの取得に失敗しました");
string ResultString = W2A(dstrText);

認識した結果が、ResultString に入ります。


wave から認識させるには?

CLSID_SpSharedRecognizer ではなくて、 CLSID_SpInprocRecognizer からオブジェクトを作成し、 BindToFileします。

USES_CONVERSION;
HRESULT hr;

CComPtr Engine; // 認識エンジンオブジェクト
CComPtr RecoCtxt;
// 認識エンジンオブジェクトの作成

// CLSID_SpSharedRecognizer マイクから録音するとき?
// CLSID_SpInprocRecognizer Waveから変換するとき?

/* これを
hr = Engine.CoCreateInstance(CLSID_SpSharedRecognizer);
if(FAILED(hr)) throw RComException(hr , "CLSID_SpSharedRecognizer 構築 に失敗");
*/
///こういう風に書き換える---------------------
CComPtr cpStream;
hr = Engine.CoCreateInstance(CLSID_SpInprocRecognizer); //CLSID_SpSharedRecognizerではないよ

if(FAILED(hr)) throw RComException(hr , "CLSID_SpInprocRecognizer 構築 に失敗");
hr = cpStream.CoCreateInstance(CLSID_SpStream);

if(FAILED(hr)) throw RComException(hr , "CoCreateInstance CLSID_SpStream に失敗");
hr = cpStream->BindToFile( L"c:\\test.wav" , SPFM_OPEN_READONLY , NULL , NULL, SPFEI_ALL_EVENTS);

if(FAILED(hr)) throw RComException(hr , "BindToFile に失敗");
hr = Engine->SetInput( cpStream, TRUE);

if(FAILED(hr)) throw RComException( Engine , CLSID_SpSharedRecognizer , hr , "SetInput に失敗");
/// ------------------------

// 認識コンテクストオブジェクトの作成

hr = Engine->CreateRecoContext(&RecoCtxt);

if(FAILED(hr)) throw RComException(hr , "CreateRecoContext に失敗");
hr = RecoCtxt->SetNotifyWin32Event();

if ( FAILED(hr) ) throw RComException(hr , "SetNotifyWin32Event に失敗");
hr = RecoCtxt->SetInterest(SPFEI(SPEI_RECOGNITION), SPFEI(SPEI_RECOGNITION));

if ( FAILED(hr) ) throw RComException(hr , "SetInterest に失敗");
hr = RecoCtxt->SetAudioOptions(SPAO_RETAIN_AUDIO, NULL, NULL);

if ( FAILED(hr) ) throw RComException(hr , "SetAudioOptions に失敗");


CComPtr dictationGrammar;
//メインとなる文法の作成

hr = RecoCtxt->CreateGrammar(0, &dictationGrammar);

if ( FAILED(hr) ) throw RComException(hr , "CreateGrammar に失敗");
hr = dictationGrammar->LoadDictation(NULL, SPLO_STATIC);

if ( FAILED(hr) ) throw RComException(hr , "LoadDictation に失敗");
//録音開始

hr = dictationGrammar->SetDictationState( SPRS_ACTIVE );

if ( FAILED(hr) ) throw RComException(hr , "SetDictationState に失敗");

puts("go!");
CSpEvent event;
//録音が終わるまで大待機

hr = RecoCtxt->WaitForNotifyEvent(INFINITE);

if ( FAILED(hr) ) throw RComException(hr , "WaitForNotifyEvent に失敗");
hr = event.GetFrom( RecoCtxt );

if ( FAILED(hr) ) throw RComException(hr , "GetFrom に失敗");
//認識した結果

ISpRecoResult* result;

result = event.RecoResult();
//認識した文字列の取得

CSpDynamicString dstrText;

hr = result->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE, &dstrText, NULL);

if ( FAILED(hr) ) throw RComException(hr , "録音したテキストの取得に失敗しました");

string ResultString = W2A(dstrText);



認識した結果が、ResultString に入ります。

音声認識のテストは、音がうるさいので、引きこもりが最も活動的になる夜にやりにいくですからねー。

録音した wav を使ってテストすれば、ご近所からも不審者と思われずに大丈夫です。



マイクからXMLで指定したルールにより認識するには?

USES_CONVERSION;
HRESULT hr;

CComPtr Engine; // 認識エンジンオブジェクト
CComPtr RecoCtxt;
// 認識エンジンオブジェクトの作成

// CLSID_SpSharedRecognizer マイクから録音するとき?
// CLSID_SpInprocRecognizer Waveから変換するとき?

hr = Engine.CoCreateInstance(CLSID_SpSharedRecognizer);

if(FAILED(hr)) throw RComException(hr , "CLSID_SpSharedRecognizer 構築 に失敗");

// 認識コンテクストオブジェクトの作成

hr = Engine->CreateRecoContext(&RecoCtxt);

if(FAILED(hr)) throw RComException(hr , "CreateRecoContext に失敗");
hr = RecoCtxt->SetNotifyWin32Event();

if ( FAILED(hr) ) throw RComException(hr , "SetNotifyWin32Event に失敗");
hr = RecoCtxt->SetInterest(SPFEI(SPEI_RECOGNITION), SPFEI(SPEI_RECOGNITION));

if ( FAILED(hr) ) throw RComException(hr , "SetInterest に失敗");
hr = RecoCtxt->SetAudioOptions(SPAO_RETAIN_AUDIO, NULL, NULL);

if ( FAILED(hr) ) throw RComException(hr , "SetAudioOptions に失敗");


CComPtr dictationGrammar;
//メインとなる文法の作成

hr = RecoCtxt->CreateGrammar(0, &dictationGrammar);

if ( FAILED(hr) ) throw RComException(hr , "CreateGrammar に失敗");
//録音開始

/*これを
hr = dictationGrammar->SetDictationState( SPRS_ACTIVE );
if ( FAILED(hr) ) throw RComException(hr , "SetDictationState に失敗");
*/

///こういう風に書き換える---------------------


//ユーザ指定ファイルからのロード

hr = dictationGrammar->LoadCmdFromFile( L"c:\\test.xml" ,SPLO_STATIC);

if ( FAILED(hr) ) throw RComException(hr , "LoadCmdFromFile に失敗");
//録音開始

hr = dictationGrammar->SetRuleState(NULL, NULL, SPRS_ACTIVE );

if ( FAILED(hr) ) throw RComException(hr , "SetRuleState に失敗");

////--------------------------------------




puts("go!");
CSpEvent event;
//録音が終わるまで大待機

hr = RecoCtxt->WaitForNotifyEvent(INFINITE);

if ( FAILED(hr) ) throw RComException(hr , "WaitForNotifyEvent に失敗");
hr = event.GetFrom( RecoCtxt );

if ( FAILED(hr) ) throw RComException(hr , "GetFrom に失敗");
//認識した結果

ISpRecoResult* result;

result = event.RecoResult();
//認識した文字列の取得

CSpDynamicString dstrText;

hr = result->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE, &dstrText, NULL);

if ( FAILED(hr) ) throw RComException(hr , "録音したテキストの取得に失敗しました");

string ResultString = W2A(dstrText);




//追加------------------------------

//認識に XMLを使用した場合、代入された結果を得る.

map
ResultMap; //これに結果を代入する

SPPHRASE *pPhrase;

event.RecoResult()->GetPhrase(&pPhrase);

const SPPHRASEPROPERTY *pProp;

for (pProp = pPhrase->pProperties; pProp; pProp = pProp->pNextSibling)

{

string a = W2A(pProp->pszName);

ResultMap[ W2A(pProp->pszName) ] = W2A(pProp->pszValue);

}

CoTaskMemFree(pPhrase);

///---------------------------------

認識した結果が、ResultString に入ります。
また、 XML で指定した名前と結果の配列が ResultMap に格納されます。
PHP でいう _POST みたいな感じで格納されるといえばわかりやすいでしょうか。

ここら辺は、http://julius.sourceforge.jp/sapi/index.html を参考にしました。
ありがとうございました。

マイクから自由単語で認識した結果を wav で保存するには?

USES_CONVERSION;
HRESULT hr;

CComPtr Engine; // 認識エンジンオブジェクト
CComPtr RecoCtxt;
// 認識エンジンオブジェクトの作成

// CLSID_SpSharedRecognizer マイクから録音するとき?
// CLSID_SpInprocRecognizer Waveから変換するとき?

hr = Engine.CoCreateInstance(CLSID_SpSharedRecognizer);

if(FAILED(hr)) throw RComException(hr , "CLSID_SpSharedRecognizer 構築 に失敗");

// 認識コンテクストオブジェクトの作成

hr = Engine->CreateRecoContext(&RecoCtxt);

if(FAILED(hr)) throw RComException(hr , "CreateRecoContext に失敗");
hr = RecoCtxt->SetNotifyWin32Event();

if ( FAILED(hr) ) throw RComException(hr , "SetNotifyWin32Event に失敗");
hr = RecoCtxt->SetInterest(SPFEI(SPEI_RECOGNITION), SPFEI(SPEI_RECOGNITION));

if ( FAILED(hr) ) throw RComException(hr , "SetInterest に失敗");
hr = RecoCtxt->SetAudioOptions(SPAO_RETAIN_AUDIO, NULL, NULL);

if ( FAILED(hr) ) throw RComException(hr , "SetAudioOptions に失敗");


CComPtr dictationGrammar;
//メインとなる文法の作成

hr = RecoCtxt->CreateGrammar(0, &dictationGrammar);
if ( FAILED(hr) ) throw RComException(hr , "CreateGrammar に失敗");

hr = dictationGrammar->LoadDictation(NULL, SPLO_STATIC);
if ( FAILED(hr) ) throw RComException(hr , "LoadDictation に失敗");

//録音開始
hr = dictationGrammar->SetDictationState( SPRS_ACTIVE );
if ( FAILED(hr) ) throw RComException(hr , "SetDictationState に失敗");

puts("go!");
CSpEvent event;

//録音が終わるまで大待機
hr = RecoCtxt->WaitForNotifyEvent(INFINITE);
if ( FAILED(hr) ) throw RComException(hr , "WaitForNotifyEvent に失敗");

hr = event.GetFrom( RecoCtxt );
if ( FAILED(hr) ) throw RComException(hr , "GetFrom に失敗");

//認識した結果

ISpRecoResult* result;

result = event.RecoResult();
//認識した文字列の取得

CSpDynamicString dstrText;
hr = result->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE, &dstrText, NULL);
if ( FAILED(hr) ) throw RComException(hr , "録音したテキストの取得に失敗しました");

string ResultString = W2A(dstrText);


//追加------------------------------

CComPtr ResultStream;
CComPtr voice;

hr = RecoCtxt->GetVoice(&voice);
if(FAILED(hr)) throw RComException(hr , "GetVoice に失敗");

hr = event.RecoResult()->GetAudio( 0, 0, &ResultStream );
if ( FAILED(hr) ) throw RComException(hr , "GetAudio に失敗しました");

CComPtr cpWavStream;
CComPtr cpOldStream;
CSpStreamFormat OriginalFmt;
voice->GetOutputStream( &cpOldStream );
OriginalFmt.AssignFormat(cpOldStream);

hr = SPBindToFile( L"C:\\output.wav",SPFM_CREATE_ALWAYS, &cpWavStream,&OriginalFmt.FormatId(), OriginalFmt.WaveFormatExPtr() )
voice->SetOutput(cpWavStream,TRUE);

hr = voice->SpeakStream( ResultStream , SPF_DEFAULT, 0 );
if ( FAILED(hr) ) throw RComException(hr , "SpeakStream に失敗しました");
///---------------------------------


あなたがコンピュータさまに、話し掛けたキモイ声が、 C:\output.wav に保存されます。




WindowsXPの人は SAPI 5.1 ぐらいが入っているから何もしなくてもいいけど、
Windows2Kの人は、 SAPIが入っていない?? っぽいんで、
http://www.microsoft.com/downloads/details.aspx?FamilyId=5E86EC97-40A7-453F-B0EE-6583171B4530&displaylang=en#filelist
からダウンロードして入れるといいよ。

上のドキュメント書いているときは、 SAPI4 で試しているので、5で動くかは謎。後で確認するかも。
posted by rti at 17:08| Comment(1) | TrackBack(0) | 日記 | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
SAPI4.0を利用してwaveファイルから音声認識をするコンソールプログラムを作成したいと考えています。
なんか3さんの「wave から認識させるには?」を参考にさせていただこうと思ったのですが、知識がないために、プログラム言語として何を利用しているのか、また、どうやったら実行できるのかが分かりません。ご教授いただけると幸いです。よろしくお願い申し上げます。
Posted by Hiy at 2008年02月20日 16:33
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

この記事へのトラックバックURL
http://blog.seesaa.jp/tb/39248968

この記事へのトラックバック