diff options
author | file <file@f38db490-d61c-443f-a65b-d21fe96a405b> | 2006-04-13 00:18:52 +0000 |
---|---|---|
committer | file <file@f38db490-d61c-443f-a65b-d21fe96a405b> | 2006-04-13 00:18:52 +0000 |
commit | f51c79cc26059333eccb17bfc84a9c38804bc06a (patch) | |
tree | 2df79ba8d344f5eecd8d06832c818a799f7e4492 /apps/app_speech_utils.c | |
parent | 6f6ba7555c9d2cd7212060938d7ff8c1fe914b7c (diff) |
Updates to speech recognition API and dialplan utilities. Moved to using dialplan functions, and some other misc things.
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@19645 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'apps/app_speech_utils.c')
-rw-r--r-- | apps/app_speech_utils.c | 583 |
1 files changed, 399 insertions, 184 deletions
diff --git a/apps/app_speech_utils.c b/apps/app_speech_utils.c index 264f56ba1..babf39b5d 100644 --- a/apps/app_speech_utils.c +++ b/apps/app_speech_utils.c @@ -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(¤t); + 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(¤t); - 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; } |