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

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Mon Apr 10 16:29:51 MST 2006


Author: file
Date: Mon Apr 10 18:29:50 2006
New Revision: 18979

URL: http://svn.digium.com/view/asterisk?rev=18979&view=rev
Log:
Presenting a revised data stores and oh my, a generic speech recognition API! I wonder what we can do with this now...

Added:
    trunk/apps/app_speech_utils.c   (with props)
    trunk/doc/datastores.txt   (with props)
    trunk/doc/speechrec.txt   (with props)
    trunk/include/asterisk/speech.h   (with props)
    trunk/res/res_speech.c   (with props)
Modified:
    trunk/.cleancount
    trunk/channel.c
    trunk/include/asterisk/channel.h

Modified: trunk/.cleancount
URL: http://svn.digium.com/view/asterisk/trunk/.cleancount?rev=18979&r1=18978&r2=18979&view=diff
==============================================================================
--- trunk/.cleancount (original)
+++ trunk/.cleancount Mon Apr 10 18:29:50 2006
@@ -1,1 +1,1 @@
-13
+14

Added: trunk/apps/app_speech_utils.c
URL: http://svn.digium.com/view/asterisk/trunk/apps/app_speech_utils.c?rev=18979&view=auto
==============================================================================
--- trunk/apps/app_speech_utils.c (added)
+++ trunk/apps/app_speech_utils.c Mon Apr 10 18:29:50 2006
@@ -1,0 +1,575 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Joshua Colp <jcolp at 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 at digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
+
+#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"
+
+static char *tdesc = "Dialplan Speech Applications";
+
+LOCAL_USER_DECL;
+
+/* 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 as dialplan variables.\n"
+"The first text and score are ${TEXT0} AND ${SCORE0} while the second are ${TEXT1} and ${SCORE1}.\n"
+"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.\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";
+
+/*! \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;
+}
+
+/*! \brief SpeechCreate() Dialplan Application */
+static int speech_create(struct ast_channel *chan, void *data)
+{
+	struct localuser *u = NULL;
+	struct ast_speech *speech = NULL;
+	struct ast_datastore *datastore = NULL;
+
+	LOCAL_USER_ADD(u);
+
+	/* Request a speech object */
+	speech = ast_speech_new(data, AST_FORMAT_SLINEAR);
+	if (speech == NULL) {
+		/* Not available */
+		pbx_builtin_setvar_helper(chan, "ERROR", "1");
+		LOCAL_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");
+		LOCAL_USER_REMOVE(u);
+		return 0;
+	}
+	datastore->data = speech;
+	ast_channel_datastore_add(chan, datastore);
+
+	LOCAL_USER_REMOVE(u);
+
+	return 0;
+}
+
+/*! \brief SpeechDeactivateGrammar(Grammar Name) Dialplan Application */
+static int speech_deactivate(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;
+        }
+
+        /* Deactivate the grammar on the speech object */
+        res = ast_speech_grammar_deactivate(speech, data);
+
+        LOCAL_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 localuser *u = NULL;
+	struct ast_speech *speech = find_speech(chan);
+
+	LOCAL_USER_ADD(u);
+
+	if (speech == NULL) {
+		LOCAL_USER_REMOVE(u);
+		return -1;
+	}
+
+	/* Activate the grammar on the speech object */
+	res = ast_speech_grammar_activate(speech, data);
+
+	LOCAL_USER_REMOVE(u);
+
+	return res;
+}
+
+/*! \brief SpeechStart() Dialplan Application */
+static int speech_start(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;
+	}
+
+	ast_speech_start(speech);
+
+	LOCAL_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 localuser *u = NULL;
+        struct ast_speech *speech = find_speech(chan);
+
+        LOCAL_USER_ADD(u);
+
+        if (speech == NULL) {
+                LOCAL_USER_REMOVE(u);
+                return -1;
+        }
+
+	if (speech->processing_sound != NULL) {
+		free(speech->processing_sound);
+		speech->processing_sound = NULL;
+	}
+
+	speech->processing_sound = strdup(data);
+
+        LOCAL_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;
+        struct ast_filestream *vfs=NULL;
+
+        fs = ast_openstream(chan, filename, preflang);
+        if (fs)
+                vfs = ast_openvstream(chan, filename, preflang);
+        if (fs){
+                if (ast_applystream(chan, fs))
+                        return -1;
+                if (vfs && ast_applystream(chan, vfs))
+                        return -1;
+                if (ast_playstream(fs))
+                        return -1;
+                if (vfs && ast_playstream(vfs))
+                        return -1;
+                return 0;
+        }
+        return -1;
+}
+
+/*! \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 */
+                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 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) {
+						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) {
+                                        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:
+			/* Assume there will be no results by default */
+			pbx_builtin_setvar_helper(chan, "RESULTS", "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:
+				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) {
+						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;
+		}
+	}
+
+	/* 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)
+{
+	int res = 0;
+        struct localuser *u = NULL;
+	struct ast_speech *speech = find_speech(chan);
+	struct ast_datastore *datastore = NULL;
+
+	LOCAL_USER_ADD(u);
+
+	if (speech == NULL) {
+		LOCAL_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);
+	}
+
+	LOCAL_USER_REMOVE(u);
+
+	return res;
+}
+
+int unload_module(void)
+{
+	int res = 0;
+
+	res = ast_unregister_application("SpeechCreate");
+	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");
+
+	STANDARD_HANGUP_LOCALUSERS;
+
+	return res;	
+}
+
+int load_module(void)
+{
+	int res = 0;
+
+	res = ast_register_application("SpeechCreate", speech_create, "Create a Speech Structure", speechcreate_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);
+	
+	return res;
+}
+
+int reload(void)
+{
+	return 0;
+}
+
+const char *description(void)
+{
+	return tdesc;
+}
+
+int usecount(void)
+{
+	int res;
+
+	STANDARD_USECOUNT(res);
+
+	return res;
+}
+
+const char *key()
+{
+	return ASTERISK_GPL_KEY;
+}

Propchange: trunk/apps/app_speech_utils.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: trunk/apps/app_speech_utils.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: trunk/apps/app_speech_utils.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: trunk/channel.c
URL: http://svn.digium.com/view/asterisk/trunk/channel.c?rev=18979&r1=18978&r2=18979&view=diff
==============================================================================
--- trunk/channel.c (original)
+++ trunk/channel.c Mon Apr 10 18:29:50 2006
@@ -653,6 +653,7 @@
 	headp = &tmp->varshead;
 	ast_mutex_init(&tmp->lock);
 	AST_LIST_HEAD_INIT_NOLOCK(headp);
+	AST_LIST_HEAD_INIT_NOLOCK(&tmp->datastores);
 	strcpy(tmp->context, "default");
 	ast_string_field_set(tmp, language, defaultlanguage);
 	strcpy(tmp->exten, "s");
@@ -928,6 +929,7 @@
 	struct ast_var_t *vardata;
 	struct ast_frame *f, *fp;
 	struct varshead *headp;
+	struct ast_datastore *datastore = NULL;
 	char name[AST_CHANNEL_NAME];
 	
 	headp=&chan->varshead;
@@ -981,6 +983,18 @@
 		ast_frfree(fp);
 	}
 	
+	/* Get rid of each of the data stores on the channel */
+	AST_LIST_LOCK(&chan->datastores);
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->datastores, datastore, list) {
+		/* Remove from the list */
+		AST_LIST_REMOVE_CURRENT(&chan->datastores, list);
+		/* Free the data store */
+		ast_channel_datastore_free(datastore);
+	}
+	AST_LIST_TRAVERSE_SAFE_END
+	AST_LIST_UNLOCK(&chan->datastores);
+	AST_LIST_HEAD_DESTROY(&chan->datastores);
+
 	/* loop over the variables list, freeing all data and deleting list items */
 	/* no need to lock the list, as the channel is already locked */
 	
