[asterisk-commits] file: trunk r90656 - in /trunk: CHANGES include/asterisk/agi.h res/res_agi.c

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Dec 3 15:03:05 CST 2007


Author: file
Date: Mon Dec  3 15:03:05 2007
New Revision: 90656

URL: http://svn.digium.com/view/asterisk?view=rev&rev=90656
Log:
Add AGI commands for speech recognition. These mirror the dialplan applications mostly but present the information in a nicer fashion. The SPEECH RECOGNIZE command for example will return the results instead of having to query the dialplan functions.

Modified:
    trunk/CHANGES
    trunk/include/asterisk/agi.h
    trunk/res/res_agi.c

Modified: trunk/CHANGES
URL: http://svn.digium.com/view/asterisk/trunk/CHANGES?view=diff&rev=90656&r1=90655&r2=90656
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Mon Dec  3 15:03:05 2007
@@ -300,6 +300,11 @@
 ------------------------
   * Brazilian Portuguese (pt-BR) in VM, and say.c was added
   * Added support for the Hungarian language for saying numbers, dates, and times.
+
+AGI Changes
+-----------
+  * Added SPEECH commands for speech recognition. A complete listing can be found
+     using agi show.
 
 Miscellaneous 
 -------------

Modified: trunk/include/asterisk/agi.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/agi.h?view=diff&rev=90656&r1=90655&r2=90656
==============================================================================
--- trunk/include/asterisk/agi.h (original)
+++ trunk/include/asterisk/agi.h Mon Dec  3 15:03:05 2007
@@ -32,6 +32,7 @@
 	int audio;	/* FD for audio output */
 	int ctrl;		/* FD for input control */
 	unsigned int fast:1; /* flag for fast agi or not */
+	struct ast_speech *speech; /* Speech structure for speech recognition */
 } AGI;
 
 typedef struct agi_command {

Modified: trunk/res/res_agi.c
URL: http://svn.digium.com/view/asterisk/trunk/res/res_agi.c?view=diff&rev=90656&r1=90655&r2=90656
==============================================================================
--- trunk/res/res_agi.c (original)
+++ trunk/res/res_agi.c Mon Dec  3 15:03:05 2007
@@ -51,6 +51,7 @@
 #include "asterisk/lock.h"
 #include "asterisk/strings.h"
 #include "asterisk/agi.h"
+#include "asterisk/speech.h"
 
 #define MAX_ARGS 128
 #define AGI_NANDFS_RETRY 3
@@ -1337,6 +1338,291 @@
 	return RESULT_SUCCESS;
 }
 
