Julius 4.2
msvc/SampleApp/Julius.cpp
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 }