Merge "astobj2: Create function to copy weak proxied objects from container."
[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 /*** MODULEINFO
29         <support_level>core</support_level>
30         <depend>res_speech</depend>
31  ***/
32
33 #include "asterisk.h"
34
35 #include "asterisk/file.h"
36 #include "asterisk/channel.h"
37 #include "asterisk/pbx.h"
38 #include "asterisk/module.h"
39 #include "asterisk/lock.h"
40 #include "asterisk/app.h"
41 #include "asterisk/speech.h"
42
43 /*** DOCUMENTATION
44         <application name="SpeechCreate" language="en_US">
45                 <synopsis>
46                         Create a Speech Structure.
47                 </synopsis>
48                 <syntax>
49                         <parameter name="engine_name" required="true" />
50                 </syntax>
51                 <description>
52                         <para>This application creates information to be used by all the other applications.
53                         It must be called before doing any speech recognition activities such as activating a grammar.
54                         It takes the engine name to use as the argument, if not specified the default engine will be used.</para>
55                         <para>Sets the ERROR channel variable to 1 if the engine cannot be used.</para>
56                 </description>
57         </application>
58         <application name="SpeechActivateGrammar" language="en_US">
59                 <synopsis>
60                         Activate a grammar.
61                 </synopsis>
62                 <syntax>
63                         <parameter name="grammar_name" required="true" />
64                 </syntax>
65                 <description>
66                         <para>This activates the specified grammar to be recognized by the engine.
67                         A grammar tells the speech recognition engine what to recognize, and how to portray it back to you
68                         in the dialplan. The grammar name is the only argument to this application.</para>
69                         <para>Hangs up the channel on failure. If this is not desired, use TryExec.</para>
70                 </description>
71         </application>
72         <application name="SpeechStart" language="en_US">
73                 <synopsis>
74                         Start recognizing voice in the audio stream.
75                 </synopsis>
76                 <syntax />
77                 <description>
78                         <para>Tell the speech recognition engine that it should start trying to get results from audio being
79                         fed to it.</para>
80                         <para>Hangs up the channel on failure. If this is not desired, use TryExec.</para>
81                 </description>
82         </application>
83         <application name="SpeechBackground" language="en_US">
84                 <synopsis>
85                         Play a sound file and wait for speech to be recognized.
86                 </synopsis>
87                 <syntax>
88                         <parameter name="sound_file" required="true" />
89                         <parameter name="timeout">
90                                 <para>Timeout integer in seconds. Note the timeout will only start
91                                 once the sound file has stopped playing.</para>
92                         </parameter>
93                         <parameter name="options">
94                                 <optionlist>
95                                         <option name="n">
96                                                 <para>Don't answer the channel if it has not already been answered.</para>
97                                         </option>
98                                 </optionlist>
99                         </parameter>
100                 </syntax>
101                 <description>
102                         <para>This application plays a sound file and waits for the person to speak. Once they start speaking playback
103                         of the file stops, and silence is heard. Once they stop talking the processing sound is played to indicate
104                         the speech recognition engine is working. Once results are available the application returns and results
105                         (score and text) are available using dialplan functions.</para>
106                         <para>The first text and score are ${SPEECH_TEXT(0)} AND ${SPEECH_SCORE(0)} while the second are ${SPEECH_TEXT(1)}
107                         and ${SPEECH_SCORE(1)}.</para>
108                         <para>The first argument is the sound file and the second is the timeout integer in seconds.</para>
109                         <para>Hangs up the channel on failure. If this is not desired, use TryExec.</para>
110
111                 </description>
112         </application>
113         <application name="SpeechDeactivateGrammar" language="en_US">
114                 <synopsis>
115                         Deactivate a grammar.
116                 </synopsis>
117                 <syntax>
118                         <parameter name="grammar_name" required="true">
119                                 <para>The grammar name to deactivate</para>
120                         </parameter>
121                 </syntax>
122                 <description>
123                         <para>This deactivates the specified grammar so that it is no longer recognized.</para>
124                         <para>Hangs up the channel on failure. If this is not desired, use TryExec.</para>
125                 </description>
126         </application>
127         <application name="SpeechProcessingSound" language="en_US">
128                 <synopsis>
129                         Change background processing sound.
130                 </synopsis>
131                 <syntax>
132                         <parameter name="sound_file" required="true" />
133                 </syntax>
134                 <description>
135                         <para>This changes the processing sound that SpeechBackground plays back when the speech recognition engine is
136                         processing and working to get results.</para>
137                         <para>Hangs up the channel on failure. If this is not desired, use TryExec.</para>
138                 </description>
139         </application>
140         <application name="SpeechDestroy" language="en_US">
141                 <synopsis>
142                         End speech recognition.
143                 </synopsis>
144                 <syntax />
145                 <description>
146                         <para>This destroys the information used by all the other speech recognition applications.
147                         If you call this application but end up wanting to recognize more speech, you must call SpeechCreate()
148                         again before calling any other application.</para>
149                         <para>Hangs up the channel on failure. If this is not desired, use TryExec.</para>
150                 </description>
151         </application>
152         <application name="SpeechLoadGrammar" language="en_US">
153                 <synopsis>
154                         Load a grammar.
155                 </synopsis>
156                 <syntax>
157                         <parameter name="grammar_name" required="true" />
158                         <parameter name="path" required="true" />
159                 </syntax>
160                 <description>
161                         <para>Load a grammar only on the channel, not globally.</para>
162                         <para>Hangs up the channel on failure. If this is not desired, use TryExec.</para>
163                 </description>
164         </application>
165         <application name="SpeechUnloadGrammar" language="en_US">
166                 <synopsis>
167                         Unload a grammar.
168                 </synopsis>
169                 <syntax>
170                         <parameter name="grammar_name" required="true" />
171                 </syntax>
172                 <description>
173                         <para>Unload a grammar.</para>
174                         <para>Hangs up the channel on failure. If this is not desired, use TryExec.</para>
175                 </description>
176         </application>
177         <function name="SPEECH_SCORE" language="en_US">
178                 <synopsis>
179                         Gets the confidence score of a result.
180                 </synopsis>
181                 <syntax argsep="/">
182                         <parameter name="nbest_number" />
183                         <parameter name="result_number" required="true" />
184                 </syntax>
185                 <description>
186                         <para>Gets the confidence score of a result.</para>
187                 </description>
188         </function>
189         <function name="SPEECH_TEXT" language="en_US">
190                 <synopsis>
191                         Gets the recognized text of a result.
192                 </synopsis>
193                 <syntax argsep="/">
194                         <parameter name="nbest_number" />
195                         <parameter name="result_number" required="true" />
196                 </syntax>
197                 <description>
198                         <para>Gets the recognized text of a result.</para>
199                 </description>
200         </function>
201         <function name="SPEECH_GRAMMAR" language="en_US">
202                 <synopsis>
203                         Gets the matched grammar of a result if available.
204                 </synopsis>
205                 <syntax argsep="/">
206                         <parameter name="nbest_number" />
207                         <parameter name="result_number" required="true" />
208                 </syntax>
209                 <description>
210                         <para>Gets the matched grammar of a result if available.</para>
211                 </description>
212         </function>
213         <function name="SPEECH_ENGINE" language="en_US">
214                 <synopsis>
215                         Get or change a speech engine specific attribute.
216                 </synopsis>
217                 <syntax>
218                         <parameter name="name" required="true" />
219                 </syntax>
220                 <description>
221                         <para>Changes a speech engine specific attribute.</para>
222                 </description>
223         </function>
224         <function name="SPEECH_RESULTS_TYPE" language="en_US">
225                 <synopsis>
226                         Sets the type of results that will be returned.
227                 </synopsis>
228                 <syntax />
229                 <description>
230                         <para>Sets the type of results that will be returned. Valid options are normal or nbest.</para>
231                 </description>
232         </function>
233         <function name="SPEECH" language="en_US">
234                 <synopsis>
235                         Gets information about speech recognition results.
236                 </synopsis>
237                 <syntax>
238                         <parameter name="argument" required="true">
239                                 <enumlist>
240                                         <enum name="status">
241                                                 <para>Returns <literal>1</literal> upon speech object existing,
242                                                 or <literal>0</literal> if not</para>
243                                         </enum>
244                                         <enum name="spoke">
245                                                 <para>Returns <literal>1</literal> if spoker spoke,
246                                                 or <literal>0</literal> if not</para>
247                                         </enum>
248                                         <enum name="results">
249                                                 <para>Returns number of results that were recognized.</para>
250                                         </enum>
251                                 </enumlist>
252                         </parameter>
253                 </syntax>
254                 <description>
255                         <para>Gets information about speech recognition results.</para>
256                 </description>
257         </function>
258  ***/
259
260 /*! \brief Helper function used by datastores to destroy the speech structure upon hangup */
261 static void destroy_callback(void *data)
262 {
263         struct ast_speech *speech = (struct ast_speech*)data;
264
265         if (speech == NULL) {
266                 return;
267         }
268
269         /* Deallocate now */
270         ast_speech_destroy(speech);
271
272         return;
273 }
274
275 /*! \brief Static structure for datastore information */
276 static const struct ast_datastore_info speech_datastore = {
277         .type = "speech",
278         .destroy = destroy_callback
279 };
280
281 /*! \brief Helper function used to find the speech structure attached to a channel */
282 static struct ast_speech *find_speech(struct ast_channel *chan)
283 {
284         struct ast_speech *speech = NULL;
285         struct ast_datastore *datastore = NULL;
286
287         if (!chan) {
288                 return NULL;
289         }
290
291         ast_channel_lock(chan);
292         datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
293         ast_channel_unlock(chan);
294         if (datastore == NULL) {
295                 return NULL;
296         }
297         speech = datastore->data;
298
299         return speech;
300 }
301
302 /*!
303  * \internal
304  * \brief Destroy the speech datastore on the given channel.
305  *
306  * \param chan Channel to destroy speech datastore.
307  *
308  * \retval 0 on success.
309  * \retval -1 not found.
310  */
311 static int speech_datastore_destroy(struct ast_channel *chan)
312 {
313         struct ast_datastore *datastore;
314         int res;
315
316         ast_channel_lock(chan);
317         datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
318         if (datastore) {
319                 ast_channel_datastore_remove(chan, datastore);
320         }
321         ast_channel_unlock(chan);
322         if (datastore) {
323                 ast_datastore_free(datastore);
324                 res = 0;
325         } else {
326                 res = -1;
327         }
328         return res;
329 }
330
331 /* Helper function to find a specific speech recognition result by number and nbest alternative */
332 static struct ast_speech_result *find_result(struct ast_speech_result *results, char *result_num)
333 {
334         struct ast_speech_result *result = results;
335         char *tmp = NULL;
336         int nbest_num = 0, wanted_num = 0, i = 0;
337
338         if (!result) {
339                 return NULL;
340         }
341
342         if ((tmp = strchr(result_num, '/'))) {
343                 *tmp++ = '\0';
344                 nbest_num = atoi(result_num);
345                 wanted_num = atoi(tmp);
346         } else {
347                 wanted_num = atoi(result_num);
348         }
349
350         do {
351                 if (result->nbest_num != nbest_num)
352                         continue;
353                 if (i == wanted_num)
354                         break;
355                 i++;
356         } while ((result = AST_LIST_NEXT(result, list)));
357
358         return result;
359 }
360
361 /*! \brief SPEECH_SCORE() Dialplan Function */
362 static int speech_score(struct ast_channel *chan, const char *cmd, char *data,
363                        char *buf, size_t len)
364 {
365         struct ast_speech_result *result = NULL;
366         struct ast_speech *speech = find_speech(chan);
367         char tmp[128] = "";
368
369         if (data == NULL || speech == NULL || !(result = find_result(speech->results, data))) {
370                 return -1;
371         }
372
373         snprintf(tmp, sizeof(tmp), "%d", result->score);
374
375         ast_copy_string(buf, tmp, len);
376
377         return 0;
378 }
379
380 static struct ast_custom_function speech_score_function = {
381         .name = "SPEECH_SCORE",
382         .read = speech_score,
383         .write = NULL,
384 };
385
386 /*! \brief SPEECH_TEXT() Dialplan Function */
387 static int speech_text(struct ast_channel *chan, const char *cmd, char *data,
388                         char *buf, size_t len)
389 {
390         struct ast_speech_result *result = NULL;
391         struct ast_speech *speech = find_speech(chan);
392
393         if (data == NULL || speech == NULL || !(result = find_result(speech->results, data))) {
394                 return -1;
395         }
396
397         if (result->text != NULL) {
398                 ast_copy_string(buf, result->text, len);
399         } else {
400                 buf[0] = '\0';
401         }
402
403         return 0;
404 }
405
406 static struct ast_custom_function speech_text_function = {
407         .name = "SPEECH_TEXT",
408         .read = speech_text,
409         .write = NULL,
410 };
411
412 /*! \brief SPEECH_GRAMMAR() Dialplan Function */
413 static int speech_grammar(struct ast_channel *chan, const char *cmd, char *data,
414                         char *buf, size_t len)
415 {
416         struct ast_speech_result *result = NULL;
417         struct ast_speech *speech = find_speech(chan);
418
419         if (data == NULL || speech == NULL || !(result = find_result(speech->results, data))) {
420                 return -1;
421         }
422
423         if (result->grammar != NULL) {
424                 ast_copy_string(buf, result->grammar, len);
425         } else {
426                 buf[0] = '\0';
427         }
428
429         return 0;
430 }
431
432 static struct ast_custom_function speech_grammar_function = {
433         .name = "SPEECH_GRAMMAR",
434         .read = speech_grammar,
435         .write = NULL,
436 };
437
438 /*! \brief SPEECH_ENGINE() Dialplan Set Function */
439 static int speech_engine_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
440 {
441         struct ast_speech *speech = find_speech(chan);
442
443         if (data == NULL || speech == NULL) {
444                 return -1;
445         }
446
447         ast_speech_change(speech, data, value);
448
449         return 0;
450 }
451
452 /*! \brief SPEECH_ENGINE() Dialplan Get Function */
453 static int speech_engine_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
454 {
455         struct ast_speech *speech = find_speech(chan);
456
457         if (!data || !speech) {
458                 return -1;
459         }
460
461         return ast_speech_get_setting(speech, data, buf, len);
462 }
463
464 static struct ast_custom_function speech_engine_function = {
465         .name = "SPEECH_ENGINE",
466         .read = speech_engine_read,
467         .write = speech_engine_write,
468 };
469
470 /*! \brief SPEECH_RESULTS_TYPE() Dialplan Function */
471 static int speech_results_type_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
472 {
473         struct ast_speech *speech = find_speech(chan);
474
475         if (data == NULL || speech == NULL)
476                 return -1;
477
478         if (!strcasecmp(value, "normal"))
479                 ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NORMAL);
480         else if (!strcasecmp(value, "nbest"))
481                 ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NBEST);
482
483         return 0;
484 }
485
486 static struct ast_custom_function speech_results_type_function = {
487         .name = "SPEECH_RESULTS_TYPE",
488         .read = NULL,
489         .write = speech_results_type_write,
490 };
491
492 /*! \brief SPEECH() Dialplan Function */
493 static int speech_read(struct ast_channel *chan, const char *cmd, char *data,
494                         char *buf, size_t len)
495 {
496         int results = 0;
497         struct ast_speech_result *result = NULL;
498         struct ast_speech *speech = find_speech(chan);
499         char tmp[128] = "";
500
501         /* Now go for the various options */
502         if (!strcasecmp(data, "status")) {
503                 if (speech != NULL)
504                         ast_copy_string(buf, "1", len);
505                 else
506                         ast_copy_string(buf, "0", len);
507                 return 0;
508         }
509
510         /* Make sure we have a speech structure for everything else */
511         if (speech == NULL) {
512                 return -1;
513         }
514
515         /* Check to see if they are checking for silence */
516         if (!strcasecmp(data, "spoke")) {
517                 if (ast_test_flag(speech, AST_SPEECH_SPOKE))
518                         ast_copy_string(buf, "1", len);
519                 else
520                         ast_copy_string(buf, "0", len);
521         } else if (!strcasecmp(data, "results")) {
522                 /* Count number of results */
523                 for (result = speech->results; result; result = AST_LIST_NEXT(result, list))
524                         results++;
525                 snprintf(tmp, sizeof(tmp), "%d", results);
526                 ast_copy_string(buf, tmp, len);
527         } else {
528                 buf[0] = '\0';
529         }
530
531         return 0;
532 }
533
534 static struct ast_custom_function speech_function = {
535         .name = "SPEECH",
536         .read = speech_read,
537         .write = NULL,
538 };
539
540
541
542 /*! \brief SpeechCreate() Dialplan Application */
543 static int speech_create(struct ast_channel *chan, const char *data)
544 {
545         struct ast_speech *speech = NULL;
546         struct ast_datastore *datastore = NULL;
547
548         /* Request a speech object */
549         speech = ast_speech_new(data, ast_channel_nativeformats(chan));
550         if (speech == NULL) {
551                 /* Not available */
552                 pbx_builtin_setvar_helper(chan, "ERROR", "1");
553                 return 0;
554         }
555
556         datastore = ast_datastore_alloc(&speech_datastore, NULL);
557         if (datastore == NULL) {
558                 ast_speech_destroy(speech);
559                 pbx_builtin_setvar_helper(chan, "ERROR", "1");
560                 return 0;
561         }
562         pbx_builtin_setvar_helper(chan, "ERROR", NULL);
563         datastore->data = speech;
564         ast_channel_lock(chan);
565         ast_channel_datastore_add(chan, datastore);
566         ast_channel_unlock(chan);
567
568         return 0;
569 }
570
571 /*! \brief SpeechLoadGrammar(Grammar Name,Path) Dialplan Application */
572 static int speech_load(struct ast_channel *chan, const char *vdata)
573 {
574         int res = 0;
575         struct ast_speech *speech = find_speech(chan);
576         char *data;
577         AST_DECLARE_APP_ARGS(args,
578                 AST_APP_ARG(grammar);
579                 AST_APP_ARG(path);
580         );
581
582         data = ast_strdupa(vdata);
583         AST_STANDARD_APP_ARGS(args, data);
584
585         if (speech == NULL)
586                 return -1;
587
588         if (args.argc != 2)
589                 return -1;
590
591         /* Load the grammar locally on the object */
592         res = ast_speech_grammar_load(speech, args.grammar, args.path);
593
594         return res;
595 }
596
597 /*! \brief SpeechUnloadGrammar(Grammar Name) Dialplan Application */
598 static int speech_unload(struct ast_channel *chan, const char *data)
599 {
600         int res = 0;
601         struct ast_speech *speech = find_speech(chan);
602
603         if (speech == NULL)
604                 return -1;
605
606         /* Unload the grammar */
607         res = ast_speech_grammar_unload(speech, data);
608
609         return res;
610 }
611
612 /*! \brief SpeechDeactivateGrammar(Grammar Name) Dialplan Application */
613 static int speech_deactivate(struct ast_channel *chan, const char *data)
614 {
615         int res = 0;
616         struct ast_speech *speech = find_speech(chan);
617
618         if (speech == NULL)
619                 return -1;
620
621         /* Deactivate the grammar on the speech object */
622         res = ast_speech_grammar_deactivate(speech, data);
623
624         return res;
625 }
626
627 /*! \brief SpeechActivateGrammar(Grammar Name) Dialplan Application */
628 static int speech_activate(struct ast_channel *chan, const char *data)
629 {
630         int res = 0;
631         struct ast_speech *speech = find_speech(chan);
632
633         if (speech == NULL)
634                 return -1;
635
636         /* Activate the grammar on the speech object */
637         res = ast_speech_grammar_activate(speech, data);
638
639         return res;
640 }
641
642 /*! \brief SpeechStart() Dialplan Application */
643 static int speech_start(struct ast_channel *chan, const char *data)
644 {
645         int res = 0;
646         struct ast_speech *speech = find_speech(chan);
647
648         if (speech == NULL)
649                 return -1;
650
651         ast_speech_start(speech);
652
653         return res;
654 }
655
656 /*! \brief SpeechProcessingSound(Sound File) Dialplan Application */
657 static int speech_processing_sound(struct ast_channel *chan, const char *data)
658 {
659         int res = 0;
660         struct ast_speech *speech = find_speech(chan);
661
662         if (speech == NULL)
663                 return -1;
664
665         if (speech->processing_sound != NULL) {
666                 ast_free(speech->processing_sound);
667                 speech->processing_sound = NULL;
668         }
669
670         speech->processing_sound = ast_strdup(data);
671
672         return res;
673 }
674
675 /*! \brief Helper function used by speech_background to playback a soundfile */
676 static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
677 {
678         struct ast_filestream *fs = NULL;
679
680         if (!(fs = ast_openstream(chan, filename, preflang)))
681                 return -1;
682
683         if (ast_applystream(chan, fs))
684                 return -1;
685
686         ast_playstream(fs);
687
688         return 0;
689 }
690
691 enum {
692         SB_OPT_NOANSWER = (1 << 0),
693 };
694
695 AST_APP_OPTIONS(speech_background_options, BEGIN_OPTIONS
696         AST_APP_OPTION('n', SB_OPT_NOANSWER),
697 END_OPTIONS );
698
699 /*! \brief SpeechBackground(Sound File,Timeout) Dialplan Application */
700 static int speech_background(struct ast_channel *chan, const char *data)
701 {
702         unsigned int timeout = 0;
703         int res = 0, done = 0, started = 0, quieted = 0, max_dtmf_len = 0;
704         struct ast_speech *speech = find_speech(chan);
705         struct ast_frame *f = NULL;
706         RAII_VAR(struct ast_format *, oldreadformat, NULL, ao2_cleanup);
707         char dtmf[AST_MAX_EXTENSION] = "";
708         struct timeval start = { 0, 0 }, current;
709         char *parse, *filename_tmp = NULL, *filename = NULL, tmp[2] = "", dtmf_terminator = '#';
710         const char *tmp2 = NULL;
711         struct ast_flags options = { 0 };
712         AST_DECLARE_APP_ARGS(args,
713                 AST_APP_ARG(soundfile);
714                 AST_APP_ARG(timeout);
715                 AST_APP_ARG(options);
716         );
717
718         parse = ast_strdupa(data);
719         AST_STANDARD_APP_ARGS(args, parse);
720
721         if (speech == NULL)
722                 return -1;
723
724         if (!ast_strlen_zero(args.options)) {
725                 char *options_buf = ast_strdupa(args.options);
726                 ast_app_parse_options(speech_background_options, &options, NULL, options_buf);
727         }
728
729         /* If channel is not already answered, then answer it */
730         if (ast_channel_state(chan) != AST_STATE_UP && !ast_test_flag(&options, SB_OPT_NOANSWER)
731                 && ast_answer(chan)) {
732                         return -1;
733         }
734
735         /* Record old read format */
736         oldreadformat = ao2_bump(ast_channel_readformat(chan));
737
738         /* Change read format to be signed linear */
739         if (ast_set_read_format(chan, speech->format))
740                 return -1;
741
742         if (!ast_strlen_zero(args.soundfile)) {
743                 /* Yay sound file */
744                 filename_tmp = ast_strdupa(args.soundfile);
745                 if (!ast_strlen_zero(args.timeout)) {
746                         if ((timeout = atof(args.timeout) * 1000.0) == 0)
747                                 timeout = -1;
748                 } else
749                         timeout = 0;
750         }
751
752         /* See if the maximum DTMF length variable is set... we use a variable in case they want to carry it through their entire dialplan */
753         ast_channel_lock(chan);
754         if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_MAXLEN")) && !ast_strlen_zero(tmp2)) {
755                 max_dtmf_len = atoi(tmp2);
756         }
757
758         /* See if a terminator is specified */
759         if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_TERMINATOR"))) {
760                 if (ast_strlen_zero(tmp2))
761                         dtmf_terminator = '\0';
762                 else
763                         dtmf_terminator = tmp2[0];
764         }
765         ast_channel_unlock(chan);
766
767         /* Before we go into waiting for stuff... make sure the structure is ready, if not - start it again */
768         if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
769                 ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
770                 ast_speech_start(speech);
771         }
772
773         /* Ensure no streams are currently running */
774         ast_stopstream(chan);
775
776         /* Okay it's streaming so go into a loop grabbing frames! */
777         while (done == 0) {
778                 /* If the filename is null and stream is not running, start up a new sound file */
779                 if (!quieted && (ast_channel_streamid(chan) == -1 && ast_channel_timingfunc(chan) == NULL) && (filename = strsep(&filename_tmp, "&"))) {
780                         /* Discard old stream information */
781                         ast_stopstream(chan);
782                         /* Start new stream */
783                         speech_streamfile(chan, filename, ast_channel_language(chan));
784                 }
785
786                 /* Run scheduled stuff */
787                 ast_sched_runq(ast_channel_sched(chan));
788
789                 /* Yay scheduling */
790                 res = ast_sched_wait(ast_channel_sched(chan));
791                 if (res < 0)
792                         res = 1000;
793
794                 /* If there is a frame waiting, get it - if not - oh well */
795                 if (ast_waitfor(chan, res) > 0) {
796                         f = ast_read(chan);
797                         if (f == NULL) {
798                                 /* The channel has hung up most likely */
799                                 done = 3;
800                                 break;
801                         }
802                 }
803
804                 /* Do timeout check (shared between audio/dtmf) */
805                 if ((!quieted || strlen(dtmf)) && started == 1) {
806                         current = ast_tvnow();
807                         if ((ast_tvdiff_ms(current, start)) >= timeout) {
808                                 done = 1;
809                                 if (f)
810                                         ast_frfree(f);
811                                 break;
812                         }
813                 }
814
815                 /* Do checks on speech structure to see if it's changed */
816                 ast_mutex_lock(&speech->lock);
817                 if (ast_test_flag(speech, AST_SPEECH_QUIET)) {
818                         if (ast_channel_stream(chan))
819                                 ast_stopstream(chan);
820                         ast_clear_flag(speech, AST_SPEECH_QUIET);
821                         quieted = 1;
822                 }
823                 /* Check state so we can see what to do */
824                 switch (speech->state) {
825                 case AST_SPEECH_STATE_READY:
826                         /* If audio playback has stopped do a check for timeout purposes */
827                         if (ast_channel_streamid(chan) == -1 && ast_channel_timingfunc(chan) == NULL)
828                                 ast_stopstream(chan);
829                         if (!quieted && ast_channel_stream(chan) == NULL && timeout && started == 0 && !filename_tmp) {
830                                 if (timeout == -1) {
831                                         done = 1;
832                                         if (f)
833                                                 ast_frfree(f);
834                                         break;
835                                 }
836                                 start = ast_tvnow();
837                                 started = 1;
838                         }
839                         /* Write audio frame out to speech engine if no DTMF has been received */
840                         if (!strlen(dtmf) && f != NULL && f->frametype == AST_FRAME_VOICE) {
841                                 ast_speech_write(speech, f->data.ptr, f->datalen);
842                         }
843                         break;
844                 case AST_SPEECH_STATE_WAIT:
845                         /* Cue up waiting sound if not already playing */
846                         if (!strlen(dtmf)) {
847                                 if (ast_channel_stream(chan) == NULL) {
848                                         if (speech->processing_sound != NULL) {
849                                                 if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound, "none")) {
850                                                         speech_streamfile(chan, speech->processing_sound, ast_channel_language(chan));
851                                                 }
852                                         }
853                                 } else if (ast_channel_streamid(chan) == -1 && ast_channel_timingfunc(chan) == NULL) {
854                                         ast_stopstream(chan);
855                                         if (speech->processing_sound != NULL) {
856                                                 if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound, "none")) {
857                                                         speech_streamfile(chan, speech->processing_sound, ast_channel_language(chan));
858                                                 }
859                                         }
860                                 }
861                         }
862                         break;
863                 case AST_SPEECH_STATE_DONE:
864                         /* Now that we are done... let's switch back to not ready state */
865                         ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
866                         if (!strlen(dtmf)) {
867                                 /* Copy to speech structure the results, if available */
868                                 speech->results = ast_speech_results_get(speech);
869                                 /* Break out of our background too */
870                                 done = 1;
871                                 /* Stop audio playback */
872                                 if (ast_channel_stream(chan) != NULL) {
873                                         ast_stopstream(chan);
874                                 }
875                         }
876                         break;
877                 default:
878                         break;
879                 }
880                 ast_mutex_unlock(&speech->lock);
881
882                 /* Deal with other frame types */
883                 if (f != NULL) {
884                         /* Free the frame we received */
885                         switch (f->frametype) {
886                         case AST_FRAME_DTMF:
887                                 if (dtmf_terminator != '\0' && f->subclass.integer == dtmf_terminator) {
888                                         done = 1;
889                                 } else {
890                                         quieted = 1;
891                                         if (ast_channel_stream(chan) != NULL) {
892                                                 ast_stopstream(chan);
893                                         }
894                                         if (!started) {
895                                                 /* Change timeout to be 5 seconds for DTMF input */
896                                                 timeout = (ast_channel_pbx(chan) && ast_channel_pbx(chan)->dtimeoutms) ? ast_channel_pbx(chan)->dtimeoutms : 5000;
897                                                 started = 1;
898                                         }
899                                         start = ast_tvnow();
900                                         snprintf(tmp, sizeof(tmp), "%c", f->subclass.integer);
901                                         strncat(dtmf, tmp, sizeof(dtmf) - strlen(dtmf) - 1);
902                                         /* If the maximum length of the DTMF has been reached, stop now */
903                                         if (max_dtmf_len && strlen(dtmf) == max_dtmf_len)
904                                                 done = 1;
905                                 }
906                                 break;
907                         case AST_FRAME_CONTROL:
908                                 switch (f->subclass.integer) {
909                                 case AST_CONTROL_HANGUP:
910                                         /* Since they hung up we should destroy the speech structure */
911                                         done = 3;
912                                 default:
913                                         break;
914                                 }
915                         default:
916                                 break;
917                         }
918                         ast_frfree(f);
919                         f = NULL;
920                 }
921         }
922
923         if (!ast_strlen_zero(dtmf)) {
924                 /* We sort of make a results entry */
925                 speech->results = ast_calloc(1, sizeof(*speech->results));
926                 if (speech->results != NULL) {
927                         ast_speech_dtmf(speech, dtmf);
928                         speech->results->score = 1000;
929                         speech->results->text = ast_strdup(dtmf);
930                         speech->results->grammar = ast_strdup("dtmf");
931                 }
932                 ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
933         }
934
935         /* See if it was because they hung up */
936         if (done == 3) {
937                 speech_datastore_destroy(chan);
938         } else {
939                 /* Channel is okay so restore read format */
940                 ast_set_read_format(chan, oldreadformat);
941         }
942
943         return 0;
944 }
945
946
947 /*! \brief SpeechDestroy() Dialplan Application */
948 static int speech_destroy(struct ast_channel *chan, const char *data)
949 {
950         if (!chan) {
951                 return -1;
952         }
953         return speech_datastore_destroy(chan);
954 }
955
956 static int unload_module(void)
957 {
958         int res = 0;
959
960         res = ast_unregister_application("SpeechCreate");
961         res |= ast_unregister_application("SpeechLoadGrammar");
962         res |= ast_unregister_application("SpeechUnloadGrammar");
963         res |= ast_unregister_application("SpeechActivateGrammar");
964         res |= ast_unregister_application("SpeechDeactivateGrammar");
965         res |= ast_unregister_application("SpeechStart");
966         res |= ast_unregister_application("SpeechBackground");
967         res |= ast_unregister_application("SpeechDestroy");
968         res |= ast_unregister_application("SpeechProcessingSound");
969         res |= ast_custom_function_unregister(&speech_function);
970         res |= ast_custom_function_unregister(&speech_score_function);
971         res |= ast_custom_function_unregister(&speech_text_function);
972         res |= ast_custom_function_unregister(&speech_grammar_function);
973         res |= ast_custom_function_unregister(&speech_engine_function);
974         res |= ast_custom_function_unregister(&speech_results_type_function);
975
976         return res;
977 }
978
979 static int load_module(void)
980 {
981         int res = 0;
982
983         res = ast_register_application_xml("SpeechCreate", speech_create);
984         res |= ast_register_application_xml("SpeechLoadGrammar", speech_load);
985         res |= ast_register_application_xml("SpeechUnloadGrammar", speech_unload);
986         res |= ast_register_application_xml("SpeechActivateGrammar", speech_activate);
987         res |= ast_register_application_xml("SpeechDeactivateGrammar", speech_deactivate);
988         res |= ast_register_application_xml("SpeechStart", speech_start);
989         res |= ast_register_application_xml("SpeechBackground", speech_background);
990         res |= ast_register_application_xml("SpeechDestroy", speech_destroy);
991         res |= ast_register_application_xml("SpeechProcessingSound", speech_processing_sound);
992         res |= ast_custom_function_register(&speech_function);
993         res |= ast_custom_function_register(&speech_score_function);
994         res |= ast_custom_function_register(&speech_text_function);
995         res |= ast_custom_function_register(&speech_grammar_function);
996         res |= ast_custom_function_register(&speech_engine_function);
997         res |= ast_custom_function_register(&speech_results_type_function);
998
999         return res;
1000 }
1001
1002 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Dialplan Speech Applications",
1003         .support_level = AST_MODULE_SUPPORT_CORE,
1004         .load = load_module,
1005         .unload = unload_module,
1006         .requires = "res_speech",
1007 );