+static int handle_speechcreate(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+	/* If a structure already exists, return an error */
+        if (agi->speech) {
+		ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+		return RESULT_SUCCESS;
+	}
+	
+	if ((agi->speech = ast_speech_new(argv[2], AST_FORMAT_SLINEAR)))
+		ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+	else
+		ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+	
+	return RESULT_SUCCESS;
+}
+
+static int handle_speechset(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+	/* Check for minimum arguments */
+        if (argc != 3)
+		return RESULT_SHOWUSAGE;
+	
+	/* Check to make sure speech structure exists */
+	if (!agi->speech) {
+		ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+		return RESULT_SUCCESS;
+	}
+	
+	ast_speech_change(agi->speech, argv[2], argv[3]);
+	ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+	
+	return RESULT_SUCCESS;
+}
+
+static int handle_speechdestroy(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+	if (agi->speech) {
+		ast_speech_destroy(agi->speech);
+		agi->speech = NULL;
+		ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+	} else {
+		ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+	}
+	
+	return RESULT_SUCCESS;
+}
+
+static int handle_speechloadgrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+	if (argc != 5)
+		return RESULT_SHOWUSAGE;
+	
+	if (!agi->speech) {
+		ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+		return RESULT_SUCCESS;
+	}
+	
+	if (ast_speech_grammar_load(agi->speech, argv[3], argv[4]))
+		ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+	else
+		ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+	
+	return RESULT_SUCCESS;
+}
+
+static int handle_speechunloadgrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+	if (argc != 4)
+		return RESULT_SHOWUSAGE;
+	
+	if (!agi->speech) {
+		ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+		return RESULT_SUCCESS;
+	}
+	
+	if (ast_speech_grammar_unload(agi->speech, argv[3]))
+		ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+	else
+		ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+	
+	return RESULT_SUCCESS;
+}
+
+static int handle_speechactivategrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+	if (argc != 4)
+		return RESULT_SHOWUSAGE;
+	
+	if (!agi->speech) {
+		ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+		return RESULT_SUCCESS;
+	}
+	
+	if (ast_speech_grammar_activate(agi->speech, argv[3]))
+		ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+	else
+		ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+	
+	return RESULT_SUCCESS;
+}
+
+static int handle_speechdeactivategrammar(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+	if (argc != 4)
+		return RESULT_SHOWUSAGE;
+	
+	if (!agi->speech) {
+		ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+		return RESULT_SUCCESS;
+	}
+	
+	if (ast_speech_grammar_deactivate(agi->speech, argv[3]))
+		ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+	else
+		ast_agi_fdprintf(chan, agi->fd, "200 result=1\n");
+	
+	return RESULT_SUCCESS;
+}
+
+static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang, int offset)
+{
+	struct ast_filestream *fs = NULL;
+	
+	if (!(fs = ast_openstream(chan, filename, preflang)))
+		return -1;
+	
+	if (offset)
+		ast_seekstream(fs, offset, SEEK_SET);
+	
+	if (ast_applystream(chan, fs))
+		return -1;
+	
+	if (ast_playstream(fs))
+		return -1;
+	
+	return 0;
+}
+
+static int handle_speechrecognize(struct ast_channel *chan, AGI *agi, int argc, char **argv)
+{
+	struct ast_speech *speech = agi->speech;
+	char *prompt, dtmf = 0, tmp[4096] = "", *buf = tmp;
+	int timeout = 0, offset = 0, old_read_format = 0, res = 0, i = 0;
+	long current_offset = 0;
+	const char *reason = NULL;
+	struct ast_frame *fr = NULL;
+	struct ast_speech_result *result = NULL;
+	size_t left = sizeof(tmp);
+	time_t start = 0, current;
+	
+	if (argc < 4)
+		return RESULT_SHOWUSAGE;
+	
+	if (!speech) {
+		ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+		return RESULT_SUCCESS;
+	}
+	
+	prompt = argv[2];
+	timeout = atoi(argv[3]);
+	
+	/* If offset is specified then convert from text to integer */
+	if (argc == 5)
+		offset = atoi(argv[4]);
+	
+	/* We want frames coming in signed linear */
+	old_read_format = chan->readformat;
+	if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
+		ast_agi_fdprintf(chan, agi->fd, "200 result=0\n");
+		return RESULT_SUCCESS;
+	}
+	
+	/* Setup speech structure */
+	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);
+	}
+	
+	/* Start playing prompt */
+	speech_streamfile(chan, prompt, chan->language, offset);
+	
+	/* Go into loop reading in frames, passing to speech thingy, checking for hangup, all that jazz */
+	while (ast_strlen_zero(reason)) {
+		/* Run scheduled items */
+                ast_sched_runq(chan->sched);
+		
+		/* See maximum time of waiting */
+		if ((res = ast_sched_wait(chan->sched)) < 0)
+			res = 1000;
+		
+		/* Wait for frame */
+		if (ast_waitfor(chan, res) > 0) {
+			if (!(fr = ast_read(chan))) {
+				reason = "hangup";
+				break;
+			}
+		}
+		
+		/* Perform timeout check */
+		if ((timeout > 0) && (start > 0)) {
+			time(&current);
+			if ((current - start) >= timeout) {
+				reason = "timeout";
+				if (fr)
+					ast_frfree(fr);
+				break;
+			}
+		}
+		
+		/* Check the speech structure for any changes */
+		ast_mutex_lock(&speech->lock);
+		
+		/* See if we need to quiet the audio stream playback */
+		if (ast_test_flag(speech, AST_SPEECH_QUIET) && chan->stream) {
+			current_offset = ast_tellstream(chan->stream);
+			ast_stopstream(chan);
+			ast_clear_flag(speech, AST_SPEECH_QUIET);
+		}
+		
+		/* Check each state */
+		switch (speech->state) {
+		case AST_SPEECH_STATE_READY:
+			/* If the stream is done, start timeout calculation */
+			if ((timeout > 0) && ((!chan->stream) || (chan->streamid == -1 && chan->timingfunc == NULL))) {
+				ast_stopstream(chan);
+				time(&start);
+			}
+			/* Write audio frame data into speech engine if possible */
+			if (fr && fr->frametype == AST_FRAME_VOICE)
+				ast_speech_write(speech, fr->data, fr->datalen);
+			break;
+		case AST_SPEECH_STATE_WAIT:
+			/* Cue waiting sound if not already playing */
+			if ((!chan->stream) || (chan->streamid == -1 && chan->timingfunc == NULL)) {
+				ast_stopstream(chan);
+				/* If a processing sound exists, or is not none - play it */
+				if (!ast_strlen_zero(speech->processing_sound) && strcasecmp(speech->processing_sound, "none"))
+					speech_streamfile(chan, speech->processing_sound, chan->language, 0);
+			}
+			break;
+		case AST_SPEECH_STATE_DONE:
+			/* Get the results */
+			speech->results = ast_speech_results_get(speech);
+			/* Change state to not ready */
+			ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
+			reason = "speech";
+			break;
+		default:
+			break;
+		}
+		ast_mutex_unlock(&speech->lock);
+		
+		/* Check frame for DTMF or hangup */
+		if (fr) {
+			if (fr->frametype == AST_FRAME_DTMF) {
+				reason = "dtmf";
+				dtmf = fr->subclass;
+			} else if (fr->frametype == AST_FRAME_CONTROL && fr->subclass == AST_CONTROL_HANGUP) {
+				reason = "hangup";
+			}
+			ast_frfree(fr);
+		}
+	}
+	
+	if (!strcasecmp(reason, "speech")) {
+		/* Build string containing speech results */
+                for (result = speech->results; result; result = AST_LIST_NEXT(result, list)) {
+			/* Build result string */
+			ast_build_string(&buf, &left, "%sscore%d=%d text%d=\"%s\" grammar%d=%s", (i > 0 ? " " : ""), i, result->score, i, result->text, i, result->grammar);
+                        /* Increment result count */
+			i++;
+		}
+                /* Print out */
+		ast_agi_fdprintf(chan, agi->fd, "200 result=1 (speech) endpos=%ld results=%d %s\n", current_offset, i, tmp);
+	} else if (!strcasecmp(reason, "dtmf")) {
+		ast_agi_fdprintf(chan, agi->fd, "200 result=1 (digit) digit=%c endpos=%ld\n", dtmf, current_offset);
+	} else if (!strcasecmp(reason, "hangup") || !strcasecmp(reason, "timeout")) {
+		ast_agi_fdprintf(chan, agi->fd, "200 result=1 (%s) endpos=%ld\n", reason, current_offset);
+	} else {
+		ast_agi_fdprintf(chan, agi->fd, "200 result=0 endpos=%ld\n", current_offset);
+	}
+	
+	return RESULT_SUCCESS;
+}
+
 static char usage_setmusic[] =
 " Usage: SET MUSIC ON <on|off> <class>\n"
 "	Enables/Disables the music on hold generator.  If <class> is\n"
