Mon Aug 31 12:29:59 2015

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

Generated on 31 Aug 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1