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