Updates to speech recognition API and dialplan utilities. Moved to using dialplan...
authorJoshua Colp <jcolp@digium.com>
Thu, 13 Apr 2006 00:18:52 +0000 (00:18 +0000)
committerJoshua Colp <jcolp@digium.com>
Thu, 13 Apr 2006 00:18:52 +0000 (00:18 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@19645 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_speech_utils.c
doc/speechrec.txt
include/asterisk/speech.h
res/res_speech.c

index 264f56b..babf39b 100644 (file)
@@ -86,6 +86,15 @@ static char *speechdestroy_descrip =
 "If you call this application but end up wanting to recognize more speech, you must call SpeechCreate\n"
        "again before calling any other application. It takes no arguments.\n";
 
+static char *speechload_descrip =
+"SpeechLoadGrammar(Grammar Name|Path)\n"
+"Load a grammar only on the channel, not globally.\n"
+"It takes the grammar name as first argument and path as second.\n";
+
+static char *speechunload_descrip =
+"SpeechUnloadGrammar(Grammar Name)\n"
+"Unload a grammar. It takes the grammar name as the only argument.\n";
+
 /*! \brief Helper function used by datastores to destroy the speech structure upon hangup */
 static void destroy_callback(void *data)
 {
@@ -122,6 +131,161 @@ static struct ast_speech *find_speech(struct ast_channel *chan)
        return speech;
 }
 
+/* Helper function to find a specific speech recognition result by number */
+static struct ast_speech_result *find_result(struct ast_speech_result *results, int num)
+{
+       struct ast_speech_result *result = NULL;
+       int i = 0;
+
+       result = results;
+       while (result) {
+               if (i == num)
+                       break;
+               i++;
+               result = result->next;
+       }
+
+       return result;
+}
+
+/*! \brief SPEECH_SCORE() Dialplan Function */
+static int speech_score(struct ast_channel *chan, char *cmd, char *data,
+                      char *buf, size_t len)
+{
+       struct ast_speech_result *result = NULL;
+       struct ast_speech *speech = find_speech(chan);
+       char tmp[128] = "";
+
+       if (data == NULL || speech == NULL || !(result = find_result(speech->results, atoi(data))))
+               return -1;
+       
+       snprintf(tmp, sizeof(tmp), "%d", result->score);
+       
+       ast_copy_string(buf, tmp, len);
+
+       return 0;
+}
+
+static struct ast_custom_function speech_score_function = {
+        .name = "SPEECH_SCORE",
+        .synopsis = "Gets the confidence score of a result.\n",
+        .syntax = "SPEECH_SCORE(result number)",
+        .desc =
+        "Gets the confidence score of a result.\n",
+        .read = speech_score,
+        .write = NULL,
+};
+
+/*! \brief SPEECH_TEXT() Dialplan Function */
+static int speech_text(struct ast_channel *chan, char *cmd, char *data,
+                       char *buf, size_t len)
+{
+        struct ast_speech_result *result = NULL;
+        struct ast_speech *speech = find_speech(chan);
+
+       if (data == NULL || speech == NULL || !(result = find_result(speech->results, atoi(data))))
+                return -1;
+
+       if (result->text != NULL)
+               ast_copy_string(buf, result->text, len);
+
+        return 0;
+}
+
+static struct ast_custom_function speech_text_function = {
+        .name = "SPEECH_TEXT",
+        .synopsis = "Gets the recognized text of a result.\n",
+        .syntax = "SPEECH_TEXT(result number)",
+        .desc =
+        "Gets the recognized text of a result.\n",
+        .read = speech_text,
+        .write = NULL,
+};
+
+/*! \brief SPEECH_GRAMMAR() Dialplan Function */
+static int speech_grammar(struct ast_channel *chan, char *cmd, char *data,
+                       char *buf, size_t len)
+{
+        struct ast_speech_result *result = NULL;
+        struct ast_speech *speech = find_speech(chan);
+
+       if (data == NULL || speech == NULL || !(result = find_result(speech->results, atoi(data))))
+                return -1;
+
+       if (result->grammar != NULL)
+               ast_copy_string(buf, result->grammar, len);
+
+        return 0;
+}
+
+static struct ast_custom_function speech_grammar_function = {
+        .name = "SPEECH_GRAMMAR",
+        .synopsis = "Gets the matched grammar of a result if available.",
+        .syntax = "SPEECH_GRAMMAR(result number)",
+        .desc =
+        "Gets the matched grammar of a result if available.\n",
+        .read = speech_grammar,
+        .write = NULL,
+};
+
+/*! \brief SPEECH() Dialplan Function */
+static int speech_read(struct ast_channel *chan, char *cmd, char *data,
+                       char *buf, size_t len)
+{
+       int results = 0;
+       struct ast_speech_result *result = NULL;
+       struct ast_speech *speech = find_speech(chan);
+       char tmp[128] = "";
+
+       /* Now go for the various options */
+       if (!strcasecmp(data, "status")) {
+               if (speech != NULL)
+                       ast_copy_string(buf, "1", len);
+               else
+                       ast_copy_string(buf, "0", len);
+               return 0;
+       }
+
+       /* Make sure we have a speech structure for everything else */
+       if (speech == NULL) {
+               return -1;
+       }
+
+       /* Check to see if they are checking for silence */
+       if (!strcasecmp(data, "spoke")) {
+               if (ast_test_flag(speech, AST_SPEECH_SPOKE))
+                       ast_copy_string(buf, "1", len);
+               else
+                       ast_copy_string(buf, "0", len);
+       } else if (!strcasecmp(data, "results")) {
+               /* Count number of results */
+               result = speech->results;
+               while (result) {
+                       results++;
+                       result = result->next;
+               }
+               snprintf(tmp, sizeof(tmp), "%d", results);
+               ast_copy_string(buf, tmp, len);
+       }
+
+       return 0;
+}
+
+static struct ast_custom_function speech_function = {
+        .name = "SPEECH",
+        .synopsis = "Gets information about speech recognition results.",
+        .syntax = "SPEECH(argument)",
+        .desc =
+       "Gets information about speech recognition results.\n"
+       "status:   Returns 1 upon speech object existing, or 0 if not\n"
+       "spoke:  Returns 1 if spoker spoke, or 0 if not\n"
+       "results:  Returns number of results that were recognized\n",
+        .read = speech_read,
+        .write = NULL,
+};
+
+
+
 /*! \brief SpeechCreate() Dialplan Application */
 static int speech_create(struct ast_channel *chan, void *data)
 {
@@ -155,6 +319,63 @@ static int speech_create(struct ast_channel *chan, void *data)
        return 0;
 }
 
+/*! \brief SpeechLoadGrammar(Grammar Name|Path) Dialplan Application */
+static int speech_load(struct ast_channel *chan, void *data)
+{
+       int res = 0, argc = 0;
+       struct localuser *u = NULL;
+       struct ast_speech *speech = find_speech(chan);
+       char *argv[2], *args = NULL, *name = NULL, *path = NULL;
+
+       if (!(args = ast_strdupa(data)))
+                return -1;
+
+       LOCAL_USER_ADD(u);
+
+       if (speech == NULL) {
+               LOCAL_USER_REMOVE(u);
+                return -1;
+        }
+
+       /* Parse out arguments */
+       argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0]));
+       if (argc != 2) {
+               LOCAL_USER_REMOVE(u);
+               return -1;
+       }
+       name = argv[0];
+       path = argv[1];
+
+        /* Load the grammar locally on the object */
+        res = ast_speech_grammar_load(speech, name, path);
+
+        LOCAL_USER_REMOVE(u);
+
+        return res;
+}
+
+/*! \brief SpeechUnloadGrammar(Grammar Name) Dialplan Application */
+static int speech_unload(struct ast_channel *chan, void *data)
+{
+        int res = 0;
+        struct localuser *u = NULL;
+        struct ast_speech *speech = find_speech(chan);
+
+        LOCAL_USER_ADD(u);
+
+        if (speech == NULL) {
+                LOCAL_USER_REMOVE(u);
+                return -1;
+        }
+
+        /* Unload the grammar */
+        res = ast_speech_grammar_unload(speech, data);
+
+        LOCAL_USER_REMOVE(u);
+
+        return res;
+}
+
 /*! \brief SpeechDeactivateGrammar(Grammar Name) Dialplan Application */
 static int speech_deactivate(struct ast_channel *chan, void *data)
 {
@@ -272,227 +493,208 @@ static int speech_streamfile(struct ast_channel *chan, const char *filename, con
 /*! \brief SpeechBackground(Sound File|Timeout) Dialplan Application */
 static int speech_background(struct ast_channel *chan, void *data)
 {
-       unsigned int timeout = 0;
-       int res = 0, done = 0, concepts = 0, argc = 0, started = 0;
-       struct localuser *u = NULL;
-       struct ast_speech *speech = find_speech(chan);
-       struct ast_speech_result *results = NULL, *result = NULL;
-       struct ast_frame *f = NULL;
-       int oldreadformat = AST_FORMAT_SLINEAR;
-       char tmp[256] = "", tmp2[256] = "";
-       char dtmf[AST_MAX_EXTENSION] = "";
-       time_t start, current;
-       struct ast_datastore *datastore = NULL;
-       char *argv[2], *args = NULL, *filename = NULL;
-
-       if (!(args = ast_strdupa(data)))
+        unsigned int timeout = 0;
+        int res = 0, done = 0, argc = 0, started = 0;
+        struct localuser *u = NULL;
+        struct ast_speech *speech = find_speech(chan);
+        struct ast_frame *f = NULL;
+        int oldreadformat = AST_FORMAT_SLINEAR;
+        char dtmf[AST_MAX_EXTENSION] = "";
+        time_t start, current;
+        struct ast_datastore *datastore = NULL;
+        char *argv[2], *args = NULL, *filename = NULL, tmp[2] = "";
+
+        if (!(args = ast_strdupa(data)))
                 return -1;
 
-       LOCAL_USER_ADD(u);
+        LOCAL_USER_ADD(u);
 
-       if (speech == NULL) {
-               LOCAL_USER_REMOVE(u);
-               return -1;
-       }
+        if (speech == NULL) {
+                LOCAL_USER_REMOVE(u);
+                return -1;
+        }
 
-       /* Record old read format */
-       oldreadformat = chan->readformat;
+        /* Record old read format */
+        oldreadformat = chan->readformat;
 
-       /* Change read format to be signed linear */
-       if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
-               LOCAL_USER_REMOVE(u);
-               return -1;
-       }
+        /* Change read format to be signed linear */
+        if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
+                LOCAL_USER_REMOVE(u);
+                return -1;
+        }
 
-       /* Parse out options */
-       argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0]));
-       if (argc > 0) {
-               /* Yay sound file */
-               filename = argv[0];
-               if (argv[1] != NULL)
-                       timeout = atoi(argv[1]);
-       }
+        /* Parse out options */
+        argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0]));
+        if (argc > 0) {
+                /* Yay sound file */
+                filename = argv[0];
+                if (argv[1] != NULL)
+                        timeout = atoi(argv[1]);
+        }
 
