Fri Jul 24 00:40:42 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: 179256 $");
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 
00144    if ((tmp = strchr(result_num, '/'))) {
00145       *tmp++ = '\0';
00146       nbest_num = atoi(result_num);
00147       wanted_num = atoi(tmp);
00148    } else {
00149       wanted_num = atoi(result_num);
00150    }
00151 
00152    do {
00153       if (result->nbest_num != nbest_num)
00154          continue;
00155       if (i == wanted_num)
00156          break;
00157       i++;
00158    } while ((result = AST_LIST_NEXT(result, list)));
00159 
00160    return result;
00161 }
00162 
00163 /*! \brief SPEECH_SCORE() Dialplan Function */
00164 static int speech_score(struct ast_channel *chan, const char *cmd, char *data,
00165              char *buf, size_t len)
00166 {
00167    struct ast_speech_result *result = NULL;
00168    struct ast_speech *speech = find_speech(chan);
00169    char tmp[128] = "";
00170 
00171    if (data == NULL || speech == NULL || !(result = find_result(speech->results, data))) {
00172       return -1;
00173    }
00174    
00175    snprintf(tmp, sizeof(tmp), "%d", result->score);
00176    
00177    ast_copy_string(buf, tmp, len);
00178 
00179    return 0;
00180 }
00181 
00182 static struct ast_custom_function speech_score_function = {
00183    .name = "SPEECH_SCORE",
00184    .synopsis = "Gets the confidence score of a result.",
00185    .syntax = "SPEECH_SCORE([nbest number/]result number)",
00186    .desc =
00187    "Gets the confidence score of a result.\n",
00188    .read = speech_score,
00189    .write = NULL,
00190 };
00191 
00192 /*! \brief SPEECH_TEXT() Dialplan Function */
00193 static int speech_text(struct ast_channel *chan, const char *cmd, char *data,
00194          char *buf, size_t len)
00195 {
00196    struct ast_speech_result *result = NULL;
00197    struct ast_speech *speech = find_speech(chan);
00198 
00199    if (data == NULL || speech == NULL || !(result = find_result(speech->results, data))) {
00200       return -1;
00201    }
00202 
00203    if (result->text != NULL) {
00204       ast_copy_string(buf, result->text, len);
00205    } else {
00206       buf[0] = '\0';
00207    }
00208 
00209    return 0;
00210 }
00211 
00212 static struct ast_custom_function speech_text_function = {
00213    .name = "SPEECH_TEXT",
00214    .synopsis = "Gets the recognized text of a result.",
00215    .syntax = "SPEECH_TEXT([nbest number/]result number)",
00216    .desc =
00217    "Gets the recognized text of a result.\n",
00218    .read = speech_text,
00219    .write = NULL,
00220 };
00221 
00222 /*! \brief SPEECH_GRAMMAR() Dialplan Function */
00223 static int speech_grammar(struct ast_channel *chan, const char *cmd, char *data,
00224          char *buf, size_t len)
00225 {
00226    struct ast_speech_result *result = NULL;
00227    struct ast_speech *speech = find_speech(chan);
00228 
00229    if (data == NULL || speech == NULL || !(result = find_result(speech->results, data))) {
00230       return -1;
00231    }
00232 
00233    if (result->grammar != NULL) {
00234       ast_copy_string(buf, result->grammar, len);
00235    } else {
00236       buf[0] = '\0';
00237    }
00238 
00239    return 0;
00240 }
00241 
00242 static struct ast_custom_function speech_grammar_function = {
00243    .name = "SPEECH_GRAMMAR",
00244    .synopsis = "Gets the matched grammar of a result if available.",
00245    .syntax = "SPEECH_GRAMMAR([nbest number/]result number)",
00246    .desc =
00247    "Gets the matched grammar of a result if available.\n",
00248    .read = speech_grammar,
00249    .write = NULL,
00250 };
00251 
00252 /*! \brief SPEECH_ENGINE() Dialplan Function */
00253 static int speech_engine_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00254 {
00255    struct ast_speech *speech = find_speech(chan);
00256 
00257    if (data == NULL || speech == NULL) {
00258       return -1;
00259    }
00260 
00261    ast_speech_change(speech, data, value);
00262 
00263    return 0;
00264 }
00265 
00266 static struct ast_custom_function speech_engine_function = {
00267    .name = "SPEECH_ENGINE",
00268    .synopsis = "Change a speech engine specific attribute.",
00269    .syntax = "SPEECH_ENGINE(name)=value",
00270    .desc =
00271    "Changes a speech engine specific attribute.\n",
00272    .read = NULL,
00273    .write = speech_engine_write,
00274 };
00275 
00276 /*! \brief SPEECH_RESULTS_TYPE() Dialplan Function */
00277 static int speech_results_type_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00278 {
00279    struct ast_speech *speech = find_speech(chan);
00280 
00281    if (data == NULL || speech == NULL)
00282       return -1;
00283 
00284    if (!strcasecmp(value, "normal"))
00285       ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NORMAL);
00286    else if (!strcasecmp(value, "nbest"))
00287       ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NBEST);
00288 
00289    return 0;
00290 }
00291 
00292 static struct ast_custom_function speech_results_type_function = {
00293    .name = "SPEECH_RESULTS_TYPE",
00294    .synopsis = "Sets the type of results that will be returned.",
00295    .syntax = "SPEECH_RESULTS_TYPE()=results type",
00296    .desc =
00297    "Sets the type of results that will be returned. Valid options are normal or nbest.",
00298    .read = NULL,
00299    .write = speech_results_type_write,
00300 };
00301 
00302 /*! \brief SPEECH() Dialplan Function */
00303 static int speech_read(struct ast_channel *chan, const char *cmd, char *data,
00304          char *buf, size_t len)
00305 {
00306    int results = 0;
00307    struct ast_speech_result *result = NULL;
00308    struct ast_speech *speech = find_speech(chan);
00309    char tmp[128] = "";
00310 
00311    /* Now go for the various options */
00312    if (!strcasecmp(data, "status")) {
00313       if (speech != NULL)
00314          ast_copy_string(buf, "1", len);
00315       else
00316          ast_copy_string(buf, "0", len);
00317       return 0;
00318    }
00319 
00320    /* Make sure we have a speech structure for everything else */
00321    if (speech == NULL) {
00322       return -1;
00323    }
00324 
00325    /* Check to see if they are checking for silence */
00326    if (!strcasecmp(data, "spoke")) {
00327       if (ast_test_flag(speech, AST_SPEECH_SPOKE))
00328          ast_copy_string(buf, "1", len);
00329       else
00330          ast_copy_string(buf, "0", len);
00331    } else if (!strcasecmp(data, "results")) {
00332       /* Count number of results */
00333       for (result = speech->results; result; result = AST_LIST_NEXT(result, list))
00334          results++;
00335       snprintf(tmp, sizeof(tmp), "%d", results);
00336       ast_copy_string(buf, tmp, len);
00337    } else {
00338       buf[0] = '\0';
00339    }
00340 
00341    return 0;
00342 }
00343 
00344 static struct ast_custom_function speech_function = {
00345    .name = "SPEECH",
00346    .synopsis = "Gets information about speech recognition results.",
00347    .syntax = "SPEECH(argument)",
00348    .desc =
00349    "Gets information about speech recognition results.\n"
00350    "status:   Returns 1 upon speech object existing, or 0 if not\n"
00351    "spoke:  Returns 1 if spoker spoke, or 0 if not\n"
00352    "results:  Returns number of results that were recognized\n",
00353    .read = speech_read,
00354    .write = NULL,
00355 };
00356 
00357 
00358 
00359 /*! \brief SpeechCreate() Dialplan Application */
00360 static int speech_create(struct ast_channel *chan, void *data)
00361 {
00362    struct ast_speech *speech = NULL;
00363    struct ast_datastore *datastore = NULL;
00364 
00365    /* Request a speech object */
00366    speech = ast_speech_new(data, chan->nativeformats);
00367    if (speech == NULL) {
00368       /* Not available */
00369       pbx_builtin_setvar_helper(chan, "ERROR", "1");
00370       return 0;
00371    }
00372 
00373    datastore = ast_datastore_alloc(&speech_datastore, NULL);
00374    if (datastore == NULL) {
00375       ast_speech_destroy(speech);
00376       pbx_builtin_setvar_helper(chan, "ERROR", "1");
00377       return 0;
00378    }
00379    pbx_builtin_setvar_helper(chan, "ERROR", NULL);
00380    datastore->data = speech;
00381    ast_channel_datastore_add(chan, datastore);
00382 
00383    return 0;
00384 }
00385 
00386 /*! \brief SpeechLoadGrammar(Grammar Name,Path) Dialplan Application */
00387 static int speech_load(struct ast_channel *chan, void *vdata)
00388 {
00389    int res = 0;
00390    struct ast_speech *speech = find_speech(chan);
00391    char *data;
00392    AST_DECLARE_APP_ARGS(args,
00393       AST_APP_ARG(grammar);
00394       AST_APP_ARG(path);
00395    );
00396 
00397    data = ast_strdupa(vdata);
00398    AST_STANDARD_APP_ARGS(args, data);
00399 
00400    if (speech == NULL)
00401       return -1;
00402 
00403    if (args.argc != 2)
00404       return -1;
00405 
00406    /* Load the grammar locally on the object */
00407    res = ast_speech_grammar_load(speech, args.grammar, args.path);
00408 
00409    return res;
00410 }
00411 
00412 /*! \brief SpeechUnloadGrammar(Grammar Name) Dialplan Application */
00413 static int speech_unload(struct ast_channel *chan, void *data)
00414 {
00415    int res = 0;
00416    struct ast_speech *speech = find_speech(chan);
00417 
00418    if (speech == NULL)
00419       return -1;
00420 
00421    /* Unload the grammar */
00422    res = ast_speech_grammar_unload(speech, data);
00423 
00424    return res;
00425 }
00426 
00427 /*! \brief SpeechDeactivateGrammar(Grammar Name) Dialplan Application */
00428 static int speech_deactivate(struct ast_channel *chan, void *data)
00429 {
00430    int res = 0;
00431    struct ast_speech *speech = find_speech(chan);
00432 
00433    if (speech == NULL)
00434       return -1;
00435 
00436    /* Deactivate the grammar on the speech object */
00437    res = ast_speech_grammar_deactivate(speech, data);
00438 
00439    return res;
00440 }
00441 
00442 /*! \brief SpeechActivateGrammar(Grammar Name) Dialplan Application */
00443 static int speech_activate(struct ast_channel *chan, void *data)
00444 {
00445    int res = 0;
00446    struct ast_speech *speech = find_speech(chan);
00447 
00448    if (speech == NULL)
00449       return -1;
00450 
00451    /* Activate the grammar on the speech object */
00452    res = ast_speech_grammar_activate(speech, data);
00453 
00454    return res;
00455 }
00456 
00457 /*! \brief SpeechStart() Dialplan Application */
00458 static int speech_start(struct ast_channel *chan, void *data)
00459 {
00460    int res = 0;
00461    struct ast_speech *speech = find_speech(chan);
00462 
00463    if (speech == NULL)
00464       return -1;
00465 
00466    ast_speech_start(speech);
00467 
00468    return res;
00469 }
00470 
00471 /*! \brief SpeechProcessingSound(Sound File) Dialplan Application */
00472 static int speech_processing_sound(struct ast_channel *chan, void *data)
00473 {
00474    int res = 0;
00475    struct ast_speech *speech = find_speech(chan);
00476 
00477    if (speech == NULL)
00478       return -1;
00479 
00480    if (speech->processing_sound != NULL) {
00481       ast_free(speech->processing_sound);
00482       speech->processing_sound = NULL;
00483    }
00484 
00485    speech->processing_sound = ast_strdup(data);
00486 
00487    return res;
00488 }
00489 
00490 /*! \brief Helper function used by speech_background to playback a soundfile */
00491 static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
00492 {
00493    struct ast_filestream *fs = NULL;
00494 
00495    if (!(fs = ast_openstream(chan, filename, preflang)))
00496       return -1;
00497    
00498    if (ast_applystream(chan, fs))
00499       return -1;
00500    
00501    ast_playstream(fs);
00502 
00503    return 0;
00504 }
00505 
00506 enum {
00507    SB_OPT_NOANSWER = (1 << 0),
00508 };
00509 
00510 AST_APP_OPTIONS(speech_background_options, BEGIN_OPTIONS
00511    AST_APP_OPTION('n', SB_OPT_NOANSWER),
00512 END_OPTIONS );
00513 
00514 /*! \brief SpeechBackground(Sound File,Timeout) Dialplan Application */
00515 static int speech_background(struct ast_channel *chan, void *data)
00516 {
00517    unsigned int timeout = 0;
00518    int res = 0, done = 0, started = 0, quieted = 0, max_dtmf_len = 0;
00519    struct ast_speech *speech = find_speech(chan);
00520    struct ast_frame *f = NULL;
00521    int oldreadformat = AST_FORMAT_SLINEAR;
00522    char dtmf[AST_MAX_EXTENSION] = "";
00523    struct timeval start = { 0, 0 }, current;
00524    struct ast_datastore *datastore = NULL;
00525    char *parse, *filename_tmp = NULL, *filename = NULL, tmp[2] = "", dtmf_terminator = '#';
00526    const char *tmp2 = NULL;
00527    struct ast_flags options = { 0 };
00528    AST_DECLARE_APP_ARGS(args,
00529       AST_APP_ARG(soundfile);
00530       AST_APP_ARG(timeout);
00531       AST_APP_ARG(options);
00532    );
00533 
00534    parse = ast_strdupa(data);
00535    AST_STANDARD_APP_ARGS(args, parse);
00536 
00537    if (speech == NULL)
00538       return -1;
00539 
00540    if (!ast_strlen_zero(args.options)) {
00541       char *options_buf = ast_strdupa(args.options);
00542       ast_app_parse_options(speech_background_options, &options, NULL, options_buf);
00543    }
00544 
00545    /* If channel is not already answered, then answer it */
00546    if (chan->_state != AST_STATE_UP && !ast_test_flag(&options, SB_OPT_NOANSWER)
00547       && ast_answer(chan)) {
00548          return -1;
00549    }
00550 
00551    /* Record old read format */
00552    oldreadformat = chan->readformat;
00553 
00554    /* Change read format to be signed linear */
00555    if (ast_set_read_format(chan, speech->format))
00556       return -1;
00557 
00558    if (!ast_strlen_zero(args.soundfile)) {
00559       /* Yay sound file */
00560       filename_tmp = ast_strdupa(args.soundfile);
00561       if (!ast_strlen_zero(args.timeout)) {
00562          if ((timeout = atof(args.timeout) * 1000.0) == 0)
00563             timeout = -1;
00564       } else
00565          timeout = 0;
00566    }
00567 
00568    /* See if the maximum DTMF length variable is set... we use a variable in case they want to carry it through their entire dialplan */
00569    ast_channel_lock(chan);
00570    if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_MAXLEN")) && !ast_strlen_zero(tmp2)) {
00571       max_dtmf_len = atoi(tmp2);
00572    }
00573    
00574    /* See if a terminator is specified */
00575    if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_TERMINATOR"))) {
00576       if (ast_strlen_zero(tmp2))
00577          dtmf_terminator = '\0';
00578       else
00579          dtmf_terminator = tmp2[0];
00580    }
00581    ast_channel_unlock(chan);
00582 
00583    /* Before we go into waiting for stuff... make sure the structure is ready, if not - start it again */
00584    if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
00585       ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
00586       ast_speech_start(speech);
00587    }
00588 
00589    /* Ensure no streams are currently running */
00590    ast_stopstream(chan);
00591 
00592    /* Okay it's streaming so go into a loop grabbing frames! */
00593    while (done == 0) {
00594       /* If the filename is null and stream is not running, start up a new sound file */
00595       if (!quieted && (chan->streamid == -1 && chan->timingfunc == NULL) && (filename = strsep(&filename_tmp, "&"))) {
00596          /* Discard old stream information */
00597          ast_stopstream(chan);
00598          /* Start new stream */
00599          speech_streamfile(chan, filename, chan->language);
00600       }
00601 
00602       /* Run scheduled stuff */
00603       ast_sched_runq(chan->sched);
00604 
00605       /* Yay scheduling */
00606       res = ast_sched_wait(chan->sched);
00607       if (res < 0)
00608          res = 1000;
00609 
00610       /* If there is a frame waiting, get it - if not - oh well */
00611       if (ast_waitfor(chan, res) > 0) {
00612          f = ast_read(chan);
00613          if (f == NULL) {
00614             /* The channel has hung up most likely */
00615             done = 3;
00616             break;
00617          }
00618       }
00619 
00620       /* Do timeout check (shared between audio/dtmf) */
00621       if ((!quieted || strlen(dtmf)) && started == 1) {
00622          current = ast_tvnow();
00623          if ((ast_tvdiff_ms(current, start)) >= timeout) {
00624             done = 1;
00625             if (f)
00626                ast_frfree(f);
00627             break;
00628          }
00629       }
00630 
00631       /* Do checks on speech structure to see if it's changed */
00632       ast_mutex_lock(&speech->lock);
00633       if (ast_test_flag(speech, AST_SPEECH_QUIET)) {
00634          if (chan->stream)
00635             ast_stopstream(chan);
00636          ast_clear_flag(speech, AST_SPEECH_QUIET);
00637          quieted = 1;
00638       }
00639       /* Check state so we can see what to do */
00640       switch (speech->state) {
00641       case AST_SPEECH_STATE_READY:
00642          /* If audio playback has stopped do a check for timeout purposes */
00643          if (chan->streamid == -1 && chan->timingfunc == NULL)
00644             ast_stopstream(chan);
00645          if (!quieted && chan->stream == NULL && timeout && started == 0 && !filename_tmp) {
00646             if (timeout == -1) {
00647                done = 1;
00648                if (f)
00649                   ast_frfree(f);
00650                break;
00651             }
00652             start = ast_tvnow();
00653             started = 1;
00654          }
00655          /* Write audio frame out to speech engine if no DTMF has been received */
00656          if (!strlen(dtmf) && f != NULL && f->frametype == AST_FRAME_VOICE) {
00657             ast_speech_write(speech, f->data.ptr, f->datalen);
00658          }
00659          break;
00660       case AST_SPEECH_STATE_WAIT:
00661          /* Cue up waiting sound if not already playing */
00662          if (!strlen(dtmf)) {
00663             if (chan->stream == NULL) {
00664                if (speech->processing_sound != NULL) {
00665                   if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound, "none")) {
00666                      speech_streamfile(chan, speech->processing_sound, chan->language);
00667                   }
00668                }
00669             } else if (chan->streamid == -1 && chan->timingfunc == NULL) {
00670                ast_stopstream(chan);
00671                if (speech->processing_sound != NULL) {
00672                   if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound, "none")) {
00673                      speech_streamfile(chan, speech->processing_sound, chan->language);
00674                   }
00675                }
00676             }
00677          }
00678          break;
00679       case AST_SPEECH_STATE_DONE:
00680          /* Now that we are done... let's switch back to not ready state */
00681          ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
00682          if (!strlen(dtmf)) {
00683             /* Copy to speech structure the results, if available */
00684             speech->results = ast_speech_results_get(speech);
00685             /* Break out of our background too */
00686             done = 1;
00687             /* Stop audio playback */
00688             if (chan->stream != NULL) {
00689                ast_stopstream(chan);
00690             }
00691          }
00692          break;
00693       default:
00694          break;
00695       }
00696       ast_mutex_unlock(&speech->lock);
00697 
00698       /* Deal with other frame types */
00699       if (f != NULL) {
00700          /* Free the frame we received */
00701          switch (f->frametype) {
00702          case AST_FRAME_DTMF:
00703             if (dtmf_terminator != '\0' && f->subclass == dtmf_terminator) {
00704                done = 1;
00705             } else {
00706                if (chan->stream != NULL) {
00707                   ast_stopstream(chan);
00708                }
00709                if (!started) {
00710                   /* Change timeout to be 5 seconds for DTMF input */
00711                   timeout = (chan->pbx && chan->pbx->dtimeoutms) ? chan->pbx->dtimeoutms : 5000;
00712                   started = 1;
00713                }
00714                start = ast_tvnow();
00715                snprintf(tmp, sizeof(tmp), "%c", f->subclass);
00716                strncat(dtmf, tmp, sizeof(dtmf) - strlen(dtmf) - 1);
00717                /* If the maximum length of the DTMF has been reached, stop now */
00718                if (max_dtmf_len && strlen(dtmf) == max_dtmf_len)
00719                   done = 1;
00720             }
00721             break;
00722          case AST_FRAME_CONTROL:
00723             switch (f->subclass) {
00724             case AST_CONTROL_HANGUP:
00725                /* Since they hung up we should destroy the speech structure */
00726                done = 3;
00727             default:
00728                break;
00729             }
00730          default:
00731             break;
00732          }
00733          ast_frfree(f);
00734          f = NULL;
00735       }
00736    }
00737 
00738    if (!ast_strlen_zero(dtmf)) {
00739       /* We sort of make a results entry */
00740       speech->results = ast_calloc(1, sizeof(*speech->results));
00741       if (speech->results != NULL) {
00742          ast_speech_dtmf(speech, dtmf);
00743          speech->results->score = 1000;
00744          speech->results->text = ast_strdup(dtmf);
00745          speech->results->grammar = ast_strdup("dtmf");
00746       }
00747       ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
00748    }
00749 
00750    /* See if it was because they hung up */
00751    if (done == 3) {
00752       /* Destroy speech structure */
00753       ast_speech_destroy(speech);
00754       datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
00755       if (datastore != NULL)
00756          ast_channel_datastore_remove(chan, datastore);
00757    } else {
00758       /* Channel is okay so restore read format */
00759       ast_set_read_format(chan, oldreadformat);
00760    }
00761 
00762    return 0;
00763 }
00764 
00765 
00766 /*! \brief SpeechDestroy() Dialplan Application */
00767 static int speech_destroy(struct ast_channel *chan, void *data)
00768 {
00769    int res = 0;
00770    struct ast_speech *speech = find_speech(chan);
00771    struct ast_datastore *datastore = NULL;
00772 
00773    if (speech == NULL)
00774       return -1;
00775 
00776    /* Destroy speech structure */
00777    ast_speech_destroy(speech);
00778 
00779    datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
00780    if (datastore != NULL) {
00781       ast_channel_datastore_remove(chan, datastore);
00782    }
00783 
00784    return res;
00785 }
00786 
00787 static int unload_module(void)
00788 {
00789    int res = 0;
00790 
00791    res = ast_unregister_application("SpeechCreate");
00792    res |= ast_unregister_application("SpeechLoadGrammar");
00793    res |= ast_unregister_application("SpeechUnloadGrammar");
00794    res |= ast_unregister_application("SpeechActivateGrammar");
00795    res |= ast_unregister_application("SpeechDeactivateGrammar");
00796    res |= ast_unregister_application("SpeechStart");
00797    res |= ast_unregister_application("SpeechBackground");
00798    res |= ast_unregister_application("SpeechDestroy");
00799    res |= ast_unregister_application("SpeechProcessingSound");
00800    res |= ast_custom_function_unregister(&speech_function);
00801    res |= ast_custom_function_unregister(&speech_score_function);
00802    res |= ast_custom_function_unregister(&speech_text_function);
00803    res |= ast_custom_function_unregister(&speech_grammar_function);
00804    res |= ast_custom_function_unregister(&speech_engine_function);
00805    res |= ast_custom_function_unregister(&speech_results_type_function);
00806 
00807    return res; 
00808 }
00809 
00810 static int load_module(void)
00811 {
00812    int res = 0;
00813 
00814    res = ast_register_application("SpeechCreate", speech_create, "Create a Speech Structure", speechcreate_descrip);
00815    res |= ast_register_application("SpeechLoadGrammar", speech_load, "Load a Grammar", speechload_descrip);
00816    res |= ast_register_application("SpeechUnloadGrammar", speech_unload, "Unload a Grammar", speechunload_descrip);
00817    res |= ast_register_application("SpeechActivateGrammar", speech_activate, "Activate a Grammar", speechactivategrammar_descrip);
00818    res |= ast_register_application("SpeechDeactivateGrammar", speech_deactivate, "Deactivate a Grammar", speechdeactivategrammar_descrip);
00819    res |= ast_register_application("SpeechStart", speech_start, "Start recognizing voice in the audio stream", speechstart_descrip);
00820    res |= ast_register_application("SpeechBackground", speech_background, "Play a sound file and wait for speech to be recognized", speechbackground_descrip);
00821    res |= ast_register_application("SpeechDestroy", speech_destroy, "End speech recognition", speechdestroy_descrip);
00822    res |= ast_register_application("SpeechProcessingSound", speech_processing_sound, "Change background processing sound", speechprocessingsound_descrip);
00823    res |= ast_custom_function_register(&speech_function);
00824    res |= ast_custom_function_register(&speech_score_function);
00825    res |= ast_custom_function_register(&speech_text_function);
00826    res |= ast_custom_function_register(&speech_grammar_function);
00827    res |= ast_custom_function_register(&speech_engine_function);
00828    res |= ast_custom_function_register(&speech_results_type_function);
00829 
00830    return res;
00831 }
00832 
00833 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan Speech Applications");

Generated on Fri Jul 24 00:40:42 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7