30e27e3e0537d1b334ec3ab423e5cfa4d09e5fab
[asterisk/asterisk.git] / apps / app_talkdetect.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@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 Playback a file with audio detect
22  * 
23  * \ingroup applications
24  */
25  
26 #include <string.h>
27 #include <stdlib.h>
28
29 #include "asterisk.h"
30
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32
33 #include "asterisk/lock.h"
34 #include "asterisk/file.h"
35 #include "asterisk/logger.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
43 static char *tdesc = "Playback with Talk Detection";
44
45 static char *app = "BackgroundDetect";
46
47 static char *synopsis = "Background a file with talk detect";
48
49 static char *descrip = 
50 "  BackgroundDetect(filename[|sil[|min|[max]]]):  Plays  back  a  given\n"
51 "filename, waiting for interruption from a given digit (the digit must\n"
52 "start the beginning of a valid extension, or it will be ignored).\n"
53 "During the playback of the file, audio is monitored in the receive\n"
54 "direction, and if a period of non-silence which is greater than 'min' ms\n"
55 "yet less than 'max' ms is followed by silence for at least 'sil' ms then\n"
56 "the audio playback is aborted and processing jumps to the 'talk' extension\n"
57 "if available.  If unspecified, sil, min, and max default to 1000, 100, and\n"
58 "infinity respectively.\n";
59
60 STANDARD_LOCAL_USER;
61
62 LOCAL_USER_DECL;
63
64 static int background_detect_exec(struct ast_channel *chan, void *data)
65 {
66         int res = 0;
67         struct localuser *u;
68         char *tmp;
69         char *options;
70         char *stringp;
71         struct ast_frame *fr;
72         int notsilent=0;
73         struct timeval start = { 0, 0};
74         int sil = 1000;
75         int min = 100;
76         int max = -1;
77         int x;
78         int origrformat=0;
79         struct ast_dsp *dsp;
80         
81         if (ast_strlen_zero(data)) {
82                 ast_log(LOG_WARNING, "BackgroundDetect requires an argument (filename)\n");
83                 return -1;
84         }
85
86         LOCAL_USER_ADD(u);
87
88         tmp = ast_strdupa(data);
89         if (!tmp) {
90                 ast_log(LOG_ERROR, "Out of memory\n");
91                 LOCAL_USER_REMOVE(u);
92                 return -1;
93         }       
94
95         stringp=tmp;
96         strsep(&stringp, "|");
97         options = strsep(&stringp, "|");
98         if (options) {
99                 if ((sscanf(options, "%d", &x) == 1) && (x > 0))
100                         sil = x;
101                 options = strsep(&stringp, "|");
102                 if (options) {
103                         if ((sscanf(options, "%d", &x) == 1) && (x > 0))
104                                 min = x;
105                         options = strsep(&stringp, "|");
106                         if (options) {
107                                 if ((sscanf(options, "%d", &x) == 1) && (x > 0))
108                                         max = x;
109                         }
110                 }
111         }
112         ast_log(LOG_DEBUG, "Preparing detect of '%s', sil=%d,min=%d,max=%d\n", 
113                                                 tmp, sil, min, max);
114         if (chan->_state != AST_STATE_UP) {
115                 /* Otherwise answer unless we're supposed to send this while on-hook */
116                 res = ast_answer(chan);
117         }
118         if (!res) {
119                 origrformat = chan->readformat;
120                 if ((res = ast_set_read_format(chan, AST_FORMAT_SLINEAR))) 
121                         ast_log(LOG_WARNING, "Unable to set read format to linear!\n");
122         }
123         if (!(dsp = ast_dsp_new())) {
124                 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
125                 res = -1;
126         }
127         if (!res) {
128                 ast_stopstream(chan);
129                 res = ast_streamfile(chan, tmp, chan->language);
130                 if (!res) {
131                         while(chan->stream) {
132                                 res = ast_sched_wait(chan->sched);
133                                 if ((res < 0) && !chan->timingfunc) {
134                                         res = 0;
135                                         break;
136                                 }
137                                 if (res < 0)
138                                         res = 1000;
139                                 res = ast_waitfor(chan, res);
140                                 if (res < 0) {
141                                         ast_log(LOG_WARNING, "Waitfor failed on %s\n", chan->name);
142                                         break;
143                                 } else if (res > 0) {
144                                         fr = ast_read(chan);
145                                         if (!fr) {
146                                                 res = -1;
147                                                 break;
148                                         } else if (fr->frametype == AST_FRAME_DTMF) {
149                                                 char t[2];
150                                                 t[0] = fr->subclass;
151                                                 t[1] = '\0';
152                                                 if (ast_canmatch_extension(chan, chan->context, t, 1, chan->cid.cid_num)) {
153                                                         /* They entered a valid  extension, or might be anyhow */
154                                                         res = fr->subclass;
155                                                         ast_frfree(fr);
156                                                         break;
157                                                 }
158                                         } else if ((fr->frametype == AST_FRAME_VOICE) && (fr->subclass == AST_FORMAT_SLINEAR)) {
159                                                 int totalsilence;
160                                                 int ms;
161                                                 res = ast_dsp_silence(dsp, fr, &totalsilence);
162                                                 if (res && (totalsilence > sil)) {
163                                                         /* We've been quiet a little while */
164                                                         if (notsilent) {
165                                                                 /* We had heard some talking */
166                                                                 ms = ast_tvdiff_ms(ast_tvnow(), start);
167                                                                 ms -= sil;
168                                                                 if (ms < 0)
169                                                                         ms = 0;
170                                                                 if ((ms > min) && ((max < 0) || (ms < max))) {
171                                                                         char ms_str[10];
172                                                                         ast_log(LOG_DEBUG, "Found qualified token of %d ms\n", ms);
173
174                                                                         /* Save detected talk time (in milliseconds) */ 
175                                                                         sprintf(ms_str, "%d", ms );     
176                                                                         pbx_builtin_setvar_helper(chan, "TALK_DETECTED", ms_str);
177                                                                         
178                                                                         ast_goto_if_exists(chan, chan->context, "talk", 1);
179                                                                         res = 0;
180                                                                         ast_frfree(fr);
181                                                                         break;
182                                                                 } else
183                                                                         ast_log(LOG_DEBUG, "Found unqualified token of %d ms\n", ms);
184                                                                 notsilent = 0;
185                                                         }
186                                                 } else {
187                                                         if (!notsilent) {
188                                                                 /* Heard some audio, mark the begining of the token */
189                                                                 start = ast_tvnow();
190                                                                 ast_log(LOG_DEBUG, "Start of voice token!\n");
191                                                                 notsilent = 1;
192                                                         }
193                                                 }
194                                                 
195                                         }
196                                         ast_frfree(fr);
197                                 }
198                                 ast_sched_runq(chan->sched);
199                         }
200                         ast_stopstream(chan);
201                 } else {
202                         ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char *)data);
203                         res = 0;
204                 }
205         }
206         if (res > -1) {
207                 if (origrformat && ast_set_read_format(chan, origrformat)) {
208                         ast_log(LOG_WARNING, "Failed to restore read format for %s to %s\n", 
209                                 chan->name, ast_getformatname(origrformat));
210                 }
211         }
212         if (dsp)
213                 ast_dsp_free(dsp);
214         LOCAL_USER_REMOVE(u);
215         return res;
216 }
217
218 int unload_module(void)
219 {
220         int res;
221
222         res = ast_unregister_application(app);
223         
224         STANDARD_HANGUP_LOCALUSERS;
225
226         return res;     
227 }
228
229 int load_module(void)
230 {
231         return ast_register_application(app, background_detect_exec, synopsis, descrip);
232 }
233
234 char *description(void)
235 {
236         return tdesc;
237 }
238
239 int usecount(void)
240 {
241         int res;
242         STANDARD_USECOUNT(res);
243         return res;
244 }
245
246 char *key()
247 {
248         return ASTERISK_GPL_KEY;
249 }