@@ -992,6 +1006,111 @@
 	AST_LIST_UNLOCK(&channels);
 
 	ast_device_state_changed_literal(name);
+}
+
+struct ast_datastore *ast_channel_datastore_alloc(const struct ast_datastore_info *info, char *uid)
+{
+	struct ast_datastore *datastore = NULL;
+
+	/* Make sure we at least have type so we can identify this */
+	if (info == NULL) {
+		return NULL;
+	}
+
+	/* Allocate memory for datastore and clear it */
+	datastore = ast_calloc(1, sizeof(*datastore));
+	if (datastore == NULL) {
+		return NULL;
+	}
+
+	datastore->info = info;
+
+	if (uid != NULL) {
+		datastore->uid = ast_strdup(uid);
+	}
+
+	return datastore;
+}
+
+int ast_channel_datastore_free(struct ast_datastore *datastore)
+{
+	int res = 0;
+
+	/* Using the destroy function (if present) destroy the data */
+	if (datastore->info->destroy != NULL && datastore->data != NULL) {
+		datastore->info->destroy(datastore->data);
+		datastore->data = NULL;
+	}
+
+	/* Free allocated UID memory */
+	if (datastore->uid != NULL) {
+		free(datastore->uid);
+		datastore->uid = NULL;
+	}
+
+	/* Finally free memory used by ourselves */
+	free(datastore);
+	datastore = NULL;
+
+	return res;
+}
+
+int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
+{
+	int res = 0;
+
+	AST_LIST_LOCK(&chan->datastores);
+	AST_LIST_INSERT_HEAD(&chan->datastores, datastore, list);
+	AST_LIST_UNLOCK(&chan->datastores);
+
+	return res;
+}
+
+int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
+{
+	struct ast_datastore *datastore2 = NULL;
+	int res = -1;
+
+	/* Find our position and remove ourselves */
+	AST_LIST_LOCK(&chan->datastores);
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->datastores, datastore2, list) {
+		if (datastore2 == datastore) {
+			AST_LIST_REMOVE_CURRENT(&chan->datastores, list);
+			res = 0;
+			break;
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END
+	AST_LIST_UNLOCK(&chan->datastores);
+
+	return res;
+}
+
+struct ast_datastore *ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, char *uid)
+{
+	struct ast_datastore *datastore = NULL;
+	
+	if (info == NULL)
+		return NULL;
+
+	AST_LIST_LOCK(&chan->datastores);
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->datastores, datastore, list) {
+		if (datastore->info == info) {
+			if (uid != NULL && datastore->uid != NULL) {
+				if (!strcasecmp(uid, datastore->uid)) {
+					/* Matched by type AND uid */
+					break;
+				}
+			} else {
+				/* Matched by type at least */
+				break;
+			}
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END
+	AST_LIST_UNLOCK(&chan->datastores);
+
+	return datastore;
 }
 
 int ast_channel_spy_add(struct ast_channel *chan, struct ast_channel_spy *spy)
@@ -3048,6 +3167,11 @@
 		if (x != AST_GENERATOR_FD)
 			original->fds[x] = clone->fds[x];
 	}
