diff options
Diffstat (limited to 'apps/app_speech_utils.c')
-rw-r--r-- | apps/app_speech_utils.c | 875 |
1 files changed, 875 insertions, 0 deletions
diff --git a/apps/app_speech_utils.c b/apps/app_speech_utils.c new file mode 100644 index 000000000..702e9348f --- /dev/null +++ b/apps/app_speech_utils.c @@ -0,0 +1,875 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2006, Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Speech Recognition Utility Applications + * + * \author Joshua Colp <jcolp@digium.com> + * + * \ingroup applications + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include "asterisk/file.h" +#include "asterisk/logger.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/module.h" +#include "asterisk/lock.h" +#include "asterisk/app.h" +#include "asterisk/speech.h" + +/* Descriptions for each application */ +static char *speechcreate_descrip = +"SpeechCreate(engine name)\n" +"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" +"It takes the engine name to use as the argument, if not specified the default engine will be used.\n"; + +static char *speechactivategrammar_descrip = +"SpeechActivateGrammar(Grammar Name)\n" +"This activates the specified grammar to be recognized by the engine. A grammar tells the speech recognition engine what to recognize, \n" + "and how to portray it back to you in the dialplan. The grammar name is the only argument to this application.\n"; + +static char *speechstart_descrip = +"SpeechStart()\n" + "Tell the speech recognition engine that it should start trying to get results from audio being fed to it. This has no arguments.\n"; + +static char *speechbackground_descrip = +"SpeechBackground(Sound File|Timeout)\n" +"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" +"Once they stop talking the processing sound is played to indicate the speech recognition engine is working.\n" +"Once results are available the application returns and results (score and text) are available using dialplan functions.\n" +"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" +"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"; + +static char *speechdeactivategrammar_descrip = +"SpeechDeactivateGrammar(Grammar Name)\n" + "This deactivates the specified grammar so that it is no longer recognized. The only argument is the grammar name to deactivate.\n"; + +static char *speechprocessingsound_descrip = +"SpeechProcessingSound(Sound File)\n" +"This changes the processing sound that SpeechBackground plays back when the speech recognition engine is processing and working to get results.\n" + "It takes the sound file as the only argument.\n"; + +static char *speechdestroy_descrip = +"SpeechDestroy()\n" +"This destroys the information used by all the other speech recognition applications.\n" +"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) +{ + struct ast_speech *speech = (struct ast_speech*)data; + + if (speech == NULL) { + return; + } + + /* Deallocate now */ + ast_speech_destroy(speech); + + return; +} + +/*! \brief Static structure for datastore information */ +static const struct ast_datastore_info speech_datastore = { + .type = "speech", + .destroy = destroy_callback +}; + +/*! \brief Helper function used to find the speech structure attached to a channel */ +static struct ast_speech *find_speech(struct ast_channel *chan) +{ + struct ast_speech *speech = NULL; + struct ast_datastore *datastore = NULL; + + datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL); + if (datastore == NULL) { + return NULL; + } + speech = datastore->data; + + return speech; +} + +/* Helper function to find a specific speech recognition result by number and nbest alternative */ +static struct ast_speech_result *find_result(struct ast_speech_result *results, char *result_num) +{ + struct ast_speech_result *result = results; + char *tmp = NULL; + int nbest_num = 0, wanted_num = 0, i = 0; + + if (!result) + return NULL; + + if ((tmp = strchr(result_num, '/'))) { + *tmp++ = '\0'; + nbest_num = atoi(result_num); + wanted_num = atoi(tmp); + } else { + wanted_num = atoi(result_num); + } + + do { + if (result->nbest_num != nbest_num) + continue; + if (i == wanted_num) + break; + i++; + } while ((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, 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.", + .syntax = "SPEECH_SCORE([nbest number/]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, data))) + return -1; + + if (result->text != NULL) { + ast_copy_string(buf, result->text, len); + } else { + buf[0] = '\0'; + } + + return 0; +} + +static struct ast_custom_function speech_text_function = { + .name = "SPEECH_TEXT", + .synopsis = "Gets the recognized text of a result.", + .syntax = "SPEECH_TEXT([nbest number/]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, data))) + return -1; + + if (result->grammar != NULL) { + ast_copy_string(buf, result->grammar, len); + } else { + buf[0] = '\0'; + } + + 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([nbest number/]result number)", + .desc = + "Gets the matched grammar of a result if available.\n", + .read = speech_grammar, + .write = NULL, +}; + +/*! \brief SPEECH_ENGINE() Dialplan Function */ +static int speech_engine_write(struct ast_channel *chan, char *cmd, char *data, const char *value) +{ + struct ast_speech *speech = find_speech(chan); + + if (data == NULL || speech == NULL) + return -1; + + ast_speech_change(speech, data, value); + + return 0; +} + +static struct ast_custom_function speech_engine_function = { + .name = "SPEECH_ENGINE", + .synopsis = "Change a speech engine specific attribute.", + .syntax = "SPEECH_ENGINE(name)=value", + .desc = + "Changes a speech engine specific attribute.\n", + .read = NULL, + .write = speech_engine_write, +}; + +/*! \brief SPEECH_RESULTS_TYPE() Dialplan Function */ +static int speech_results_type_write(struct ast_channel *chan, char *cmd, char *data, const char *value) +{ + struct ast_speech *speech = find_speech(chan); + + if (data == NULL || speech == NULL) + return -1; + + if (!strcasecmp(value, "normal")) + ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NORMAL); + else if (!strcasecmp(value, "nbest")) + ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NBEST); + + return 0; +} + +static struct ast_custom_function speech_results_type_function = { + .name = "SPEECH_RESULTS_TYPE", + .synopsis = "Sets the type of results that will be returned.", + .syntax = "SPEECH_RESULTS_TYPE()=results type", + .desc = + "Sets the type of results that will be returned. Valid options are normal or nbest.", + .read = NULL, + .write = speech_results_type_write, +}; + +/*! \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); + } else { + buf[0] = '\0'; + } + + 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) +{ + struct ast_module_user *u = NULL; + struct ast_speech *speech = NULL; + struct ast_datastore *datastore = NULL; + + u = ast_module_user_add(chan); + + /* Request a speech object */ + speech = ast_speech_new(data, AST_FORMAT_SLINEAR); + if (speech == NULL) { + /* Not available */ + pbx_builtin_setvar_helper(chan, "ERROR", "1"); + ast_module_user_remove(u); + return 0; + } + + datastore = ast_channel_datastore_alloc(&speech_datastore, NULL); + if (datastore == NULL) { + ast_speech_destroy(speech); + pbx_builtin_setvar_helper(chan, "ERROR", "1"); + ast_module_user_remove(u); + return 0; + } + datastore->data = speech; + ast_channel_datastore_add(chan, datastore); + + ast_module_user_remove(u); + + 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 ast_module_user *u = NULL; + struct ast_speech *speech = find_speech(chan); + char *argv[2], *args = NULL, *name = NULL, *path = NULL; + + args = ast_strdupa(data); + + u = ast_module_user_add(chan); + + if (speech == NULL) { + ast_module_user_remove(u); + return -1; + } + + /* Parse out arguments */ + argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])); + if (argc != 2) { + ast_module_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); + + ast_module_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 ast_module_user *u = NULL; + struct ast_speech *speech = find_speech(chan); + + u = ast_module_user_add(chan); + + if (speech == NULL) { + ast_module_user_remove(u); + return -1; + } + + /* Unload the grammar */ + res = ast_speech_grammar_unload(speech, data); + + ast_module_user_remove(u); + + return res; +} + +/*! \brief SpeechDeactivateGrammar(Grammar Name) Dialplan Application */ +static int speech_deactivate(struct ast_channel *chan, void *data) +{ + int res = 0; + struct ast_module_user *u = NULL; + struct ast_speech *speech = find_speech(chan); + + u = ast_module_user_add(chan); + + if (speech == NULL) { + ast_module_user_remove(u); + return -1; + } + + /* Deactivate the grammar on the speech object */ + res = ast_speech_grammar_deactivate(speech, data); + + ast_module_user_remove(u); + + return res; +} + +/*! \brief SpeechActivateGrammar(Grammar Name) Dialplan Application */ +static int speech_activate(struct ast_channel *chan, void *data) +{ + int res = 0; + struct ast_module_user *u = NULL; + struct ast_speech *speech = find_speech(chan); + + u = ast_module_user_add(chan); + + if (speech == NULL) { + ast_module_user_remove(u); + return -1; + } + + /* Activate the grammar on the speech object */ + res = ast_speech_grammar_activate(speech, data); + + ast_module_user_remove(u); + + return res; +} + +/*! \brief SpeechStart() Dialplan Application */ +static int speech_start(struct ast_channel *chan, void *data) +{ + int res = 0; + struct ast_module_user *u = NULL; + struct ast_speech *speech = find_speech(chan); + + u = ast_module_user_add(chan); + + if (speech == NULL) { + ast_module_user_remove(u); + return -1; + } + + ast_speech_start(speech); + + ast_module_user_remove(u); + + return res; +} + +/*! \brief SpeechProcessingSound(Sound File) Dialplan Application */ +static int speech_processing_sound(struct ast_channel *chan, void *data) +{ + int res = 0; + struct ast_module_user *u = NULL; + struct ast_speech *speech = find_speech(chan); + + u = ast_module_user_add(chan); + + if (speech == NULL) { + ast_module_user_remove(u); + return -1; + } + + if (speech->processing_sound != NULL) { + free(speech->processing_sound); + speech->processing_sound = NULL; + } + + speech->processing_sound = strdup(data); + + ast_module_user_remove(u); + + return res; +} + +/*! \brief Helper function used by speech_background to playback a soundfile */ +static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang) +{ + struct ast_filestream *fs = NULL; + + if (!(fs = ast_openstream(chan, filename, preflang))) + return -1; + + if (ast_applystream(chan, fs)) + return -1; + + ast_playstream(fs); + + return 0; +} + +/*! \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, argc = 0, started = 0, quieted = 0, max_dtmf_len = 0; + struct ast_module_user *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_tmp = NULL, *filename = NULL, tmp[2] = "", dtmf_terminator = '#'; + const char *tmp2 = NULL; + + args = ast_strdupa(data); + + u = ast_module_user_add(chan); + + if (speech == NULL) { + ast_module_user_remove(u); + return -1; + } + + /* If channel is not already answered, then answer it */ + if (chan->_state != AST_STATE_UP && ast_answer(chan)) { + ast_module_user_remove(u); + return -1; + } + + /* Record old read format */ + oldreadformat = chan->readformat; + + /* Change read format to be signed linear */ + if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) { + ast_module_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_tmp = ast_strdupa(argv[0]); + if (!ast_strlen_zero(argv[1])) { + if ((timeout = atoi(argv[1])) == 0) + timeout = -1; + } else + timeout = 0; + } + + /* See if the maximum DTMF length variable is set... we use a variable in case they want to carry it through their entire dialplan */ + if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_MAXLEN")) && !ast_strlen_zero(tmp2)) + max_dtmf_len = atoi(tmp2); + + /* See if a terminator is specified */ + if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_TERMINATOR"))) { + if (ast_strlen_zero(tmp2)) + dtmf_terminator = '\0'; + else + dtmf_terminator = tmp2[0]; + } + + /* 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); + } + + /* Ensure no streams are currently running */ + ast_stopstream(chan); + + /* Okay it's streaming so go into a loop grabbing frames! */ + while (done == 0) { + /* If the filename is null and stream is not running, start up a new sound file */ + if (!quieted && (chan->streamid == -1 && chan->timingfunc == NULL) && (filename = strsep(&filename_tmp, "&"))) { + /* Discard old stream information */ + ast_stopstream(chan); + /* Start new stream */ + speech_streamfile(chan, filename, chan->language); + } + + /* 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; + break; + } + } + + /* Do timeout check (shared between audio/dtmf) */ + if ((!quieted || strlen(dtmf)) && started == 1) { + time(¤t); + if ((current-start) >= timeout) { + done = 1; + if (f) + ast_frfree(f); + 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)) { + if (chan->stream) + ast_stopstream(chan); + ast_clear_flag(speech, AST_SPEECH_QUIET); + quieted = 1; + } + /* 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 (!quieted && chan->stream == NULL && timeout && started == 0 && !filename_tmp) { + if (timeout == -1) { + done = 1; + if (f) + ast_frfree(f); + break; + } + time(&start); + started = 1; + } + /* Write audio frame out to speech engine if no DTMF has been received */ + if (!strlen(dtmf) && 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 (!strlen(dtmf)) { + 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); + if (speech->processing_sound != NULL) { + 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: + /* Now that we are done... let's switch back to not ready state */ + ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY); + if (!strlen(dtmf)) { + /* Copy to speech structure the results, if available */ + speech->results = ast_speech_results_get(speech); + /* 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 (dtmf_terminator != '\0' && f->subclass == dtmf_terminator) { + done = 1; + } else { + if (chan->stream != NULL) { + ast_stopstream(chan); + } + if (!started) { + /* Change timeout to be 5 seconds for DTMF input */ + timeout = (chan->pbx && chan->pbx->dtimeout) ? chan->pbx->dtimeout : 5; + started = 1; + } + time(&start); + snprintf(tmp, sizeof(tmp), "%c", f->subclass); + strncat(dtmf, tmp, sizeof(dtmf) - strlen(dtmf) - 1); + /* If the maximum length of the DTMF has been reached, stop now */ + if (max_dtmf_len && strlen(dtmf) == max_dtmf_len) + done = 1; + } + 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; + } + } + + if (strlen(dtmf)) { + /* We sort of make a results entry */ + speech->results = ast_calloc(1, sizeof(*speech->results)); + if (speech->results != NULL) { + ast_speech_dtmf(speech, dtmf); + speech->results->score = 1000; + speech->results->text = strdup(dtmf); + speech->results->grammar = strdup("dtmf"); + } + ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY); + } + + /* 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); + } + + ast_module_user_remove(u); + + return 0; +} + + +/*! \brief SpeechDestroy() Dialplan Application */ +static int speech_destroy(struct ast_channel *chan, void *data) +{ + int res = 0; + struct ast_module_user *u = NULL; + struct ast_speech *speech = find_speech(chan); + struct ast_datastore *datastore = NULL; + + u = ast_module_user_add(chan); + + if (speech == NULL) { + ast_module_user_remove(u); + return -1; + } + + /* 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); + } + + ast_module_user_remove(u); + + return res; +} + +static 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); + res |= ast_custom_function_unregister(&speech_engine_function); + res |= ast_custom_function_unregister(&speech_results_type_function); + + ast_module_user_hangup_all(); + + return res; +} + +static 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 voice in the audio stream", 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); + res |= ast_custom_function_register(&speech_engine_function); + res |= ast_custom_function_register(&speech_results_type_function); + + return res; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan Speech Applications"); |