Wed Jan 8 2020 09:49:40

Asterisk developer's documentation


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