+	/* Move data stores over */
+	if (AST_LIST_FIRST(&clone->datastores))
+                AST_LIST_INSERT_TAIL(&original->datastores, AST_LIST_FIRST(&clone->datastores), list);
+	AST_LIST_HEAD_INIT_NOLOCK(&clone->datastores);
+
 	clone_variables(original, clone);
 	AST_LIST_HEAD_INIT_NOLOCK(&clone->varshead);
 	/* Presense of ADSI capable CPE follows clone */

Added: trunk/doc/datastores.txt
URL: http://svn.digium.com/view/asterisk/trunk/doc/datastores.txt?rev=18979&view=auto
==============================================================================
--- trunk/doc/datastores.txt (added)
+++ trunk/doc/datastores.txt Mon Apr 10 18:29:50 2006
@@ -1,0 +1,68 @@
+Asterisk Channel Data Stores
+============================
+
+* What is a data store?
+
+A data store is a way of storing complex data (such as a structure) on a channel
+so it can be retrieved at a later time by another application, or the same application.
+
+If the data store is not freed by said application though, a callback to a destroy function
+occurs which frees the memory used by the data in the data store so no memory loss occurs.
+
+* A datastore info structure
+static const struct example_datastore {
+       .type = "example",
+       .destroy = callback_destroy
+};
+
+This is a needed structure that contains information about a datastore, it's used by many API calls.
+
+* How do you create a data store?
+
+1. Use ast_channel_datastore_alloc function to return a pre-allocated structure
+   Ex: datastore = ast_channel_datastore_alloc(&example_datastore, "uid");
+   This function takes two arguments: (datastore info structure, uid)
+2. Attach data and destroy callback to pre-allocated structure.
+   Ex: datastore->data = mysillydata;
+       datastore->destroy = callback_destroy;
+3. Add datastore to the channel
+   Ex: ast_channel_datastore_add(chan, datastore);
+   This function takes two arguments: (pointer to channel, pointer to data store)
+
+Full Example:
+
+void callback_destroy(void *data)
+{
+	free(data);
+}
+
+struct ast_datastore *datastore = NULL;
+datastore = ast_channel_datastore_alloc(&example_datastore, NULL);
+datastore->data = mysillydata;
+datastore->destroy = callback_destroy;
+ast_channel_datastore_add(chan, datastore);
+
+NOTE: Because you're passing a pointer to a function in your module, you'll want to include
+this in your use count. When allocated increment, when destroyed decrement.
+
+* How do you remove a data store?
+
+1. Find the data store
+   Ex: datastore = ast_channel_datastore_find(chan, &example_datastore, NULL);
+   This function takes three arguments: (pointer to channel, datastore info structure, uid)
+2. Remove the data store from the channel
+   Ex: ast_channel_datastore_remove(chan, datastore);
+   This function takes two arguments: (pointer to channel, pointer to data store)
+3. If we want to now, free the memory or do stuff to the data on the data store
+   If we do then we will want to unset the data and callback
+   Ex: datastore->data = NULL;
+       datastore->destroy = NULL;
+4. Free the data store
+   Ex: ast_channel_datastore_free(datastore);
+   This function takes one argument: (pointer to data store)
+
+* How do you find a data store?
+
+1. Find the data store
+   Ex: datastore = ast_channel_datastore_find(chan, &example_datastore, NULL);
+   This function takes three arguments: (pointer to channel, datastore info structure, uid)

