Updates to speech recognition API and dialplan utilities. Moved to using dialplan...
[asterisk/asterisk.git] / apps / app_speech_utils.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;
 }