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

Generated on Sat Mar 10 01:53:59 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7