Asterisk - The Open Source Telephony Project GIT-master-7e7a603
app_speech_utils.c
Go to the documentation of this file.
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 <depend>res_speech</depend>
30 <support_level>core</support_level>
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" argsep="&amp;">
89 <para>Ampersand separated list of filenames. If the filename
90 is a relative filename (it does not begin with a slash), it
91 will be searched for in the Asterisk sounds directory. If the
92 filename is able to be parsed as a URL, Asterisk will
93 download the file and then begin playback on it. To include a
94 literal <literal>&amp;</literal> in the URL you can enclose
95 the URL in single quotes.</para>
96 <argument name="sound_file" required="true" />
97 <argument name="sound_file2" multiple="true" />
98 </parameter>
99 <parameter name="timeout">
100 <para>Timeout integer in seconds. Note the timeout will only start
101 once the sound file has stopped playing.</para>
102 </parameter>
103 <parameter name="options">
104 <optionlist>
105 <option name="n">
106 <para>Don't answer the channel if it has not already been answered.</para>
107 </option>
108 <option name="p">
109 <para>Return partial results when backend is terminated by timeout.</para>
110 </option>
111 </optionlist>
112 </parameter>
113 </syntax>
114 <description>
115 <para>This application plays a sound file and waits for the person to speak. Once they start speaking playback
116 of the file stops, and silence is heard. Once they stop talking the processing sound is played to indicate
117 the speech recognition engine is working. Once results are available the application returns and results
118 (score and text) are available using dialplan functions.</para>
119 <para>The first text and score are ${SPEECH_TEXT(0)} AND ${SPEECH_SCORE(0)} while the second are ${SPEECH_TEXT(1)}
120 and ${SPEECH_SCORE(1)}.</para>
121 <para>The first argument is the sound file and the second is the timeout integer in seconds.</para>
122 <para>Hangs up the channel on failure. If this is not desired, use TryExec.</para>
123
124 </description>
125 </application>
126 <application name="SpeechDeactivateGrammar" language="en_US">
127 <synopsis>
128 Deactivate a grammar.
129 </synopsis>
130 <syntax>
131 <parameter name="grammar_name" required="true">
132 <para>The grammar name to deactivate</para>
133 </parameter>
134 </syntax>
135 <description>
136 <para>This deactivates the specified grammar so that it is no longer recognized.</para>
137 <para>Hangs up the channel on failure. If this is not desired, use TryExec.</para>
138 </description>
139 </application>
140 <application name="SpeechProcessingSound" language="en_US">
141 <synopsis>
142 Change background processing sound.
143 </synopsis>
144 <syntax>
145 <parameter name="sound_file" required="true" />
146 </syntax>
147 <description>
148 <para>This changes the processing sound that SpeechBackground plays back when the speech recognition engine is
149 processing and working to get results.</para>
150 <para>Hangs up the channel on failure. If this is not desired, use TryExec.</para>
151 </description>
152 </application>
153 <application name="SpeechDestroy" language="en_US">
154 <synopsis>
155 End speech recognition.
156 </synopsis>
157 <syntax />
158 <description>
159 <para>This destroys the information used by all the other speech recognition applications.
160 If you call this application but end up wanting to recognize more speech, you must call SpeechCreate()
161 again before calling any other application.</para>
162 <para>Hangs up the channel on failure. If this is not desired, use TryExec.</para>
163 </description>
164 </application>
165 <application name="SpeechLoadGrammar" language="en_US">
166 <synopsis>
167 Load a grammar.
168 </synopsis>
169 <syntax>
170 <parameter name="grammar_name" required="true" />
171 <parameter name="path" required="true" />
172 </syntax>
173 <description>
174 <para>Load a grammar only on the channel, not globally.</para>
175 <para>Hangs up the channel on failure. If this is not desired, use TryExec.</para>
176 </description>
177 </application>
178 <application name="SpeechUnloadGrammar" language="en_US">
179 <synopsis>
180 Unload a grammar.
181 </synopsis>
182 <syntax>
183 <parameter name="grammar_name" required="true" />
184 </syntax>
185 <description>
186 <para>Unload a grammar.</para>
187 <para>Hangs up the channel on failure. If this is not desired, use TryExec.</para>
188 </description>
189 </application>
190 <function name="SPEECH_SCORE" language="en_US">
191 <synopsis>
192 Gets the confidence score of a result.
193 </synopsis>
194 <syntax argsep="/">
195 <parameter name="nbest_number" />
196 <parameter name="result_number" required="true" />
197 </syntax>
198 <description>
199 <para>Gets the confidence score of a result.</para>
200 </description>
201 </function>
202 <function name="SPEECH_TEXT" language="en_US">
203 <synopsis>
204 Gets the recognized text of a result.
205 </synopsis>
206 <syntax argsep="/">
207 <parameter name="nbest_number" />
208 <parameter name="result_number" required="true" />
209 </syntax>
210 <description>
211 <para>Gets the recognized text of a result.</para>
212 </description>
213 </function>
214 <function name="SPEECH_GRAMMAR" language="en_US">
215 <synopsis>
216 Gets the matched grammar of a result if available.
217 </synopsis>
218 <syntax argsep="/">
219 <parameter name="nbest_number" />
220 <parameter name="result_number" required="true" />
221 </syntax>
222 <description>
223 <para>Gets the matched grammar of a result if available.</para>
224 </description>
225 </function>
226 <function name="SPEECH_ENGINE" language="en_US">
227 <synopsis>
228 Get or change a speech engine specific attribute.
229 </synopsis>
230 <syntax>
231 <parameter name="name" required="true" />
232 </syntax>
233 <description>
234 <para>Changes a speech engine specific attribute.</para>
235 </description>
236 </function>
237 <function name="SPEECH_RESULTS_TYPE" language="en_US">
238 <synopsis>
239 Sets the type of results that will be returned.
240 </synopsis>
241 <syntax />
242 <description>
243 <para>Sets the type of results that will be returned. Valid options are normal or nbest.</para>
244 </description>
245 </function>
246 <function name="SPEECH" language="en_US">
247 <synopsis>
248 Gets information about speech recognition results.
249 </synopsis>
250 <syntax>
251 <parameter name="argument" required="true">
252 <enumlist>
253 <enum name="status">
254 <para>Returns <literal>1</literal> upon speech object existing,
255 or <literal>0</literal> if not</para>
256 </enum>
257 <enum name="spoke">
258 <para>Returns <literal>1</literal> if spoker spoke,
259 or <literal>0</literal> if not</para>
260 </enum>
261 <enum name="results">
262 <para>Returns number of results that were recognized.</para>
263 </enum>
264 </enumlist>
265 </parameter>
266 </syntax>
267 <description>
268 <para>Gets information about speech recognition results.</para>
269 </description>
270 </function>
271 ***/
272
273/*! \brief Helper function used by datastores to destroy the speech structure upon hangup */
274static void destroy_callback(void *data)
275{
276 struct ast_speech *speech = (struct ast_speech*)data;
277
278 if (speech == NULL) {
279 return;
280 }
281
282 /* Deallocate now */
283 ast_speech_destroy(speech);
284
285 return;
286}
287
288/*! \brief Static structure for datastore information */
290 .type = "speech",
291 .destroy = destroy_callback
292};
293
294/*! \brief Helper function used to find the speech structure attached to a channel */
295static struct ast_speech *find_speech(struct ast_channel *chan)
296{
297 struct ast_speech *speech = NULL;
298 struct ast_datastore *datastore = NULL;
299
300 if (!chan) {
301 return NULL;
302 }
303
304 ast_channel_lock(chan);
306 ast_channel_unlock(chan);
307 if (datastore == NULL) {
308 return NULL;
309 }
310 speech = datastore->data;
311
312 return speech;
313}
314
315/*!
316 * \internal
317 * \brief Destroy the speech datastore on the given channel.
318 *
319 * \param chan Channel to destroy speech datastore.
320 *
321 * \retval 0 on success.
322 * \retval -1 not found.
323 */
324static int speech_datastore_destroy(struct ast_channel *chan)
325{
326 struct ast_datastore *datastore;
327 int res;
328
329 ast_channel_lock(chan);
331 if (datastore) {
332 ast_channel_datastore_remove(chan, datastore);
333 }
334 ast_channel_unlock(chan);
335 if (datastore) {
336 ast_datastore_free(datastore);
337 res = 0;
338 } else {
339 res = -1;
340 }
341 return res;
342}
343
344/* Helper function to find a specific speech recognition result by number and nbest alternative */
345static struct ast_speech_result *find_result(struct ast_speech_result *results, char *result_num)
346{
347 struct ast_speech_result *result = results;
348 char *tmp = NULL;
349 int nbest_num = 0, wanted_num = 0, i = 0;
350
351 if (!result) {
352 return NULL;
353 }
354
355 if ((tmp = strchr(result_num, '/'))) {
356 *tmp++ = '\0';
357 nbest_num = atoi(result_num);
358 wanted_num = atoi(tmp);
359 } else {
360 wanted_num = atoi(result_num);
361 }
362
363 do {
364 if (result->nbest_num != nbest_num)
365 continue;
366 if (i == wanted_num)
367 break;
368 i++;
369 } while ((result = AST_LIST_NEXT(result, list)));
370
371 return result;
372}
373
374/*! \brief SPEECH_SCORE() Dialplan Function */
375static int speech_score(struct ast_channel *chan, const char *cmd, char *data,
376 char *buf, size_t len)
377{
378 struct ast_speech_result *result = NULL;
379 struct ast_speech *speech = find_speech(chan);
380 char tmp[128] = "";
381
382 if (data == NULL || speech == NULL || !(result = find_result(speech->results, data))) {
383 return -1;
384 }
385
386 snprintf(tmp, sizeof(tmp), "%d", result->score);
387
389
390 return 0;
391}
392
394 .name = "SPEECH_SCORE",
395 .read = speech_score,
396 .write = NULL,
397};
398
399/*! \brief SPEECH_TEXT() Dialplan Function */
400static int speech_text(struct ast_channel *chan, const char *cmd, char *data,
401 char *buf, size_t len)
402{
403 struct ast_speech_result *result = NULL;
404 struct ast_speech *speech = find_speech(chan);
405
406 if (data == NULL || speech == NULL || !(result = find_result(speech->results, data))) {
407 return -1;
408 }
409
410 if (result->text != NULL) {
411 ast_copy_string(buf, result->text, len);
412 } else {
413 buf[0] = '\0';
414 }
415
416 return 0;
417}
418
420 .name = "SPEECH_TEXT",
421 .read = speech_text,
422 .write = NULL,
423};
424
425/*! \brief SPEECH_GRAMMAR() Dialplan Function */
426static int speech_grammar(struct ast_channel *chan, const char *cmd, char *data,
427 char *buf, size_t len)
428{
429 struct ast_speech_result *result = NULL;
430 struct ast_speech *speech = find_speech(chan);
431
432 if (data == NULL || speech == NULL || !(result = find_result(speech->results, data))) {
433 return -1;
434 }
435
436 if (result->grammar != NULL) {
437 ast_copy_string(buf, result->grammar, len);
438 } else {
439 buf[0] = '\0';
440 }
441
442 return 0;
443}
444
446 .name = "SPEECH_GRAMMAR",
447 .read = speech_grammar,
448 .write = NULL,
449};
450
451/*! \brief SPEECH_ENGINE() Dialplan Set Function */
452static int speech_engine_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
453{
454 struct ast_speech *speech = find_speech(chan);
455
456 if (data == NULL || speech == NULL) {
457 return -1;
458 }
459
460 ast_speech_change(speech, data, value);
461
462 return 0;
463}
464
465/*! \brief SPEECH_ENGINE() Dialplan Get Function */
466static int speech_engine_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
467{
468 struct ast_speech *speech = find_speech(chan);
469
470 if (!data || !speech) {
471 return -1;
472 }
473
474 return ast_speech_get_setting(speech, data, buf, len);
475}
476
478 .name = "SPEECH_ENGINE",
479 .read = speech_engine_read,
480 .write = speech_engine_write,
481};
482
483/*! \brief SPEECH_RESULTS_TYPE() Dialplan Function */
484static int speech_results_type_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
485{
486 struct ast_speech *speech = find_speech(chan);
487
488 if (data == NULL || speech == NULL)
489 return -1;
490
491 if (!strcasecmp(value, "normal"))
493 else if (!strcasecmp(value, "nbest"))
495
496 return 0;
497}
498
500 .name = "SPEECH_RESULTS_TYPE",
501 .read = NULL,
503};
504
505/*! \brief SPEECH() Dialplan Function */
506static int speech_read(struct ast_channel *chan, const char *cmd, char *data,
507 char *buf, size_t len)
508{
509 int results = 0;
510 struct ast_speech_result *result = NULL;
511 struct ast_speech *speech = find_speech(chan);
512 char tmp[128] = "";
513
514 /* Now go for the various options */
515 if (!strcasecmp(data, "status")) {
516 if (speech != NULL)
517 ast_copy_string(buf, "1", len);
518 else
519 ast_copy_string(buf, "0", len);
520 return 0;
521 }
522
523 /* Make sure we have a speech structure for everything else */
524 if (speech == NULL) {
525 return -1;
526 }
527
528 /* Check to see if they are checking for silence */
529 if (!strcasecmp(data, "spoke")) {
530 if (ast_test_flag(speech, AST_SPEECH_SPOKE))
531 ast_copy_string(buf, "1", len);
532 else
533 ast_copy_string(buf, "0", len);
534 } else if (!strcasecmp(data, "results")) {
535 /* Count number of results */
536 for (result = speech->results; result; result = AST_LIST_NEXT(result, list))
537 results++;
538 snprintf(tmp, sizeof(tmp), "%d", results);
540 } else {
541 buf[0] = '\0';
542 }
543
544 return 0;
545}
546
548 .name = "SPEECH",
549 .read = speech_read,
550 .write = NULL,
551};
552
553
554
555/*! \brief SpeechCreate() Dialplan Application */
556static int speech_create(struct ast_channel *chan, const char *data)
557{
558 struct ast_speech *speech = NULL;
559 struct ast_datastore *datastore = NULL;
560
561 /* Request a speech object */
563 if (speech == NULL) {
564 /* Not available */
565 pbx_builtin_setvar_helper(chan, "ERROR", "1");
566 return 0;
567 }
568
570 if (datastore == NULL) {
571 ast_speech_destroy(speech);
572 pbx_builtin_setvar_helper(chan, "ERROR", "1");
573 return 0;
574 }
575 pbx_builtin_setvar_helper(chan, "ERROR", NULL);
576 datastore->data = speech;
577 ast_channel_lock(chan);
578 ast_channel_datastore_add(chan, datastore);
579 ast_channel_unlock(chan);
580
581 return 0;
582}
583
584/*! \brief SpeechLoadGrammar(Grammar Name,Path) Dialplan Application */
585static int speech_load(struct ast_channel *chan, const char *vdata)
586{
587 int res = 0;
588 struct ast_speech *speech = find_speech(chan);
589 char *data;
591 AST_APP_ARG(grammar);
592 AST_APP_ARG(path);
593 );
594
595 data = ast_strdupa(vdata);
597
598 if (speech == NULL)
599 return -1;
600
601 if (args.argc != 2)
602 return -1;
603
604 /* Load the grammar locally on the object */
605 res = ast_speech_grammar_load(speech, args.grammar, args.path);
606
607 return res;
608}
609
610/*! \brief SpeechUnloadGrammar(Grammar Name) Dialplan Application */
611static int speech_unload(struct ast_channel *chan, const char *data)
612{
613 int res = 0;
614 struct ast_speech *speech = find_speech(chan);
615
616 if (speech == NULL)
617 return -1;
618
619 /* Unload the grammar */
620 res = ast_speech_grammar_unload(speech, data);
621
622 return res;
623}
624
625/*! \brief SpeechDeactivateGrammar(Grammar Name) Dialplan Application */
626static int speech_deactivate(struct ast_channel *chan, const char *data)
627{
628 int res = 0;
629 struct ast_speech *speech = find_speech(chan);
630
631 if (speech == NULL)
632 return -1;
633
634 /* Deactivate the grammar on the speech object */
635 res = ast_speech_grammar_deactivate(speech, data);
636
637 return res;
638}
639
640/*! \brief SpeechActivateGrammar(Grammar Name) Dialplan Application */
641static int speech_activate(struct ast_channel *chan, const char *data)
642{
643 int res = 0;
644 struct ast_speech *speech = find_speech(chan);
645
646 if (speech == NULL)
647 return -1;
648
649 /* Activate the grammar on the speech object */
650 res = ast_speech_grammar_activate(speech, data);
651
652 return res;
653}
654
655/*! \brief SpeechStart() Dialplan Application */
656static int speech_start(struct ast_channel *chan, const char *data)
657{
658 int res = 0;
659 struct ast_speech *speech = find_speech(chan);
660
661 if (speech == NULL)
662 return -1;
663
664 ast_speech_start(speech);
665
666 return res;
667}
668
669/*! \brief SpeechProcessingSound(Sound File) Dialplan Application */
670static int speech_processing_sound(struct ast_channel *chan, const char *data)
671{
672 int res = 0;
673 struct ast_speech *speech = find_speech(chan);
674
675 if (speech == NULL)
676 return -1;
677
678 if (speech->processing_sound != NULL) {
679 ast_free(speech->processing_sound);
680 speech->processing_sound = NULL;
681 }
682
684
685 return res;
686}
687
688/*! \brief Helper function used by speech_background to playback a soundfile */
689static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
690{
691 struct ast_filestream *fs = NULL;
692
693 if (!(fs = ast_openstream(chan, filename, preflang)))
694 return -1;
695
696 if (ast_applystream(chan, fs))
697 return -1;
698
699 ast_playstream(fs);
700
701 return 0;
702}
703
704enum {
705 SB_OPT_NOANSWER = (1 << 0),
707};
708
713
714/*! \brief SpeechBackground(Sound File,Timeout) Dialplan Application */
715static int speech_background(struct ast_channel *chan, const char *data)
716{
717 unsigned int timeout = 0;
718 int res = 0, done = 0, started = 0, quieted = 0, max_dtmf_len = 0;
719 struct ast_speech *speech = find_speech(chan);
720 struct ast_frame *f = NULL;
721 RAII_VAR(struct ast_format *, oldreadformat, NULL, ao2_cleanup);
722 char dtmf[AST_MAX_EXTENSION] = "";
723 struct timeval start = { 0, 0 }, current;
724 char *parse, *filename_tmp = NULL, *filename = NULL, tmp[2] = "", dtmf_terminator = '#';
725 const char *tmp2 = NULL;
726 struct ast_flags options = { 0 };
728 AST_APP_ARG(soundfile);
729 AST_APP_ARG(timeout);
731 );
732
733 parse = ast_strdupa(data);
735
736 if (speech == NULL)
737 return -1;
738
739 if (!ast_strlen_zero(args.options)) {
740 char *options_buf = ast_strdupa(args.options);
742 }
743
744 /* If channel is not already answered, then answer it */
746 && ast_answer(chan)) {
747 return -1;
748 }
749
750 /* Record old read format */
751 oldreadformat = ao2_bump(ast_channel_readformat(chan));
752
753 /* Change read format to be signed linear */
754 if (ast_set_read_format(chan, speech->format))
755 return -1;
756
757 if (!ast_strlen_zero(args.soundfile)) {
758 /* Yay sound file */
759 filename_tmp = ast_strdupa(args.soundfile);
760 if (!ast_strlen_zero(args.timeout)) {
761 if ((timeout = atof(args.timeout) * 1000.0) == 0)
762 timeout = -1;
763 } else
764 timeout = 0;
765 }
766
767 /* See if the maximum DTMF length variable is set... we use a variable in case they want to carry it through their entire dialplan */
768 ast_channel_lock(chan);
769 if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_MAXLEN")) && !ast_strlen_zero(tmp2)) {
770 max_dtmf_len = atoi(tmp2);
771 }
772
773 /* See if a terminator is specified */
774 if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_TERMINATOR"))) {
775 if (ast_strlen_zero(tmp2))
776 dtmf_terminator = '\0';
777 else
778 dtmf_terminator = tmp2[0];
779 }
780 ast_channel_unlock(chan);
781
782 /* Before we go into waiting for stuff... make sure the structure is ready, if not - start it again */
783 if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
785 ast_speech_start(speech);
786 }
787
788 /* Ensure no streams are currently running */
789 ast_stopstream(chan);
790
791 /* Okay it's streaming so go into a loop grabbing frames! */
792 while (done == 0) {
793 /* If the filename is null and stream is not running, start up a new sound file */
794 if (!quieted
795 && ast_channel_streamid(chan) == -1
796 && ast_channel_timingfunc(chan) == NULL
797 && (filename = ast_strsep(&filename_tmp, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
798 /* Discard old stream information */
799 ast_stopstream(chan);
800 /* Start new stream */
801 speech_streamfile(chan, filename, ast_channel_language(chan));
802 }
803
804 /* Run scheduled stuff */
806
807 /* Yay scheduling */
809 if (res < 0)
810 res = 1000;
811
812 /* If there is a frame waiting, get it - if not - oh well */
813 if (ast_waitfor(chan, res) > 0) {
814 f = ast_read(chan);
815 if (f == NULL) {
816 /* The channel has hung up most likely */
817 done = 3;
818 break;
819 }
820 }
821
822 /* Do timeout check (shared between audio/dtmf) */
823 if ((!quieted || strlen(dtmf)) && started == 1) {
824 current = ast_tvnow();
825 if ((ast_tvdiff_ms(current, start)) >= timeout) {
826 done = 1;
827 if (f)
828 ast_frfree(f);
829 break;
830 }
831 }
832
833 /* Do checks on speech structure to see if it's changed */
834 ast_mutex_lock(&speech->lock);
835 if (ast_test_flag(speech, AST_SPEECH_QUIET)) {
836 if (ast_channel_stream(chan))
837 ast_stopstream(chan);
839 quieted = 1;
840 }
841 /* Check state so we can see what to do */
842 switch (speech->state) {
844 /* If audio playback has stopped do a check for timeout purposes */
845 if (ast_channel_streamid(chan) == -1 && ast_channel_timingfunc(chan) == NULL)
846 ast_stopstream(chan);
847 if (!quieted && ast_channel_stream(chan) == NULL && timeout && started == 0 && !filename_tmp) {
848 if (timeout == -1) {
849 done = 1;
850 if (f)
851 ast_frfree(f);
852 break;
853 }
854 start = ast_tvnow();
855 started = 1;
856 }
857 /* Write audio frame out to speech engine if no DTMF has been received */
858 if (!strlen(dtmf) && f != NULL && f->frametype == AST_FRAME_VOICE) {
859 ast_speech_write(speech, f->data.ptr, f->datalen);
860 }
861 break;
863 /* Cue up waiting sound if not already playing */
864 if (!strlen(dtmf)) {
865 if (ast_channel_stream(chan) == NULL) {
866 if (speech->processing_sound != NULL) {
867 if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound, "none")) {
869 }
870 }
871 } else if (ast_channel_streamid(chan) == -1 && ast_channel_timingfunc(chan) == NULL) {
872 ast_stopstream(chan);
873 if (speech->processing_sound != NULL) {
874 if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound, "none")) {
876 }
877 }
878 }
879 }
880 break;
882 /* Now that we are done... let's switch back to not ready state */
884 if (!strlen(dtmf)) {
885 /* Copy to speech structure the results, if available */
886 speech->results = ast_speech_results_get(speech);
887 /* Break out of our background too */
888 done = 1;
889 /* Stop audio playback */
890 if (ast_channel_stream(chan) != NULL) {
891 ast_stopstream(chan);
892 }
893 }
894 break;
895 default:
896 break;
897 }
898 ast_mutex_unlock(&speech->lock);
899
900 /* Deal with other frame types */
901 if (f != NULL) {
902 /* Free the frame we received */
903 switch (f->frametype) {
904 case AST_FRAME_DTMF:
905 if (dtmf_terminator != '\0' && f->subclass.integer == dtmf_terminator) {
906 done = 1;
907 } else {
908 quieted = 1;
909 if (ast_channel_stream(chan) != NULL) {
910 ast_stopstream(chan);
911 }
912 if (!started) {
913 /* Change timeout to be 5 seconds for DTMF input */
914 timeout = (ast_channel_pbx(chan) && ast_channel_pbx(chan)->dtimeoutms) ? ast_channel_pbx(chan)->dtimeoutms : 5000;
915 started = 1;
916 }
917 start = ast_tvnow();
918 snprintf(tmp, sizeof(tmp), "%c", f->subclass.integer);
919 strncat(dtmf, tmp, sizeof(dtmf) - strlen(dtmf) - 1);
920 /* If the maximum length of the DTMF has been reached, stop now */
921 if (max_dtmf_len && strlen(dtmf) == max_dtmf_len)
922 done = 1;
923 }
924 break;
926 switch (f->subclass.integer) {
928 /* Since they hung up we should destroy the speech structure */
929 done = 3;
930 default:
931 break;
932 }
933 default:
934 break;
935 }
936 ast_frfree(f);
937 f = NULL;
938 }
939 }
940
942 /* Copy to speech structure the results, even partial ones, if desired and available */
943 speech->results = ast_speech_results_get(speech);
944 } else if (!ast_strlen_zero(dtmf)) {
945 /* We sort of make a results entry */
946 speech->results = ast_calloc(1, sizeof(*speech->results));
947 if (speech->results != NULL) {
948 ast_speech_dtmf(speech, dtmf);
949 speech->results->score = 1000;
950 speech->results->text = ast_strdup(dtmf);
951 speech->results->grammar = ast_strdup("dtmf");
952 }
954 }
955
956 /* See if it was because they hung up */
957 if (done == 3) {
959 } else {
960 /* Channel is okay so restore read format */
961 ast_set_read_format(chan, oldreadformat);
962 }
963
964 return 0;
965}
966
967
968/*! \brief SpeechDestroy() Dialplan Application */
969static int speech_destroy(struct ast_channel *chan, const char *data)
970{
971 if (!chan) {
972 return -1;
973 }
974 return speech_datastore_destroy(chan);
975}
976
977static int unload_module(void)
978{
979 int res = 0;
980
981 res = ast_unregister_application("SpeechCreate");
982 res |= ast_unregister_application("SpeechLoadGrammar");
983 res |= ast_unregister_application("SpeechUnloadGrammar");
984 res |= ast_unregister_application("SpeechActivateGrammar");
985 res |= ast_unregister_application("SpeechDeactivateGrammar");
986 res |= ast_unregister_application("SpeechStart");
987 res |= ast_unregister_application("SpeechBackground");
988 res |= ast_unregister_application("SpeechDestroy");
989 res |= ast_unregister_application("SpeechProcessingSound");
996
997 return res;
998}
999
1000static int load_module(void)
1001{
1002 int res = 0;
1003
1004 res = ast_register_application_xml("SpeechCreate", speech_create);
1005 res |= ast_register_application_xml("SpeechLoadGrammar", speech_load);
1006 res |= ast_register_application_xml("SpeechUnloadGrammar", speech_unload);
1007 res |= ast_register_application_xml("SpeechActivateGrammar", speech_activate);
1008 res |= ast_register_application_xml("SpeechDeactivateGrammar", speech_deactivate);
1009 res |= ast_register_application_xml("SpeechStart", speech_start);
1010 res |= ast_register_application_xml("SpeechBackground", speech_background);
1011 res |= ast_register_application_xml("SpeechDestroy", speech_destroy);
1012 res |= ast_register_application_xml("SpeechProcessingSound", speech_processing_sound);
1019
1020 return res;
1021}
1022
1023AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Dialplan Speech Applications",
1024 .support_level = AST_MODULE_SUPPORT_CORE,
1025 .load = load_module,
1026 .unload = unload_module,
1027 .requires = "res_speech",
static int speech_grammar(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
SPEECH_GRAMMAR() Dialplan Function.
static int speech_unload(struct ast_channel *chan, const char *data)
SpeechUnloadGrammar(Grammar Name) Dialplan Application.
static int speech_text(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
SPEECH_TEXT() Dialplan Function.
static struct ast_custom_function speech_engine_function
static struct ast_custom_function speech_score_function
static const struct ast_datastore_info speech_datastore
Static structure for datastore information.
static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
Helper function used by speech_background to playback a soundfile.
static const struct ast_app_option speech_background_options[128]
static int speech_create(struct ast_channel *chan, const char *data)
SpeechCreate() Dialplan Application.
static int speech_results_type_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
SPEECH_RESULTS_TYPE() Dialplan Function.
@ SB_OPT_NOANSWER
@ SB_OPT_PARTIALRESULTS
static int speech_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
SPEECH() Dialplan Function.
static struct ast_speech_result * find_result(struct ast_speech_result *results, char *result_num)
static int speech_score(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
SPEECH_SCORE() Dialplan Function.
static struct ast_custom_function speech_results_type_function
static int speech_activate(struct ast_channel *chan, const char *data)
SpeechActivateGrammar(Grammar Name) Dialplan Application.
static int speech_start(struct ast_channel *chan, const char *data)
SpeechStart() Dialplan Application.
static int speech_datastore_destroy(struct ast_channel *chan)
static int speech_engine_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
SPEECH_ENGINE() Dialplan Set Function.
static int speech_engine_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
SPEECH_ENGINE() Dialplan Get Function.
static int speech_load(struct ast_channel *chan, const char *vdata)
SpeechLoadGrammar(Grammar Name,Path) Dialplan Application.
static struct ast_custom_function speech_function
static int speech_destroy(struct ast_channel *chan, const char *data)
SpeechDestroy() Dialplan Application.
static void destroy_callback(void *data)
Helper function used by datastores to destroy the speech structure upon hangup.
static int load_module(void)
static int unload_module(void)
static int speech_processing_sound(struct ast_channel *chan, const char *data)
SpeechProcessingSound(Sound File) Dialplan Application.
static int speech_background(struct ast_channel *chan, const char *data)
SpeechBackground(Sound File,Timeout) Dialplan Application.
static int speech_deactivate(struct ast_channel *chan, const char *data)
SpeechDeactivateGrammar(Grammar Name) Dialplan Application.
static struct ast_speech * find_speech(struct ast_channel *chan)
Helper function used to find the speech structure attached to a channel.
static struct ast_custom_function speech_text_function
static struct ast_custom_function speech_grammar_function
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
static int tmp()
Definition: bt_open.c:389
static PGresult * result
Definition: cel_pgsql.c:84
General Asterisk PBX channel definitions.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2394
#define ast_channel_lock(chan)
Definition: channel.h:2922
struct ast_format_cap * ast_channel_nativeformats(const struct ast_channel *chan)
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3162
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4257
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
Definition: channel.c:5762
ast_timing_func_t ast_channel_timingfunc(const struct ast_channel *chan)
const char * ast_channel_language(const struct ast_channel *chan)
int ast_channel_streamid(const struct ast_channel *chan)
struct ast_sched_context * ast_channel_sched(const struct ast_channel *chan)
struct ast_filestream * ast_channel_stream(const struct ast_channel *chan)
struct ast_pbx * ast_channel_pbx(const struct ast_channel *chan)
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
#define ast_channel_unlock(chan)
Definition: channel.h:2923
#define AST_MAX_EXTENSION
Definition: channel.h:134
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2399
struct ast_format * ast_channel_readformat(struct ast_channel *chan)
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:85
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
Generic File Format Support. Should be included by clients of the file handling routines....
struct ast_filestream * ast_openstream(struct ast_channel *chan, const char *filename, const char *preflang)
Opens stream for use in seeking, playing.
Definition: file.c:790
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
Applies a open stream to a channel.
Definition: file.c:1057
int ast_playstream(struct ast_filestream *s)
Play a open stream on a channel.
Definition: file.c:1063
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define END_OPTIONS
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define BEGIN_OPTIONS
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3056
#define AST_FRAME_DTMF
#define ast_frfree(fr)
@ AST_FRAME_VOICE
@ AST_FRAME_CONTROL
@ AST_CONTROL_HANGUP
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:439
Asterisk locking-related definitions:
#define ast_mutex_unlock(a)
Definition: lock.h:190
#define ast_mutex_lock(a)
Definition: lock.h:189
size_t current
Definition: main/cli.c:113
Asterisk module definitions.
@ AST_MODFLAG_DEFAULT
Definition: module.h:315
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
Core PBX routines and definitions.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
#define NULL
Definition: resample.c:96
int ast_sched_runq(struct ast_sched_context *con)
Runs the queue.
Definition: sched.c:786
int ast_sched_wait(struct ast_sched_context *con) attribute_warn_unused_result
Determines number of seconds until the next outstanding event to take place.
Definition: sched.c:433
Generic Speech Recognition API.
int ast_speech_dtmf(struct ast_speech *speech, const char *dtmf)
Signal to the engine that DTMF was received.
Definition: res_speech.c:154
int ast_speech_grammar_deactivate(struct ast_speech *speech, const char *grammar_name)
Deactivate a grammar on a speech structure.
Definition: res_speech.c:72
int ast_speech_grammar_unload(struct ast_speech *speech, const char *grammar_name)
Unload a grammar.
Definition: res_speech.c:84
void ast_speech_start(struct ast_speech *speech)
Indicate to the speech engine that audio is now going to start being written.
Definition: res_speech.c:122
int ast_speech_write(struct ast_speech *speech, void *data, int len)
Write audio to the speech engine.
Definition: res_speech.c:144
int ast_speech_change(struct ast_speech *speech, const char *name, const char *value)
Change an engine specific attribute.
Definition: res_speech.c:169
struct ast_speech * ast_speech_new(const char *engine_name, const struct ast_format_cap *formats)
Create a new speech structure.
Definition: res_speech.c:181
int ast_speech_destroy(struct ast_speech *speech)
Destroy a speech structure.
Definition: res_speech.c:251
int ast_speech_change_results_type(struct ast_speech *speech, enum ast_speech_results_type results_type)
Change the type of results we want.
Definition: res_speech.c:308
int ast_speech_grammar_load(struct ast_speech *speech, const char *grammar_name, const char *grammar)
Load a grammar on a speech structure (not globally)
Definition: res_speech.c:78
int ast_speech_grammar_activate(struct ast_speech *speech, const char *grammar_name)
Activate a grammar on a speech structure.
Definition: res_speech.c:66
int ast_speech_get_setting(struct ast_speech *speech, const char *name, char *buf, size_t len)
Get an engine specific attribute.
Definition: res_speech.c:175
int ast_speech_change_state(struct ast_speech *speech, int state)
Change state of a speech structure.
Definition: res_speech.c:278
@ AST_SPEECH_STATE_DONE
Definition: speech.h:42
@ AST_SPEECH_STATE_READY
Definition: speech.h:40
@ AST_SPEECH_STATE_NOT_READY
Definition: speech.h:39
@ AST_SPEECH_STATE_WAIT
Definition: speech.h:41
@ AST_SPEECH_RESULTS_TYPE_NBEST
Definition: speech.h:47
@ AST_SPEECH_RESULTS_TYPE_NORMAL
Definition: speech.h:46
struct ast_speech_result * ast_speech_results_get(struct ast_speech *speech)
Get speech recognition results.
Definition: res_speech.c:90
@ AST_SPEECH_QUIET
Definition: speech.h:32
@ AST_SPEECH_SPOKE
Definition: speech.h:33
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
@ AST_STRSEP_TRIM
Definition: strings.h:256
@ AST_STRSEP_STRIP
Definition: strings.h:255
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
char * ast_strsep(char **s, const char sep, uint32_t flags)
Act like strsep but ignore separators inside quotes.
Definition: utils.c:1835
Main Channel structure associated with a channel.
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Structure for a data store type.
Definition: datastore.h:31
const char * type
Definition: datastore.h:32
Structure for a data store object.
Definition: datastore.h:64
void * data
Definition: datastore.h:66
This structure is allocated by file.c in one chunk, together with buf_size and desc_size bytes of mem...
Definition: mod_format.h:101
char * filename
Definition: mod_format.h:107
Structure used to handle boolean flags.
Definition: utils.h:199
Definition of a media format.
Definition: format.c:43
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass
union ast_frame::@226 data
enum ast_frame_type frametype
int dtimeoutms
Definition: pbx.h:215
char * grammar
Definition: speech.h:119
char * processing_sound
Definition: speech.h:60
void * data
Definition: speech.h:66
int state
Definition: speech.h:62
struct ast_format * format
Definition: speech.h:64
ast_mutex_t lock
Definition: speech.h:56
struct ast_speech_result * results
Definition: speech.h:68
int value
Definition: syslog.c:37
int done
Definition: test_amihooks.c:48
const char * args
static struct test_options options
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_clear_flag(p, flag)
Definition: utils.h:77