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.
21 * \brief Speech Recognition Utility Applications
23 * \author Joshua Colp <jcolp@digium.com>
25 * \ingroup applications
30 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$");
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
=
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
=
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
;
103 ast_speech_destroy(speech
);
108 /*! \brief Static structure for datastore information */
109 static const struct ast_datastore_info speech_datastore
= {
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
) {
124 speech
= datastore
->data
;
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
;
134 int nbest_num
= 0, wanted_num
= 0, i
= 0;
139 if ((tmp
= strchr(result_num
, '/'))) {
141 nbest_num
= atoi(result_num
);
142 wanted_num
= atoi(tmp
);
144 wanted_num
= atoi(result_num
);
148 if (result
->nbest_num
!= nbest_num
)
153 } while ((result
= result
->next
));
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
);
166 if (data
== NULL
|| speech
== NULL
|| !(result
= find_result(speech
->results
, data
)))
169 snprintf(tmp
, sizeof(tmp
), "%d", result
->score
);
171 ast_copy_string(buf
, tmp
, len
);
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)",
181 "Gets the confidence score of a result.\n",
182 .read
= speech_score
,
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
)))
196 if (result
->text
!= NULL
)
197 ast_copy_string(buf
, result
->text
, len
);
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)",
207 "Gets the recognized text of a result.\n",
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
)))
222 if (result
->grammar
!= NULL
)
223 ast_copy_string(buf
, result
->grammar
, len
);
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)",
233 "Gets the matched grammar of a result if available.\n",
234 .read
= speech_grammar
,
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
)
246 ast_speech_change(speech
, data
, value
);
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",
256 "Changes a speech engine specific attribute.\n",
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
)
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
);
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",
282 "Sets the type of results that will be returned. Valid options are normal or nbest.",
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
)
292 struct ast_speech_result
*result
= NULL
;
293 struct ast_speech
*speech
= find_speech(chan
);
296 /* Now go for the various options */
297 if (!strcasecmp(data
, "status")) {
299 ast_copy_string(buf
, "1", len
);
301 ast_copy_string(buf
, "0", len
);
305 /* Make sure we have a speech structure for everything else */
306 if (speech
== NULL
) {
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
);
315 ast_copy_string(buf
, "0", len
);
316 } else if (!strcasecmp(data
, "results")) {
317 /* Count number of results */
318 result
= speech
->results
;
321 result
= result
->next
;
323 snprintf(tmp
, sizeof(tmp
), "%d", results
);
324 ast_copy_string(buf
, tmp
, len
);
330 static struct ast_custom_function speech_function
= {
332 .synopsis
= "Gets information about speech recognition results.",
333 .syntax
= "SPEECH(argument)",
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",
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
) {
358 pbx_builtin_setvar_helper(chan
, "ERROR", "1");
359 ast_module_user_remove(u
);
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
);
370 datastore
->data
= speech
;
371 ast_channel_datastore_add(chan
, datastore
);
373 ast_module_user_remove(u
);
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
);
395 /* Parse out arguments */
396 argc
= ast_app_separate_args(args
, '|', argv
, sizeof(argv
) / sizeof(argv
[0]));
398 ast_module_user_remove(u
);
404 /* Load the grammar locally on the object */
405 res
= ast_speech_grammar_load(speech
, name
, path
);
407 ast_module_user_remove(u
);
412 /*! \brief SpeechUnloadGrammar(Grammar Name) Dialplan Application */
413 static int speech_unload(struct ast_channel
*chan
, void *data
)
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
);
426 /* Unload the grammar */
427 res
= ast_speech_grammar_unload(speech
, data
);
429 ast_module_user_remove(u
);
434 /*! \brief SpeechDeactivateGrammar(Grammar Name) Dialplan Application */
435 static int speech_deactivate(struct ast_channel
*chan
, void *data
)
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
);
448 /* Deactivate the grammar on the speech object */
449 res
= ast_speech_grammar_deactivate(speech
, data
);
451 ast_module_user_remove(u
);
456 /*! \brief SpeechActivateGrammar(Grammar Name) Dialplan Application */
457 static int speech_activate(struct ast_channel
*chan
, void *data
)
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
);
470 /* Activate the grammar on the speech object */
471 res
= ast_speech_grammar_activate(speech
, data
);
473 ast_module_user_remove(u
);
478 /*! \brief SpeechStart() Dialplan Application */
479 static int speech_start(struct ast_channel
*chan
, void *data
)
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
);
492 ast_speech_start(speech
);
494 ast_module_user_remove(u
);
499 /*! \brief SpeechProcessingSound(Sound File) Dialplan Application */
500 static int speech_processing_sound(struct ast_channel
*chan
, void *data
)
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
);
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
);
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
)))
533 if (ast_applystream(chan
, fs
))
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
);
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
);
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
);
580 /* Parse out options */
581 argc
= ast_app_separate_args(args
, '|', argv
, sizeof(argv
) / sizeof(argv
[0]));
584 filename_tmp
= ast_strdupa(argv
[0]);
585 if (!ast_strlen_zero(argv
[1])) {
586 if ((timeout
= atoi(argv
[1])) == 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';
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! */
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
);
627 res
= ast_sched_wait(chan
->sched
);
632 /* If there is a frame waiting, get it - if not - oh well */
633 if (ast_waitfor(chan
, res
) > 0) {
636 /* The channel has hung up most likely */
642 /* Do timeout check (shared between audio/dtmf) */
643 if ((!quieted
|| strlen(dtmf
)) && started
== 1) {
645 if ((current
-start
) >= timeout
) {
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
)) {
657 ast_stopstream(chan
);
658 ast_clear_flag(speech
, AST_SPEECH_QUIET
);
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
) {
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
);
682 case AST_SPEECH_STATE_WAIT
:
683 /* Cue up waiting sound if not already playing */
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
);
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
);
705 /* Copy to speech structure the results, if available */
706 speech
->results
= ast_speech_results_get(speech
);
707 /* Break out of our background too */
709 /* Stop audio playback */
710 if (chan
->stream
!= NULL
) {
711 ast_stopstream(chan
);
718 ast_mutex_unlock(&speech
->lock
);
720 /* Deal with other frame types */
722 /* Free the frame we received */
723 switch (f
->frametype
) {
725 if (dtmf_terminator
!= '\0' && f
->subclass
== dtmf_terminator
) {
728 if (chan
->stream
!= NULL
) {
729 ast_stopstream(chan
);
732 /* Change timeout to be 5 seconds for DTMF input */
733 timeout
= (chan
->pbx
&& chan
->pbx
->dtimeout
) ? chan
->pbx
->dtimeout
: 5;
737 snprintf(tmp
, sizeof(tmp
), "%c", f
->subclass
);
738 strncat(dtmf
, tmp
, sizeof(dtmf
) - strlen(dtmf
) - 1);
739 /* If the maximum length of the DTMF has been reached, stop now */
740 if (max_dtmf_len
&& strlen(dtmf
) == max_dtmf_len
)
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 */
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 */
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
);
780 /* Channel is okay so restore read format */
781 ast_set_read_format(chan
, oldreadformat
);
784 ast_module_user_remove(u
);
790 /*! \brief SpeechDestroy() Dialplan Application */
791 static int speech_destroy(struct ast_channel
*chan
, void *data
)
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
);
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
);
818 static int unload_module(void)
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();
843 static int load_module(void)
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
);
866 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY
, "Dialplan Speech Applications");