-       /* Start streaming the file if possible and specified */
-       if (filename != NULL && ast_streamfile(chan, filename, chan->language)) {
-               /* An error occured while streaming */
-               ast_set_read_format(chan, oldreadformat);
-               LOCAL_USER_REMOVE(u);
-               return -1;
-       }
+        /* Start streaming the file if possible and specified */
+        if (filename != NULL && ast_streamfile(chan, filename, chan->language)) {
+                /* An error occured while streaming */
+                ast_set_read_format(chan, oldreadformat);
+                LOCAL_USER_REMOVE(u);
+                return -1;
+        }
 
-       /* Before we go into waiting for stuff... make sure the structure is ready, if not - start it again */
-       if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
-               speech->state = AST_SPEECH_STATE_NOT_READY;
-               ast_speech_start(speech);
-       }
+        /* Before we go into waiting for stuff... make sure the structure is ready, if not - start it again */
+        if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
+               ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
+                ast_speech_start(speech);
+        }
 
-       /* Okay it's streaming so go into a loop grabbing frames! */
-       while (done == 0) {
-               /* Run scheduled stuff */
+        /* Okay it's streaming so go into a loop grabbing frames! */
+        while (done == 0) {
+                /* Run scheduled stuff */
                 ast_sched_runq(chan->sched);
 
-               /* Yay scheduling */
-               res = ast_sched_wait(chan->sched);
-               if (res < 0) {
-                       res = 1000;
-               }
-
-               /* If there is a frame waiting, get it - if not - oh well */
-               if (ast_waitfor(chan, res) > 0) {
-                       f = ast_read(chan);
-                       if (f == NULL) {
-                               /* The channel has hung up most likely */
-                               done = 3;
+                /* Yay scheduling */
+                res = ast_sched_wait(chan->sched);
+                if (res < 0) {
+                        res = 1000;
+                }
+
+                /* If there is a frame waiting, get it - if not - oh well */
+                if (ast_waitfor(chan, res) > 0) {
+                        f = ast_read(chan);
+                        if (f == NULL) {
+                                /* The channel has hung up most likely */
+                                done = 3;
+                                break;
+                        }
+                }
+
+               /* Do timeout check (shared between audio/dtmf) */
+               if (started == 1) {
+                       time(&current);
+                       if ((current-start) >= timeout) {
+                               done = 1;
                                break;
                        }
                }
 
-               /* Do checks on speech structure to see if it's changed */
-               ast_mutex_lock(&speech->lock);
-               if (ast_test_flag(speech, AST_SPEECH_QUIET) && chan->stream != NULL) {
-                       ast_stopstream(chan);
+                /* Do checks on speech structure to see if it's changed */
+                ast_mutex_lock(&speech->lock);
+                if (ast_test_flag(speech, AST_SPEECH_QUIET) && chan->stream != NULL) {
+                        ast_stopstream(chan);
                        ast_clear_flag(speech, AST_SPEECH_QUIET);
-               }
-               /* Check state so we can see what to do */
-               switch (speech->state) {
-               case AST_SPEECH_STATE_READY:
-                       /* If audio playback has stopped do a check for timeout purposes */
-                       if (chan->streamid == -1 && chan->timingfunc == NULL)
-                               ast_stopstream(chan);
-                       if (chan->stream == NULL && timeout > 0) {
-                               /* If start time is not yet done... do it */
-                               if (started == 0) {
-                                       time(&start);
-                                       started = 1;
-                               } else {
-                                       time(&current);
-                                       if ((current-start) >= timeout) {
-                                               pbx_builtin_setvar_helper(chan, "SILENCE", "1");
-                                               done = 1;
-                                               break;
-                                       }
-                               }
-                       }
-                       /* Deal with audio frames if present */
-                       if (f != NULL && f->frametype == AST_FRAME_VOICE) {
-                               ast_speech_write(speech, f->data, f->datalen);
-                       }
-                       break;
-               case AST_SPEECH_STATE_WAIT:
-                       /* Cue up waiting sound if not already playing */
-                       if (chan->stream == NULL) {
-                               if (speech->processing_sound != NULL) {
+                }
+                /* Check state so we can see what to do */
+                switch (speech->state) {
+                case AST_SPEECH_STATE_READY:
+                        /* If audio playback has stopped do a check for timeout purposes */
+                        if (chan->streamid == -1 && chan->timingfunc == NULL)
+                                ast_stopstream(chan);
+                        if (chan->stream == NULL && timeout > 0 && started == 0) {
+                               time(&start);
+                               started = 1;
+                        }
+                        /* Deal with audio frames if present */
+                        if (f != NULL && f->frametype == AST_FRAME_VOICE) {
+                                ast_speech_write(speech, f->data, f->datalen);
+                        }
+                        break;
+                case AST_SPEECH_STATE_WAIT:
+                        /* Cue up waiting sound if not already playing */
+                        if (chan->stream == NULL) {
+                                if (speech->processing_sound != NULL) {
                                         if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) {
                                                 speech_streamfile(chan, speech->processing_sound, chan->language);
                                         }
-                               }
-                       } else if (chan->streamid == -1 && chan->timingfunc == NULL) {
-                               ast_stopstream(chan);
+                                }
+                        } else if (chan->streamid == -1 && chan->timingfunc == NULL) {
+                                ast_stopstream(chan);
                                 if (speech->processing_sound != NULL) {
-                                       if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) {
-                                               speech_streamfile(chan, speech->processing_sound, chan->language);
-                                       }
+                                        if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) {
+                                                speech_streamfile(chan, speech->processing_sound, chan->language);
+                                        }
                                 }
-                       }
-                       break;
-               case AST_SPEECH_STATE_DONE:
-                       /* Assume there will be no results by default */
-                       pbx_builtin_setvar_helper(chan, "RESULTS", "0");
-                       pbx_builtin_setvar_helper(chan, "SILENCE", "0");
-                       /* Decoding is done and over... see if we have results */
-                       results = ast_speech_results_get(speech);
-                       if (results != NULL) {
-                               for (result=results; result!=NULL; result=result->next) {
-                                       /* Text */
-                                       snprintf(tmp, sizeof(tmp), "TEXT%d", concepts);
-                                       pbx_builtin_setvar_helper(chan, tmp, result->text);
-                                       /* Now... score! */
-                                       snprintf(tmp, sizeof(tmp), "SCORE%d", concepts);
-                                       snprintf(tmp2, sizeof(tmp2), "%d", result->score);
-                                       pbx_builtin_setvar_helper(chan, tmp, tmp2);
-                                       concepts++;
-                               }
-                               /* Expose number of results to dialplan */
-                               snprintf(tmp, sizeof(tmp), "%d", concepts);
-                               pbx_builtin_setvar_helper(chan, "RESULTS", tmp);
-                               /* Destroy the results since they are now in the dialplan */
-                               ast_speech_results_free(results);
-                       }
-                       /* Now that we are done... let's switch back to not ready state */
-                       speech->state = AST_SPEECH_STATE_NOT_READY;
-                       /* Break out of our background too */
-                       done = 1;
-                       /* Stop audio playback */
-                       if (chan->stream != NULL) {
-                               ast_stopstream(chan);
-                       }
-                       break;
-               default:
-                       break;
-               }
-               ast_mutex_unlock(&speech->lock);
-
-               /* Deal with other frame types */
-               if (f != NULL) {
-                       /* Free the frame we received */
-                       switch (f->frametype) {
-                       case AST_FRAME_DTMF:
+                        }
+                        break;
+                case AST_SPEECH_STATE_DONE:
+                        /* Copy to speech structure the results, if available */
+                        speech->results = ast_speech_results_get(speech);
+                        /* Now that we are done... let's switch back to not ready state */
+                       ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
+                        /* Break out of our background too */
+                        done = 1;
+                        /* Stop audio playback */
+                        if (chan->stream != NULL) {
+                                ast_stopstream(chan);
+                        }
+                        break;
+                default:
+                        break;
+                }
+                ast_mutex_unlock(&speech->lock);
+
+                /* Deal with other frame types */
+                if (f != NULL) {
+                        /* Free the frame we received */
+                        switch (f->frametype) {
+                        case AST_FRAME_DTMF:
                                if (f->subclass == '#') {
-                                       /* Input is done, throw it into the dialplan */
-                                       pbx_builtin_setvar_helper(chan, "RESULTS", "1");
-                                       pbx_builtin_setvar_helper(chan, "SCORE0", "1000");
-                                       pbx_builtin_setvar_helper(chan, "TEXT0", dtmf);
                                        done = 1;
                                } else {
                                        if (chan->stream != NULL) {
                                                ast_stopstream(chan);
-                                       }
-                                       /* Start timeout if not already started */
-                                       if (strlen(dtmf) == 0) {
+                                               /* Change timeout to be 5 seconds for DTMF input */
+                                               timeout = 5;
                                                time(&start);
+                                               started = 1;
                                        }
-                                       /* Append to the current information */
                                        snprintf(tmp, sizeof(tmp), "%c", f->subclass);
                                        strncat(dtmf, tmp, sizeof(dtmf));
                                }
-                               break;
-                       case AST_FRAME_CONTROL:
-                               ast_log(LOG_NOTICE, "Have a control frame of subclass %d\n", f->subclass);
-                               switch (f->subclass) {
-                               case AST_CONTROL_HANGUP:
-                                       /* Since they hung up we should destroy the speech structure */
-                                       done = 3;
-                               default:
-                                       break;
-                               }
-                       default:
-                               break;
-                       }
-                       ast_frfree(f);
-                       f = NULL;
-               }
-       }
-
-       /* See if it was because they hung up */
-       if (done == 3) {
-               /* Destroy speech structure */
-               ast_speech_destroy(speech);
+                                break;
+                        case AST_FRAME_CONTROL:
+                                switch (f->subclass) {
+                                case AST_CONTROL_HANGUP:
+                                        /* Since they hung up we should destroy the speech structure */
+                                        done = 3;
+                                default:
+                                        break;
+                                }
+                        default:
+                                break;
+                        }
+                        ast_frfree(f);
+                        f = NULL;
+                }
+        }
 
