Thu Jul 9 13:40:20 2009

Asterisk developer's documentation


app_speech_utils.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2006, Digium, Inc.
00005  *
00006  * Joshua Colp <jcolp@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Speech Recognition Utility Applications
00022  *
00023  * \author Joshua Colp <jcolp@digium.com>
00024  *
00025  * \ingroup applications
00026  */
00027 
00028 #include "asterisk.h"
00029 
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 177385 $");
00031 
00032 #include "asterisk/file.h"
00033 #include "asterisk/channel.h"
00034 #include "asterisk/pbx.h"
00035 #include "asterisk/module.h"
00036 #include "asterisk/lock.h"
00037 #include "asterisk/app.h"
00038 #include "asterisk/speech.h"
00039 
00040 /* Descriptions for each application */
00041 static char *speechcreate_descrip =
00042 "  SpeechCreate(engine name):\n"
00043 "This application creates information to be used by all the other applications.\n"
00044 "It must be called before doing any speech recognition activities such as activating a grammar.\n"
00045 "It takes the engine name to use as the argument, if not specified the default engine will be used.\n";
00046 
00047 static char *speechactivategrammar_descrip =
00048 "  SpeechActivateGrammar(Grammar Name):\n"
00049 "This activates the specified grammar to be recognized by the engine.\n"
00050 "A grammar tells the speech recognition engine what to recognize, and how to portray it back to you \n"
00051 "in the dialplan. The grammar name is the only argument to this application.\n";
00052 
00053 static char *speechstart_descrip =
00054 "  SpeechStart():\n"
00055 "Tell the speech recognition engine that it should start trying to get results from audio being \n"
00056 "fed to it. This has no arguments.\n";
00057 
00058 static char *speechbackground_descrip =
00059 "  SpeechBackground(<Sound File>[,Timeout[,options]]):\n"
00060 "This application plays a sound file and waits for the person to speak. Once they start speaking playback\n"
00061 "of the file stops, and silence is heard. Once they stop talking the processing sound is played to indicate\n"
00062 "the speech recognition engine is working. Once results are available the application returns and results \n"
00063 "(score and text) are available using dialplan functions.\n"
00064 "  The first text and score are ${SPEECH_TEXT(0)} AND ${SPEECH_SCORE(0)} while the second are ${SPEECH_TEXT(1)}\n"
00065 "and ${SPEECH_SCORE(1)}.\n"
00066 "  The first argument is the sound file and the second is the timeout integer in seconds. Note the timeout will\n"
00067 "only start once the sound file has stopped playing. The third argument specifies options:\n"
00068 "  Valid Options:\n"
00069 "    n - Don't answer the channel if it has not already been answered.\n";
00070 
00071 static char *speechdeactivategrammar_descrip =
00072 "  SpeechDeactivateGrammar(Grammar Name):\n"
00073 "This deactivates the specified grammar so that it is no longer recognized.\n"
00074 "The only argument is the grammar name to deactivate.\n";
00075 
00076 static char *speechprocessingsound_descrip =
00077 "  SpeechProcessingSound(Sound File):\n"
00078 "This changes the processing sound that SpeechBackground plays back when the speech recognition engine is\n"
00079 "processing and working to get results.\n"
00080 "It takes the sound file as the only argument.\n";
00081 
00082 static char *speechdestroy_descrip =
00083 "  SpeechDestroy():\n"
00084 "This destroys the information used by all the other speech recognition applications.\n"
00085 "If you call this application but end up wanting to recognize more speech, you must call SpeechCreate\n"
00086    "again before calling any other application. It takes no arguments.\n";
00087 
00088 static char *speechload_descrip =
00089 "  SpeechLoadGrammar(Grammar Name,Path):\n"
00090 "Load a grammar only on the channel, not globally.\n"
00091 "It takes the grammar name as first argument and path as second.\n";
00092 
00093 static char *speechunload_descrip =
00094 "  SpeechUnloadGrammar(Grammar Name):\n"
00095 "Unload a grammar. It takes the grammar name as the only argument.\n";
00096 
00097 /*! \brief Helper function used by datastores to destroy the speech structure upon hangup */
00098 static void destroy_callback(void *data)
00099 {
00100    struct ast_speech *speech = (struct ast_speech*)data;
00101 
00102    if (speech == NULL) {
00103       return;
00104    }
00105 
00106    /* Deallocate now */
00107    ast_speech_destroy(speech);
00108 
00109    return;
00110 }
00111 
00112 /*! \brief Static structure for datastore information */
00113 static const struct ast_datastore_info speech_datastore = {
00114    .type = "speech",
00115    .destroy = destroy_callback
00116 };
00117 
00118 /*! \brief Helper function used to find the speech structure attached to a channel */
00119 static struct ast_speech *find_speech(struct ast_channel *chan)
00120 {
00121    struct ast_speech *speech = NULL;
00122    struct ast_datastore *datastore = NULL;
00123    
00124    datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
00125    if (datastore == NULL) {
00126       return NULL;
00127    }
00128    speech = datastore->data;
00129 
00130    return speech;
00131 }
00132 
00133 /* Helper function to find a specific speech recognition result by number and nbest alternative */
00134 static struct ast_speech_result *find_result(struct ast_speech_result *results, char *result_num)
00135 {
00136    struct ast_speech_result *result = results;
00137    char *tmp = NULL;
00138    int nbest_num = 0, wanted_num = 0, i = 0;
00139 
00140    if (!result)
00141       return NULL;
00142 
00143    if ((tmp = strchr(result_num, '/'))) {
00144       *tmp++ = '\0';
00145       nbest_num = atoi(result_num);
00146       wanted_num = atoi(tmp);
00147    } else {
00148       wanted_num = atoi(result_num);
00149    }
00150 
00151    do {
00152       if (result->nbest_num != nbest_num)
00153          continue;
00154       if (i == wanted_num)
00155          break;
00156       i++;
00157    } while ((result = AST_LIST_NEXT(result, list)));
00158 
00159    return result;
00160 }
00161 
00162 /*! \brief SPEECH_SCORE() Dialplan Function */
00163 static int speech_score(struct ast_channel *chan, const char *cmd, char *data,
00164              char *buf, size_t len)
00165 {
00166    struct ast_speech_result *result = NULL;
00167    struct ast_speech *speech = find_speech(chan);
00168    char tmp[128] = "";
00169 
00170    if (data == NULL || speech == NULL || !(result = find_result(speech->results, data)))
00171       return -1;
00172    
00173    snprintf(tmp, sizeof(tmp), "%d", result->score);
00174    
00175    ast_copy_string(buf, tmp, len);
00176 
00177    return 0;
00178 }
00179 
00180 static struct ast_custom_function speech_score_function = {
00181         .name = "SPEECH_SCORE",
00182         .synopsis = "Gets the confidence score of a result.",
00183         .syntax = "SPEECH_SCORE([nbest number/]result number)",
00184         .desc =
00185         "Gets the confidence score of a result.\n",
00186         .read = speech_score,
00187         .write = NULL,
00188 };
00189 
00190 /*! \brief SPEECH_TEXT() Dialplan Function */
00191 static int speech_text(struct ast_channel *chan, const char *cmd, char *data,
00192          char *buf, size_t len)
00193 {
00194         struct ast_speech_result *result = NULL;
00195         struct ast_speech *speech = find_speech(chan);
00196 
00197    if (data == NULL || speech == NULL || !(result = find_result(speech->results, data)))
00198                 return -1;
00199 
00200    if (result->text != NULL)
00201       ast_copy_string(buf, result->text, len);
00202 
00203         return 0;
00204 }
00205 
00206 static struct ast_custom_function speech_text_function = {
00207         .name = "SPEECH_TEXT",
00208         .synopsis = "Gets the recognized text of a result.",
00209         .syntax = "SPEECH_TEXT([nbest number/]result number)",
00210         .desc =
00211         "Gets the recognized text of a result.\n",
00212         .read = speech_text,
00213         .write = NULL,
00214 };
00215 
00216 /*! \brief SPEECH_GRAMMAR() Dialplan Function */
00217 static int speech_grammar(struct ast_channel *chan, const char *cmd, char *data,
00218          char *buf, size_t len)
00219 {
00220         struct ast_speech_result *result = NULL;
00221         struct ast_speech *speech = find_speech(chan);
00222 
00223    if (data == NULL || speech == NULL || !(result = find_result(speech->results, data)))
00224                 return -1;
00225 
00226    if (result->grammar != NULL)
00227       ast_copy_string(buf, result->grammar, len);
00228 
00229         return 0;
00230 }
00231 
00232 static struct ast_custom_function speech_grammar_function = {
00233         .name = "SPEECH_GRAMMAR",
00234         .synopsis = "Gets the matched grammar of a result if available.",
00235         .syntax = "SPEECH_GRAMMAR([nbest number/]result number)",
00236         .desc =
00237         "Gets the matched grammar of a result if available.\n",
00238         .read = speech_grammar,
00239         .write = NULL,
00240 };
00241 
00242 /*! \brief SPEECH_ENGINE() Dialplan Function */
00243 static int speech_engine_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00244 {
00245    struct ast_speech *speech = find_speech(chan);
00246 
00247    if (data == NULL || speech == NULL)
00248       return -1;
00249 
00250    ast_speech_change(speech, data, value);
00251 
00252    return 0;
00253 }
00254 
00255 static struct ast_custom_function speech_engine_function = {
00256    .name = "SPEECH_ENGINE",
00257    .synopsis = "Change a speech engine specific attribute.",
00258    .syntax = "SPEECH_ENGINE(name)=value",
00259    .desc =
00260    "Changes a speech engine specific attribute.\n",
00261    .read = NULL,
00262    .write = speech_engine_write,
00263 };
00264 
00265 /*! \brief SPEECH_RESULTS_TYPE() Dialplan Function */
00266 static int speech_results_type_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00267 {
00268    struct ast_speech *speech = find_speech(chan);
00269 
00270    if (data == NULL || speech == NULL)
00271       return -1;
00272 
00273    if (!strcasecmp(value, "normal"))
00274       ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NORMAL);
00275    else if (!strcasecmp(value, "nbest"))
00276       ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NBEST);
00277 
00278    return 0;
00279 }
00280 
00281 static struct ast_custom_function speech_results_type_function = {
00282    .name = "SPEECH_RESULTS_TYPE",
00283    .synopsis = "Sets the type of results that will be returned.",
00284    .syntax = "SPEECH_RESULTS_TYPE()=results type",
00285    .desc =
00286    "Sets the type of results that will be returned. Valid options are normal or nbest.",
00287    .read = NULL,
00288    .write = speech_results_type_write,
00289 };
00290 
00291 /*! \brief SPEECH() Dialplan Function */
00292 static int speech_read(struct ast_channel *chan, const char *cmd, char *data,
00293          char *buf, size_t len)
00294 {
00295    int results = 0;
00296    struct ast_speech_result *result = NULL;
00297    struct ast_speech *speech = find_speech(chan);
00298    char tmp[128] = "";
00299 
00300    /* Now go for the various options */
00301    if (!strcasecmp(data, "status")) {
00302       if (speech != NULL)
00303          ast_copy_string(buf, "1", len);
00304       else
00305          ast_copy_string(buf, "0", len);
00306       return 0;
00307    }
00308 
00309    /* Make sure we have a speech structure for everything else */
00310    if (speech == NULL) {
00311       return -1;
00312    }
00313 
00314    /* Check to see if they are checking for silence */
00315    if (!strcasecmp(data, "spoke")) {
00316       if (ast_test_flag(speech, AST_SPEECH_SPOKE))
00317          ast_copy_string(buf, "1", len);
00318       else
00319          ast_copy_string(buf, "0", len);
00320    } else if (!strcasecmp(data, "results")) {
00321       /* Count number of results */
00322       for (result = speech->results; result; result = AST_LIST_NEXT(result, list))
00323          results++;
00324       snprintf(tmp, sizeof(tmp), "%d", results);
00325       ast_copy_string(buf, tmp, len);
00326    }
00327 
00328    return 0;
00329 }
00330 
00331 static struct ast_custom_function speech_function = {
00332         .name = "SPEECH",
00333         .synopsis = "Gets information about speech recognition results.",
00334         .syntax = "SPEECH(argument)",
00335         .desc =
00336    "Gets information about speech recognition results.\n"
00337    "status:   Returns 1 upon speech object existing, or 0 if not\n"
00338    "spoke:  Returns 1 if spoker spoke, or 0 if not\n"
00339    "results:  Returns number of results that were recognized\n",
00340         .read = speech_read,
00341         .write = NULL,
00342 };
00343 
00344 
00345 
00346 /*! \brief SpeechCreate() Dialplan Application */
00347 static int speech_create(struct ast_channel *chan, void *data)
00348 {
00349    struct ast_speech *speech = NULL;
00350    struct ast_datastore *datastore = NULL;
00351 
00352    /* Request a speech object */
00353    speech = ast_speech_new(data, chan->nativeformats);
00354    if (speech == NULL) {
00355       /* Not available */
00356       pbx_builtin_setvar_helper(chan, "ERROR", "1");
00357       return 0;
00358    }
00359 
00360    datastore = ast_channel_datastore_alloc(&speech_datastore, NULL);
00361    if (datastore == NULL) {
00362       ast_speech_destroy(speech);
00363       pbx_builtin_setvar_helper(chan, "ERROR", "1");
00364       return 0;
00365    }
00366    pbx_builtin_setvar_helper(chan, "ERROR", NULL);
00367    datastore->data = speech;
00368    ast_channel_datastore_add(chan, datastore);
00369 
00370    return 0;
00371 }
00372 
00373 /*! \brief SpeechLoadGrammar(Grammar Name,Path) Dialplan Application */
00374 static int speech_load(struct ast_channel *chan, void *vdata)
00375 {
00376    int res = 0;
00377    struct ast_speech *speech = find_speech(chan);
00378    char *data;
00379    AST_DECLARE_APP_ARGS(args,
00380       AST_APP_ARG(grammar);
00381       AST_APP_ARG(path);
00382    );
00383 
00384    data = ast_strdupa(vdata);
00385    AST_STANDARD_APP_ARGS(args, data);
00386 
00387    if (speech == NULL)
00388       return -1;
00389 
00390    if (args.argc != 2)
00391       return -1;
00392 
00393    /* Load the grammar locally on the object */
00394    res = ast_speech_grammar_load(speech, args.grammar, args.path);
00395 
00396    return res;
00397 }
00398 
00399 /*! \brief SpeechUnloadGrammar(Grammar Name) Dialplan Application */
00400 static int speech_unload(struct ast_channel *chan, void *data)
00401 {
00402    int res = 0;
00403    struct ast_speech *speech = find_speech(chan);
00404 
00405    if (speech == NULL)
00406       return -1;
00407 
00408    /* Unload the grammar */
00409    res = ast_speech_grammar_unload(speech, data);
00410 
00411    return res;
00412 }
00413 
00414 /*! \brief SpeechDeactivateGrammar(Grammar Name) Dialplan Application */
00415 static int speech_deactivate(struct ast_channel *chan, void *data)
00416 {
00417    int res = 0;
00418    struct ast_speech *speech = find_speech(chan);
00419 
00420    if (speech == NULL)
00421       return -1;
00422 
00423    /* Deactivate the grammar on the speech object */
00424    res = ast_speech_grammar_deactivate(speech, data);
00425 
00426    return res;
00427 }
00428 
00429 /*! \brief SpeechActivateGrammar(Grammar Name) Dialplan Application */
00430 static int speech_activate(struct ast_channel *chan, void *data)
00431 {
00432    int res = 0;
00433    struct ast_speech *speech = find_speech(chan);
00434 
00435    if (speech == NULL)
00436       return -1;
00437 
00438    /* Activate the grammar on the speech object */
00439    res = ast_speech_grammar_activate(speech, data);
00440 
00441    return res;
00442 }
00443 
00444 /*! \brief SpeechStart() Dialplan Application */
00445 static int speech_start(struct ast_channel *chan, void *data)
00446 {
00447    int res = 0;
00448    struct ast_speech *speech = find_speech(chan);
00449 
00450    if (speech == NULL)
00451       return -1;
00452 
00453    ast_speech_start(speech);
00454 
00455    return res;
00456 }
00457 
00458 /*! \brief SpeechProcessingSound(Sound File) Dialplan Application */
00459 static int speech_processing_sound(struct ast_channel *chan, void *data)
00460 {
00461    int res = 0;
00462    struct ast_speech *speech = find_speech(chan);
00463 
00464    if (speech == NULL)
00465       return -1;
00466 
00467    if (speech->processing_sound != NULL) {
00468       ast_free(speech->processing_sound);
00469       speech->processing_sound = NULL;
00470    }
00471 
00472    speech->processing_sound = ast_strdup(data);
00473 
00474    return res;
00475 }
00476 
00477 /*! \brief Helper function used by speech_background to playback a soundfile */
00478 static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
00479 {
00480    struct ast_filestream *fs = NULL;
00481 
00482    if (!(fs = ast_openstream(chan, filename, preflang)))
00483       return -1;
00484    
00485    if (ast_applystream(chan, fs))
00486       return -1;
00487    
00488    ast_playstream(fs);
00489 
00490    return 0;
00491 }
00492 
00493 enum {
00494    SB_OPT_NOANSWER = (1 << 0),
00495 };
00496 
00497 AST_APP_OPTIONS(speech_background_options, BEGIN_OPTIONS
00498    AST_APP_OPTION('n', SB_OPT_NOANSWER),
00499 END_OPTIONS );
00500 
00501 /*! \brief SpeechBackground(Sound File,Timeout) Dialplan Application */
00502 static int speech_background(struct ast_channel *chan, void *data)
00503 {
00504    unsigned int timeout = 0;
00505    int res = 0, done = 0, started = 0, quieted = 0, max_dtmf_len = 0;
00506    struct ast_speech *speech = find_speech(chan);
00507    struct ast_frame *f = NULL;
00508    int oldreadformat = AST_FORMAT_SLINEAR;
00509    char dtmf[AST_MAX_EXTENSION] = "";
00510    time_t start, current;
00511    struct ast_datastore *datastore = NULL;
00512    char *parse, *filename_tmp = NULL, *filename = NULL, tmp[2] = "", dtmf_terminator = '#';
00513    const char *tmp2 = NULL;
00514    struct ast_flags options = { 0 };
00515    AST_DECLARE_APP_ARGS(args,
00516       AST_APP_ARG(soundfile);
00517       AST_APP_ARG(timeout);
00518       AST_APP_ARG(options);
00519    );
00520 
00521    parse = ast_strdupa(data);
00522    AST_STANDARD_APP_ARGS(args, parse);
00523 
00524    if (speech == NULL)
00525       return -1;
00526 
00527    if (!ast_strlen_zero(args.options)) {
00528       char *options_buf = ast_strdupa(args.options);
00529       ast_app_parse_options(speech_background_options, &options, NULL, options_buf);
00530    }
00531 
00532    /* If channel is not already answered, then answer it */
00533    if (chan->_state != AST_STATE_UP && !ast_test_flag(&options, SB_OPT_NOANSWER)
00534       && ast_answer(chan)) {
00535          return -1;
00536    }
00537 
00538    /* Record old read format */
00539    oldreadformat = chan->readformat;
00540 
00541    /* Change read format to be signed linear */
00542    if (ast_set_read_format(chan, speech->format))
00543       return -1;
00544 
00545    if (!ast_strlen_zero(args.soundfile)) {
00546       /* Yay sound file */
00547       filename_tmp = ast_strdupa(args.soundfile);
00548       if (!ast_strlen_zero(args.timeout)) {
00549          if ((timeout = atoi(args.timeout)) == 0)
00550             timeout = -1;
00551       } else
00552          timeout = 0;
00553    }
00554 
00555    /* See if the maximum DTMF length variable is set... we use a variable in case they want to carry it through their entire dialplan */
00556    if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_MAXLEN")) && !ast_strlen_zero(tmp2))
00557       max_dtmf_len = atoi(tmp2);
00558 
00559    /* See if a terminator is specified */
00560    if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_TERMINATOR"))) {
00561       if (ast_strlen_zero(tmp2))
00562          dtmf_terminator = '\0';
00563       else
00564          dtmf_terminator = tmp2[0];
00565    }
00566 
00567    /* Before we go into waiting for stuff... make sure the structure is ready, if not - start it again */
00568    if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
00569       ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
00570       ast_speech_start(speech);
00571    }
00572 
00573    /* Ensure no streams are currently running */
00574    ast_stopstream(chan);
00575 
00576    /* Okay it's streaming so go into a loop grabbing frames! */
00577    while (done == 0) {
00578       /* If the filename is null and stream is not running, start up a new sound file */
00579       if (!quieted && (chan->streamid == -1 && chan->timingfunc == NULL) && (filename = strsep(&filename_tmp, "&"))) {
00580          /* Discard old stream information */
00581          ast_stopstream(chan);
00582          /* Start new stream */
00583          speech_streamfile(chan, filename, chan->language);
00584       }
00585 
00586       /* Run scheduled stuff */
00587       ast_sched_runq(chan->sched);
00588 
00589       /* Yay scheduling */
00590       res = ast_sched_wait(chan->sched);
00591       if (res < 0)
00592          res = 1000;
00593 
00594       /* If there is a frame waiting, get it - if not - oh well */
00595       if (ast_waitfor(chan, res) > 0) {
00596          f = ast_read(chan);
00597          if (f == NULL) {
00598             /* The channel has hung up most likely */
00599             done = 3;
00600             break;
00601          }
00602       }
00603 
00604       /* Do timeout check (shared between audio/dtmf) */
00605       if ((!quieted || strlen(dtmf)) && started == 1) {
00606          time(&current);
00607          if ((current-start) >= timeout) {
00608             done = 1;
00609             if (f)
00610                ast_frfree(f);
00611             break;
00612          }
00613       }
00614 
00615       /* Do checks on speech structure to see if it's changed */
00616       ast_mutex_lock(&speech->lock);
00617       if (ast_test_flag(speech, AST_SPEECH_QUIET)) {
00618          if (chan->stream)
00619             ast_stopstream(chan);
00620          ast_clear_flag(speech, AST_SPEECH_QUIET);
00621          quieted = 1;
00622       }
00623       /* Check state so we can see what to do */
00624       switch (speech->state) {
00625       case AST_SPEECH_STATE_READY:
00626          /* If audio playback has stopped do a check for timeout purposes */
00627          if (chan->streamid == -1 && chan->timingfunc == NULL)
00628             ast_stopstream(chan);
00629          if (!quieted && chan->stream == NULL && timeout && started == 0 && !filename_tmp) {
00630             if (timeout == -1) {
00631                done = 1;
00632                if (f)
00633                   ast_frfree(f);
00634                break;
00635             }
00636             time(&start);
00637             started = 1;
00638          }
00639          /* Write audio frame out to speech engine if no DTMF has been received */
00640          if (!strlen(dtmf) && f != NULL && f->frametype == AST_FRAME_VOICE) {
00641             ast_speech_write(speech, f->data, f->datalen);
00642          }
00643          break;
00644       case AST_SPEECH_STATE_WAIT:
00645          /* Cue up waiting sound if not already playing */
00646          if (!strlen(dtmf)) {
00647             if (chan->stream == NULL) {
00648                if (speech->processing_sound != NULL) {
00649                   if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) {
00650                      speech_streamfile(chan, speech->processing_sound, chan->language);
00651                   }
00652                }
00653             } else if (chan->streamid == -1 && chan->timingfunc == NULL) {
00654                ast_stopstream(chan);
00655                if (speech->processing_sound != NULL) {
00656                   if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) {
00657                      speech_streamfile(chan, speech->processing_sound, chan->language);
00658                   }
00659                }
00660             }
00661          }
00662          break;
00663       case AST_SPEECH_STATE_DONE:
00664          /* Now that we are done... let's switch back to not ready state */
00665          ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
00666          if (!strlen(dtmf)) {
00667             /* Copy to speech structure the results, if available */
00668             speech->results = ast_speech_results_get(speech);
00669             /* Break out of our background too */
00670             done = 1;
00671             /* Stop audio playback */
00672             if (chan->stream != NULL) {
00673                ast_stopstream(chan);
00674             }
00675          }
00676          break;
00677       default:
00678          break;
00679       }
00680       ast_mutex_unlock(&speech->lock);
00681 
00682       /* Deal with other frame types */
00683       if (f != NULL) {
00684          /* Free the frame we received */
00685          switch (f->frametype) {
00686          case AST_FRAME_DTMF:
00687             if (dtmf_terminator != '\0' && f->subclass == dtmf_terminator) {
00688                done = 1;
00689             } else {
00690                if (chan->stream != NULL) {
00691                   ast_stopstream(chan);
00692                }
00693                if (!started) {
00694                   /* Change timeout to be 5 seconds for DTMF input */
00695                   timeout = (chan->pbx && chan->pbx->dtimeout) ? chan->pbx->dtimeout : 5;
00696                   started = 1;
00697                }
00698                time(&start);
00699                snprintf(tmp, sizeof(tmp), "%c", f->subclass);
00700                strncat(dtmf, tmp, sizeof(dtmf) - strlen(dtmf) - 1);
00701                /* If the maximum length of the DTMF has been reached, stop now */
00702                if (max_dtmf_len && strlen(dtmf) == max_dtmf_len)
00703                   done = 1;
00704             }
00705             break;
00706          case AST_FRAME_CONTROL:
00707             switch (f->subclass) {
00708             case AST_CONTROL_HANGUP:
00709                /* Since they hung up we should destroy the speech structure */
00710                done = 3;
00711             default:
00712                break;
00713             }
00714          default:
00715             break;
00716          }
00717          ast_frfree(f);
00718          f = NULL;
00719       }
00720    }
00721 
00722    if (!ast_strlen_zero(dtmf)) {
00723       /* We sort of make a results entry */
00724       speech->results = ast_calloc(1, sizeof(*speech->results));
00725       if (speech->results != NULL) {
00726          ast_speech_dtmf(speech, dtmf);
00727          speech->results->score = 1000;
00728          speech->results->text = ast_strdup(dtmf);
00729          speech->results->grammar = ast_strdup("dtmf");
00730       }
00731       ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
00732    }
00733 
00734    /* See if it was because they hung up */
00735    if (done == 3) {
00736       /* Destroy speech structure */
00737       ast_speech_destroy(speech);
00738       datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
00739       if (datastore != NULL)
00740          ast_channel_datastore_remove(chan, datastore);
00741    } else {
00742       /* Channel is okay so restore read format */
00743       ast_set_read_format(chan, oldreadformat);
00744    }
00745 
00746    return 0;
00747 }
00748 
00749 
00750 /*! \brief SpeechDestroy() Dialplan Application */
00751 static int speech_destroy(struct ast_channel *chan, void *data)
00752 {
00753    int res = 0;
00754    struct ast_speech *speech = find_speech(chan);
00755    struct ast_datastore *datastore = NULL;
00756 
00757    if (speech == NULL)
00758       return -1;
00759 
00760    /* Destroy speech structure */
00761    ast_speech_destroy(speech);
00762 
00763    datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
00764    if (datastore != NULL) {
00765       ast_channel_datastore_remove(chan, datastore);
00766    }
00767 
00768    return res;
00769 }
00770 
00771 static int unload_module(void)
00772 {
00773    int res = 0;
00774 
00775    res = ast_unregister_application("SpeechCreate");
00776    res |= ast_unregister_application("SpeechLoadGrammar");
00777    res |= ast_unregister_application("SpeechUnloadGrammar");
00778    res |= ast_unregister_application("SpeechActivateGrammar");
00779    res |= ast_unregister_application("SpeechDeactivateGrammar");
00780    res |= ast_unregister_application("SpeechStart");
00781    res |= ast_unregister_application("SpeechBackground");
00782    res |= ast_unregister_application("SpeechDestroy");
00783    res |= ast_unregister_application("SpeechProcessingSound");
00784    res |= ast_custom_function_unregister(&speech_function);
00785    res |= ast_custom_function_unregister(&speech_score_function);
00786    res |= ast_custom_function_unregister(&speech_text_function);
00787    res |= ast_custom_function_unregister(&speech_grammar_function);
00788    res |= ast_custom_function_unregister(&speech_engine_function);
00789    res |= ast_custom_function_unregister(&speech_results_type_function);
00790 
00791    return res; 
00792 }
00793 
00794 static int load_module(void)
00795 {
00796    int res = 0;
00797 
00798    res = ast_register_application("SpeechCreate", speech_create, "Create a Speech Structure", speechcreate_descrip);
00799    res |= ast_register_application("SpeechLoadGrammar", speech_load, "Load a Grammar", speechload_descrip);
00800    res |= ast_register_application("SpeechUnloadGrammar", speech_unload, "Unload a Grammar", speechunload_descrip);
00801    res |= ast_register_application("SpeechActivateGrammar", speech_activate, "Activate a Grammar", speechactivategrammar_descrip);
00802    res |= ast_register_application("SpeechDeactivateGrammar", speech_deactivate, "Deactivate a Grammar", speechdeactivategrammar_descrip);
00803    res |= ast_register_application("SpeechStart", speech_start, "Start recognizing voice in the audio stream", speechstart_descrip);
00804    res |= ast_register_application("SpeechBackground", speech_background, "Play a sound file and wait for speech to be recognized", speechbackground_descrip);
00805    res |= ast_register_application("SpeechDestroy", speech_destroy, "End speech recognition", speechdestroy_descrip);
00806    res |= ast_register_application("SpeechProcessingSound", speech_processing_sound, "Change background processing sound", speechprocessingsound_descrip);
00807    res |= ast_custom_function_register(&speech_function);
00808    res |= ast_custom_function_register(&speech_score_function);
00809    res |= ast_custom_function_register(&speech_text_function);
00810    res |= ast_custom_function_register(&speech_grammar_function);
00811    res |= ast_custom_function_register(&speech_engine_function);
00812    res |= ast_custom_function_register(&speech_results_type_function);
00813 
00814    return res;
00815 }
00816 
00817 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan Speech Applications");

Generated on Thu Jul 9 13:40:20 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7