Julius 4.2
|
00001 00011 #include <julius/juliuslib.h> 00012 #include "Julius.h" 00013 #include "process.h" 00014 00015 // ----------------------------------------------------------------------------- 00016 // JuliusLib callback functions 00017 // 00018 00019 #define JCALLBACK(A, B) static void A (Recog *recog, void *data) { cJulius *j = (cJulius *) data; PostMessage(j->getWindow(), WM_JULIUS, B, 0L);} 00020 00021 JCALLBACK(callback_engine_active, JEVENT_ENGINE_ACTIVE) 00022 JCALLBACK(callback_engine_inactive, JEVENT_ENGINE_INACTIVE) 00023 JCALLBACK(callback_audio_ready, JEVENT_AUDIO_READY) 00024 JCALLBACK(callback_audio_begin, JEVENT_AUDIO_BEGIN) 00025 JCALLBACK(callback_audio_end, JEVENT_AUDIO_END) 00026 JCALLBACK(callback_recog_begin, JEVENT_RECOG_BEGIN) 00027 JCALLBACK(callback_recog_end, JEVENT_RECOG_END) 00028 JCALLBACK(callback_recog_frame, JEVENT_RECOG_FRAME) 00029 JCALLBACK(callback_engine_pause, JEVENT_ENGINE_PAUSE) 00030 JCALLBACK(callback_engine_resume, JEVENT_ENGINE_RESUME) 00031 00032 // callback to get result 00033 static void callback_result_final(Recog *recog, void *data) 00034 { 00035 cJulius *j = (cJulius *)data; 00036 00037 int i; 00038 WORD_INFO *winfo; 00039 WORD_ID *seq; 00040 int seqnum; 00041 Sentence *s; 00042 RecogProcess *r; 00043 static char str[2048]; 00044 static wchar_t wstr[2048]; 00045 size_t size = 0; 00046 WPARAM wparam = 0; 00047 _locale_t locale; 00048 unsigned int code; 00049 00050 r = j->getRecog()->process_list; 00051 if (! r->live) return; 00052 00053 if (r->result.status < 0) { /* no results obtained */ 00054 00055 switch(r->result.status) { 00056 case J_RESULT_STATUS_REJECT_POWER: 00057 strcpy(str, "<input rejected by power>"); 00058 break; 00059 case J_RESULT_STATUS_TERMINATE: 00060 strcpy(str, "<input teminated by request>"); 00061 break; 00062 case J_RESULT_STATUS_ONLY_SILENCE: 00063 strcpy(str, "<input rejected by decoder (silence input result)>"); 00064 break; 00065 case J_RESULT_STATUS_REJECT_GMM: 00066 strcpy(str, "<input rejected by GMM>"); 00067 break; 00068 case J_RESULT_STATUS_REJECT_SHORT: 00069 strcpy(str, "<input rejected by short input>"); 00070 break; 00071 case J_RESULT_STATUS_FAIL: 00072 strcpy(str, "<search failed>"); 00073 break; 00074 } 00075 code = (- r->result.status); 00076 00077 } else { 00078 00079 winfo = r->lm->winfo; 00080 s = &(r->result.sent[0]); 00081 seq = s->word; 00082 seqnum = s->word_num; 00083 str[0] = '\0'; 00084 for(i=0;i<seqnum;i++) strcat(str, winfo->woutput[seq[i]]); 00085 code = 0; 00086 } 00087 00088 // convert to wide char 00089 //mbstowcs_s( &size, wstr, str, strlen(str)+1); 00090 locale = j->getModelLocale(); 00091 if (locale) _mbstowcs_s_l( &size, wstr, str, strlen(str)+1, locale); 00092 else mbstowcs_s( &size, wstr, str, strlen(str)+1); 00093 00094 00095 // set status parameter 00096 wparam = (code << 16) + JEVENT_RESULT_FINAL; 00097 00098 // send message 00099 PostMessage(j->getWindow(), WM_JULIUS, wparam, (LPARAM)wstr); 00100 } 00101 00102 // callbackk for pause 00103 static void callback_wait_for_resume(Recog *recog, void *data) 00104 { 00105 cJulius *j = (cJulius *)data; 00106 // Stop running the engine thread 00107 SuspendThread( j->getThreadHandle() ); 00108 // the engine thread will wait until the main thread calls ResumeThread() for it 00109 } 00110 00111 #ifdef APP_ADIN 00112 static int callback_adin_fetch_input(SP16 *sampleBuffer, int reqlen) 00113 { 00114 // If shared audio buffer has some new data, or some data remains from the last call, 00115 // get the samples into sampleBuffer at most reqlen length. 00116 } 00117 #endif 00118 00119 // main function for the engine thread 00120 unsigned __stdcall recogThreadMain( void *param ) 00121 { 00122 int ret; 00123 Recog *recog = (Recog *)param; 00124 ret = j_recognize_stream(recog); 00125 _endthreadex(ret); 00126 return(ret); 00127 } 00128 00129 //----------------------------------------------------------------------------------------- 00130 // Julius class definition 00131 //----------------------------------------------------------------------------------------- 00132 00133 //============= 00134 // Constructor 00135 //============= 00136 cJulius::cJulius( void ) : m_jconf( NULL ), m_recog( NULL ), m_opened( false ), m_threadHandle( NULL ), m_fpLogFile( NULL ), m_modelLocale( NULL ) 00137 { 00138 #ifdef APP_ADIN 00139 m_appsource = 0; 00140 #endif 00141 setLogFile( "juliuslog.txt" ); 00142 } 00143 00144 //============ 00145 // Destructor 00146 //============ 00147 cJulius::~cJulius( void ) 00148 { 00149 release(); 00150 if ( m_fpLogFile ) { 00151 fclose(m_fpLogFile); 00152 m_fpLogFile = NULL; 00153 } 00154 } 00155 00156 //================================= 00157 // Initialize, with argument array 00158 //================================= 00159 bool cJulius::initialize( int argnum, char *argarray[]) 00160 { 00161 bool ret; 00162 00163 release(); 00164 00165 m_jconf = j_config_load_args_new( argnum, argarray ); 00166 00167 if (m_jconf == NULL) { /* error */ 00168 return false; 00169 } 00170 00171 ret = createEngine(); 00172 00173 return ret; 00174 } 00175 00176 //=============================== 00177 // Initialize, with a Jconf file 00178 //=============================== 00179 bool cJulius::initialize( char *filename ) 00180 { 00181 bool ret; 00182 00183 release(); 00184 00185 if (! loadJconf( filename )) return false; 00186 00187 ret = createEngine(); 00188 00189 return ret; 00190 } 00191 00192 //=================== 00193 // Load a Jconf file 00194 //=================== 00195 bool cJulius::loadJconf( char *filename ) 00196 { 00197 if (m_jconf) { 00198 if (j_config_load_file(m_jconf, filename) == -1) { 00199 return false; 00200 } 00201 } else { 00202 m_jconf = j_config_load_file_new( filename ); 00203 if (m_jconf == NULL) { /* error */ 00204 return false; 00205 } 00206 } 00207 return true; 00208 } 00209 00210 //============================================== 00211 // Set a log file to save Julius engine outputs 00212 //============================================== 00213 void cJulius::setLogFile( const char *filename ) 00214 { 00215 if (m_fpLogFile) { 00216 fclose( m_fpLogFile ); 00217 } 00218 m_fpLogFile = fopen( filename, "w" ); 00219 if (m_fpLogFile) { 00220 jlog_set_output( m_fpLogFile ); 00221 } 00222 } 00223 00224 //====================== 00225 // set locale of the LM 00226 //====================== 00227 void cJulius::setModelLocale( const char *locale ) 00228 { 00229 m_modelLocale = _create_locale( LC_CTYPE, locale); 00230 } 00231 00232 //======================================== 00233 // return current model locale for output 00234 //======================================== 00235 _locale_t cJulius::getModelLocale( void ) 00236 { 00237 return( m_modelLocale ); 00238 } 00239 00240 //======================== 00241 // Create engine instance 00242 //======================== 00243 bool cJulius::createEngine( void ) 00244 { 00245 #ifdef APP_ADIN 00246 ADIn *a; 00247 #endif 00248 00249 if (!m_jconf) return false; 00250 if (m_recog) return false; 00251 00252 #ifdef APP_ADIN 00253 if (m_appsource != 0) { 00254 switch(m_appsource) { 00255 case 1: // buffer input, batch 00256 m_recog->jconf->input.type = INPUT_WAVEFORM; 00257 m_recog->jconf->input.speech_input = SP_RAWFILE; 00258 m_recog->jconf->decodeopt.realtime_flag = FALSE; 00259 break; 00260 case 2: // buffer input, incremental 00261 m_recog->jconf->input.type = INPUT_WAVEFORM; 00262 m_recog->jconf->input.speech_input = SP_RAWFILE; 00263 m_recog->jconf->decodeopt.realtime_flag = TRUE; 00264 break; 00265 } 00266 } 00267 #endif 00268 00269 // Create engine instance 00270 m_recog = j_create_instance_from_jconf(m_jconf); 00271 if (m_recog == NULL) { 00272 return false; 00273 } 00274 00275 // Register callbacks 00276 callback_add(m_recog, CALLBACK_EVENT_PROCESS_ONLINE, ::callback_engine_active, this); 00277 callback_add(m_recog, CALLBACK_EVENT_PROCESS_OFFLINE, ::callback_engine_inactive, this); 00278 callback_add(m_recog, CALLBACK_EVENT_SPEECH_READY, ::callback_audio_ready, this); 00279 callback_add(m_recog, CALLBACK_EVENT_SPEECH_START, ::callback_audio_begin, this); 00280 callback_add(m_recog, CALLBACK_EVENT_SPEECH_STOP, ::callback_audio_end, this); 00281 callback_add(m_recog, CALLBACK_EVENT_RECOGNITION_BEGIN, ::callback_recog_begin, this); 00282 callback_add(m_recog, CALLBACK_EVENT_RECOGNITION_END, ::callback_recog_end, this); 00283 callback_add(m_recog, CALLBACK_EVENT_PASS1_FRAME, ::callback_recog_frame, this); 00284 callback_add(m_recog, CALLBACK_EVENT_PAUSE, ::callback_engine_pause, this); 00285 callback_add(m_recog, CALLBACK_EVENT_RESUME, ::callback_engine_resume, this); 00286 callback_add(m_recog, CALLBACK_RESULT, ::callback_result_final, this); 00287 callback_add(m_recog, CALLBACK_PAUSE_FUNCTION, ::callback_wait_for_resume, this); 00288 00289 #ifdef APP_ADIN 00290 // Initialize application side audio input 00291 if (m_appsource != 0) { 00292 a = m_recog->adin; 00293 switch(m_appsource) { 00294 case 1: // buffer input, batch 00295 a->ad_standby = NULL; 00296 a->ad_begin = NULL; 00297 a->ad_end = NULL; 00298 a->ad_resume = NULL; 00299 a->ad_pause = NULL; 00300 a->ad_terminate = NULL; 00301 a->ad_read = callback_adin_fetch_input; 00302 a->ad_input_name = NULL; 00303 a->silence_cut_default = FALSE; 00304 a->enable_thread = FALSE; 00305 break; 00306 case 2: // buffer input, incremental 00307 a->ad_standby = NULL; 00308 a->ad_begin = NULL; 00309 a->ad_end = NULL; 00310 a->ad_resume = NULL; 00311 a->ad_pause = NULL; 00312 a->ad_terminate = NULL; 00313 a->ad_read = callback_adin_fetch_input; 00314 a->ad_input_name = NULL; 00315 a->silence_cut_default = FALSE; 00316 a->enable_thread = FALSE; 00317 break; 00318 } 00319 a->ds = NULL; 00320 a->down_sample = FALSE; 00321 if (adin_standby(a, m_recog->jconf->input.sfreq, NULL) == FALSE) return false; 00322 if (adin_setup_param(a, m_recog->jconf) == FALSE) return false; 00323 a->input_side_segment = FALSE; 00324 } else { 00325 // Let JuliusLib get audio input 00326 if (! j_adin_init( m_recog ) ) return false; 00327 } 00328 #else 00329 if (! j_adin_init( m_recog ) ) return false; 00330 #endif 00331 00332 return true; 00333 } 00334 00335 //============================================== 00336 // Open stream and start the recognition thread 00337 //============================================== 00338 bool cJulius::startProcess( HWND hWnd ) 00339 { 00340 00341 if ( ! m_recog ) return false; 00342 00343 // store window hanlder to send event message 00344 m_hWnd = hWnd; 00345 00346 if (m_opened == false) { 00347 // open device 00348 switch(j_open_stream(m_recog, NULL)) { 00349 case 0: /* succeeded */ 00350 break; 00351 case -1: /* error */ 00352 // fprintf(stderr, "error in input stream\n"); 00353 return false; 00354 case -2: /* end of recognition process */ 00355 //fprintf(stderr, "failed to begin input stream\n"); 00356 return false; 00357 } 00358 // create recognition thread 00359 m_threadHandle = (HANDLE)_beginthreadex(NULL, 0, ::recogThreadMain, m_recog, 0, NULL); 00360 if (m_threadHandle == 0) { 00361 j_close_stream(m_recog); 00362 return false; 00363 } 00364 SetThreadPriority(m_threadHandle, THREAD_PRIORITY_HIGHEST); 00365 00366 m_opened = true; 00367 } 00368 00369 return true; 00370 } 00371 00372 //================================================== 00373 // Close audio stream and detach recognition thread 00374 //================================================== 00375 void cJulius::stopProcess( void ) 00376 { 00377 00378 if ( ! m_recog ) return; 00379 00380 if (m_opened) { 00381 // recognition thread will exit when audio input is closed 00382 j_close_stream(m_recog); 00383 m_opened = false; 00384 } 00385 } 00386 00387 //=================== 00388 // Pause recognition 00389 //=================== 00390 void cJulius::pause( void ) 00391 { 00392 if ( ! m_recog ) return; 00393 // request library to pause 00394 // After pause, the recognition thread will issue pause event 00395 // and then enter callback_wait_for_resume(), where thread will pause. 00396 j_request_terminate(m_recog); 00397 } 00398 00399 //==================== 00400 // Resume recognition 00401 //==================== 00402 void cJulius::resume( void ) 00403 { 00404 if ( ! m_recog ) return; 00405 // request library to resume 00406 j_request_resume(m_recog); 00407 // resume the recognition thread 00408 ResumeThread( m_threadHandle ); 00409 } 00410 00411 //================== 00412 // Load DFA grammar 00413 //================== 00414 bool cJulius::loadGrammar( WORD_INFO *winfo, DFA_INFO *dfa, char *dictfile, char *dfafile, RecogProcess *r ) 00415 { 00416 boolean ret; 00417 00418 if ( ! m_recog ) return false; 00419 00420 // load grammar 00421 switch( r->lmvar ) { 00422 case LM_DFA_WORD: 00423 ret = init_wordlist(winfo, dictfile, r->lm->am->hmminfo, 00424 r->lm->config->wordrecog_head_silence_model_name, 00425 r->lm->config->wordrecog_tail_silence_model_name, 00426 (r->lm->config->wordrecog_silence_context_name[0] == '\0') ? NULL : r->lm->config->wordrecog_silence_context_name, 00427 r->lm->config->forcedict_flag); 00428 if (ret == FALSE) { 00429 return false; 00430 } 00431 break; 00432 case LM_DFA_GRAMMAR: 00433 ret = init_voca(winfo, dictfile, r->lm->am->hmminfo, FALSE, r->lm->config->forcedict_flag); 00434 if (ret == FALSE) { 00435 return false; 00436 } 00437 ret = init_dfa(dfa, dfafile); 00438 if (ret == FALSE) { 00439 return false; 00440 } 00441 break; 00442 } 00443 00444 return true; 00445 } 00446 00447 //================= 00448 // Add DFA grammar 00449 //================= 00450 bool cJulius::addGrammar( char *name, char *dictfile, char *dfafile, bool deleteAll ) 00451 { 00452 WORD_INFO *winfo; 00453 DFA_INFO *dfa; 00454 RecogProcess *r; 00455 boolean ret; 00456 00457 if ( ! m_recog ) return false; 00458 00459 r = m_recog->process_list; 00460 00461 // load grammar 00462 switch( r->lmvar ) { 00463 case LM_DFA_WORD: 00464 winfo = word_info_new(); 00465 dfa = NULL; 00466 ret = loadGrammar( winfo, NULL, dictfile, NULL, r ); 00467 if ( ! ret ) { 00468 word_info_free(winfo); 00469 return false; 00470 } 00471 break; 00472 case LM_DFA_GRAMMAR: 00473 winfo = word_info_new(); 00474 dfa = dfa_info_new(); 00475 ret = loadGrammar( winfo, dfa, dictfile, dfafile, r ); 00476 if ( ! ret ) { 00477 word_info_free(winfo); 00478 dfa_info_free(dfa); 00479 return false; 00480 } 00481 } 00482 if ( deleteAll ) { 00483 /* delete all existing grammars */ 00484 multigram_delete_all(r->lm); 00485 } 00486 /* register the new grammar to multi-gram tree */ 00487 multigram_add(dfa, winfo, name, r->lm); 00488 /* need to rebuild the global lexicon */ 00489 /* tell engine to update at requested timing */ 00490 schedule_grammar_update(m_recog); 00491 /* make sure this process will be activated */ 00492 r->active = 1; 00493 00494 PostMessage(getWindow(), WM_JULIUS, JEVENT_GRAM_UPDATE, 0L); 00495 00496 return true; 00497 } 00498 00499 //============== 00500 // Swap grammar 00501 //============== 00502 bool cJulius::changeGrammar( char *name, char *dictfile, char *dfafile ) 00503 { 00504 if ( ! m_recog ) return false; 00505 return addGrammar(name, dictfile, dfafile, true); 00506 } 00507 00508 //============================ 00509 // Delete grammar by its name 00510 //============================ 00511 bool cJulius::deleteGrammar( char *name ) 00512 { 00513 RecogProcess *r; 00514 int gid; 00515 00516 if ( ! m_recog ) return false; 00517 00518 r = m_recog->process_list; 00519 00520 gid = multigram_get_id_by_name(r->lm, name); 00521 if (gid == -1) return false; 00522 00523 if (multigram_delete(gid, r->lm) == FALSE) { /* deletion marking failed */ 00524 return false; 00525 } 00526 /* need to rebuild the global lexicon */ 00527 /* tell engine to update at requested timing */ 00528 schedule_grammar_update(m_recog); 00529 00530 PostMessage(getWindow(), WM_JULIUS, JEVENT_GRAM_UPDATE, 0L); 00531 00532 return true; 00533 } 00534 00535 //================================ 00536 // Deactivate grammar by its name 00537 //================================ 00538 bool cJulius::deactivateGrammar( char *name ) 00539 { 00540 RecogProcess *r; 00541 int gid; 00542 int ret; 00543 00544 if ( ! m_recog ) return false; 00545 00546 r = m_recog->process_list; 00547 00548 gid = multigram_get_id_by_name(r->lm, name); 00549 if (gid == -1) return false; 00550 00551 ret = multigram_deactivate(gid, r->lm); 00552 if (ret == 1) { 00553 /* already active */ 00554 return true; 00555 } else if (ret == -1) { 00556 /* not found */ 00557 return false; 00558 } 00559 /* tell engine to update at requested timing */ 00560 schedule_grammar_update(m_recog); 00561 PostMessage(getWindow(), WM_JULIUS, JEVENT_GRAM_UPDATE, 0L); 00562 00563 return true; 00564 } 00565 00566 //================================= 00567 // Re-activate grammar by its name 00568 //================================= 00569 bool cJulius::activateGrammar( char *name ) 00570 { 00571 RecogProcess *r; 00572 int gid; 00573 int ret; 00574 00575 if ( ! m_recog ) return false; 00576 00577 r = m_recog->process_list; 00578 00579 gid = multigram_get_id_by_name(r->lm, name); 00580 if (gid == -1) return false; 00581 00582 ret = multigram_activate(gid, r->lm); 00583 if (ret == 1) { 00584 /* already active */ 00585 return true; 00586 } else if (ret == -1) { 00587 /* not found */ 00588 return false; 00589 } 00590 /* tell engine to update at requested timing */ 00591 schedule_grammar_update(m_recog); 00592 PostMessage(getWindow(), WM_JULIUS, JEVENT_GRAM_UPDATE, 0L); 00593 00594 return true; 00595 } 00596 00597 //===================================== 00598 // Stop processes and release all data 00599 //===================================== 00600 void cJulius::release( void ) 00601 { 00602 00603 if ( ! m_recog ) return; 00604 00605 stopProcess(); 00606 00607 if (m_threadHandle) { 00608 CloseHandle(m_threadHandle); 00609 m_threadHandle = NULL; 00610 } else { 00611 if ( m_recog ) { 00612 j_recog_free( m_recog ); // jconf will be released inside this 00613 m_recog = NULL; 00614 m_jconf = NULL; 00615 } 00616 if ( m_jconf ) { 00617 j_jconf_free( m_jconf ); 00618 m_jconf = NULL; 00619 } 00620 } 00621 00622 }