Mon Apr 28 2014 10:05:35

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