Wed Mar 4 19:58:00 2009

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

Generated on Wed Mar 4 19:58:00 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7