julius/module.c

Go to the documentation of this file.
00001 
00017 /*
00018  * Copyright (c) 1991-2006 Kawahara Lab., Kyoto University
00019  * Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology
00020  * Copyright (c) 2005-2006 Julius project team, Nagoya Institute of Technology
00021  * All rights reserved
00022  */
00023 
00024 #include <julius.h>
00025 #include <stdarg.h>
00026 
00027 #define MAXBUFLEN 4096 
00028 static int listen_sd = -1;      
00029 static char mbuf[MAXBUFLEN];    
00030 
00031 
00043 static boolean process_active = TRUE;
00044 
00050 static boolean process_want_terminate = FALSE;
00051 
00059 static boolean process_want_reload = FALSE;
00060 
00061 #ifdef USE_DFA
00063 enum{SM_TERMINATE, SM_PAUSE, SM_WAIT};
00065 static short gram_switch_input_method = SM_PAUSE;
00066 #endif
00067 
00088 void
00089 main_module_loop()
00090 {
00091 #if !defined(_WIN32) || defined(__CYGWIN32__)
00092   pid_t cid;
00093 #endif
00094   
00095   /* prepare socket to listen */
00096   if ((listen_sd = ready_as_server(module_port)) < 0) {
00097     j_error("Error: failed to bind socket\n");
00098   }
00099 
00100   j_printf  ("///////////////////////////////\n");
00101   j_printf  ("///  Module mode ready\n");
00102   j_printf  ("///  waiting client at %5d\n", module_port);
00103   j_printf  ("///////////////////////////////\n");
00104   j_printf  ("///  ");
00105 
00106 #if defined(_WIN32) && !defined(__CYGWIN32__)
00107   
00108   /* Win32: no fork, just wait for one connection and proceed */
00109   if ((module_sd = accept_from(listen_sd)) < 0) {
00110     j_error("Error: failed to accept connection\n");
00111   }
00112   /* call main recognition loop here */
00113   main_recognition_loop();
00114  
00115 #else
00116 
00117   /* server loop */
00118   for(;;) {
00119     /* accept connection from a client */
00120     if ((module_sd = accept_from(listen_sd)) < 0) {
00121       j_error("Error: failed to accept connection\n");
00122     }
00123     /* fork this process */
00124     if ((cid = fork()) != 0) {
00125       /* parent process */
00126       j_printf("///  forked [%d]\n", cid);
00127       close_socket(module_sd); /* parent not need socket to the client */
00128       continue;
00129     }
00130     /* child not need listen socket */
00131     close_socket(listen_sd);
00132     /* call main recognition loop here */
00133     main_recognition_loop();
00134   }
00135 
00136 #endif
00137 }
00138 
00151 boolean
00152 module_disconnect()
00153 {
00154   if (close_socket(module_sd) < 0) return FALSE;
00155   module_sd = -1;
00156   return TRUE;
00157 }
00158 
00171 boolean
00172 module_is_connected()
00173 {
00174   if (module_sd < 0) return FALSE;
00175   return TRUE;
00176 }
00177 
00178 
00179 /*
00180   MSOCK_COMMAND_DIE ,    exit program 
00181   MSOCK_COMMAND_PAUSE ,  stop recog. keeping the current input 
00182   MSOCK_COMMAND_TERMINATE , stop recog. dropping the current input 
00183   MSOCK_COMMAND_RESUME};         resume the stopped process 
00184 */
00185 
00186 #ifdef USE_DFA
00187 
00218 static boolean
00219 msock_read_grammar(DFA_INFO **ret_dfa, WORD_INFO **ret_winfo)
00220 {
00221   DFA_INFO *dfa;
00222   WORD_INFO *winfo;
00223 
00224   /* load grammar: dfa and dict in turn */
00225   dfa = dfa_info_new();
00226   if (!
00227 #ifdef WINSOCK
00228       rddfa_sd(module_sd, dfa)
00229 #else
00230       rddfa_fd(module_sd, dfa)
00231 #endif
00232       ) {
00233     return FALSE;
00234   }
00235   winfo = word_info_new();
00236   /* ignore MONOTREE */
00237   if (!
00238 #ifdef WINSOCK
00239       voca_load_htkdict_sd(module_sd, winfo, hmminfo, FALSE)
00240 #else
00241       voca_load_htkdict_fd(module_sd, winfo, hmminfo, FALSE)
00242 #endif
00243       ) {
00244     dfa_info_free(dfa);
00245     return FALSE;
00246   }
00247   *ret_dfa = dfa;
00248   *ret_winfo = winfo;
00249   return TRUE;
00250 }
00251 
00268 static void
00269 set_grammar_switch_timing_flag()
00270 {
00271   if (process_active) {
00272     /* if recognition is currently running, tell engine how/when to
00273        re-construct global lexicon. */
00274     switch(gram_switch_input_method) {
00275     case SM_TERMINATE:  /* discard input now and change (immediate) */
00276       process_want_terminate = TRUE;
00277       process_want_reload = TRUE;
00278       break;
00279     case SM_PAUSE:              /* segment input now, recognize it, and then change */
00280       process_want_terminate = FALSE;
00281       process_want_reload = TRUE;
00282       break;
00283     case SM_WAIT:               /* wait until the current input end and recognition completed */
00284       process_want_terminate = FALSE;
00285       process_want_reload = FALSE;
00286       break;
00287     }
00288     /* After the update, recognition will restart without sleeping. */
00289   } else {
00290     /* If recognition is currently not running, the received
00291        grammars are merely stored in memory here.  The re-construction of
00292        global lexicon will be delayed: it will be re-built just before
00293        the recognition process starts next time. */
00294   }
00295 }
00296 #endif
00297 
00325 void
00326 msock_exec_command(char *command)
00327 {
00328 #ifdef USE_DFA
00329   DFA_INFO *new_dfa;
00330   WORD_INFO *new_winfo;
00331   static char *buf = NULL, *p, *q;
00332   int gid;
00333   char namebuf[MAXGRAMNAMELEN];
00334 
00335   if (buf == NULL) buf = mymalloc(MAXBUFLEN);
00336 #endif
00337 
00338   /* prompt the received command string */
00339   j_printf("[[%s]]\n",command);
00340 
00341   if (strmatch(command, "STATUS")) {
00342     /* return status */
00343     if (process_active) {
00344       module_send(module_sd, "<SYSINFO PROCESS=\"ACTIVE\"/>\n.\n");
00345     } else {
00346       module_send(module_sd, "<SYSINFO PROCESS=\"SLEEP\"/>\n.\n");
00347     }
00348   } else if (strmatch(command, "DIE")) {
00349 #if defined(_WIN32) && !defined(__CYGWIN32__)
00350     /* Win32: this is single process and has not forked, so
00351        we just disconnect the connection here.  */
00352     module_disconnect();
00353 #else
00354     /* Unix: this is a forked process, so exit here. */
00355     module_disconnect();
00356     j_exit();
00357 #endif
00358   } else if (strmatch(command, "VERSION")) {
00359     /* return version */
00360     module_send(module_sd, "<ENGINEINFO TYPE=\"%s\" VERSION=\"%s\" CONF=\"%s\"/>\n.\n",
00361                 PRODUCTNAME, VERSION, SETUP);
00362   } else if (strmatch(command, "PAUSE")) {
00363     /* pause recognition: will stop when the current input ends */
00364     if (process_active) {
00365       process_want_terminate = FALSE;
00366       process_want_reload = TRUE;
00367       process_active = FALSE;
00368     }
00369     if (speech_input == SP_ADINNET) {
00370       /* when taking speech from adinnet client,
00371          always tell the client to stop recording */
00372       adin_tcpip_send_pause();
00373     }
00374   } else if (strmatch(command, "TERMINATE")) {
00375     /* terminate recognition: input will terminate immidiately */
00376     /* set flags to stop adin to terminate immediately, and
00377        stop process */
00378     if (process_active) {
00379       process_want_terminate = TRUE;
00380       process_want_reload = TRUE;
00381       process_active = FALSE;
00382     }
00383     if (speech_input == SP_ADINNET) {
00384       /* when taking speech input from adinnet client,
00385          always tell the client to stop recording immediately */
00386       adin_tcpip_send_terminate();
00387     }
00388   } else if (strmatch(command, "RESUME")) {
00389     /* restart the paused/terminated recognition process */
00390     if (process_active == FALSE) {
00391       process_want_terminate = FALSE;
00392       process_active = TRUE;
00393     }
00394     if (speech_input == SP_ADINNET) {
00395       /* when taking speech from adinnet client,
00396          tell the client to restart recording */
00397       adin_tcpip_send_resume();
00398     }
00399 #ifdef USE_DFA
00400   } else if (strmatch(command, "INPUTONCHANGE")) {
00401     /* change grammar switching timing policy */
00402     if (
00403 #ifdef WINSOCK
00404         getl_sd(buf, MAXBUFLEN, module_sd)
00405 #else   
00406         getl_fd(buf, MAXBUFLEN, module_sd)
00407 #endif
00408         == NULL) {
00409       j_error("Error: msock(INPUTONCHANGE): no argument\n");
00410     }
00411     if (strmatch(buf, "TERMINATE")) {
00412       gram_switch_input_method = SM_TERMINATE;
00413     } else if (strmatch(buf, "PAUSE")) {
00414       gram_switch_input_method = SM_PAUSE;
00415     } else if (strmatch(buf, "WAIT")) {
00416       gram_switch_input_method = SM_WAIT;
00417     } else {
00418       j_error("Error: msock(INPUTONCHANGE): unknown method [%s]\n", buf);
00419     }
00420   } else if (strnmatch(command, "CHANGEGRAM", strlen("CHANGEGRAM"))) {
00421     /* receive grammar (DFA + DICT) from the socket, and swap the whole grammar  */
00422     /* read grammar name if any */
00423     p = &(command[strlen("CHANGEGRAM")]);
00424     while (*p == ' ' && *p != '\r' && *p != '\n' && *p != '\0') p++;
00425     if (*p != '\r' && *p != '\n' && *p != '\0') {
00426       q = buf;
00427       while (*p != ' ' && *p != '\r' && *p != '\n' && *p != '\0') *q++ = *p++;
00428       *q = '\0';
00429       p = buf;
00430     } else {
00431       p = NULL;
00432     }
00433     /* read a new grammar via socket */
00434     if (msock_read_grammar(&new_dfa, &new_winfo) == FALSE) {
00435       module_send(module_sd, "<GRAMMAR STATUS=\"ERROR\" REASON=\"WRONG DATA\"/>\n.\n");
00436     } else {
00437       /* delete all existing grammars */
00438       multigram_delete_all();
00439       /* register the new grammar to multi-gram tree */
00440       multigram_add(new_dfa, new_winfo, p);
00441       /* need to rebuild the global lexicon */
00442       /* set engine flag to tell how to switch the grammar when active */
00443       set_grammar_switch_timing_flag();
00444       module_send(module_sd, "<GRAMMAR STATUS=\"RECEIVED\"/>\n.\n");
00445     }
00446   } else if (strnmatch(command, "ADDGRAM", strlen("ADDGRAM"))) {
00447     /* receive grammar and add it to the current grammars */
00448     /* read grammar name if any */
00449     p = &(command[strlen("ADDGRAM")]);
00450     while (*p == ' ' && *p != '\r' && *p != '\n' && *p != '\0') p++;
00451     if (*p != '\r' && *p != '\n' && *p != '\0') {
00452       q = buf;
00453       while (*p != ' ' && *p != '\r' && *p != '\n' && *p != '\0') *q++ = *p++;
00454       *q = '\0';
00455       p = buf;
00456     } else {
00457       p = NULL;
00458     }
00459     /* read a new grammar via socket */
00460     if (msock_read_grammar(&new_dfa, &new_winfo) == FALSE) {
00461       module_send(module_sd, "<GRAMMAR STATUS=\"ERROR\" REASON=\"WRONG DATA\"/>\n.\n");
00462     } else {
00463       /* add it to multi-gram tree */
00464       multigram_add(new_dfa, new_winfo, p);
00465       /* need to rebuild the global lexicon */
00466       /* set engine flag to tell how to switch the grammar when active */
00467       set_grammar_switch_timing_flag();
00468       module_send(module_sd, "<GRAMMAR STATUS=\"RECEIVED\"/>\n.\n");
00469     }
00470   } else if (strmatch(command, "DELGRAM")) {
00471     /* remove the grammar specified by ID */
00472     /* read a list of grammar IDs to be deleted */
00473     if (
00474 #ifdef WINSOCK
00475         getl_sd(buf, MAXBUFLEN, module_sd)
00476 #else
00477         getl_fd(buf, MAXBUFLEN, module_sd)
00478 #endif
00479         == NULL) {
00480       j_error("Error: msock(DELGRAM): no argument\n");
00481     }
00482     /* extract IDs and mark them as delete
00483        (actual deletion will be performed on the next 
00484     */
00485     for(p=strtok(buf," ");p;p=strtok(NULL," ")) {
00486       gid = atoi(p);
00487       if (multigram_delete(gid) == FALSE) { /* deletion marking failed */
00488         j_printerr("Warning: msock(DELGRAM): gram #%d failed to delete, ignored\n", gid);
00489       }
00490     }
00491     /* need to rebuild the global lexicon */
00492     /* set engine flag to tell how to switch the grammar when active */
00493     set_grammar_switch_timing_flag();
00494   } else if (strmatch(command, "ACTIVATEGRAM")) {
00495     /* activate grammar in this engine */
00496     /* read a list of grammar IDs to be activated */
00497     if (
00498 #ifdef WINSOCK
00499         getl_sd(buf, MAXBUFLEN, module_sd)
00500 #else
00501         getl_fd(buf, MAXBUFLEN, module_sd)
00502 #endif
00503         == NULL) {
00504       j_error("Error: msock(ACTIVATEGRAM): no argument\n");
00505     }
00506     /* mark them as active */
00507     for(p=strtok(buf," ");p;p=strtok(NULL," ")) {
00508       gid = atoi(p);
00509       multigram_activate(gid);
00510     }
00511     /* tell engine when to change active status */
00512     set_grammar_switch_timing_flag();
00513   } else if (strmatch(command, "DEACTIVATEGRAM")) {
00514     /* deactivate grammar in this engine */
00515     /* read a list of grammar IDs to be de-activated */
00516     if (
00517 #ifdef WINSOCK
00518         getl_sd(buf, MAXBUFLEN, module_sd)
00519 #else
00520         getl_fd(buf, MAXBUFLEN, module_sd)
00521 #endif
00522         == NULL) {
00523       j_error("Error: msock(DEACTIVATEGRAM): no argument\n");
00524     }
00525     /* mark them as not active */
00526     for(p=strtok(buf," ");p;p=strtok(NULL," ")) {
00527       gid = atoi(p);
00528       multigram_deactivate(gid);
00529     }
00530     /* tell engine when to change active status */
00531     set_grammar_switch_timing_flag();
00532   } else if (strmatch(command, "SYNCGRAM")) {
00533     /* update grammar if necessary */
00534     multigram_exec();
00535     module_send(module_sd, "<GRAMMAR STATUS=\"READY\"/>\n.\n");
00536 #endif
00537   }
00538 }
00539 
00540 
00562 boolean
00563 module_is_active()
00564 {
00565   return process_active;
00566 }
00567 
00591 boolean
00592 module_wants_terminate()
00593 {
00594   return process_want_terminate;
00595 }
00596 
00607 void
00608 module_reset_reload()
00609 {
00610   process_want_reload = FALSE;
00611 }
00612 
00625 void
00626 msock_process_command()
00627 {
00628   if (
00629 #ifdef WINSOCK
00630       getl_sd(mbuf, MAXBUFLEN, module_sd)
00631 #else
00632       getl_fd(mbuf, MAXBUFLEN, module_sd)
00633 #endif
00634       != NULL) {
00635     msock_exec_command(mbuf);
00636   }
00637 }
00638 
00651 void
00652 msock_check_and_process_command()
00653 {
00654   fd_set rfds;
00655   int ret;
00656   struct timeval tv;
00657 
00658   /* check if some commands are waiting in queue */
00659   FD_ZERO(&rfds);
00660   FD_SET(module_sd, &rfds);
00661   tv.tv_sec = 0;
00662   tv.tv_usec = 0;             /* 0 msec timeout: return immediately */
00663   ret = select(module_sd+1, &rfds, NULL, NULL, &tv);
00664   if (ret < 0) {
00665     perror("msock_check_and_process_command: cannot poll\n");
00666   }
00667   if (ret > 0) {
00668     /* there is data to read */
00669     /* process command and change status if necessaty */
00670     while(select(module_sd+1, &rfds, NULL, NULL, &tv) > 0 &&
00671 #ifdef WINSOCK
00672           getl_sd(mbuf, MAXBUFLEN, module_sd)
00673 #else
00674           getl_fd(mbuf, MAXBUFLEN, module_sd)
00675 #endif
00676           != NULL) {
00677       msock_exec_command(mbuf);
00678     }
00679   }
00680 }
00681 
00706 int
00707 msock_check_in_adin()
00708 {
00709   if (module_is_connected()) {
00710     /* module: check command and terminate recording when requested */
00711     msock_check_and_process_command();
00712     /* With audio input via adinnet, TERMINATE command will issue terminate
00713        command to the adinnet client.  The client then stops recording
00714        immediately and return end-of-segment ack.  Then it will cause this
00715        process to stop recognition as normal.  So we need not to
00716        perform immediate termination at this callback, but just ignore the
00717        results in the main.c.  */
00718     if (module_wants_terminate() /* TERMINATE ... force termination */
00719         && speech_input != SP_ADINNET) {
00720       return(-2);
00721     }
00722     if (process_want_reload) {
00723       return(-1);
00724     }
00725   }
00726   return(0);
00727 }

Generated on Tue Dec 26 16:16:33 2006 for Julius by  doxygen 1.5.0