-               datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
-               if (datastore != NULL) {
-                       ast_channel_datastore_remove(chan, datastore);
+       if (strlen(dtmf) > 0 && speech->results == NULL) {
+               /* We sort of make a results entry */
+               speech->results = ast_calloc(1, sizeof(*speech->results));
+               if (speech->results != NULL) {
+                       speech->results->score = 1000;
+                       speech->results->text = strdup(dtmf);
+                       speech->results->grammar = strdup("dtmf");
                }
-       } else {
-               /* Channel is okay so restore read format */
-               ast_set_read_format(chan, oldreadformat);
        }
 
-       LOCAL_USER_REMOVE(u);
+        /* See if it was because they hung up */
+        if (done == 3) {
+                /* Destroy speech structure */
+                ast_speech_destroy(speech);
+                datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
+                if (datastore != NULL) {
+                        ast_channel_datastore_remove(chan, datastore);
+                }
+        } else {
+                /* Channel is okay so restore read format */
+                ast_set_read_format(chan, oldreadformat);
+        }
 
-       return 0;
+        LOCAL_USER_REMOVE(u);
+
+        return 0;
 }
 
+
 /*! \brief SpeechDestroy() Dialplan Application */
 static int speech_destroy(struct ast_channel *chan, void *data)
 {
@@ -526,11 +728,18 @@ int unload_module(void)
        int res = 0;
 
        res = ast_unregister_application("SpeechCreate");
+       res |= ast_unregister_application("SpeechLoadGrammar");
+       res |= ast_unregister_application("SpeechUnloadGrammar");
        res |= ast_unregister_application("SpeechActivateGrammar");
         res |= ast_unregister_application("SpeechDeactivateGrammar");
        res |= ast_unregister_application("SpeechStart");
        res |= ast_unregister_application("SpeechBackground");
        res |= ast_unregister_application("SpeechDestroy");
+       res |= ast_unregister_application("SpeechProcessingSound");
+       res |= ast_custom_function_unregister(&speech_function);
+       res |= ast_custom_function_unregister(&speech_score_function);
+       res |= ast_custom_function_unregister(&speech_text_function);
+       res |= ast_custom_function_unregister(&speech_grammar_function);
 
        STANDARD_HANGUP_LOCALUSERS;
 
@@ -542,13 +751,19 @@ int load_module(void)
        int res = 0;
 
        res = ast_register_application("SpeechCreate", speech_create, "Create a Speech Structure", speechcreate_descrip);
+       res |= ast_register_application("SpeechLoadGrammar", speech_load, "Load a Grammar", speechload_descrip);
+       res |= ast_register_application("SpeechUnloadGrammar", speech_unload, "Unload a Grammar", speechunload_descrip);
        res |= ast_register_application("SpeechActivateGrammar", speech_activate, "Activate a Grammar", speechactivategrammar_descrip);
         res |= ast_register_application("SpeechDeactivateGrammar", speech_deactivate, "Deactivate a Grammar", speechdeactivategrammar_descrip);
        res |= ast_register_application("SpeechStart", speech_start, "Start recognizing", speechstart_descrip);
        res |= ast_register_application("SpeechBackground", speech_background, "Play a sound file and wait for speech to be recognized", speechbackground_descrip);
        res |= ast_register_application("SpeechDestroy", speech_destroy, "End speech recognition", speechdestroy_descrip);
        res |= ast_register_application("SpeechProcessingSound", speech_processing_sound, "Change background processing sound", speechprocessingsound_descrip);
-       
+       res |= ast_custom_function_register(&speech_function);
+       res |= ast_custom_function_register(&speech_score_function);
+       res |= ast_custom_function_register(&speech_text_function);
+       res |= ast_custom_function_register(&speech_grammar_function);
+
        return res;
 }
 
