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

Generated on Thu Apr 16 06:27:12 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6