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