Thu Apr 28 2011 16:56:41

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