index 25291f9..19fff17 100644 (file)
@@ -12,6 +12,14 @@ This application creates information to be used by all the other applications. I
 
 If an error occurs are you are not able to create an object, the variable ERROR will be set to 1. You can then exit your speech recognition specific context and play back an error message, or resort to a DTMF based IVR.
 
+SpeechLoadGrammar(Grammar Name|Path):
+
+Loads grammar locally on a channel. Note that the grammar is only available as long as the channel exists, and you must call SpeechUnloadGrammar before all is done or you may cause a memory leak. First argument is the grammar name that it will be loaded as and second argument is the path to the grammar.
+
+SpeechUnloadGrammar(Grammar Name):
+
+Unloads a locally loaded grammar and frees any memory used by it. The only argument is the name of the grammar to unload.
+
 SpeechActivateGrammar(Grammar Name):
 
 This activates the specified grammar to be recognized by the engine. A grammar tells the speech recognition engine what to recognize, and how to portray it back to you in the dialplan. The grammar name is the only argument to this application.
@@ -22,7 +30,7 @@ Tell the speech recognition engine that it should start trying to get results fr
 
 SpeechBackground(Sound File|Timeout):
 
-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. Once they stop talking the processing sound is played to indicate the speech recognition engine is working. Once results are available the application returns and results (score and text) are available as dialplan variables. The first text and score are ${TEXT0} AND ${SCORE0} while the second are ${TEXT1} and ${SCORE1}. This may change in the future, however, to use a dialplan function instead of dialplan variables. Note it is possible to have more then one result. 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. If the timeout is reached, then the variable SILENCE is set to 1 so that you will know that the person did not speak anything.
+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. Once they stop talking the processing sound is played to indicate the speech recognition engine is working. Note it is possible to have more then one result. 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.
 
 SpeechDeactivateGrammar(Grammar Name):
 
