Merged revisions 59939 via svnmerge from
[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, quieted = 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 (!ast_strlen_zero(argv[1])) {
548                         if ((timeout = atoi(argv[1])) == 0)
549                                 timeout = -1;
550                 } else
551                         timeout = 0;
552         }
553
554         /* Before we go into waiting for stuff... make sure the structure is ready, if not - start it again */
555         if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
556                 ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
557                 ast_speech_start(speech);
558         }
559
560         /* Ensure no streams are currently running */
561         ast_stopstream(chan);
562
563         /* Okay it's streaming so go into a loop grabbing frames! */
564         while (done == 0) {
565                 /* If the filename is null and stream is not running, start up a new sound file */
566                 if (!quieted && (chan->streamid == -1 && chan->timingfunc == NULL) && (filename = strsep(&filename_tmp, "&"))) {
567                         /* Discard old stream information */
568                         ast_stopstream(chan);
569                         /* Start new stream */
570                         speech_streamfile(chan, filename, chan->language);
571                 }
572
573                 /* Run scheduled stuff */
574                 ast_sched_runq(chan->sched);
575
576                 /* Yay scheduling */
577                 res = ast_sched_wait(chan->sched);
578                 if (res < 0) {
579                         res = 1000;
580                 }
581
582                 /* If there is a frame waiting, get it - if not - oh well */
583                 if (ast_waitfor(chan, res) > 0) {
584                         f = ast_read(chan);
585                         if (f == NULL) {
586                                 /* The channel has hung up most likely */
587                                 done = 3;
588                                 break;
589                         }
590                 }
591
592                 /* Do timeout check (shared between audio/dtmf) */
593                 if (!quieted && started == 1) {
594                         time(&current);
595                         if ((current-start) >= timeout) {
596                                 done = 1;
597                                 if (f)
598                                         ast_frfree(f);
599                                 break;
600                         }
601                 }
602
603                 /* Do checks on speech structure to see if it's changed */
604                 ast_mutex_lock(&speech->lock);
605                 if (ast_test_flag(speech, AST_SPEECH_QUIET)) {
606                         if (chan->stream)
607                                 ast_stopstream(chan);
608                         ast_clear_flag(speech, AST_SPEECH_QUIET);
609                         quieted = 1;
610                 }
611                 /* Check state so we can see what to do */
612                 switch (speech->state) {
613                 case AST_SPEECH_STATE_READY:
614                         /* If audio playback has stopped do a check for timeout purposes */
615                         if (chan->streamid == -1 && chan->timingfunc == NULL)
616                                 ast_stopstream(chan);
617                         if (!quieted && chan->stream == NULL && timeout && started == 0 && !filename_tmp) {
618                                 if (timeout == -1) {
619                                         done = 1;
620                                         if (f)
621                                                 ast_frfree(f);
622                                         break;
623                                 }
624                                 time(&start);
625                                 started = 1;
626                         }
627                         /* Write audio frame out to speech engine if no DTMF has been received */
628                         if (!strlen(dtmf) && f != NULL && f->frametype == AST_FRAME_VOICE) {
629                                 ast_speech_write(speech, f->data, f->datalen);
630                         }
631                         break;
632                 case AST_SPEECH_STATE_WAIT:
633                         /* Cue up waiting sound if not already playing */
634                         if (!strlen(dtmf)) {
635                                 if (chan->stream == NULL) {
636                                         if (speech->processing_sound != NULL) {
637                                                 if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) {
638                                                         speech_streamfile(chan, speech->processing_sound, chan->language);
639                                                 }
640                                         }
641                                 } else if (chan->streamid == -1 && chan->timingfunc == NULL) {
642                                         ast_stopstream(chan);
643                                         if (speech->processing_sound != NULL) {
644                                                 if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) {
645                                                         speech_streamfile(chan, speech->processing_sound, chan->language);
646                                                 }
647                                         }
648                                 }
649                         }
650                         break;
651                 case AST_SPEECH_STATE_DONE:
652                         /* Now that we are done... let's switch back to not ready state */
653                         ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
654                         if (!strlen(dtmf)) {
655                                 /* Copy to speech structure the results, if available */
656                                 speech->results = ast_speech_results_get(speech);
657                                 /* Break out of our background too */
658                                 done = 1;
659                                 /* Stop audio playback */
660                                 if (chan->stream != NULL) {
661                                         ast_stopstream(chan);
662                                 }
663                         }
664                         break;
665                 default:
666                         break;
667                 }
668                 ast_mutex_unlock(&speech->lock);
669
670                 /* Deal with other frame types */
671                 if (f != NULL) {
672                         /* Free the frame we received */
673                         switch (f->frametype) {
674                         case AST_FRAME_DTMF:
675                                 if (f->subclass == '#') {
676                                         done = 1;
677                                 } else {
678                                         if (chan->stream != NULL) {
679                                                 ast_stopstream(chan);
680                                                 /* Change timeout to be 5 seconds for DTMF input */
681                                                 timeout = (chan->pbx && chan->pbx->dtimeout) ? chan->pbx->dtimeout : 5;
682                                                 time(&start);
683                                                 started = 1;
684                                         }
685                                         snprintf(tmp, sizeof(tmp), "%c", f->subclass);
686                                         strncat(dtmf, tmp, sizeof(dtmf));
687                                 }
688                                 break;
689                         case AST_FRAME_CONTROL:
690                                 switch (f->subclass) {
691                                 case AST_CONTROL_HANGUP:
692                                         /* Since they hung up we should destroy the speech structure */
693                                         done = 3;
694                                 default:
695                                         break;
696                                 }
697                         default:
698                                 break;
699                         }
700                         ast_frfree(f);
701                         f = NULL;
702                 }
703         }
704
705         if (strlen(dtmf)) {
706                 /* We sort of make a results entry */
707                 speech->results = ast_calloc(1, sizeof(*speech->results));
708                 if (speech->results != NULL) {
709                         speech->results->score = 1000;
710                         speech->results->text = strdup(dtmf);
711                         speech->results->grammar = strdup("dtmf");
712                 }
713         }
714
715         /* See if it was because they hung up */
716         if (done == 3) {
717                 /* Destroy speech structure */
718                 ast_speech_destroy(speech);
719                 datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
720                 if (datastore != NULL) {
721                         ast_channel_datastore_remove(chan, datastore);
722                 }
723         } else {
724                 /* Channel is okay so restore read format */
725                 ast_set_read_format(chan, oldreadformat);
726         }
727
728         ast_module_user_remove(u);
729
730         return 0;
731 }
732
733
734 /*! \brief SpeechDestroy() Dialplan Application */
735 static int speech_destroy(struct ast_channel *chan, void *data)
736 {
737         int res = 0;
738         struct ast_module_user *u = NULL;
739         struct ast_speech *speech = find_speech(chan);
740         struct ast_datastore *datastore = NULL;
741
742         u = ast_module_user_add(chan);
743
744         if (speech == NULL) {
745                 ast_module_user_remove(u);
746                 return -1;
747         }
748
749         /* Destroy speech structure */
750         ast_speech_destroy(speech);
751
752         datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
753         if (datastore != NULL) {
754                 ast_channel_datastore_remove(chan, datastore);
755         }
756
757         ast_module_user_remove(u);
758
759         return res;
760 }
761
762 static int unload_module(void)
763 {
764         int res = 0;
765
766         res = ast_unregister_application("SpeechCreate");
767         res |= ast_unregister_application("SpeechLoadGrammar");
768         res |= ast_unregister_application("SpeechUnloadGrammar");
769         res |= ast_unregister_application("SpeechActivateGrammar");
770         res |= ast_unregister_application("SpeechDeactivateGrammar");
771         res |= ast_unregister_application("SpeechStart");
772         res |= ast_unregister_application("SpeechBackground");
773         res |= ast_unregister_application("SpeechDestroy");
774         res |= ast_unregister_application("SpeechProcessingSound");
775         res |= ast_custom_function_unregister(&speech_function);
776         res |= ast_custom_function_unregister(&speech_score_function);
777         res |= ast_custom_function_unregister(&speech_text_function);
778         res |= ast_custom_function_unregister(&speech_grammar_function);
779         res |= ast_custom_function_unregister(&speech_engine_function);
780
781         ast_module_user_hangup_all();
782
783         return res;     
784 }
785
786 static int load_module(void)
787 {
788         int res = 0;
789
790         res = ast_register_application("SpeechCreate", speech_create, "Create a Speech Structure", speechcreate_descrip);
791         res |= ast_register_application("SpeechLoadGrammar", speech_load, "Load a Grammar", speechload_descrip);
792         res |= ast_register_application("SpeechUnloadGrammar", speech_unload, "Unload a Grammar", speechunload_descrip);
793         res |= ast_register_application("SpeechActivateGrammar", speech_activate, "Activate a Grammar", speechactivategrammar_descrip);
794         res |= ast_register_application("SpeechDeactivateGrammar", speech_deactivate, "Deactivate a Grammar", speechdeactivategrammar_descrip);
795         res |= ast_register_application("SpeechStart", speech_start, "Start recognizing voice in the audio stream", speechstart_descrip);
796         res |= ast_register_application("SpeechBackground", speech_background, "Play a sound file and wait for speech to be recognized", speechbackground_descrip);
797         res |= ast_register_application("SpeechDestroy", speech_destroy, "End speech recognition", speechdestroy_descrip);
798         res |= ast_register_application("SpeechProcessingSound", speech_processing_sound, "Change background processing sound", speechprocessingsound_descrip);
799         res |= ast_custom_function_register(&speech_function);
800         res |= ast_custom_function_register(&speech_score_function);
801         res |= ast_custom_function_register(&speech_text_function);
802         res |= ast_custom_function_register(&speech_grammar_function);
803         res |= ast_custom_function_register(&speech_engine_function);
804
805         return res;
806 }
807
808 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan Speech Applications");