Mon Oct 8 12:38:56 2012

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: 362815 $");
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    datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
00289    if (datastore == NULL) {
00290       return NULL;
00291    }
00292    speech = datastore->data;
00293 
00294    return speech;
00295 }
00296 
00297 /* Helper function to find a specific speech recognition result by number and nbest alternative */
00298 static struct ast_speech_result *find_result(struct ast_speech_result *results, char *result_num)
00299 {
00300    struct ast_speech_result *result = results;
00301    char *tmp = NULL;
00302    int nbest_num = 0, wanted_num = 0, i = 0;
00303 
00304    if (!result) {
00305       return NULL;
00306    }
00307 
00308    if ((tmp = strchr(result_num, '/'))) {
00309       *tmp++ = '\0';
00310       nbest_num = atoi(result_num);
00311       wanted_num = atoi(tmp);
00312    } else {
00313       wanted_num = atoi(result_num);
00314    }
00315 
00316    do {
00317       if (result->nbest_num != nbest_num)
00318          continue;
00319       if (i == wanted_num)
00320          break;
00321       i++;
00322    } while ((result = AST_LIST_NEXT(result, list)));
00323 
00324    return result;
00325 }
00326 
00327 /*! \brief SPEECH_SCORE() Dialplan Function */
00328 static int speech_score(struct ast_channel *chan, const char *cmd, char *data,
00329              char *buf, size_t len)
00330 {
00331    struct ast_speech_result *result = NULL;
00332    struct ast_speech *speech = find_speech(chan);
00333    char tmp[128] = "";
00334 
00335    if (data == NULL || speech == NULL || !(result = find_result(speech->results, data))) {
00336       return -1;
00337    }
00338    
00339    snprintf(tmp, sizeof(tmp), "%d", result->score);
00340    
00341    ast_copy_string(buf, tmp, len);
00342 
00343    return 0;
00344 }
00345 
00346 static struct ast_custom_function speech_score_function = {
00347    .name = "SPEECH_SCORE",
00348    .read = speech_score,
00349    .write = NULL,
00350 };
00351 
00352 /*! \brief SPEECH_TEXT() Dialplan Function */
00353 static int speech_text(struct ast_channel *chan, const char *cmd, char *data,
00354          char *buf, size_t len)
00355 {
00356    struct ast_speech_result *result = NULL;
00357    struct ast_speech *speech = find_speech(chan);
00358 
00359    if (data == NULL || speech == NULL || !(result = find_result(speech->results, data))) {
00360       return -1;
00361    }
00362 
00363    if (result->text != NULL) {
00364       ast_copy_string(buf, result->text, len);
00365    } else {
00366       buf[0] = '\0';
00367    }
00368 
00369    return 0;
00370 }
00371 
00372 static struct ast_custom_function speech_text_function = {
00373    .name = "SPEECH_TEXT",
00374    .read = speech_text,
00375    .write = NULL,
00376 };
00377 
00378 /*! \brief SPEECH_GRAMMAR() Dialplan Function */
00379 static int speech_grammar(struct ast_channel *chan, const char *cmd, char *data,
00380          char *buf, size_t len)
00381 {
00382    struct ast_speech_result *result = NULL;
00383    struct ast_speech *speech = find_speech(chan);
00384 
00385    if (data == NULL || speech == NULL || !(result = find_result(speech->results, data))) {
00386       return -1;
00387    }
00388 
00389    if (result->grammar != NULL) {
00390       ast_copy_string(buf, result->grammar, len);
00391    } else {
00392       buf[0] = '\0';
00393    }
00394 
00395    return 0;
00396 }
00397 
00398 static struct ast_custom_function speech_grammar_function = {
00399    .name = "SPEECH_GRAMMAR",
00400    .read = speech_grammar,
00401    .write = NULL,
00402 };
00403 
00404 /*! \brief SPEECH_ENGINE() Dialplan Function */
00405 static int speech_engine_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00406 {
00407    struct ast_speech *speech = find_speech(chan);
00408 
00409    if (data == NULL || speech == NULL) {
00410       return -1;
00411    }
00412 
00413    ast_speech_change(speech, data, value);
00414 
00415    return 0;
00416 }
00417 
00418 static struct ast_custom_function speech_engine_function = {
00419    .name = "SPEECH_ENGINE",
00420    .read = NULL,
00421    .write = speech_engine_write,
00422 };
00423 
00424 /*! \brief SPEECH_RESULTS_TYPE() Dialplan Function */
00425 static int speech_results_type_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00426 {
00427    struct ast_speech *speech = find_speech(chan);
00428 
00429    if (data == NULL || speech == NULL)
00430       return -1;
00431 
00432    if (!strcasecmp(value, "normal"))
00433       ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NORMAL);
00434    else if (!strcasecmp(value, "nbest"))
00435       ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NBEST);
00436 
00437    return 0;
00438 }
00439 
00440 static struct ast_custom_function speech_results_type_function = {
00441    .name = "SPEECH_RESULTS_TYPE",
00442    .read = NULL,
00443    .write = speech_results_type_write,
00444 };
00445 
00446 /*! \brief SPEECH() Dialplan Function */
00447 static int speech_read(struct ast_channel *chan, const char *cmd, char *data,
00448          char *buf, size_t len)
00449 {
00450    int results = 0;
00451    struct ast_speech_result *result = NULL;
00452    struct ast_speech *speech = find_speech(chan);
00453    char tmp[128] = "";
00454 
00455    /* Now go for the various options */
00456    if (!strcasecmp(data, "status")) {
00457       if (speech != NULL)
00458          ast_copy_string(buf, "1", len);
00459       else
00460          ast_copy_string(buf, "0", len);
00461       return 0;
00462    }
00463 
00464    /* Make sure we have a speech structure for everything else */
00465    if (speech == NULL) {
00466       return -1;
00467    }
00468 
00469    /* Check to see if they are checking for silence */
00470    if (!strcasecmp(data, "spoke")) {
00471       if (ast_test_flag(speech, AST_SPEECH_SPOKE))
00472          ast_copy_string(buf, "1", len);
00473       else
00474          ast_copy_string(buf, "0", len);
00475    } else if (!strcasecmp(data, "results")) {
00476       /* Count number of results */
00477       for (result = speech->results; result; result = AST_LIST_NEXT(result, list))
00478          results++;
00479       snprintf(tmp, sizeof(tmp), "%d", results);
00480       ast_copy_string(buf, tmp, len);
00481    } else {
00482       buf[0] = '\0';
00483    }
00484 
00485    return 0;
00486 }
00487 
00488 static struct ast_custom_function speech_function = {
00489    .name = "SPEECH",
00490    .read = speech_read,
00491    .write = NULL,
00492 };
00493 
00494 
00495 
00496 /*! \brief SpeechCreate() Dialplan Application */
00497 static int speech_create(struct ast_channel *chan, const char *data)
00498 {
00499    struct ast_speech *speech = NULL;
00500    struct ast_datastore *datastore = NULL;
00501 
00502    /* Request a speech object */
00503    speech = ast_speech_new(data, chan->nativeformats);
00504    if (speech == NULL) {
00505       /* Not available */
00506       pbx_builtin_setvar_helper(chan, "ERROR", "1");
00507       return 0;
00508    }
00509 
00510    datastore = ast_datastore_alloc(&speech_datastore, NULL);
00511    if (datastore == NULL) {
00512       ast_speech_destroy(speech);
00513       pbx_builtin_setvar_helper(chan, "ERROR", "1");
00514       return 0;
00515    }
00516    pbx_builtin_setvar_helper(chan, "ERROR", NULL);
00517    datastore->data = speech;
00518    ast_channel_datastore_add(chan, datastore);
00519 
00520    return 0;
00521 }
00522 
00523 /*! \brief SpeechLoadGrammar(Grammar Name,Path) Dialplan Application */
00524 static int speech_load(struct ast_channel *chan, const char *vdata)
00525 {
00526    int res = 0;
00527    struct ast_speech *speech = find_speech(chan);
00528    char *data;
00529    AST_DECLARE_APP_ARGS(args,
00530       AST_APP_ARG(grammar);
00531       AST_APP_ARG(path);
00532    );
00533 
00534    data = ast_strdupa(vdata);
00535    AST_STANDARD_APP_ARGS(args, data);
00536 
00537    if (speech == NULL)
00538       return -1;
00539 
00540    if (args.argc != 2)
00541       return -1;
00542 
00543    /* Load the grammar locally on the object */
00544    res = ast_speech_grammar_load(speech, args.grammar, args.path);
00545 
00546    return res;
00547 }
00548 
00549 /*! \brief SpeechUnloadGrammar(Grammar Name) Dialplan Application */
00550 static int speech_unload(struct ast_channel *chan, const char *data)
00551 {
00552    int res = 0;
00553    struct ast_speech *speech = find_speech(chan);
00554 
00555    if (speech == NULL)
00556       return -1;
00557 
00558    /* Unload the grammar */
00559    res = ast_speech_grammar_unload(speech, data);
00560 
00561    return res;
00562 }
00563 
00564 /*! \brief SpeechDeactivateGrammar(Grammar Name) Dialplan Application */
00565 static int speech_deactivate(struct ast_channel *chan, const char *data)
00566 {
00567    int res = 0;
00568    struct ast_speech *speech = find_speech(chan);
00569 
00570    if (speech == NULL)
00571       return -1;
00572 
00573    /* Deactivate the grammar on the speech object */
00574    res = ast_speech_grammar_deactivate(speech, data);
00575 
00576    return res;
00577 }
00578 
00579 /*! \brief SpeechActivateGrammar(Grammar Name) Dialplan Application */
00580 static int speech_activate(struct ast_channel *chan, const char *data)
00581 {
00582    int res = 0;
00583    struct ast_speech *speech = find_speech(chan);
00584 
00585    if (speech == NULL)
00586       return -1;
00587 
00588    /* Activate the grammar on the speech object */
00589    res = ast_speech_grammar_activate(speech, data);
00590 
00591    return res;
00592 }
00593 
00594 /*! \brief SpeechStart() Dialplan Application */
00595 static int speech_start(struct ast_channel *chan, const char *data)
00596 {
00597    int res = 0;
00598    struct ast_speech *speech = find_speech(chan);
00599 
00600    if (speech == NULL)
00601       return -1;
00602 
00603    ast_speech_start(speech);
00604 
00605    return res;
00606 }
00607 
00608 /*! \brief SpeechProcessingSound(Sound File) Dialplan Application */
00609 static int speech_processing_sound(struct ast_channel *chan, const char *data)
00610 {
00611    int res = 0;
00612    struct ast_speech *speech = find_speech(chan);
00613 
00614    if (speech == NULL)
00615       return -1;
00616 
00617    if (speech->processing_sound != NULL) {
00618       ast_free(speech->processing_sound);
00619       speech->processing_sound = NULL;
00620    }
00621 
00622    speech->processing_sound = ast_strdup(data);
00623 
00624    return res;
00625 }
00626 
00627 /*! \brief Helper function used by speech_background to playback a soundfile */
00628 static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
00629 {
00630    struct ast_filestream *fs = NULL;
00631 
00632    if (!(fs = ast_openstream(chan, filename, preflang)))
00633       return -1;
00634    
00635    if (ast_applystream(chan, fs))
00636       return -1;
00637    
00638    ast_playstream(fs);
00639 
00640    return 0;
00641 }
00642 
00643 enum {
00644    SB_OPT_NOANSWER = (1 << 0),
00645 };
00646 
00647 AST_APP_OPTIONS(speech_background_options, BEGIN_OPTIONS
00648    AST_APP_OPTION('n', SB_OPT_NOANSWER),
00649 END_OPTIONS );
00650 
00651 /*! \brief SpeechBackground(Sound File,Timeout) Dialplan Application */
00652 static int speech_background(struct ast_channel *chan, const char *data)
00653 {
00654    unsigned int timeout = 0;
00655    int res = 0, done = 0, started = 0, quieted = 0, max_dtmf_len = 0;
00656    struct ast_speech *speech = find_speech(chan);
00657    struct ast_frame *f = NULL;
00658    int oldreadformat = AST_FORMAT_SLINEAR;
00659    char dtmf[AST_MAX_EXTENSION] = "";
00660    struct timeval start = { 0, 0 }, current;
00661    struct ast_datastore *datastore = NULL;
00662    char *parse, *filename_tmp = NULL, *filename = NULL, tmp[2] = "", dtmf_terminator = '#';
00663    const char *tmp2 = NULL;
00664    struct ast_flags options = { 0 };
00665    AST_DECLARE_APP_ARGS(args,
00666       AST_APP_ARG(soundfile);
00667       AST_APP_ARG(timeout);
00668       AST_APP_ARG(options);
00669    );
00670 
00671    parse = ast_strdupa(data);
00672    AST_STANDARD_APP_ARGS(args, parse);
00673 
00674    if (speech == NULL)
00675       return -1;
00676 
00677    if (!ast_strlen_zero(args.options)) {
00678       char *options_buf = ast_strdupa(args.options);
00679       ast_app_parse_options(speech_background_options, &options, NULL, options_buf);
00680    }
00681 
00682    /* If channel is not already answered, then answer it */
00683    if (chan->_state != AST_STATE_UP && !ast_test_flag(&options, SB_OPT_NOANSWER)
00684       && ast_answer(chan)) {
00685          return -1;
00686    }
00687 
00688    /* Record old read format */
00689    oldreadformat = chan->readformat;
00690 
00691    /* Change read format to be signed linear */
00692    if (ast_set_read_format(chan, speech->format))
00693       return -1;
00694 
00695    if (!ast_strlen_zero(args.soundfile)) {
00696       /* Yay sound file */
00697       filename_tmp = ast_strdupa(args.soundfile);
00698       if (!ast_strlen_zero(args.timeout)) {
00699          if ((timeout = atof(args.timeout) * 1000.0) == 0)
00700             timeout = -1;
00701       } else
00702          timeout = 0;
00703    }
00704 
00705    /* See if the maximum DTMF length variable is set... we use a variable in case they want to carry it through their entire dialplan */
00706    ast_channel_lock(chan);
00707    if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_MAXLEN")) && !ast_strlen_zero(tmp2)) {
00708       max_dtmf_len = atoi(tmp2);
00709    }
00710    
00711    /* See if a terminator is specified */
00712    if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_TERMINATOR"))) {
00713       if (ast_strlen_zero(tmp2))
00714          dtmf_terminator = '\0';
00715       else
00716          dtmf_terminator = tmp2[0];
00717    }
00718    ast_channel_unlock(chan);
00719 
00720    /* Before we go into waiting for stuff... make sure the structure is ready, if not - start it again */
00721    if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
00722       ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
00723       ast_speech_start(speech);
00724    }
00725 
00726    /* Ensure no streams are currently running */
00727    ast_stopstream(chan);
00728 
00729    /* Okay it's streaming so go into a loop grabbing frames! */
00730    while (done == 0) {
00731       /* If the filename is null and stream is not running, start up a new sound file */
00732       if (!quieted && (chan->streamid == -1 && chan->timingfunc == NULL) && (filename = strsep(&filename_tmp, "&"))) {
00733          /* Discard old stream information */
00734          ast_stopstream(chan);
00735          /* Start new stream */
00736          speech_streamfile(chan, filename, chan->language);
00737       }
00738 
00739       /* Run scheduled stuff */
00740       ast_sched_runq(chan->sched);
00741 
00742       /* Yay scheduling */
00743       res = ast_sched_wait(chan->sched);
00744       if (res < 0)
00745          res = 1000;
00746 
00747       /* If there is a frame waiting, get it - if not - oh well */
00748       if (ast_waitfor(chan, res) > 0) {
00749          f = ast_read(chan);
00750          if (f == NULL) {
00751             /* The channel has hung up most likely */
00752             done = 3;
00753             break;
00754          }
00755       }
00756 
00757       /* Do timeout check (shared between audio/dtmf) */
00758       if ((!quieted || strlen(dtmf)) && started == 1) {
00759          current = ast_tvnow();
00760          if ((ast_tvdiff_ms(current, start)) >= timeout) {
00761             done = 1;
00762             if (f)
00763                ast_frfree(f);
00764             break;
00765          }
00766       }
00767 
00768       /* Do checks on speech structure to see if it's changed */
00769       ast_mutex_lock(&speech->lock);
00770       if (ast_test_flag(speech, AST_SPEECH_QUIET)) {
00771          if (chan->stream)
00772             ast_stopstream(chan);
00773          ast_clear_flag(speech, AST_SPEECH_QUIET);
00774          quieted = 1;
00775       }
00776       /* Check state so we can see what to do */
00777       switch (speech->state) {
00778       case AST_SPEECH_STATE_READY:
00779          /* If audio playback has stopped do a check for timeout purposes */
00780          if (chan->streamid == -1 && chan->timingfunc == NULL)
00781             ast_stopstream(chan);
00782          if (!quieted && chan->stream == NULL && timeout && started == 0 && !filename_tmp) {
00783             if (timeout == -1) {
00784                done = 1;
00785                if (f)
00786                   ast_frfree(f);
00787                break;
00788             }
00789             start = ast_tvnow();
00790             started = 1;
00791          }
00792          /* Write audio frame out to speech engine if no DTMF has been received */
00793          if (!strlen(dtmf) && f != NULL && f->frametype == AST_FRAME_VOICE) {
00794             ast_speech_write(speech, f->data.ptr, f->datalen);
00795          }
00796          break;
00797       case AST_SPEECH_STATE_WAIT:
00798          /* Cue up waiting sound if not already playing */
00799          if (!strlen(dtmf)) {
00800             if (chan->stream == NULL) {
00801                if (speech->processing_sound != NULL) {
00802                   if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound, "none")) {
00803                      speech_streamfile(chan, speech->processing_sound, chan->language);
00804                   }
00805                }
00806             } else if (chan->streamid == -1 && chan->timingfunc == NULL) {
00807                ast_stopstream(chan);
00808                if (speech->processing_sound != NULL) {
00809                   if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound, "none")) {
00810                      speech_streamfile(chan, speech->processing_sound, chan->language);
00811                   }
00812                }
00813             }
00814          }
00815          break;
00816       case AST_SPEECH_STATE_DONE:
00817          /* Now that we are done... let's switch back to not ready state */
00818          ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
00819          if (!strlen(dtmf)) {
00820             /* Copy to speech structure the results, if available */
00821             speech->results = ast_speech_results_get(speech);
00822             /* Break out of our background too */
00823             done = 1;
00824             /* Stop audio playback */
00825             if (chan->stream != NULL) {
00826                ast_stopstream(chan);
00827             }
00828          }
00829          break;
00830       default:
00831          break;
00832       }
00833       ast_mutex_unlock(&speech->lock);
00834 
00835       /* Deal with other frame types */
00836       if (f != NULL) {
00837          /* Free the frame we received */
00838          switch (f->frametype) {
00839          case AST_FRAME_DTMF:
00840             if (dtmf_terminator != '\0' && f->subclass.integer == dtmf_terminator) {
00841                done = 1;
00842             } else {
00843                quieted = 1;
00844                if (chan->stream != NULL) {
00845                   ast_stopstream(chan);
00846                }
00847                if (!started) {
00848                   /* Change timeout to be 5 seconds for DTMF input */
00849                   timeout = (chan->pbx && chan->pbx->dtimeoutms) ? chan->pbx->dtimeoutms : 5000;
00850                   started = 1;
00851                }
00852                start = ast_tvnow();
00853                snprintf(tmp, sizeof(tmp), "%c", f->subclass.integer);
00854                strncat(dtmf, tmp, sizeof(dtmf) - strlen(dtmf) - 1);
00855                /* If the maximum length of the DTMF has been reached, stop now */
00856                if (max_dtmf_len && strlen(dtmf) == max_dtmf_len)
00857                   done = 1;
00858             }
00859             break;
00860          case AST_FRAME_CONTROL:
00861             switch (f->subclass.integer) {
00862             case AST_CONTROL_HANGUP:
00863                /* Since they hung up we should destroy the speech structure */
00864                done = 3;
00865             default:
00866                break;
00867             }
00868          default:
00869             break;
00870          }
00871          ast_frfree(f);
00872          f = NULL;
00873       }
00874    }
00875 
00876    if (!ast_strlen_zero(dtmf)) {
00877       /* We sort of make a results entry */
00878       speech->results = ast_calloc(1, sizeof(*speech->results));
00879       if (speech->results != NULL) {
00880          ast_speech_dtmf(speech, dtmf);
00881          speech->results->score = 1000;
00882          speech->results->text = ast_strdup(dtmf);
00883          speech->results->grammar = ast_strdup("dtmf");
00884       }
00885       ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
00886    }
00887 
00888    /* See if it was because they hung up */
00889    if (done == 3) {
00890       /* Destroy speech structure */
00891       ast_speech_destroy(speech);
00892       datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
00893       if (datastore != NULL)
00894          ast_channel_datastore_remove(chan, datastore);
00895    } else {
00896       /* Channel is okay so restore read format */
00897       ast_set_read_format(chan, oldreadformat);
00898    }
00899 
00900    return 0;
00901 }
00902 
00903 
00904 /*! \brief SpeechDestroy() Dialplan Application */
00905 static int speech_destroy(struct ast_channel *chan, const char *data)
00906 {
00907    int res = 0;
00908    struct ast_speech *speech = find_speech(chan);
00909    struct ast_datastore *datastore = NULL;
00910 
00911    if (speech == NULL)
00912       return -1;
00913 
00914    /* Destroy speech structure */
00915    ast_speech_destroy(speech);
00916 
00917    datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
00918    if (datastore != NULL) {
00919       ast_channel_datastore_remove(chan, datastore);
00920    }
00921 
00922    return res;
00923 }
00924 
00925 static int unload_module(void)
00926 {
00927    int res = 0;
00928 
00929    res = ast_unregister_application("SpeechCreate");
00930    res |= ast_unregister_application("SpeechLoadGrammar");
00931    res |= ast_unregister_application("SpeechUnloadGrammar");
00932    res |= ast_unregister_application("SpeechActivateGrammar");
00933    res |= ast_unregister_application("SpeechDeactivateGrammar");
00934    res |= ast_unregister_application("SpeechStart");
00935    res |= ast_unregister_application("SpeechBackground");
00936    res |= ast_unregister_application("SpeechDestroy");
00937    res |= ast_unregister_application("SpeechProcessingSound");
00938    res |= ast_custom_function_unregister(&speech_function);
00939    res |= ast_custom_function_unregister(&speech_score_function);
00940    res |= ast_custom_function_unregister(&speech_text_function);
00941    res |= ast_custom_function_unregister(&speech_grammar_function);
00942    res |= ast_custom_function_unregister(&speech_engine_function);
00943    res |= ast_custom_function_unregister(&speech_results_type_function);
00944 
00945    return res; 
00946 }
00947 
00948 static int load_module(void)
00949 {
00950    int res = 0;
00951 
00952    res = ast_register_application_xml("SpeechCreate", speech_create);
00953    res |= ast_register_application_xml("SpeechLoadGrammar", speech_load);
00954    res |= ast_register_application_xml("SpeechUnloadGrammar", speech_unload);
00955    res |= ast_register_application_xml("SpeechActivateGrammar", speech_activate);
00956    res |= ast_register_application_xml("SpeechDeactivateGrammar", speech_deactivate);
00957    res |= ast_register_application_xml("SpeechStart", speech_start);
00958    res |= ast_register_application_xml("SpeechBackground", speech_background);
00959    res |= ast_register_application_xml("SpeechDestroy", speech_destroy);
00960    res |= ast_register_application_xml("SpeechProcessingSound", speech_processing_sound);
00961    res |= ast_custom_function_register(&speech_function);
00962    res |= ast_custom_function_register(&speech_score_function);
00963    res |= ast_custom_function_register(&speech_text_function);
00964    res |= ast_custom_function_register(&speech_grammar_function);
00965    res |= ast_custom_function_register(&speech_engine_function);
00966    res |= ast_custom_function_register(&speech_results_type_function);
00967 
00968    return res;
00969 }
00970 
00971 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Dialplan Speech Applications",
00972       .load = load_module,
00973       .unload = unload_module,
00974       .nonoptreq = "res_speech",
00975       );

Generated on Mon Oct 8 12:38:56 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7