@@ -36,6 +44,34 @@ SpeechDestroy():
 
 This destroys the information used by all the other speech recognition applications. If you call this application but end up wanting to recognize more speech, you must call SpeechCreate again before calling any other application. It takes no arguments.
 
+Getting Result Information:
+
+The speech recognition utilities module exports several dialplan functions that you can use to examine results.
+
+${SPEECH(status)}:
+
+Returns 1 if SpeechCreate has been called. This uses the same check that applications do to see if a speech object is setup. If it returns 0 then you know you can not use other speech applications.
+
+${SPEECH(spoke)}:
+
+Returns 1 if the speaker spoke something, or 0 if they were silent.
+
+${SPEECH(results)}:
+
+Returns the number of results that are available.
+
+${SPEECH_SCORE(result number)}:
+
+Returns the score of a result.
+
+${SPEECH_TEXT(result number)}:
+
+Returns the recognized text of a result.
+
+${SPEECH_GRAMMAR(result number)}:
+
+Returns the matched grammar of the result.
+
 Dialplan Flow:
 
 1. Create a speech recognition object using SpeechCreate()
@@ -74,7 +110,7 @@ exten => s,3,SpeechStart()
 exten => s,4,SpeechBackground(who-would-you-like-to-dial)
 exten => s,5,SpeechDeactivateGrammar(company-directory)
 exten => s,6,SpeechDestroy()
