update comment to match the state of the code
[asterisk-bristuff.git] / apps / app_speech_utils.c
blobe45459166c6e5ac80e0f0a157cbeeaa43e79246a
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2006, Digium, Inc.
6 * Joshua Colp <jcolp@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
19 /*! \file
21 * \brief Speech Recognition Utility Applications
23 * \author Joshua Colp <jcolp@digium.com>
25 * \ingroup applications
28 #include "asterisk.h"
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
37 #include "asterisk/file.h"
38 #include "asterisk/logger.h"
39 #include "asterisk/channel.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/module.h"
42 #include "asterisk/lock.h"
43 #include "asterisk/app.h"
44 #include "asterisk/speech.h"
46 /* Descriptions for each application */
47 static char *speechcreate_descrip =
48 "SpeechCreate(engine name)\n"
49 "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"
50 "It takes the engine name to use as the argument, if not specified the default engine will be used.\n";
52 static char *speechactivategrammar_descrip =
53 "SpeechActivateGrammar(Grammar Name)\n"
54 "This activates the specified grammar to be recognized by the engine. A grammar tells the speech recognition engine what to recognize, \n"
55 "and how to portray it back to you in the dialplan. The grammar name is the only argument to this application.\n";
57 static char *speechstart_descrip =
58 "SpeechStart()\n"
59 "Tell the speech recognition engine that it should start trying to get results from audio being fed to it. This has no arguments.\n";
61 static char *speechbackground_descrip =
62 "SpeechBackground(Sound File|Timeout)\n"
63 "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"
64 "Once they stop talking the processing sound is played to indicate the speech recognition engine is working.\n"
65 "Once results are available the application returns and results (score and text) are available using dialplan functions.\n"
66 "The first text and score are ${SPEECH_TEXT(0)} AND ${SPEECH_SCORE(0)} while the second are ${SPEECH_TEXT(1)} and ${SPEECH_SCORE(1)}.\n"
67 "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";
69 static char *speechdeactivategrammar_descrip =
70 "SpeechDeactivateGrammar(Grammar Name)\n"
71 "This deactivates the specified grammar so that it is no longer recognized. The only argument is the grammar name to deactivate.\n";
73 static char *speechprocessingsound_descrip =
74 "SpeechProcessingSound(Sound File)\n"
75 "This changes the processing sound that SpeechBackground plays back when the speech recognition engine is processing and working to get results.\n"
76 "It takes the sound file as the only argument.\n";
78 static char *speechdestroy_descrip =
79 "SpeechDestroy()\n"
80 "This destroys the information used by all the other speech recognition applications.\n"
81 "If you call this application but end up wanting to recognize more speech, you must call SpeechCreate\n"
82 "again before calling any other application. It takes no arguments.\n";
84 static char *speechload_descrip =
85 "SpeechLoadGrammar(Grammar Name|Path)\n"
86 "Load a grammar only on the channel, not globally.\n"
87 "It takes the grammar name as first argument and path as second.\n";
89 static char *speechunload_descrip =
90 "SpeechUnloadGrammar(Grammar Name)\n"
91 "Unload a grammar. It takes the grammar name as the only argument.\n";
93 /*! \brief Helper function used by datastores to destroy the speech structure upon hangup */
94 static void destroy_callback(void *data)
96 struct ast_speech *speech = (struct ast_speech*)data;
98 if (speech == NULL) {
99 return;
102 /* Deallocate now */
103 ast_speech_destroy(speech);
105 return;
108 /*! \brief Static structure for datastore information */
109 static const struct ast_datastore_info speech_datastore = {
110 .type = "speech",
111 .destroy = destroy_callback
114 /*! \brief Helper function used to find the speech structure attached to a channel */
115 static struct ast_speech *find_speech(struct ast_channel *chan)
117 struct ast_speech *speech = NULL;
118 struct ast_datastore *datastore = NULL;
120 datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
121 if (datastore == NULL) {
122 return NULL;
124 speech = datastore->data;
126 return speech;
129 /* Helper function to find a specific speech recognition result by number and nbest alternative */
130 static struct ast_speech_result *find_result(struct ast_speech_result *results, char *result_num)
132 struct ast_speech_result *result = results;
133 char *tmp = NULL;
134 int nbest_num = 0, wanted_num = 0, i = 0;
136 if (!result)
137 return NULL;
139 if ((tmp = strchr(result_num, '/'))) {
140 *tmp++ = '\0';
141 nbest_num = atoi(result_num);
142 wanted_num = atoi(tmp);
143 } else {
144 wanted_num = atoi(result_num);
147 do {
148 if (result->nbest_num != nbest_num)
149 continue;
150 if (i == wanted_num)
151 break;
152 i++;
153 } while ((result = result->next));
155 return result;
158 /*! \brief SPEECH_SCORE() Dialplan Function */
159 static int speech_score(struct ast_channel *chan, char *cmd, char *data,
160 char *buf, size_t len)
162 struct ast_speech_result *result = NULL;
163 struct ast_speech *speech = find_speech(chan);
164 char tmp[128] = "";
166 if (data == NULL || speech == NULL || !(result = find_result(speech->results, data)))
167 return -1;
169 snprintf(tmp, sizeof(tmp), "%d", result->score);
171 ast_copy_string(buf, tmp, len);
173 return 0;
176 static struct ast_custom_function speech_score_function = {
177 .name = "SPEECH_SCORE",
178 .synopsis = "Gets the confidence score of a result.",
179 .syntax = "SPEECH_SCORE([nbest number/]result number)",
180 .desc =
181 "Gets the confidence score of a result.\n",
182 .read = speech_score,
183 .write = NULL,
186 /*! \brief SPEECH_TEXT() Dialplan Function */
187 static int speech_text(struct ast_channel *chan, char *cmd, char *data,
188 char *buf, size_t len)
190 struct ast_speech_result *result = NULL;
191 struct ast_speech *speech = find_speech(chan);
193 if (data == NULL || speech == NULL || !(result = find_result(speech->results, data)))
194 return -1;
196 if (result->text != NULL)
197 ast_copy_string(buf, result->text, len);
199 return 0;
202 static struct ast_custom_function speech_text_function = {
203 .name = "SPEECH_TEXT",
204 .synopsis = "Gets the recognized text of a result.",
205 .syntax = "SPEECH_TEXT([nbest number/]result number)",
206 .desc =
207 "Gets the recognized text of a result.\n",
208 .read = speech_text,
209 .write = NULL,
212 /*! \brief SPEECH_GRAMMAR() Dialplan Function */
213 static int speech_grammar(struct ast_channel *chan, char *cmd, char *data,
214 char *buf, size_t len)
216 struct ast_speech_result *result = NULL;
217 struct ast_speech *speech = find_speech(chan);
219 if (data == NULL || speech == NULL || !(result = find_result(speech->results, data)))
220 return -1;
222 if (result->grammar != NULL)
223 ast_copy_string(buf, result->grammar, len);
225 return 0;
228 static struct ast_custom_function speech_grammar_function = {
229 .name = "SPEECH_GRAMMAR",
230 .synopsis = "Gets the matched grammar of a result if available.",
231 .syntax = "SPEECH_GRAMMAR([nbest number/]result number)",
232 .desc =
233 "Gets the matched grammar of a result if available.\n",
234 .read = speech_grammar,
235 .write = NULL,
238 /*! \brief SPEECH_ENGINE() Dialplan Function */
239 static int speech_engine_write(struct ast_channel *chan, char *cmd, char *data, const char *value)
241 struct ast_speech *speech = find_speech(chan);
243 if (data == NULL || speech == NULL)
244 return -1;
246 ast_speech_change(speech, data, value);
248 return 0;
251 static struct ast_custom_function speech_engine_function = {
252 .name = "SPEECH_ENGINE",
253 .synopsis = "Change a speech engine specific attribute.",
254 .syntax = "SPEECH_ENGINE(name)=value",
255 .desc =
256 "Changes a speech engine specific attribute.\n",
257 .read = NULL,
258 .write = speech_engine_write,
261 /*! \brief SPEECH_RESULTS_TYPE() Dialplan Function */
262 static int speech_results_type_write(struct ast_channel *chan, char *cmd, char *data, const char *value)
264 struct ast_speech *speech = find_speech(chan);
266 if (data == NULL || speech == NULL)
267 return -1;
269 if (!strcasecmp(value, "normal"))
270 ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NORMAL);
271 else if (!strcasecmp(value, "nbest"))
272 ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NBEST);
274 return 0;
277 static struct ast_custom_function speech_results_type_function = {
278 .name = "SPEECH_RESULTS_TYPE",
279 .synopsis = "Sets the type of results that will be returned.",
280 .syntax = "SPEECH_RESULTS_TYPE()=results type",
281 .desc =
282 "Sets the type of results that will be returned. Valid options are normal or nbest.",
283 .read = NULL,
284 .write = speech_results_type_write,
287 /*! \brief SPEECH() Dialplan Function */
288 static int speech_read(struct ast_channel *chan, char *cmd, char *data,
289 char *buf, size_t len)
291 int results = 0;
292 struct ast_speech_result *result = NULL;
293 struct ast_speech *speech = find_speech(chan);
294 char tmp[128] = "";
296 /* Now go for the various options */
297 if (!strcasecmp(data, "status")) {
298 if (speech != NULL)
299 ast_copy_string(buf, "1", len);
300 else
301 ast_copy_string(buf, "0", len);
302 return 0;
305 /* Make sure we have a speech structure for everything else */
306 if (speech == NULL) {
307 return -1;
310 /* Check to see if they are checking for silence */
311 if (!strcasecmp(data, "spoke")) {
312 if (ast_test_flag(speech, AST_SPEECH_SPOKE))
313 ast_copy_string(buf, "1", len);
314 else
315 ast_copy_string(buf, "0", len);
316 } else if (!strcasecmp(data, "results")) {
317 /* Count number of results */
318 result = speech->results;
319 while (result) {
320 results++;
321 result = result->next;
323 snprintf(tmp, sizeof(tmp), "%d", results);
324 ast_copy_string(buf, tmp, len);
327 return 0;
330 static struct ast_custom_function speech_function = {
331 .name = "SPEECH",
332 .synopsis = "Gets information about speech recognition results.",
333 .syntax = "SPEECH(argument)",
334 .desc =
335 "Gets information about speech recognition results.\n"
336 "status: Returns 1 upon speech object existing, or 0 if not\n"
337 "spoke: Returns 1 if spoker spoke, or 0 if not\n"
338 "results: Returns number of results that were recognized\n",
339 .read = speech_read,
340 .write = NULL,
345 /*! \brief SpeechCreate() Dialplan Application */
346 static int speech_create(struct ast_channel *chan, void *data)
348 struct ast_module_user *u = NULL;
349 struct ast_speech *speech = NULL;
350 struct ast_datastore *datastore = NULL;
352 u = ast_module_user_add(chan);
354 /* Request a speech object */
355 speech = ast_speech_new(data, AST_FORMAT_SLINEAR);
356 if (speech == NULL) {
357 /* Not available */
358 pbx_builtin_setvar_helper(chan, "ERROR", "1");
359 ast_module_user_remove(u);
360 return 0;
363 datastore = ast_channel_datastore_alloc(&speech_datastore, NULL);
364 if (datastore == NULL) {
365 ast_speech_destroy(speech);
366 pbx_builtin_setvar_helper(chan, "ERROR", "1");
367 ast_module_user_remove(u);
368 return 0;
370 datastore->data = speech;
371 ast_channel_datastore_add(chan, datastore);
373 ast_module_user_remove(u);
375 return 0;
378 /*! \brief SpeechLoadGrammar(Grammar Name|Path) Dialplan Application */
379 static int speech_load(struct ast_channel *chan, void *data)
381 int res = 0, argc = 0;
382 struct ast_module_user *u = NULL;
383 struct ast_speech *speech = find_speech(chan);
384 char *argv[2], *args = NULL, *name = NULL, *path = NULL;
386 args = ast_strdupa(data);
388 u = ast_module_user_add(chan);
390 if (speech == NULL) {
391 ast_module_user_remove(u);
392 return -1;
395 /* Parse out arguments */
396 argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0]));
397 if (argc != 2) {
398 ast_module_user_remove(u);
399 return -1;
401 name = argv[0];
402 path = argv[1];
404 /* Load the grammar locally on the object */
405 res = ast_speech_grammar_load(speech, name, path);
407 ast_module_user_remove(u);
409 return res;
412 /*! \brief SpeechUnloadGrammar(Grammar Name) Dialplan Application */
413 static int speech_unload(struct ast_channel *chan, void *data)
415 int res = 0;
416 struct ast_module_user *u = NULL;
417 struct ast_speech *speech = find_speech(chan);
419 u = ast_module_user_add(chan);
421 if (speech == NULL) {
422 ast_module_user_remove(u);
423 return -1;
426 /* Unload the grammar */
427 res = ast_speech_grammar_unload(speech, data);
429 ast_module_user_remove(u);
431 return res;
434 /*! \brief SpeechDeactivateGrammar(Grammar Name) Dialplan Application */
435 static int speech_deactivate(struct ast_channel *chan, void *data)
437 int res = 0;
438 struct ast_module_user *u = NULL;
439 struct ast_speech *speech = find_speech(chan);
441 u = ast_module_user_add(chan);
443 if (speech == NULL) {
444 ast_module_user_remove(u);
445 return -1;
448 /* Deactivate the grammar on the speech object */
449 res = ast_speech_grammar_deactivate(speech, data);
451 ast_module_user_remove(u);
453 return res;
456 /*! \brief SpeechActivateGrammar(Grammar Name) Dialplan Application */
457 static int speech_activate(struct ast_channel *chan, void *data)
459 int res = 0;
460 struct ast_module_user *u = NULL;
461 struct ast_speech *speech = find_speech(chan);
463 u = ast_module_user_add(chan);
465 if (speech == NULL) {
466 ast_module_user_remove(u);
467 return -1;
470 /* Activate the grammar on the speech object */
471 res = ast_speech_grammar_activate(speech, data);
473 ast_module_user_remove(u);
475 return res;
478 /*! \brief SpeechStart() Dialplan Application */
479 static int speech_start(struct ast_channel *chan, void *data)
481 int res = 0;
482 struct ast_module_user *u = NULL;
483 struct ast_speech *speech = find_speech(chan);
485 u = ast_module_user_add(chan);
487 if (speech == NULL) {
488 ast_module_user_remove(u);
489 return -1;
492 ast_speech_start(speech);
494 ast_module_user_remove(u);
496 return res;
499 /*! \brief SpeechProcessingSound(Sound File) Dialplan Application */
500 static int speech_processing_sound(struct ast_channel *chan, void *data)
502 int res = 0;
503 struct ast_module_user *u = NULL;
504 struct ast_speech *speech = find_speech(chan);
506 u = ast_module_user_add(chan);
508 if (speech == NULL) {
509 ast_module_user_remove(u);
510 return -1;
513 if (speech->processing_sound != NULL) {
514 free(speech->processing_sound);
515 speech->processing_sound = NULL;
518 speech->processing_sound = strdup(data);
520 ast_module_user_remove(u);
522 return res;
525 /*! \brief Helper function used by speech_background to playback a soundfile */
526 static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
528 struct ast_filestream *fs = NULL;
530 if (!(fs = ast_openstream(chan, filename, preflang)))
531 return -1;
533 if (ast_applystream(chan, fs))
534 return -1;
536 ast_playstream(fs);
538 return 0;
541 /*! \brief SpeechBackground(Sound File|Timeout) Dialplan Application */
542 static int speech_background(struct ast_channel *chan, void *data)
544 unsigned int timeout = 0;
545 int res = 0, done = 0, argc = 0, started = 0, quieted = 0, max_dtmf_len = 0;
546 struct ast_module_user *u = NULL;
547 struct ast_speech *speech = find_speech(chan);
548 struct ast_frame *f = NULL;
549 int oldreadformat = AST_FORMAT_SLINEAR;
550 char dtmf[AST_MAX_EXTENSION] = "";
551 time_t start, current;
552 struct ast_datastore *datastore = NULL;
553 char *argv[2], *args = NULL, *filename_tmp = NULL, *filename = NULL, tmp[2] = "", dtmf_terminator = '#';
554 const char *tmp2 = NULL;
556 args = ast_strdupa(data);
558 u = ast_module_user_add(chan);
560 if (speech == NULL) {
561 ast_module_user_remove(u);
562 return -1;
565 /* If channel is not already answered, then answer it */
566 if (chan->_state != AST_STATE_UP && ast_answer(chan)) {
567 ast_module_user_remove(u);
568 return -1;
571 /* Record old read format */
572 oldreadformat = chan->readformat;
574 /* Change read format to be signed linear */
575 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
576 ast_module_user_remove(u);
577 return -1;
580 /* Parse out options */
581 argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0]));
582 if (argc > 0) {
583 /* Yay sound file */
584 filename_tmp = ast_strdupa(argv[0]);
585 if (!ast_strlen_zero(argv[1])) {
586 if ((timeout = atoi(argv[1])) == 0)
587 timeout = -1;
588 } else
589 timeout = 0;
592 /* See if the maximum DTMF length variable is set... we use a variable in case they want to carry it through their entire dialplan */
593 if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_MAXLEN")) && !ast_strlen_zero(tmp2))
594 max_dtmf_len = atoi(tmp2);
596 /* See if a terminator is specified */
597 if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_TERMINATOR"))) {
598 if (ast_strlen_zero(tmp2))
599 dtmf_terminator = '\0';
600 else
601 dtmf_terminator = tmp2[0];
604 /* Before we go into waiting for stuff... make sure the structure is ready, if not - start it again */
605 if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
606 ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
607 ast_speech_start(speech);
610 /* Ensure no streams are currently running */
611 ast_stopstream(chan);
613 /* Okay it's streaming so go into a loop grabbing frames! */
614 while (done == 0) {
615 /* If the filename is null and stream is not running, start up a new sound file */
616 if (!quieted && (chan->streamid == -1 && chan->timingfunc == NULL) && (filename = strsep(&filename_tmp, "&"))) {
617 /* Discard old stream information */
618 ast_stopstream(chan);
619 /* Start new stream */
620 speech_streamfile(chan, filename, chan->language);
623 /* Run scheduled stuff */
624 ast_sched_runq(chan->sched);
626 /* Yay scheduling */
627 res = ast_sched_wait(chan->sched);
628 if (res < 0) {
629 res = 1000;
632 /* If there is a frame waiting, get it - if not - oh well */
633 if (ast_waitfor(chan, res) > 0) {
634 f = ast_read(chan);
635 if (f == NULL) {
636 /* The channel has hung up most likely */
637 done = 3;
638 break;
642 /* Do timeout check (shared between audio/dtmf) */
643 if ((!quieted || strlen(dtmf)) && started == 1) {
644 time(&current);
645 if ((current-start) >= timeout) {
646 done = 1;
647 if (f)
648 ast_frfree(f);
649 break;
653 /* Do checks on speech structure to see if it's changed */
654 ast_mutex_lock(&speech->lock);
655 if (ast_test_flag(speech, AST_SPEECH_QUIET)) {
656 if (chan->stream)
657 ast_stopstream(chan);
658 ast_clear_flag(speech, AST_SPEECH_QUIET);
659 quieted = 1;
661 /* Check state so we can see what to do */
662 switch (speech->state) {
663 case AST_SPEECH_STATE_READY:
664 /* If audio playback has stopped do a check for timeout purposes */
665 if (chan->streamid == -1 && chan->timingfunc == NULL)
666 ast_stopstream(chan);
667 if (!quieted && chan->stream == NULL && timeout && started == 0 && !filename_tmp) {
668 if (timeout == -1) {
669 done = 1;
670 if (f)
671 ast_frfree(f);
672 break;
674 time(&start);
675 started = 1;
677 /* Write audio frame out to speech engine if no DTMF has been received */
678 if (!strlen(dtmf) && f != NULL && f->frametype == AST_FRAME_VOICE) {
679 ast_speech_write(speech, f->data, f->datalen);
681 break;
682 case AST_SPEECH_STATE_WAIT:
683 /* Cue up waiting sound if not already playing */
684 if (!strlen(dtmf)) {
685 if (chan->stream == NULL) {
686 if (speech->processing_sound != NULL) {
687 if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) {
688 speech_streamfile(chan, speech->processing_sound, chan->language);
691 } else if (chan->streamid == -1 && chan->timingfunc == NULL) {
692 ast_stopstream(chan);
693 if (speech->processing_sound != NULL) {
694 if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) {
695 speech_streamfile(chan, speech->processing_sound, chan->language);
700 break;
701 case AST_SPEECH_STATE_DONE:
702 /* Now that we are done... let's switch back to not ready state */
703 ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
704 if (!strlen(dtmf)) {
705 /* Copy to speech structure the results, if available */
706 speech->results = ast_speech_results_get(speech);
707 /* Break out of our background too */
708 done = 1;
709 /* Stop audio playback */
710 if (chan->stream != NULL) {
711 ast_stopstream(chan);
714 break;
715 default:
716 break;
718 ast_mutex_unlock(&speech->lock);
720 /* Deal with other frame types */
721 if (f != NULL) {
722 /* Free the frame we received */
723 switch (f->frametype) {
724 case AST_FRAME_DTMF:
725 if (dtmf_terminator != '\0' && f->subclass == dtmf_terminator) {
726 done = 1;
727 } else {
728 if (chan->stream != NULL) {
729 ast_stopstream(chan);
731 if (!started) {
732 /* Change timeout to be 5 seconds for DTMF input */
733 timeout = (chan->pbx && chan->pbx->dtimeout) ? chan->pbx->dtimeout : 5;
734 started = 1;
736 time(&start);
737 snprintf(tmp, sizeof(tmp), "%c", f->subclass);
738 strncat(dtmf, tmp, sizeof(dtmf));
739 /* If the maximum length of the DTMF has been reached, stop now */
740 if (max_dtmf_len && strlen(dtmf) == max_dtmf_len)
741 done = 1;
743 break;
744 case AST_FRAME_CONTROL:
745 switch (f->subclass) {
746 case AST_CONTROL_HANGUP:
747 /* Since they hung up we should destroy the speech structure */
748 done = 3;
749 default:
750 break;
752 default:
753 break;
755 ast_frfree(f);
756 f = NULL;
760 if (strlen(dtmf)) {
761 /* We sort of make a results entry */
762 speech->results = ast_calloc(1, sizeof(*speech->results));
763 if (speech->results != NULL) {
764 ast_speech_dtmf(speech, dtmf);
765 speech->results->score = 1000;
766 speech->results->text = strdup(dtmf);
767 speech->results->grammar = strdup("dtmf");
771 /* See if it was because they hung up */
772 if (done == 3) {
773 /* Destroy speech structure */
774 ast_speech_destroy(speech);
775 datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
776 if (datastore != NULL) {
777 ast_channel_datastore_remove(chan, datastore);
779 } else {
780 /* Channel is okay so restore read format */
781 ast_set_read_format(chan, oldreadformat);
784 ast_module_user_remove(u);
786 return 0;
790 /*! \brief SpeechDestroy() Dialplan Application */
791 static int speech_destroy(struct ast_channel *chan, void *data)
793 int res = 0;
794 struct ast_module_user *u = NULL;
795 struct ast_speech *speech = find_speech(chan);
796 struct ast_datastore *datastore = NULL;
798 u = ast_module_user_add(chan);
800 if (speech == NULL) {
801 ast_module_user_remove(u);
802 return -1;
805 /* Destroy speech structure */
806 ast_speech_destroy(speech);
808 datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
809 if (datastore != NULL) {
810 ast_channel_datastore_remove(chan, datastore);
813 ast_module_user_remove(u);
815 return res;
818 static int unload_module(void)
820 int res = 0;
822 res = ast_unregister_application("SpeechCreate");
823 res |= ast_unregister_application("SpeechLoadGrammar");
824 res |= ast_unregister_application("SpeechUnloadGrammar");
825 res |= ast_unregister_application("SpeechActivateGrammar");
826 res |= ast_unregister_application("SpeechDeactivateGrammar");
827 res |= ast_unregister_application("SpeechStart");
828 res |= ast_unregister_application("SpeechBackground");
829 res |= ast_unregister_application("SpeechDestroy");
830 res |= ast_unregister_application("SpeechProcessingSound");
831 res |= ast_custom_function_unregister(&speech_function);
832 res |= ast_custom_function_unregister(&speech_score_function);
833 res |= ast_custom_function_unregister(&speech_text_function);
834 res |= ast_custom_function_unregister(&speech_grammar_function);
835 res |= ast_custom_function_unregister(&speech_engine_function);
836 res |= ast_custom_function_unregister(&speech_results_type_function);
838 ast_module_user_hangup_all();
840 return res;
843 static int load_module(void)
845 int res = 0;
847 res = ast_register_application("SpeechCreate", speech_create, "Create a Speech Structure", speechcreate_descrip);
848 res |= ast_register_application("SpeechLoadGrammar", speech_load, "Load a Grammar", speechload_descrip);
849 res |= ast_register_application("SpeechUnloadGrammar", speech_unload, "Unload a Grammar", speechunload_descrip);
850 res |= ast_register_application("SpeechActivateGrammar", speech_activate, "Activate a Grammar", speechactivategrammar_descrip);
851 res |= ast_register_application("SpeechDeactivateGrammar", speech_deactivate, "Deactivate a Grammar", speechdeactivategrammar_descrip);
852 res |= ast_register_application("SpeechStart", speech_start, "Start recognizing voice in the audio stream", speechstart_descrip);
853 res |= ast_register_application("SpeechBackground", speech_background, "Play a sound file and wait for speech to be recognized", speechbackground_descrip);
854 res |= ast_register_application("SpeechDestroy", speech_destroy, "End speech recognition", speechdestroy_descrip);
855 res |= ast_register_application("SpeechProcessingSound", speech_processing_sound, "Change background processing sound", speechprocessingsound_descrip);
856 res |= ast_custom_function_register(&speech_function);
857 res |= ast_custom_function_register(&speech_score_function);
858 res |= ast_custom_function_register(&speech_text_function);
859 res |= ast_custom_function_register(&speech_grammar_function);
860 res |= ast_custom_function_register(&speech_engine_function);
861 res |= ast_custom_function_register(&speech_results_type_function);
863 return res;
866 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan Speech Applications");