Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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 <[email protected]>
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" />
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 
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 
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 
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 
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"))
480  else if (!strcasecmp(value, "nbest"))
482 
483  return 0;
484 }
485 
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 
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;
578  AST_APP_ARG(grammar);
579  AST_APP_ARG(path);
580  );
581 
582  data = ast_strdupa(vdata);
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 
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 };
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) {
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 */
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);
821  quieted = 1;
822  }
823  /* Check state so we can see what to do */
824  switch (speech->state) {
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;
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")) {
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")) {
858  }
859  }
860  }
861  }
862  break;
864  /* Now that we are done... let's switch back to not ready state */
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  }
933  }
934 
935  /* See if it was because they hung up */
936  if (done == 3) {
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 );
const char * name
Definition: pbx.h:119
const char * type
Definition: datastore.h:32
int state
Definition: speech.h:59
#define ast_channel_lock(chan)
Definition: channel.h:2945
Main Channel structure associated with a channel.
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
static int speech_create(struct ast_channel *chan, const char *data)
SpeechCreate() Dialplan Application.
Generic Speech Recognition API.
static int speech_unload(struct ast_channel *chan, const char *data)
SpeechUnloadGrammar(Grammar Name) Dialplan Application.
int ast_speech_destroy(struct ast_speech *speech)
Destroy a speech structure.
Definition: res_speech.c:240
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:121
int ast_sched_runq(struct ast_sched_context *con)
Runs the queue.
Definition: sched.c:755
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define BEGIN_OPTIONS
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static struct ast_custom_function speech_engine_function
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
static int speech_background(struct ast_channel *chan, const char *data)
SpeechBackground(Sound File,Timeout) Dialplan Application.
static struct ast_custom_function speech_grammar_function
static int speech_text(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
SPEECH_TEXT() Dialplan Function.
static int timeout
Definition: cdr_mysql.c:86
static int tmp()
Definition: bt_open.c:389
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:438
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4302
static int load_module(void)
Structure for a data store type.
Definition: datastore.h:31
ast_channel_state
ast_channel states
Definition: channelstate.h:35
struct ast_speech_result * ast_speech_results_get(struct ast_speech *speech)
Get speech recognition results.
Definition: res_speech.c:89
static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
Helper function used by speech_background to playback a soundfile.
Definition of a media format.
Definition: format.c:43
void * data
Definition: speech.h:63
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
#define ast_mutex_lock(a)
Definition: lock.h:187
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:98
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
Structure for a data store object.
Definition: datastore.h:68
static int speech_engine_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
SPEECH_ENGINE() Dialplan Get Function.
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:2404
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
const char * args
#define NULL
Definition: resample.c:96
int value
Definition: syslog.c:37
#define AST_FRAME_DTMF
static int speech_load(struct ast_channel *chan, const char *vdata)
SpeechLoadGrammar(Grammar Name,Path) Dialplan Application.
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
static int speech_destroy(struct ast_channel *chan, const char *data)
SpeechDestroy() 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.
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
char * grammar
Definition: speech.h:116
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
struct ast_frame_subclass subclass
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:284
#define ast_strlen_zero(foo)
Definition: strings.h:52
struct ast_format * ast_channel_readformat(struct ast_channel *chan)
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
int done
Definition: test_amihooks.c:48
#define ao2_bump(obj)
Definition: astobj2.h:491
int ast_speech_grammar_deactivate(struct ast_speech *speech, const char *grammar_name)
Deactivate a grammar on a speech structure.
Definition: res_speech.c:71
int ast_playstream(struct ast_filestream *s)
Play a open stream on a channel.
Definition: file.c:1026
General Asterisk PBX channel definitions.
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
Definition: channel.c:5849
#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:911
int ast_speech_change_state(struct ast_speech *speech, int state)
Change state of a speech structure.
Definition: res_speech.c:267
static struct ast_speech * find_speech(struct ast_channel *chan)
Helper function used to find the speech structure attached to a channel.
struct ast_pbx * ast_channel_pbx(const struct ast_channel *chan)
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
#define AST_MAX_EXTENSION
Definition: channel.h:135
static struct ast_custom_function speech_function
static int speech_deactivate(struct ast_channel *chan, const char *data)
SpeechDeactivateGrammar(Grammar Name) Dialplan Application.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
int ast_speech_grammar_unload(struct ast_speech *speech, const char *grammar_name)
Unload a grammar.
Definition: res_speech.c:83
struct ast_speech_result * results
Definition: speech.h:65
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:2906
static struct ast_custom_function speech_results_type_function
Core PBX routines and definitions.
static int speech_activate(struct ast_channel *chan, const char *data)
SpeechActivateGrammar(Grammar Name) Dialplan Application.
static int speech_engine_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
SPEECH_ENGINE() Dialplan Set Function.
struct ast_sched_context * ast_channel_sched(const struct ast_channel *chan)
static int speech_start(struct ast_channel *chan, const char *data)
SpeechStart() Dialplan Application.
int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
Applys a open stream to a channel.
Definition: file.c:1020
static void destroy_callback(void *data)
Helper function used by datastores to destroy the speech structure upon hangup.
int ast_channel_streamid(const struct ast_channel *chan)
int ast_speech_grammar_activate(struct ast_speech *speech, const char *grammar_name)
Activate a grammar on a speech structure.
Definition: res_speech.c:65
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static struct ast_speech_result * find_result(struct ast_speech_result *results, char *result_num)
static int speech_processing_sound(struct ast_channel *chan, const char *data)
SpeechProcessingSound(Sound File) Dialplan Application.
#define ast_channel_unlock(chan)
Definition: channel.h:2946
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1872
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
struct ast_format * format
Definition: speech.h:61
struct ast_filestream * ast_channel_stream(const struct ast_channel *chan)
Structure used to handle boolean flags.
Definition: utils.h:199
#define ast_clear_flag(p, flag)
Definition: utils.h:77
static int speech_datastore_destroy(struct ast_channel *chan)
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
struct ast_format_cap * ast_channel_nativeformats(const struct ast_channel *chan)
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...
static const struct ast_datastore_info speech_datastore
Static structure for datastore information.
ast_mutex_t lock
Definition: speech.h:53
void * data
Definition: datastore.h:70
char * strsep(char **str, const char *delims)
static struct ast_custom_function speech_score_function
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
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3171
static struct ast_custom_function speech_text_function
int dtimeoutms
Definition: pbx.h:212
int ast_speech_dtmf(struct ast_speech *speech, const char *dtmf)
Signal to the engine that DTMF was received.
Definition: res_speech.c:153
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
static const struct ast_app_option speech_background_options[128]
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:180
#define END_OPTIONS
#define ast_frfree(fr)
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2814
static PGresult * result
Definition: cel_pgsql.c:88
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:431
Data structure associated with a single frame of data.
const char * ast_channel_language(const struct ast_channel *chan)
char * processing_sound
Definition: speech.h:57
static int speech_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
SPEECH() Dialplan Function.
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:89
union ast_frame::@263 data
enum ast_frame_type frametype
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
int ast_speech_write(struct ast_speech *speech, void *data, int len)
Write audio to the speech engine.
Definition: res_speech.c:143
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:77
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
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:174
Asterisk module definitions.
ast_timing_func_t ast_channel_timingfunc(const struct ast_channel *chan)
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2390
int ast_speech_change(struct ast_speech *speech, const char *name, const char *value)
Change an engine specific attribute.
Definition: res_speech.c:168
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2399
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1508
static int speech_grammar(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
SPEECH_GRAMMAR() Dialplan Function.
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:187
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
static int speech_score(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
SPEECH_SCORE() Dialplan Function.
static int unload_module(void)
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:755
#define ast_mutex_unlock(a)
Definition: lock.h:188
#define AST_APP_ARG(name)
Define an application argument.