-exten => s,7,Goto(internal-extensions-${TEXT0})
+exten => s,7,Goto(internal-extensions-${SPEECH_TEXT(0)})
 
 Useful Dialplan Tidbits:
 
@@ -82,15 +118,15 @@ A simple macro that can be used for confirm of a result. Requires some sound fil
 
 [macro-speech-confirm]
 exten => s,1,SpeechActivateGrammar(yes_no)
-exten => s,2,Set(OLDTEXT0=${TEXT0})
+exten => s,2,Set(OLDTEXT0=${SPEECH_TEXT(0)})
 exten => s,3,Playback(heard)
 exten => s,4,Playback(${ARG1})
 exten => s,5,SpeechStart()
 exten => s,6,SpeechBackground(correct)
-exten => s,7,Set(CONFIRM=${TEXT0})
-exten => s,8,GotoIf($["${TEXT0}" = "1"]?9:10)
+exten => s,7,Set(CONFIRM=${SPEECH_TEXT(0)})
+exten => s,8,GotoIf($["${SPEECH_TEXT(0)}" = "1"]?9:10)
 exten => s,9,Set(CONFIRM=yes)
-exten => s,10,Set(${TEXT0}=${OLDTEXT0})
+exten => s,10,Set(${CONFIRMED}=${OLDTEXT0})
 exten => s,11,SpeechDeactivateGrammar(yes_no)
 
 C API