Propchange: trunk/doc/datastores.txt
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: trunk/doc/datastores.txt
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: trunk/doc/datastores.txt
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: trunk/doc/speechrec.txt
URL: http://svn.digium.com/view/asterisk/trunk/doc/speechrec.txt?rev=18979&view=auto
==============================================================================
--- trunk/doc/speechrec.txt (added)
+++ trunk/doc/speechrec.txt Mon Apr 10 18:29:50 2006
@@ -1,0 +1,204 @@
+Generic Speech Recognition API
+
+Dialplan Applications:
+
+The dialplan API is based around a single speech utilities application file, which exports many applications to be used for speech recognition. These include an application to prepare for speech recognition, activate a grammar, and play back a sound file while waiting for the person to speak. Using a combination of these applications you can easily make a dialplan use speech recognition without worrying about what speech recognition engine is being used.
+
+SpeechCreate(Engine Name):
+
+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. It takes the engine name to use as the argument, if not specified the default engine will be used.
+
+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.
+
+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.
+
+SpeechStart():
+
+Tell the speech recognition engine that it should start trying to get results from audio being fed to it. This has no arguments.
+
+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.
+
+SpeechDeactivateGrammar(Grammar Name):
+
+This deactivates the specified grammar so that it is no longer recognized. The only argument is the grammar name to deactivate.
+
+SpeechProcessingSound(Sound File):
+
+This changes the processing sound that SpeechBackground plays back when the speech recognition engine is processing and working to get results. It takes the sound file as the only argument.
+
+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.
+
+Dialplan Flow:
+
+1. Create a speech recognition object using SpeechCreate()
+2. Activate your grammars using SpeechActivateGrammar(Grammar Name)
+3. Call SpeechStart() to indicate you are going to do speech recognition immediately
+4. Play back your audio and wait for recognition using SpeechBackground(Sound File|Timeout)
+5. Check the results and do things based on them
+6. Deactivate your grammars using SpeechDeactivateGrammar(Grammar Name)
+7. Destroy your speech recognition object using SpeechDestroy()
+
+Dialplan Examples:
+
+This is pretty cheeky in that it does not confirmation of results. As well the way the grammar is written it returns the person's extension instead of their name so we can just do a Goto based on the result text.
+
+company-directory.gram
+
+#ABNF 1.0;
+language en-US;
+mode voice;
+tag-format <lumenvox/1.0>;
+
+root $company_directory;
+
+$josh = Joshua Colp:"6066";
+$mark = Mark Spencer:"4569";
+$kevin = Kevin Fleming:"2567";
+
+$company_directory = ($josh | $mark | $kevin) { $ = parseInt($$) };
+
+dialplan logic
+
+[dial-by-name]
+exten => s,1,SpeechCreate()
+exten => s,2,SpeechActivateGrammar(company-directory)
+exten => s,3,SpeechStart()
+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})
+
+Useful Dialplan Tidbits:
+
+A simple macro that can be used for confirm of a result. Requires some sound files. ARG1 is equal to the file to play back after "I heard..." is played.
+
+[macro-speech-confirm]
+exten => s,1,SpeechActivateGrammar(yes_no)
+exten => s,2,Set(OLDTEXT0=${TEXT0})
+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,9,Set(CONFIRM=yes)
+exten => s,10,Set(${TEXT0}=${OLDTEXT0})
+exten => s,11,SpeechDeactivateGrammar(yes_no)
+
+C API
+
+The module res_speech.so exports a C based API that any developer can use to speech recognize enable their application. The API gives greater control, but requires the developer to do more on their end in comparison to the dialplan speech utilities.
+
+For all API calls that return an integer value a non-zero value indicates an error has occured.
+
+Creating a speech structure:
+
+struct ast_speech *ast_speech_new(char *engine_name, int format)
+
+struct ast_speech *speech = ast_speech_new(NULL, AST_FORMAT_SLINEAR);
+
+This will create a new speech structure that will be returned to you. The speech recognition engine name is optional and if NULL the default one will be used. As well for now format should always be AST_FORMAT_SLINEAR.
+
+Activating a grammar:
+
+int ast_speech_grammar_activate(struct ast_speech *speech, char *grammar_name)
+
+res = ast_speech_grammar_activate(speech, "yes_no");
+
+This activates the specified grammar on the speech structure passed to it.
+
+Start recognizing audio:
+
+void ast_speech_start(struct ast_speech *speech)
+
+ast_speech_start(speech);
+
+This essentially tells the speech recognition engine that you will be feeding audio to it from then on. It MUST be called every time before you start feeding audio to the speech structure.
+
+Send audio to be recognized:
+
+int ast_speech_write(struct ast_speech *speech, void *data, int len)
+
+res = ast_speech_write(speech, fr->data, fr->datalen);
+
+This writes audio to the speech structure that will then be recognized. It must be written signed linear only at this time. In the future other formats may be supported.
+
+Checking for results:
+
+The way the generic speech recognition API is written is that the speech structure will undergo state changes to indicate progress of recognition. The states are outlined below:
+
+AST_SPEECH_STATE_NOT_READY - The speech structure is not ready to accept audio
+AST_SPEECH_STATE_READY - You may write audio to the speech structure
+AST_SPEECH_STATE_WAIT - No more audio should be written, and results will be available soon.
+AST_SPEECH_STATE_DONE - Results are available and the speech structure can only be used again by calling ast_speech_start
+
+It is up to you to monitor these states. Current state is available via a variable on the speech structure. (state)
+
+Knowing when to stop playback:
+
+If you are playing back a sound file to the user and you want to know when to stop play back because the individual started talking use the following.
+
+ast_test_flag(speech, AST_SPEECH_QUIET) - This will return a positive value when the person has started talking.
+
+Getting results:
+
+struct ast_speech_result *ast_speech_results_get(struct ast_speech *speech)
+
+struct ast_speech_result *results = ast_speech_results_get(speech);
+
+This will return a linked list of result structures. A result structure looks like the following:
+
+struct ast_speech_result {
+        /*! Recognized text */
+        char *text;
+        /*! Result score */
+        int score;
+        /*! Next result (may not always be present) */
+        struct ast_speech_result *next;
+};
+
+Freeing a set of results:
+
+int ast_speech_results_free(struct ast_speech_result *result)
+
+res = ast_speech_results_free(results);
+
+This will free all results on a linked list. Results MAY NOT be used as the memory will have been freed.
+
+Deactivating a grammar:
+
+int ast_speech_grammar_deactivate(struct ast_speech *speech, char *grammar_name)
+
+res = ast_speech_grammar_deactivate(speech, "yes_no");
+
+This deactivates the specified grammar on the speech structure.
+
+Destroying a speech structure:
+
+int ast_speech_destroy(struct ast_speech *speech)
+
+res = ast_speech_destroy(speech);
+
+This will free all associated memory with the speech structure and destroy it with the speech recognition engine.
+
+Loading a grammar on a speech structure:
+
+int ast_speech_grammar_load(struct ast_speech *speech, char *grammar_name, char *grammar)
+
+res = ast_speech_grammar_load(speech, "builtin:yes_no", "yes_no");
+
+Unloading a grammar on a speech structure:
+
+If you load a grammar on a speech structure it is preferred that you unload it as well, or you may cause a memory leak. Don't say I didn't warn you.
+
+int ast_speech_grammar_unload(struct ast_speech *speech, char *grammar_name)
+
+res = ast_speech_grammar_unload(speech, "yes_no");
+
+This unloads the specified grammar from the speech structure.

