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