@@ -161,7 +197,9 @@ struct ast_speech_result {
         char *text;
         /*! Result score */
         int score;
-        /*! Next result (may not always be present) */
+        /*! Matched grammar */
+        char *grammar;
+        /*! List information */
         struct ast_speech_result *next;
 };
 
index f6aa2b0..afd12af 100644 (file)
@@ -29,6 +29,7 @@ extern "C" {
 
 /* Speech structure flags */
 #define AST_SPEECH_QUIET (1 << 0) /* Quiet down output... they are talking */
+#define AST_SPEECH_SPOKE (1 << 1) /* Speaker did not speak */
 
 /* Speech structure states - in order of expected change */
 #define AST_SPEECH_STATE_NOT_READY 0 /* Not ready to accept audio */
@@ -50,6 +51,8 @@ struct ast_speech {
        int format;
        /*! Data for speech engine */
        void *data;
+       /*! Cached results */
+       struct ast_speech_result *results;
        /*! Pointer to the engine used by this speech structure */
        struct ast_speech_engine *engine;
 };
@@ -87,7 +90,9 @@ struct ast_speech_result {
        char *text;
        /*! Result score */
        int score;
-       /*! Next result (may not always be present) */
+       /*! Matched grammar */
+       char *grammar;
+       /*! List information */
        struct ast_speech_result *next;
 };
 
index a185768..58ef564 100644 (file)
@@ -141,6 +141,10 @@ int ast_speech_results_free(struct ast_speech_result *result)
                        free(current_result->text);
                        current_result->text = NULL;
                }
