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