874c6c69fb10ca1fd6b06e7ae859cfb19b470e9e
[asterisk/asterisk.git] / apps / app_speech_utils.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2006, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@digium.com>
7  *
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.
13  *
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.
17  */
18
19 /*! \file
20  *
21  * \brief Speech Recognition Utility Applications
22  *
23  * \author Joshua Colp <jcolp@digium.com>
24  *
25  * \ingroup applications
26  */
27
28 #include "asterisk.h"
29
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36
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"
45
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";
51
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";
56
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";
60
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 integer in seconds. Note the timeout will only start once the sound file has stopped playing.\n";
68
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";
72
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";
77
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";
83
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";
88
89 static char *speechunload_descrip =
90 "SpeechUnloadGrammar(Grammar Name)\n"
91 "Unload a grammar. It takes the grammar name as the only argument.\n";
92
93 /*! \brief Helper function used by datastores to destroy the speech structure upon hangup */
94 static void destroy_callback(void *data)
95 {
96         struct ast_speech *speech = (struct ast_speech*)data;
97
98         if (speech == NULL) {
99                 return;
100         }
101
102         /* Deallocate now */
103         ast_speech_destroy(speech);
104
105         return;
106 }
107
108 /*! \brief Static structure for datastore information */
109 static const struct ast_datastore_info speech_datastore = {
110         .type = "speech",
111         .destroy = destroy_callback
112 };
113
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)
116 {
117         struct ast_speech *speech = NULL;
118         struct ast_datastore *datastore = NULL;
119         
120         datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
121         if (datastore == NULL) {
122                 return NULL;
123         }
124         speech = datastore->data;
125
126         return speech;
127 }
128
129 /* Helper function to find a specific speech recognition result by number */
130 static struct ast_speech_result *find_result(struct ast_speech_result *results, int num)
131 {
132         struct ast_speech_result *result = NULL;
133         int i = 0;
134
135         result = results;
136         while (result) {
137                 if (i == num)
138                         break;
139                 i++;
140                 result = result->next;
141         }
142
143         return result;
144 }
145
146 /*! \brief SPEECH_SCORE() Dialplan Function */
147 static int speech_score(struct ast_channel *chan, const char *cmd, char *data,
148                        char *buf, size_t len)
149 {
150         struct ast_speech_result *result = NULL;
151         struct ast_speech *speech = find_speech(chan);
152         char tmp[128] = "";
153
154         if (data == NULL || speech == NULL || !(result = find_result(speech->results, atoi(data))))
155                 return -1;
156         
157         snprintf(tmp, sizeof(tmp), "%d", result->score);
158         
159         ast_copy_string(buf, tmp, len);
160
161         return 0;
162 }
163
164 static struct ast_custom_function speech_score_function = {
165         .name = "SPEECH_SCORE",
166         .synopsis = "Gets the confidence score of a result.",
167         .syntax = "SPEECH_SCORE(result number)",
168         .desc =
169         "Gets the confidence score of a result.\n",
170         .read = speech_score,
171         .write = NULL,
172 };
173
174 /*! \brief SPEECH_TEXT() Dialplan Function */
175 static int speech_text(struct ast_channel *chan, const char *cmd, char *data,
176                         char *buf, size_t len)
177 {
178         struct ast_speech_result *result = NULL;
179         struct ast_speech *speech = find_speech(chan);
180
181         if (data == NULL || speech == NULL || !(result = find_result(speech->results, atoi(data))))
182                 return -1;
183
184         if (result->text != NULL)
185                 ast_copy_string(buf, result->text, len);
186
187         return 0;
188 }
189
190 static struct ast_custom_function speech_text_function = {
191         .name = "SPEECH_TEXT",
192         .synopsis = "Gets the recognized text of a result.",
193         .syntax = "SPEECH_TEXT(result number)",
194         .desc =
195         "Gets the recognized text of a result.\n",
196         .read = speech_text,
197         .write = NULL,
198 };
199
200 /*! \brief SPEECH_GRAMMAR() Dialplan Function */
201 static int speech_grammar(struct ast_channel *chan, const char *cmd, char *data,
202                         char *buf, size_t len)
203 {
204         struct ast_speech_result *result = NULL;
205         struct ast_speech *speech = find_speech(chan);
206
207         if (data == NULL || speech == NULL || !(result = find_result(speech->results, atoi(data))))
208                 return -1;
209
210         if (result->grammar != NULL)
211                 ast_copy_string(buf, result->grammar, len);
212
213         return 0;
214 }
215
216 static struct ast_custom_function speech_grammar_function = {
217         .name = "SPEECH_GRAMMAR",
218         .synopsis = "Gets the matched grammar of a result if available.",
219         .syntax = "SPEECH_GRAMMAR(result number)",
220         .desc =
221         "Gets the matched grammar of a result if available.\n",
222         .read = speech_grammar,
223         .write = NULL,
224 };
225
226 /*! \brief SPEECH_ENGINE() Dialplan Function */
227 static int speech_engine_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
228 {
229         struct ast_speech *speech = find_speech(chan);
230
231         if (data == NULL || speech == NULL)
232                 return -1;
233
234         ast_speech_change(speech, data, value);
235
236         return 0;
237 }
238
239 static struct ast_custom_function speech_engine_function = {
240         .name = "SPEECH_ENGINE",
241         .synopsis = "Change a speech engine specific attribute.",
242         .syntax = "SPEECH_ENGINE(name)=value",
243         .desc =
244         "Changes a speech engine specific attribute.\n",
245         .read = NULL,
246         .write = speech_engine_write,
247 };
248
249 /*! \brief SPEECH() Dialplan Function */
250 static int speech_read(struct ast_channel *chan, const char *cmd, char *data,
251                         char *buf, size_t len)
252 {
253         int results = 0;
254         struct ast_speech_result *result = NULL;
255         struct ast_speech *speech = find_speech(chan);
256         char tmp[128] = "";
257
258         /* Now go for the various options */
259         if (!strcasecmp(data, "status")) {
260                 if (speech != NULL)
261                         ast_copy_string(buf, "1", len);
262                 else
263                         ast_copy_string(buf, "0", len);
264                 return 0;
265         }
266
267         /* Make sure we have a speech structure for everything else */
268         if (speech == NULL) {
269                 return -1;
270         }
271
272         /* Check to see if they are checking for silence */
273         if (!strcasecmp(data, "spoke")) {
274                 if (ast_test_flag(speech, AST_SPEECH_SPOKE))
275                         ast_copy_string(buf, "1", len);
276                 else
277                         ast_copy_string(buf, "0", len);
278         } else if (!strcasecmp(data, "results")) {
279                 /* Count number of results */
280                 result = speech->results;
281                 while (result) {
282                         results++;
283                         result = result->next;
284                 }
285                 snprintf(tmp, sizeof(tmp), "%d", results);
286                 ast_copy_string(buf, tmp, len);
287         }
288
289         return 0;
290 }
291
292 static struct ast_custom_function speech_function = {
293         .name = "SPEECH",
294         .synopsis = "Gets information about speech recognition results.",
295         .syntax = "SPEECH(argument)",
296         .desc =
297         "Gets information about speech recognition results.\n"
298         "status:   Returns 1 upon speech object existing, or 0 if not\n"
299         "spoke:  Returns 1 if spoker spoke, or 0 if not\n"
300         "results:  Returns number of results that were recognized\n",
301         .read = speech_read,
302         .write = NULL,
303 };
304
305
306
307 /*! \brief SpeechCreate() Dialplan Application */
308 static int speech_create(struct ast_channel *chan, void *data)
309 {
310         struct ast_module_user *u = NULL;
311         struct ast_speech *speech = NULL;
312         struct ast_datastore *datastore = NULL;
313
314         u = ast_module_user_add(chan);
315
316         /* Request a speech object */
317         speech = ast_speech_new(data, AST_FORMAT_SLINEAR);
318         if (speech == NULL) {
319                 /* Not available */
320                 pbx_builtin_setvar_helper(chan, "ERROR", "1");
321                 ast_module_user_remove(u);
322                 return 0;
323         }
324
325         datastore = ast_channel_datastore_alloc(&speech_datastore, NULL);
326         if (datastore == NULL) {
327                 ast_speech_destroy(speech);
328                 pbx_builtin_setvar_helper(chan, "ERROR", "1");
329                 ast_module_user_remove(u);
330                 return 0;
331         }
332         datastore->data = speech;
333         ast_channel_datastore_add(chan, datastore);
334
335         ast_module_user_remove(u);
336
337         return 0;
338 }
339
340 /*! \brief SpeechLoadGrammar(Grammar Name|Path) Dialplan Application */
341 static int speech_load(struct ast_channel *chan, void *data)
342 {
343         int res = 0, argc = 0;
344         struct ast_module_user *u = NULL;
345         struct ast_speech *speech = find_speech(chan);
346         char *argv[2], *args = NULL, *name = NULL, *path = NULL;
347
348         args = ast_strdupa(data);
349
350         u = ast_module_user_add(chan);
351
352         if (speech == NULL) {
353                 ast_module_user_remove(u);
354                 return -1;
355         }
356
357         /* Parse out arguments */
358         argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0]));
359         if (argc != 2) {
360                 ast_module_user_remove(u);
361                 return -1;
362         }
363         name = argv[0];
364         path = argv[1];
365
366         /* Load the grammar locally on the object */
367         res = ast_speech_grammar_load(speech, name, path);
368
369         ast_module_user_remove(u);
370
371         return res;
372 }
373
374 /*! \brief SpeechUnloadGrammar(Grammar Name) Dialplan Application */
375 static int speech_unload(struct ast_channel *chan, void *data)
376 {
377         int res = 0;
378         struct ast_module_user *u = NULL;
379         struct ast_speech *speech = find_speech(chan);
380
381         u = ast_module_user_add(chan);
382
383         if (speech == NULL) {
384                 ast_module_user_remove(u);
385                 return -1;
386         }
387
388         /* Unload the grammar */
389         res = ast_speech_grammar_unload(speech, data);
390
391         ast_module_user_remove(u);
392
393         return res;
394 }
395
396 /*! \brief SpeechDeactivateGrammar(Grammar Name) Dialplan Application */
397 static int speech_deactivate(struct ast_channel *chan, void *data)
398 {
399         int res = 0;
400         struct ast_module_user *u = NULL;
401         struct ast_speech *speech = find_speech(chan);
402
403         u = ast_module_user_add(chan);
404
405         if (speech == NULL) {
406                 ast_module_user_remove(u);
407                 return -1;
408         }
409
410         /* Deactivate the grammar on the speech object */
411         res = ast_speech_grammar_deactivate(speech, data);
412
413         ast_module_user_remove(u);
414
415         return res;
416 }
417
418 /*! \brief SpeechActivateGrammar(Grammar Name) Dialplan Application */
419 static int speech_activate(struct ast_channel *chan, void *data)
420 {
421         int res = 0;
422         struct ast_module_user *u = NULL;
423         struct ast_speech *speech = find_speech(chan);
424
425         u = ast_module_user_add(chan);
426
427         if (speech == NULL) {
428                 ast_module_user_remove(u);
429                 return -1;
430         }
431
432         /* Activate the grammar on the speech object */
433         res = ast_speech_grammar_activate(speech, data);
434
435         ast_module_user_remove(u);
436
437         return res;
438 }
439
440 /*! \brief SpeechStart() Dialplan Application */
441 static int speech_start(struct ast_channel *chan, void *data)
442 {
443         int res = 0;
444         struct ast_module_user *u = NULL;
445         struct ast_speech *speech = find_speech(chan);
446
447         u = ast_module_user_add(chan);
448
449         if (speech == NULL) {
450                 ast_module_user_remove(u);
451                 return -1;
452         }
453
454         ast_speech_start(speech);
455
456         ast_module_user_remove(u);
457
458         return res;
459 }
460
461 /*! \brief SpeechProcessingSound(Sound File) Dialplan Application */
462 static int speech_processing_sound(struct ast_channel *chan, void *data)
463 {
464         int res = 0;
465         struct ast_module_user *u = NULL;
466         struct ast_speech *speech = find_speech(chan);
467
468         u = ast_module_user_add(chan);
469
470         if (speech == NULL) {
471                 ast_module_user_remove(u);
472                 return -1;
473         }
474
475         if (speech->processing_sound != NULL) {
476                 free(speech->processing_sound);
477                 speech->processing_sound = NULL;
478         }
479
480         speech->processing_sound = strdup(data);
481
482         ast_module_user_remove(u);
483
484         return res;
485 }
486
487 /*! \brief Helper function used by speech_background to playback a soundfile */
488 static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
489 {
490         struct ast_filestream *fs = NULL;
491
492         if (!(fs = ast_openstream(chan, filename, preflang)))
493                 return -1;
494         
495         if (ast_applystream(chan, fs))
496                 return -1;
497         
498         if (ast_playstream(fs))
499                 return -1;
500
501         return 0;
502 }
503
504 /*! \brief SpeechBackground(Sound File|Timeout) Dialplan Application */
505 static int speech_background(struct ast_channel *chan, void *data)
506 {
507         unsigned int timeout = 0;
508         int res = 0, done = 0, argc = 0, started = 0;
509         struct ast_module_user *u = NULL;
510         struct ast_speech *speech = find_speech(chan);
511         struct ast_frame *f = NULL;
512         int oldreadformat = AST_FORMAT_SLINEAR;
513         char dtmf[AST_MAX_EXTENSION] = "";
514         time_t start, current;
515         struct ast_datastore *datastore = NULL;
516         char *argv[2], *args = NULL, *filename_tmp = NULL, *filename = NULL, tmp[2] = "";
517
518         args = ast_strdupa(data);
519
520         u = ast_module_user_add(chan);
521
522         if (speech == NULL) {
523                 ast_module_user_remove(u);
524                 return -1;
525         }
526
527         /* If channel is not already answered, then answer it */
528         if (chan->_state != AST_STATE_UP && ast_answer(chan)) {
529                 ast_module_user_remove(u);
530                 return -1;
531         }
532
533         /* Record old read format */
534         oldreadformat = chan->readformat;
535
536         /* Change read format to be signed linear */
537         if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
538                 ast_module_user_remove(u);
539                 return -1;
540         }
541
542         /* Parse out options */
543         argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0]));
544         if (argc > 0) {
545                 /* Yay sound file */
546                 filename_tmp = ast_strdupa(argv[0]);
547                 if (argv[1] != NULL)
548                         timeout = atoi(argv[1]);
549         }
550
551         /* Before we go into waiting for stuff... make sure the structure is ready, if not - start it again */
552         if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
553                 ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
554                 ast_speech_start(speech);
555         }
556
557         /* Ensure no streams are currently running */
558         ast_stopstream(chan);
559
560         /* Okay it's streaming so go into a loop grabbing frames! */
561         while (done == 0) {
562                 /* If the filename is null and stream is not running, start up a new sound file */
563                 if ((chan->streamid == -1 && chan->timingfunc == NULL) && (filename = strsep(&filename_tmp, "&"))) {
564                         /* Discard old stream information */
565                         ast_stopstream(chan);
566                         /* Start new stream */
567                         speech_streamfile(chan, filename, chan->language);
568                 }
569
570                 /* Run scheduled stuff */
571                 ast_sched_runq(chan->sched);
572
573                 /* Yay scheduling */
574                 res = ast_sched_wait(chan->sched);
575                 if (res < 0) {
576                         res = 1000;
577                 }
578
579                 /* If there is a frame waiting, get it - if not - oh well */
580                 if (ast_waitfor(chan, res) > 0) {
581                         f = ast_read(chan);
582                         if (f == NULL) {
583                                 /* The channel has hung up most likely */
584                                 done = 3;
585                                 break;
586                         }
587                 }
588
589                 /* Do timeout check (shared between audio/dtmf) */
590                 if (started == 1) {
591                         time(&current);
592                         if ((current-start) >= timeout) {
593                                 done = 1;
594                                 if (f)
595                                         ast_frfree(f);
596                                 break;
597                         }
598                 }
599
600                 /* Do checks on speech structure to see if it's changed */
601                 ast_mutex_lock(&speech->lock);
602                 if (ast_test_flag(speech, AST_SPEECH_QUIET) && chan->stream != NULL) {
603                         ast_stopstream(chan);
604                         ast_clear_flag(speech, AST_SPEECH_QUIET);
605                 }
606                 /* Check state so we can see what to do */
607                 switch (speech->state) {
608                 case AST_SPEECH_STATE_READY:
609                         /* If audio playback has stopped do a check for timeout purposes */
610                         if (chan->streamid == -1 && chan->timingfunc == NULL)
611                                 ast_stopstream(chan);
612                         if (chan->stream == NULL && timeout > 0 && started == 0) {
613                                 time(&start);
614                                 started = 1;
615                         }
616                         /* Deal with audio frames if present */
617                         if (f != NULL && f->frametype == AST_FRAME_VOICE) {
618                                 ast_speech_write(speech, f->data, f->datalen);
619                         }
620                         break;
621                 case AST_SPEECH_STATE_WAIT:
622                         /* Cue up waiting sound if not already playing */
623                         if (chan->stream == NULL) {
624                                 if (speech->processing_sound != NULL) {
625                                         if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) {
626                                                 speech_streamfile(chan, speech->processing_sound, chan->language);
627                                         }
628                                 }
629                         } else if (chan->streamid == -1 && chan->timingfunc == NULL) {
630                                 ast_stopstream(chan);
631                                 if (speech->processing_sound != NULL) {
632                                         if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) {
633                                                 speech_streamfile(chan, speech->processing_sound, chan->language);
634                                         }
635                                 }
636                         }
637                         break;
638                 case AST_SPEECH_STATE_DONE:
639                         /* Copy to speech structure the results, if available */
640                         speech->results = ast_speech_results_get(speech);
641                         /* Now that we are done... let's switch back to not ready state */
642                         ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
643                         /* Break out of our background too */
644                         done = 1;
645                         /* Stop audio playback */
646                         if (chan->stream != NULL) {
647                                 ast_stopstream(chan);
648                         }
649                         break;
650                 default:
651                         break;
652                 }
653                 ast_mutex_unlock(&speech->lock);
654
655                 /* Deal with other frame types */
656                 if (f != NULL) {
657                         /* Free the frame we received */
658                         switch (f->frametype) {
659                         case AST_FRAME_DTMF:
660                                 if (f->subclass == '#') {
661                                         done = 1;
662                                 } else {
663                                         if (chan->stream != NULL) {
664                                                 ast_stopstream(chan);
665                                                 /* Change timeout to be 5 seconds for DTMF input */
666                                                 timeout = 5;
667                                                 time(&start);
668                                                 started = 1;
669                                         }
670                                         snprintf(tmp, sizeof(tmp), "%c", f->subclass);
671                                         strncat(dtmf, tmp, sizeof(dtmf));
672                                 }
673                                 break;
674                         case AST_FRAME_CONTROL:
675                                 switch (f->subclass) {
676                                 case AST_CONTROL_HANGUP:
677                                         /* Since they hung up we should destroy the speech structure */
678                                         done = 3;
679                                 default:
680                                         break;
681                                 }
682                         default:
683                                 break;
684                         }
685                         ast_frfree(f);
686                         f = NULL;
687                 }
688         }
689
690         if (strlen(dtmf) > 0 && speech->results == NULL) {
691                 /* We sort of make a results entry */
692                 speech->results = ast_calloc(1, sizeof(*speech->results));
693                 if (speech->results != NULL) {
694                         speech->results->score = 1000;
695                         speech->results->text = strdup(dtmf);
696                         speech->results->grammar = strdup("dtmf");
697                 }
698         }
699
700         /* See if it was because they hung up */
701         if (done == 3) {
702                 /* Destroy speech structure */
703                 ast_speech_destroy(speech);
704                 datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
705                 if (datastore != NULL) {
706                         ast_channel_datastore_remove(chan, datastore);
707                 }
708         } else {
709                 /* Channel is okay so restore read format */
710                 ast_set_read_format(chan, oldreadformat);
711         }
712
713         ast_module_user_remove(u);
714
715         return 0;
716 }
717
718
719 /*! \brief SpeechDestroy() Dialplan Application */
720 static int speech_destroy(struct ast_channel *chan, void *data)
721 {
722         int res = 0;
723         struct ast_module_user *u = NULL;
724         struct ast_speech *speech = find_speech(chan);
725         struct ast_datastore *datastore = NULL;
726
727         u = ast_module_user_add(chan);
728
729         if (speech == NULL) {
730                 ast_module_user_remove(u);
731                 return -1;
732         }
733
734         /* Destroy speech structure */
735         ast_speech_destroy(speech);
736
737         datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
738         if (datastore != NULL) {
739                 ast_channel_datastore_remove(chan, datastore);
740         }
741
742         ast_module_user_remove(u);
743
744         return res;
745 }
746
747 static int unload_module(void)
748 {
749         int res = 0;
750
751         res = ast_unregister_application("SpeechCreate");
752         res |= ast_unregister_application("SpeechLoadGrammar");
753         res |= ast_unregister_application("SpeechUnloadGrammar");
754         res |= ast_unregister_application("SpeechActivateGrammar");
755         res |= ast_unregister_application("SpeechDeactivateGrammar");
756         res |= ast_unregister_application("SpeechStart");
757         res |= ast_unregister_application("SpeechBackground");
758         res |= ast_unregister_application("SpeechDestroy");
759         res |= ast_unregister_application("SpeechProcessingSound");
760         res |= ast_custom_function_unregister(&speech_function);
761         res |= ast_custom_function_unregister(&speech_score_function);
762         res |= ast_custom_function_unregister(&speech_text_function);
763         res |= ast_custom_function_unregister(&speech_grammar_function);
764         res |= ast_custom_function_unregister(&speech_engine_function);
765
766         ast_module_user_hangup_all();
767
768         return res;     
769 }
770
771 static int load_module(void)
772 {
773         int res = 0;
774
775         res = ast_register_application("SpeechCreate", speech_create, "Create a Speech Structure", speechcreate_descrip);
776         res |= ast_register_application("SpeechLoadGrammar", speech_load, "Load a Grammar", speechload_descrip);
777         res |= ast_register_application("SpeechUnloadGrammar", speech_unload, "Unload a Grammar", speechunload_descrip);
778         res |= ast_register_application("SpeechActivateGrammar", speech_activate, "Activate a Grammar", speechactivategrammar_descrip);
779         res |= ast_register_application("SpeechDeactivateGrammar", speech_deactivate, "Deactivate a Grammar", speechdeactivategrammar_descrip);
780         res |= ast_register_application("SpeechStart", speech_start, "Start recognizing voice in the audio stream", speechstart_descrip);
781         res |= ast_register_application("SpeechBackground", speech_background, "Play a sound file and wait for speech to be recognized", speechbackground_descrip);
782         res |= ast_register_application("SpeechDestroy", speech_destroy, "End speech recognition", speechdestroy_descrip);
783         res |= ast_register_application("SpeechProcessingSound", speech_processing_sound, "Change background processing sound", speechprocessingsound_descrip);
784         res |= ast_custom_function_register(&speech_function);
785         res |= ast_custom_function_register(&speech_score_function);
786         res |= ast_custom_function_register(&speech_text_function);
787         res |= ast_custom_function_register(&speech_grammar_function);
788         res |= ast_custom_function_register(&speech_engine_function);
789
790         return res;
791 }
792
793 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan Speech Applications");