+               if (current_result->grammar != NULL) {
+                       free(current_result->grammar);
+                       current_result->grammar = NULL;
+               }
                /* Move on and then free ourselves */
                current_result = current_result->next;
                free(prev_result);
@@ -153,6 +157,16 @@ int ast_speech_results_free(struct ast_speech_result *result)
 /*! \brief Start speech recognition on a speech structure */
 void ast_speech_start(struct ast_speech *speech)
 {
+
+       /* Clear any flags that may affect things */
+       ast_clear_flag(speech, AST_SPEECH_SPOKE);
+
+       /* If results are on the structure, free them since we are starting again */
+       if (speech->results != NULL) {
+               ast_speech_results_free(speech->results);
+               speech->results = NULL;
+       }
+
        /* If the engine needs to start stuff up, do it */
        if (speech->engine->start != NULL) {
                speech->engine->start(speech);
@@ -201,6 +215,9 @@ struct ast_speech *ast_speech_new(char *engine_name, int format)
        /* Initialize the lock */
        ast_mutex_init(&new_speech->lock);
 
+       /* Make sure no results are present */
+       new_speech->results = NULL;
+
        /* Copy over our engine pointer */
        new_speech->engine = engine;
 
@@ -224,6 +241,12 @@ int ast_speech_destroy(struct ast_speech *speech)
        /* Deinitialize the lock */
        ast_mutex_destroy(&speech->lock);
 
+       /* If results exist on the speech structure, destroy them */
+       if (speech->results != NULL) {
+               ast_speech_results_free(speech->results);
+               speech->results = NULL;
+       }
+
        /* If a processing sound is set - free the memory used by it */
        if (speech->processing_sound != NULL) {
                free(speech->processing_sound);
@@ -242,7 +265,14 @@ int ast_speech_change_state(struct ast_speech *speech, int state)
 {
        int res = 0;
 
-       speech->state = state;
+       switch (state) {
+       case AST_SPEECH_STATE_WAIT:
+               /* The engine heard audio, so they spoke */
+               ast_set_flag(speech, AST_SPEECH_SPOKE);
+       default:
+               speech->state = state;
+               break;
+       }
 
        return res;
 }