Propchange: trunk/doc/speechrec.txt
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: trunk/doc/speechrec.txt
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: trunk/doc/speechrec.txt
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: trunk/include/asterisk/channel.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/channel.h?rev=18979&r1=18978&r2=18979&view=diff
==============================================================================
--- trunk/include/asterisk/channel.h (original)
+++ trunk/include/asterisk/channel.h Mon Apr 10 18:29:50 2006
@@ -144,6 +144,26 @@
 	int (*generate)(struct ast_channel *chan, void *data, int len, int samples);
 };
 
+/*! Structure for a data store type */
+struct ast_datastore_info {
+	/*! Type of data store */
+	const char *type;
+	/*! Destroy function */
+	void (*destroy)(void *data);
+};
+
+/*! Structure for a channel data store */
+struct ast_datastore {
+	/*! Unique data store identifier */
+	char *uid;
+	/*! Contained data */
+	void *data;
+	/*! Data store type information */
+	const struct ast_datastore_info *info;
+	/*! Used for easy linking */
+	AST_LIST_ENTRY(ast_datastore) list;
+};
+
 /*! Structure for all kinds of caller ID identifications */
 struct ast_callerid {
 	/*! Malloc'd Dialed Number Identifier */
@@ -422,6 +442,9 @@
 
 	/*! Chan Spy stuff */
 	struct ast_channel_spy_list *spies;
+
+	/*! Data stores on the channel */
+	AST_LIST_HEAD(datastores, ast_datastore) datastores;
 
 	/*! For easy linking */
 	AST_LIST_ENTRY(ast_channel) chan_list;
@@ -553,6 +576,21 @@
 	CHANNEL_MANAGER_RELOAD,
 };
 
+/*! \brief Create a channel datastore structure */
+struct ast_datastore *ast_channel_datastore_alloc(const struct ast_datastore_info *info, char *uid);
+
+/*! \brief Free a channel datastore structure */
+int ast_channel_datastore_free(struct ast_datastore *datastore);
+
+/*! \brief Add a datastore to a channel */
+int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore);
+
+/*! \brief Remove a datastore from a channel */
+int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore);
+
+/*! \brief Find a datastore on a channel */
+struct ast_datastore *ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, char *uid);
+
 /*! \brief Change the state of a channel */
 int ast_setstate(struct ast_channel *chan, int state);
 

Added: trunk/include/asterisk/speech.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/speech.h?rev=18979&view=auto

[... 507 lines stripped ...]


More information about the asterisk-commits mailing list