Asterisk - The Open Source Telephony Project  18.5.0
app_talkdetect.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <[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 Playback a file with audio detect
22  *
23  * \author Mark Spencer <[email protected]>
24  *
25  * \ingroup applications
26  */
27 
28 /*** MODULEINFO
29  <support_level>core</support_level>
30  ***/
31 
32 #include "asterisk.h"
33 
34 #include "asterisk/lock.h"
35 #include "asterisk/file.h"
36 #include "asterisk/channel.h"
37 #include "asterisk/pbx.h"
38 #include "asterisk/module.h"
39 #include "asterisk/translate.h"
40 #include "asterisk/utils.h"
41 #include "asterisk/dsp.h"
42 #include "asterisk/app.h"
43 #include "asterisk/format.h"
44 #include "asterisk/format_cache.h"
45 
46 /*** DOCUMENTATION
47  <application name="BackgroundDetect" language="en_US">
48  <synopsis>
49  Background a file with talk detect.
50  </synopsis>
51  <syntax>
52  <parameter name="filename" required="true" />
53  <parameter name="sil">
54  <para>If not specified, defaults to <literal>1000</literal>.</para>
55  </parameter>
56  <parameter name="min">
57  <para>If not specified, defaults to <literal>100</literal>.</para>
58  </parameter>
59  <parameter name="max">
60  <para>If not specified, defaults to <literal>infinity</literal>.</para>
61  </parameter>
62  <parameter name="analysistime">
63  <para>If not specified, defaults to <literal>infinity</literal>.</para>
64  </parameter>
65  </syntax>
66  <description>
67  <para>Plays back <replaceable>filename</replaceable>, waiting for interruption from a given digit (the digit
68  must start the beginning of a valid extension, or it will be ignored). During
69  the playback of the file, audio is monitored in the receive direction, and if
70  a period of non-silence which is greater than <replaceable>min</replaceable> ms yet less than
71  <replaceable>max</replaceable> ms is followed by silence for at least <replaceable>sil</replaceable> ms,
72  which occurs during the first <replaceable>analysistime</replaceable> ms, then the audio playback is
73  aborted and processing jumps to the <replaceable>talk</replaceable> extension, if available.</para>
74  </description>
75  </application>
76  ***/
77 
78 static char *app = "BackgroundDetect";
79 
80 static int background_detect_exec(struct ast_channel *chan, const char *data)
81 {
82  int res = 0;
83  char *tmp;
84  struct ast_frame *fr;
85  int notsilent = 0;
86  struct timeval start = { 0, 0 };
87  struct timeval detection_start = { 0, 0 };
88  int sil = 1000;
89  int min = 100;
90  int max = -1;
91  int analysistime = -1;
92  int continue_analysis = 1;
93  int x;
94  RAII_VAR(struct ast_format *, origrformat, NULL, ao2_cleanup);
95  struct ast_dsp *dsp = NULL;
97  AST_APP_ARG(filename);
98  AST_APP_ARG(silence);
99  AST_APP_ARG(min);
100  AST_APP_ARG(max);
101  AST_APP_ARG(analysistime);
102  );
103 
104  if (ast_strlen_zero(data)) {
105  ast_log(LOG_WARNING, "BackgroundDetect requires an argument (filename)\n");
106  return -1;
107  }
108 
109  tmp = ast_strdupa(data);
111 
112  if (!ast_strlen_zero(args.silence) && (sscanf(args.silence, "%30d", &x) == 1) && (x > 0)) {
113  sil = x;
114  }
115  if (!ast_strlen_zero(args.min) && (sscanf(args.min, "%30d", &x) == 1) && (x > 0)) {
116  min = x;
117  }
118  if (!ast_strlen_zero(args.max) && (sscanf(args.max, "%30d", &x) == 1) && (x > 0)) {
119  max = x;
120  }
121  if (!ast_strlen_zero(args.analysistime) && (sscanf(args.analysistime, "%30d", &x) == 1) && (x > 0)) {
122  analysistime = x;
123  }
124 
125  ast_debug(1, "Preparing detect of '%s', sil=%d, min=%d, max=%d, analysistime=%d\n", args.filename, sil, min, max, analysistime);
126  do {
127  if (ast_channel_state(chan) != AST_STATE_UP) {
128  if ((res = ast_answer(chan))) {
129  break;
130  }
131  }
132 
133  origrformat = ao2_bump(ast_channel_readformat(chan));
134  if ((ast_set_read_format(chan, ast_format_slin))) {
135  ast_log(LOG_WARNING, "Unable to set read format to linear!\n");
136  res = -1;
137  break;
138  }
139 
140  if (!(dsp = ast_dsp_new())) {
141  ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
142  res = -1;
143  break;
144  }
145  ast_stopstream(chan);
146  if (ast_streamfile(chan, tmp, ast_channel_language(chan))) {
147  ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", ast_channel_name(chan), (char *)data);
148  break;
149  }
150  detection_start = ast_tvnow();
151  while (ast_channel_stream(chan)) {
152  res = ast_sched_wait(ast_channel_sched(chan));
153  if ((res < 0) && !ast_channel_timingfunc(chan)) {
154  res = 0;
155  break;
156  }
157  if (res < 0) {
158  res = 1000;
159  }
160  res = ast_waitfor(chan, res);
161  if (res < 0) {
162  ast_log(LOG_WARNING, "Waitfor failed on %s\n", ast_channel_name(chan));
163  break;
164  } else if (res > 0) {
165  fr = ast_read(chan);
166  if (continue_analysis && analysistime >= 0) {
167  /* If we have a limit for the time to analyze voice
168  * frames and the time has not expired */
169  if (ast_tvdiff_ms(ast_tvnow(), detection_start) >= analysistime) {
170  continue_analysis = 0;
171  ast_verb(3, "BackgroundDetect: Talk analysis time complete on %s.\n", ast_channel_name(chan));
172  }
173  }
174 
175  if (!fr) {
176  res = -1;
177  break;
178  } else if (fr->frametype == AST_FRAME_DTMF) {
179  char t[2];
180  t[0] = fr->subclass.integer;
181  t[1] = '\0';
182  if (ast_canmatch_extension(chan, ast_channel_context(chan), t, 1,
183  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
184  /* They entered a valid extension, or might be anyhow */
185  res = fr->subclass.integer;
186  ast_frfree(fr);
187  break;
188  }
189  } else if ((fr->frametype == AST_FRAME_VOICE) &&
190  (ast_format_cmp(fr->subclass.format, ast_format_slin) == AST_FORMAT_CMP_EQUAL) && continue_analysis) {
191  int totalsilence;
192  int ms;
193  res = ast_dsp_silence(dsp, fr, &totalsilence);
194  if (res && (totalsilence > sil)) {
195  /* We've been quiet a little while */
196  if (notsilent) {
197  /* We had heard some talking */
198  ms = ast_tvdiff_ms(ast_tvnow(), start);
199  ms -= sil;
200  if (ms < 0)
201  ms = 0;
202  if ((ms > min) && ((max < 0) || (ms < max))) {
203  char ms_str[12];
204  ast_debug(1, "Found qualified token of %d ms\n", ms);
205 
206  /* Save detected talk time (in milliseconds) */
207  snprintf(ms_str, sizeof(ms_str), "%d", ms);
208  pbx_builtin_setvar_helper(chan, "TALK_DETECTED", ms_str);
209 
210  ast_goto_if_exists(chan, ast_channel_context(chan), "talk", 1);
211  res = 0;
212  ast_frfree(fr);
213  break;
214  } else {
215  ast_debug(1, "Found unqualified token of %d ms\n", ms);
216  }
217  notsilent = 0;
218  }
219  } else {
220  if (!notsilent) {
221  /* Heard some audio, mark the begining of the token */
222  start = ast_tvnow();
223  ast_debug(1, "Start of voice token!\n");
224  notsilent = 1;
225  }
226  }
227  }
228  ast_frfree(fr);
229  }
231  }
232  ast_stopstream(chan);
233  } while (0);
234 
235  if (res > -1) {
236  if (origrformat && ast_set_read_format(chan, origrformat)) {
237  ast_log(LOG_WARNING, "Failed to restore read format for %s to %s\n",
238  ast_channel_name(chan), ast_format_get_name(origrformat));
239  }
240  }
241  if (dsp) {
242  ast_dsp_free(dsp);
243  }
244  return res;
245 }
246 
247 static int unload_module(void)
248 {
250 }
251 
252 static int load_module(void)
253 {
255 }
256 
257 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Playback with Talk Detection");
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
Main Channel structure associated with a channel.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1250
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
static char * app
int ast_sched_runq(struct ast_sched_context *con)
Runs the queue.
Definition: sched.c:755
Support for translation of data formats. translate.c.
void ast_dsp_free(struct ast_dsp *dsp)
Definition: dsp.c:1770
Convenient Signal Processing routines.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
#define LOG_WARNING
Definition: logger.h:274
static int unload_module(void)
struct ast_dsp * ast_dsp_new(void)
Allocates a new dsp, assumes 8khz for internal sample rate.
Definition: dsp.c:1745
static int tmp()
Definition: bt_open.c:389
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4302
ast_channel_state
ast_channel states
Definition: channelstate.h:35
Definition of a media format.
Definition: format.c:43
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition: format.c:334
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
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
#define AST_FRAME_DTMF
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define ast_verb(level,...)
Definition: logger.h:463
int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Looks for a valid matching extension.
Definition: pbx.c:4194
struct ast_frame_subclass subclass
int totalsilence
Definition: dsp.c:409
Utility functions.
Media Format API.
#define ast_strlen_zero(foo)
Definition: strings.h:52
struct ast_format * ast_channel_readformat(struct ast_channel *chan)
Number structure.
Definition: app_followme.c:154
#define ao2_bump(obj)
Definition: astobj2.h:491
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
enum ast_format_cmp_res ast_format_cmp(const struct ast_format *format1, const struct ast_format *format2)
Compare two formats.
Definition: format.c:201
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
Definition: dsp.c:405
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:85
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
static int load_module(void)
Core PBX routines and definitions.
struct ast_sched_context * ast_channel_sched(const struct ast_channel *chan)
static int background_detect_exec(struct ast_channel *chan, const char *data)
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition: pbx.c:8793
struct ast_filestream * ast_channel_stream(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...
int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence)
Process the audio frame for silence.
Definition: dsp.c:1483
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3171
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
const char * ast_channel_name(const struct ast_channel *chan)
#define ast_frfree(fr)
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2814
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)
const char * ast_channel_context(const struct ast_channel *chan)
enum ast_frame_type frametype
struct ast_format * format
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
Asterisk module definitions.
ast_timing_func_t ast_channel_timingfunc(const struct ast_channel *chan)
#define min(a, b)
Definition: f2c.h:197
#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_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
Media Format Cache API.
#define AST_APP_ARG(name)
Define an application argument.
#define max(a, b)
Definition: f2c.h:198