Thursday, January 7, 2010 9:46 PM
pebbie
Menghubungkan Speech SDK dengan GLUT
Mengawali tahun baru 2010, saya ingin bercerita tentang eksperimen yang dikerjakan selama liburan tahun baru yang lalu.
GLUT (OpenGL Utility Toolkit) merupakan library lintas platform yang membungkus fungsi-fungsi windowing dan interaksi untuk aplikasi yang berbasis openGL. Jika tidak menggunakan GLUT maka di Win32 kita harus menggunakan MFC atau memanfaatkan Win32 API (mendaftarkan window class, WndProc, membuat OpenGL Context, dll). Karena pada Visual C++ 2008 express tidak ada MFC dan ATL maka pilihan tinggal pada WIn32 API, dan tersedianya GLUT akan memudahkan pengembangan aplikasi. Esensi glut sebetulnya adalah membuat abstraksi pembuatan window dan mekanisme interaksi pada beberapa platform tanpa harus banyak mengubah kode sehingga akses untuk hal-hal yang spesifik platform tidak bisa diakses secara langsung. Sampai ketika dihadapkan pada kasus yang mengharuskan menghubungkan antara MS Speech SDK dengan Visualisasi animasi yang berbasis OpenGL (menggunakan GLUT), pendek kata : memvisualisasikan animasi wajah dengan kata yang diucapkan oleh engine TTS (the good ol' microsoft Sam LoL). Agar kita mengetahui gerak bibir (viseme) maka kita harus mendaftarkan window handle (HWND) ke komponen TTS (SpVoice) sehingga komponen tersebut dapat mengirimkan Window Message tertentu yang berisi informasi tentang viseme yang sedang dan akan diputar. duh, banyak sekali basa-basinya. mari kita mulai terjun ke kode.
Menggunakan Komponen TTS dari MS Speech SDK
#include <windows.h>
#include <sapi.h>
//...
#define WM_TTSAPPCUSTOMEVENT WM_APP // Window message used for systhesis events
//...
//tts engine
ISpVoice *spVoice;
//required variables
HWND hWnd;
WNDPROC currentWndProc;
int main(int argc, char* argv[])
{
CoInitialize( NULL );
//create ttsengine
if ( SUCCEEDED( CoCreateInstance( CLSID_SpVoice , NULL, CLSCTX_ALL, IID_ISpVoice, (void**)&spVoice ) ) ){
//tanda -*- akan digunakan sebagai penanda penyisipan kode berikutnya
//-*-
//talk now
spVoice->Speak(L"Hello World!", SVSFDefault);
spVoice->Release();
spVoice = NULL;
}
CoUninitialize();
return EXIT_SUCCESS;
}
Menerima event tiap viseme diucapkan
agar dapat menerima setiap viseme yang diucapkan, maka kita mendaftarkan window handle yang akan menerima message dan message apa yang akan digunakan.
//-*-
//register for notification
spVoice->SetInterest( SPFEI(SPEI_VISEME), SPFEI(SPEI_VISEME));
spVoice->SetNotifyWindowMessage( hWnd, WM_TTSAPPCUSTOMEVENT, 0, 0 );
Persoalannya berikutnya adalah: "jika kita menggunakan glut, bagaimana mendapatkan handle window dan bagaimana menangani message yang dikirim dari komponen Voice tersebut?"
agar dapat menjawab pertanyaan tersebut maka kita perlu melakukan beberapa trik berikut:
//hook glut wndproc
hWnd = FindWindow(L"GLUT", NULL);
currentWndProc = (WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC);
SetWindowLong(hWnd, GWL_WNDPROC, (long)winProc);
//-*-
Ya, kita perlu membuat fungsi WindowProc baru yang digunakan pada window buatan GLUT, memroses pesan yang kita perlukan lalu mengembalikan kembali ke prosedur WndProc aslinya (seperti virus jaman DOS yang meng-intercept interrupt vector, hahaha). Untuk itu, diperlukan fungsi winProc (sesuai definisi di atas) sbb:
//WindowProc Hook to handle TTS Event
LRESULT CALLBACK winProc(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg)
{
case WM_TTSAPPCUSTOMEVENT:
SPEVENT eventItem;
memset( &eventItem, 0,sizeof(SPEVENT));
while( spVoice->GetEvents(1, &eventItem, NULL ) == S_OK )
{
switch(eventItem.eEventId )
{
case SPEI_VISEME :
cur_frame = VisemeToFrame[LOWORD(eventItem.lParam)];//current viseme id
next_frame = VisemeToFrame[LOWORD(eventItem.wParam)];//next viseme id
anim_duration = HIWORD(eventItem.wParam);//this viseme duration in ms
cur_time = GetTickCount();
last_time = cur_time;
break;
default:
break;
}
//SpClearEvent( &eventItem );
}
break;
default:
return currentWndProc(hwnd, Msg, wParam, lParam);//pass the message to the original handler
}
return 0;
}
hope this helps!
Filed under: Win32, Speech SDK, TTS