Sat Apr 26 2014 22:01:27

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: 411314 $");
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, ast_channel_nativeformats(chan));
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    struct ast_format oldreadformat;
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    ast_format_clear(&oldreadformat);
00679    if (speech == NULL)
00680       return -1;
00681 
00682    if (!ast_strlen_zero(args.options)) {
00683       char *options_buf = ast_strdupa(args.options);
00684       ast_app_parse_options(speech_background_options, &options, NULL, options_buf);
00685    }
00686 
00687    /* If channel is not already answered, then answer it */
00688    if (ast_channel_state(chan) != AST_STATE_UP && !ast_test_flag(&options, SB_OPT_NOANSWER)
00689       && ast_answer(chan)) {
00690          return -1;
00691    }
00692 
00693    /* Record old read format */
00694    ast_format_copy(&oldreadformat, ast_channel_readformat(chan));
00695 
00696    /* Change read format to be signed linear */
00697    if (ast_set_read_format(chan, &speech->format))
00698       return -1;
00699 
00700    if (!ast_strlen_zero(args.soundfile)) {
00701       /* Yay sound file */
00702       filename_tmp = ast_strdupa(args.soundfile);
00703       if (!ast_strlen_zero(args.timeout)) {
00704          if ((timeout = atof(args.timeout) * 1000.0) == 0)
00705             timeout = -1;
00706       } else
00707          timeout = 0;
00708    }
00709 
00710    /* See if the maximum DTMF length variable is set... we use a variable in case they want to carry it through their entire dialplan */
00711    ast_channel_lock(chan);
00712    if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_MAXLEN")) && !ast_strlen_zero(tmp2)) {
00713       max_dtmf_len = atoi(tmp2);
00714    }
00715    
00716    /* See if a terminator is specified */
00717    if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_TERMINATOR"))) {
00718       if (ast_strlen_zero(tmp2))
00719          dtmf_terminator = '\0';
00720       else
00721          dtmf_terminator = tmp2[0];
00722    }
00723    ast_channel_unlock(chan);
00724 
00725    /* Before we go into waiting for stuff... make sure the structure is ready, if not - start it again */
00726    if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
00727       ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
00728       ast_speech_start(speech);
00729    }
00730 
00731    /* Ensure no streams are currently running */
00732    ast_stopstream(chan);
00733 
00734    /* Okay it's streaming so go into a loop grabbing frames! */
00735    while (done == 0) {
00736       /* If the filename is null and stream is not running, start up a new sound file */
00737       if (!quieted && (ast_channel_streamid(chan) == -1 && ast_channel_timingfunc(chan) == NULL) && (filename = strsep(&filename_tmp, "&"))) {
00738          /* Discard old stream information */
00739          ast_stopstream(chan);
00740          /* Start new stream */
00741          speech_streamfile(chan, filename, ast_channel_language(chan));
00742       }
00743 
00744       /* Run scheduled stuff */
00745       ast_sched_runq(ast_channel_sched(chan));
00746 
00747       /* Yay scheduling */
00748       res = ast_sched_wait(ast_channel_sched(chan));
00749       if (res < 0)
00750          res = 1000;
00751 
00752       /* If there is a frame waiting, get it - if not - oh well */
00753       if (ast_waitfor(chan, res) > 0) {
00754          f = ast_read(chan);
00755          if (f == NULL) {
00756             /* The channel has hung up most likely */
00757             done = 3;
00758             break;
00759          }
00760       }
00761 
00762       /* Do timeout check (shared between audio/dtmf) */
00763       if ((!quieted || strlen(dtmf)) && started == 1) {
00764          current = ast_tvnow();
00765          if ((ast_tvdiff_ms(current, start)) >= timeout) {
00766             done = 1;
00767             if (f)
00768                ast_frfree(f);
00769             break;
00770          }
00771       }
00772 
00773       /* Do checks on speech structure to see if it's changed */
00774       ast_mutex_lock(&speech->lock);
00775       if (ast_test_flag(speech, AST_SPEECH_QUIET)) {
00776          if (ast_channel_stream(chan))
00777             ast_stopstream(chan);
00778          ast_clear_flag(speech, AST_SPEECH_QUIET);
00779          quieted = 1;
00780       }
00781       /* Check state so we can see what to do */
00782       switch (speech->state) {
00783       case AST_SPEECH_STATE_READY:
00784          /* If audio playback has stopped do a check for timeout purposes */
00785          if (ast_channel_streamid(chan) == -1 && ast_channel_timingfunc(chan) == NULL)
00786             ast_stopstream(chan);
00787          if (!quieted && ast_channel_stream(chan) == NULL && timeout && started == 0 && !filename_tmp) {
00788             if (timeout == -1) {
00789                done = 1;
00790                if (f)
00791                   ast_frfree(f);
00792                break;
00793             }
00794             start = ast_tvnow();
00795             started = 1;
00796          }
00797          /* Write audio frame out to speech engine if no DTMF has been received */
00798          if (!strlen(dtmf) && f != NULL && f->frametype == AST_FRAME_VOICE) {
00799             ast_speech_write(speech, f->data.ptr, f->datalen);
00800          }
00801          break;
00802       case AST_SPEECH_STATE_WAIT:
00803          /* Cue up waiting sound if not already playing */
00804          if (!strlen(dtmf)) {
00805             if (ast_channel_stream(chan) == NULL) {
00806                if (speech->processing_sound != NULL) {
00807                   if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound, "none")) {
00808                      speech_streamfile(chan, speech->processing_sound, ast_channel_language(chan));
00809                   }
00810                }
00811             } else if (ast_channel_streamid(chan) == -1 && ast_channel_timingfunc(chan) == NULL) {
00812                ast_stopstream(chan);
00813                if (speech->processing_sound != NULL) {
00814                   if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound, "none")) {
00815                      speech_streamfile(chan, speech->processing_sound, ast_channel_language(chan));
00816                   }
00817                }
00818             }
00819          }
00820          break;
00821       case AST_SPEECH_STATE_DONE:
00822          /* Now that we are done... let's switch back to not ready state */
00823          ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
00824          if (!strlen(dtmf)) {
00825             /* Copy to speech structure the results, if available */
00826             speech->results = ast_speech_results_get(speech);
00827             /* Break out of our background too */
00828             done = 1;
00829             /* Stop audio playback */
00830             if (ast_channel_stream(chan) != NULL) {
00831                ast_stopstream(chan);
00832             }
00833          }
00834          break;
00835       default:
00836          break;
00837       }
00838       ast_mutex_unlock(&speech->lock);
00839 
00840       /* Deal with other frame types */
00841       if (f != NULL) {
00842          /* Free the frame we received */
00843          switch (f->frametype) {
00844          case AST_FRAME_DTMF:
00845             if (dtmf_terminator != '\0' && f->subclass.integer == dtmf_terminator) {
00846                done = 1;
00847             } else {
00848                quieted = 1;
00849                if (ast_channel_stream(chan) != NULL) {
00850                   ast_stopstream(chan);
00851                }
00852                if (!started) {
00853                   /* Change timeout to be 5 seconds for DTMF input */
00854                   timeout = (ast_channel_pbx(chan) && ast_channel_pbx(chan)->dtimeoutms) ? ast_channel_pbx(chan)->dtimeoutms : 5000;
00855                   started = 1;
00856                }
00857                start = ast_tvnow();
00858                snprintf(tmp, sizeof(tmp), "%c", f->subclass.integer);
00859                strncat(dtmf, tmp, sizeof(dtmf) - strlen(dtmf) - 1);
00860                /* If the maximum length of the DTMF has been reached, stop now */
00861                if (max_dtmf_len && strlen(dtmf) == max_dtmf_len)
00862                   done = 1;
00863             }
00864             break;
00865          case AST_FRAME_CONTROL:
00866             switch (f->subclass.integer) {
00867             case AST_CONTROL_HANGUP:
00868                /* Since they hung up we should destroy the speech structure */
00869                done = 3;
00870             default:
00871                break;
00872             }
00873          default:
00874             break;
00875          }
00876          ast_frfree(f);
00877          f = NULL;
00878       }
00879    }
00880 
00881    if (!ast_strlen_zero(dtmf)) {
00882       /* We sort of make a results entry */
00883       speech->results = ast_calloc(1, sizeof(*speech->results));
00884       if (speech->results != NULL) {
00885          ast_speech_dtmf(speech, dtmf);
00886          speech->results->score = 1000;
00887          speech->results->text = ast_strdup(dtmf);
00888          speech->results->grammar = ast_strdup("dtmf");
00889       }
00890       ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
00891    }
00892 
00893    /* See if it was because they hung up */
00894    if (done == 3) {
00895       /* Destroy speech structure */
00896       ast_speech_destroy(speech);
00897       datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
00898       if (datastore != NULL)
00899          ast_channel_datastore_remove(chan, datastore);
00900    } else {
00901       /* Channel is okay so restore read format */
00902       ast_set_read_format(chan, &oldreadformat);
00903    }
00904 
00905    return 0;
00906 }
00907 
00908 
00909 /*! \brief SpeechDestroy() Dialplan Application */
00910 static int speech_destroy(struct ast_channel *chan, const char *data)
00911 {
00912    int res = 0;
00913    struct ast_speech *speech = find_speech(chan);
00914    struct ast_datastore *datastore = NULL;
00915 
00916    if (speech == NULL)
00917       return -1;
00918 
00919    /* Destroy speech structure */
00920    ast_speech_destroy(speech);
00921 
00922    datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
00923    if (datastore != NULL) {
00924       ast_channel_datastore_remove(chan, datastore);
00925    }
00926 
00927    return res;
00928 }
00929 
00930 static int unload_module(void)
00931 {
00932    int res = 0;
00933 
00934    res = ast_unregister_application("SpeechCreate");
00935    res |= ast_unregister_application("SpeechLoadGrammar");
00936    res |= ast_unregister_application("SpeechUnloadGrammar");
00937    res |= ast_unregister_application("SpeechActivateGrammar");
00938    res |= ast_unregister_application("SpeechDeactivateGrammar");
00939    res |= ast_unregister_application("SpeechStart");
00940    res |= ast_unregister_application("SpeechBackground");
00941    res |= ast_unregister_application("SpeechDestroy");
00942    res |= ast_unregister_application("SpeechProcessingSound");
00943    res |= ast_custom_function_unregister(&speech_function);
00944    res |= ast_custom_function_unregister(&speech_score_function);
00945    res |= ast_custom_function_unregister(&speech_text_function);
00946    res |= ast_custom_function_unregister(&speech_grammar_function);
00947    res |= ast_custom_function_unregister(&speech_engine_function);
00948    res |= ast_custom_function_unregister(&speech_results_type_function);
00949 
00950    return res; 
00951 }
00952 
00953 static int load_module(void)
00954 {
00955    int res = 0;
00956 
00957    res = ast_register_application_xml("SpeechCreate", speech_create);
00958    res |= ast_register_application_xml("SpeechLoadGrammar", speech_load);
00959    res |= ast_register_application_xml("SpeechUnloadGrammar", speech_unload);
00960    res |= ast_register_application_xml("SpeechActivateGrammar", speech_activate);
00961    res |= ast_register_application_xml("SpeechDeactivateGrammar", speech_deactivate);
00962    res |= ast_register_application_xml("SpeechStart", speech_start);
00963    res |= ast_register_application_xml("SpeechBackground", speech_background);
00964    res |= ast_register_application_xml("SpeechDestroy", speech_destroy);
00965    res |= ast_register_application_xml("SpeechProcessingSound", speech_processing_sound);
00966    res |= ast_custom_function_register(&speech_function);
00967    res |= ast_custom_function_register(&speech_score_function);
00968    res |= ast_custom_function_register(&speech_text_function);
00969    res |= ast_custom_function_register(&speech_grammar_function);
00970    res |= ast_custom_function_register(&speech_engine_function);
00971    res |= ast_custom_function_register(&speech_results_type_function);
00972 
00973    return res;
00974 }
00975 
00976 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Dialplan Speech Applications",
00977       .load = load_module,
00978       .unload = unload_module,
00979       .nonoptreq = "res_speech",
00980       );