@@ -1583,6 +1869,38 @@
 static char usage_noop[] =
 " Usage: NoOp\n"
 "	Does nothing.\n";
+
+static char usage_speechcreate[] =
+" Usage: SPEECH CREATE <engine>\n"
+"       Create a speech object to be used by the other Speech AGI commands.\n";
+
+static char usage_speechset[] =
+" Usage: SPEECH SET <name> <value>\n"
+"       Set an engine-specific setting.\n";
+
+static char usage_speechdestroy[] =
+" Usage: SPEECH DESTROY\n"
+"       Destroy the speech object created by SPEECH CREATE.\n";
+
+static char usage_speechloadgrammar[] =
+" Usage: SPEECH LOAD GRAMMAR <grammar name> <path to grammar>\n"
+"       Loads the specified grammar as the specified name.\n";
+
+static char usage_speechunloadgrammar[] =
+" Usage: SPEECH UNLOAD GRAMMAR <grammar name>\n"
+"       Unloads the specified grammar.\n";
+
+static char usage_speechactivategrammar[] =
+" Usage: SPEECH ACTIVATE GRAMMAR <grammar name>\n"
+"       Activates the specified grammar on the speech object.\n";
+
+static char usage_speechdeactivategrammar[] =
+" Usage: SPEECH DEACTIVATE GRAMMAR <grammar name>\n"
+"       Deactivates the specified grammar on the speech object.\n";
+
+static char usage_speechrecognize[] =
+" Usage: SPEECH RECOGNIZE <prompt> <timeout> [<offset>]\n"
+"       Plays back given prompt while listening for speech and dtmf.\n";
 
 /*!
  * \brief AGI commands list
@@ -1625,6 +1943,14 @@
 	{ { "tdd", "mode", NULL }, handle_tddmode, "Toggles TDD mode (for the deaf)", usage_tddmode , 0 },
 	{ { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose , 1 },
 	{ { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit , 0 },
+	{ { "speech", "create", NULL }, handle_speechcreate, "Creates a speech object", usage_speechcreate, 0 },
+	{ { "speech", "set", NULL }, handle_speechset, "Sets a speech engine setting", usage_speechset, 0 },
+	{ { "speech", "destroy", NULL }, handle_speechdestroy, "Destroys a speech object", usage_speechdestroy, 1 },
+	{ { "speech", "load", "grammar", NULL }, handle_speechloadgrammar, "Loads a grammar", usage_speechloadgrammar, 0 },
+	{ { "speech", "unload", "grammar", NULL }, handle_speechunloadgrammar, "Unloads a grammar", usage_speechunloadgrammar, 1 },
+	{ { "speech", "activate", "grammar", NULL }, handle_speechactivategrammar, "Activates a grammar", usage_speechactivategrammar, 0 },
+	{ { "speech", "deactivate", "grammar", NULL }, handle_speechdeactivategrammar, "Deactivates a grammar", usage_speechdeactivategrammar, 0 },
+	{ { "speech", "recognize", NULL }, handle_speechrecognize, "Recognizes speech", usage_speechrecognize, 0 },
 };
 
 static AST_RWLIST_HEAD_STATIC(agi_commands, agi_command);
@@ -1637,7 +1963,7 @@
 	if (match)
 		ast_join(matchstr, sizeof(matchstr), match);
 
-	ast_cli(fd, "%5.5s %20.20s   %s\n","Dead","Command","Description");
+	ast_cli(fd, "%5.5s %30.30s   %s\n","Dead","Command","Description");
 	AST_RWLIST_RDLOCK(&agi_commands);
 	AST_RWLIST_TRAVERSE(&agi_commands, e, list) {
 		if (!e->cmda[0])
@@ -1648,7 +1974,7 @@
 		ast_join(fullcmd, sizeof(fullcmd), e->cmda);
 		if (match && strncasecmp(matchstr, fullcmd, strlen(matchstr)))
 			continue;
-		ast_cli(fd, "%5.5s %20.20s   %s\n", e->dead ? "Yes" : "No" , fullcmd, e->summary);
+		ast_cli(fd, "%5.5s %30.30s   %s\n", e->dead ? "Yes" : "No" , fullcmd, e->summary);
 	}
 	AST_RWLIST_UNLOCK(&agi_commands);
 




More information about the asterisk-commits mailing list