[asterisk-commits] trunk r19645 - in /trunk: apps/ doc/ include/asterisk/ res/

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Wed Apr 12 17:18:53 MST 2006


Author: file
Date: Wed Apr 12 19:18:52 2006
New Revision: 19645

URL: http://svn.digium.com/view/asterisk?rev=19645&view=rev
Log:
Updates to speech recognition API and dialplan utilities. Moved to using dialplan functions, and some other misc things.

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

Modified: trunk/apps/app_speech_utils.c
URL: http://svn.digium.com/view/asterisk/trunk/apps/app_speech_utils.c?rev=19645&r1=19644&r2=19645&view=diff
==============================================================================
--- trunk/apps/app_speech_utils.c (original)
+++ trunk/apps/app_speech_utils.c Wed Apr 12 19:18:52 2006
@@ -86,6 +86,15 @@
 "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)
 {
@@ -121,6 +130,161 @@
 
 	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)
@@ -153,6 +317,63 @@
 	LOCAL_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 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 */
@@ -272,226 +493,207 @@
 /*! \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)))
-                return -1;
-
-	LOCAL_USER_ADD(u);
-
-	if (speech == NULL) {
-		LOCAL_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)) {
-		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]);
-	}
-
-	/* 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);
-	}
-
-	/* Okay it's streaming so go into a loop grabbing frames! */
-	while (done == 0) {
-		/* Run scheduled stuff */
+        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);
+
+        if (speech == NULL) {
+                LOCAL_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)) {
+                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]);
+        }
+
+        /* 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) {
+		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 */
                 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);
+						/* Change timeout to be 5 seconds for DTMF input */
+						timeout = 5;
+						time(&start);
+						started = 1;
 					}
-					/* Start timeout if not already started */
-					if (strlen(dtmf) == 0) {
-						time(&start);
-					}
-					/* 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;
+                                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) > 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");
 		}
 	}
 
-	/* 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);
-	}
-
-	LOCAL_USER_REMOVE(u);
-
-	return 0;
-}
+        /* 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);
+        }
+
+        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 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 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;
 }
 

Modified: trunk/doc/speechrec.txt
URL: http://svn.digium.com/view/asterisk/trunk/doc/speechrec.txt?rev=19645&r1=19644&r2=19645&view=diff
==============================================================================
--- trunk/doc/speechrec.txt (original)
+++ trunk/doc/speechrec.txt Wed Apr 12 19:18:52 2006
@@ -12,6 +12,14 @@
 
 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 @@
 
 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):
 
@@ -35,6 +43,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:
 
@@ -74,7 +110,7 @@
 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 @@
 
 [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 @@
         char *text;
         /*! Result score */
         int score;
-        /*! Next result (may not always be present) */
+        /*! Matched grammar */
+        char *grammar;
+        /*! List information */
         struct ast_speech_result *next;
 };
 

Modified: trunk/include/asterisk/speech.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/speech.h?rev=19645&r1=19644&r2=19645&view=diff
==============================================================================
--- trunk/include/asterisk/speech.h (original)
+++ trunk/include/asterisk/speech.h Wed Apr 12 19:18:52 2006
@@ -29,6 +29,7 @@
 
 /* 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 @@
 	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 @@
 	char *text;
 	/*! Result score */
 	int score;
-	/*! Next result (may not always be present) */
+	/*! Matched grammar */
+	char *grammar;
+	/*! List information */
 	struct ast_speech_result *next;
 };
 

Modified: trunk/res/res_speech.c
URL: http://svn.digium.com/view/asterisk/trunk/res/res_speech.c?rev=19645&r1=19644&r2=19645&view=diff
==============================================================================
--- trunk/res/res_speech.c (original)
+++ trunk/res/res_speech.c Wed Apr 12 19:18:52 2006
@@ -141,6 +141,10 @@
 			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 @@
 /*! \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 @@
 	/* 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;
 
@@ -223,6 +240,12 @@
 
 	/* 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) {
@@ -242,7 +265,14 @@
 {
 	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;
 }



More information about the asterisk-commits mailing list