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