Add new admin features to meetme: Roll call, eject all, mute all, record in-conf
[asterisk/asterisk.git] / apps / app_amd.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2003 - 2006, Aheeva Technology.
5  *
6  * Claude Klimos (claude.klimos@aheeva.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  * A license has been granted to Digium (via disclaimer) for the use of
19  * this code.
20  */
21
22 /*! \file
23  *
24  * \brief Answering machine detection
25  *
26  * \author Claude Klimos (claude.klimos@aheeva.com)
27  */
28
29
30 #include "asterisk.h"
31
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33
34 #include "asterisk/module.h"
35 #include "asterisk/lock.h"
36 #include "asterisk/channel.h"
37 #include "asterisk/dsp.h"
38 #include "asterisk/pbx.h"
39 #include "asterisk/config.h"
40 #include "asterisk/app.h"
41
42 /*** DOCUMENTATION
43         <application name="AMD" language="en_US">
44                 <synopsis>
45                         Attempt to detect answering machines.
46                 </synopsis>
47                 <syntax>
48                         <parameter name="initialSilence" required="false">
49                                 <para>Is maximum initial silence duration before greeting.</para>
50                                 <para>If this is exceeded set as MACHINE</para>
51                         </parameter>
52                         <parameter name="greeting" required="false">
53                                 <para>is the maximum length of a greeting.</para>
54                                 <para>If this is exceeded set as MACHINE</para>
55                         </parameter>
56                         <parameter name="afterGreetingSilence" required="false">
57                                 <para>Is the silence after detecting a greeting.</para>
58                                 <para>If this is exceeded set as HUMAN</para>
59                         </parameter>
60                         <parameter name="totalAnalysis Time" required="false">
61                                 <para>Is the maximum time allowed for the algorithm</para>
62                                 <para>to decide HUMAN or MACHINE</para>
63                         </parameter>
64                         <parameter name="miniumWordLength" required="false">
65                                 <para>Is the minimum duration of Voice considered to be a word</para>
66                         </parameter>
67                         <parameter name="betweenWordSilence" required="false">
68                                 <para>Is the minimum duration of silence after a word to
69                                 consider the audio that follows to be a new word</para>
70                         </parameter>
71                         <parameter name="maximumNumberOfWords" required="false">
72                                 <para>Is the maximum number of words in a greeting</para>
73                                 <para>If this is exceeded set as MACHINE</para>
74                         </parameter>
75                         <parameter name="silenceThreshold" required="false">
76                                 <para>How long do we consider silence</para>
77                         </parameter>
78                         <parameter name="maximumWordLength" required="false">
79                                 <para>Is the maximum duration of a word to accept.</para>
80                                 <para>If exceeded set as MACHINE</para>
81                         </parameter>
82                 </syntax>
83                 <description>
84                         <para>This application attempts to detect answering machines at the beginning
85                         of outbound calls. Simply call this application after the call
86                         has been answered (outbound only, of course).</para>
87                         <para>When loaded, AMD reads amd.conf and uses the parameters specified as
88                         default values. Those default values get overwritten when the calling AMD
89                         with parameters.</para>
90                         <para>This application sets the following channel variables:</para>
91                         <variablelist>
92                                 <variable name="AMDSTATUS">
93                                         <para>This is the status of the answering machine detection</para>
94                                         <value name="MACHINE" />
95                                         <value name="HUMAN" />
96                                         <value name="NOTSURE" />
97                                         <value name="HANGUP" />
98                                 </variable>
99                                 <variable name="AMDCAUSE">
100                                         <para>Indicates the cause that led to the conclusion</para>
101                                         <value name="TOOLONG">
102                                                 Total Time.
103                                         </value>
104                                         <value name="INITIALSILENCE">
105                                                 Silence Duration - Initial Silence.
106                                         </value>
107                                         <value name="HUMAN">
108                                                 Silence Duration - afterGreetingSilence.
109                                         </value>
110                                         <value name="LONGGREETING">
111                                                 Voice Duration - Greeting.
112                                         </value>
113                                         <value name="MAXWORDLENGTH">
114                                                 Word Count - maximum number of words.
115                                         </value>        
116                                 </variable>
117                         </variablelist>
118                 </description>
119                 <see-also>
120                         <ref type="application">WaitForSilence</ref>
121                         <ref type="application">WaitForNoise</ref>
122                 </see-also>
123         </application>
124
125  ***/
126
127 static const char app[] = "AMD";
128
129 #define STATE_IN_WORD       1
130 #define STATE_IN_SILENCE    2
131
132 /* Some default values for the algorithm parameters. These defaults will be overwritten from amd.conf */
133 static int dfltInitialSilence       = 2500;
134 static int dfltGreeting             = 1500;
135 static int dfltAfterGreetingSilence = 800;
136 static int dfltTotalAnalysisTime    = 5000;
137 static int dfltMinimumWordLength    = 100;
138 static int dfltBetweenWordsSilence  = 50;
139 static int dfltMaximumNumberOfWords = 3;
140 static int dfltSilenceThreshold     = 256;
141 static int dfltMaximumWordLength    = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
142
143 /* Set to the lowest ms value provided in amd.conf or application parameters */
144 static int dfltMaxWaitTimeForFrame  = 50;
145
146 static void isAnsweringMachine(struct ast_channel *chan, const char *data)
147 {
148         int res = 0;
149         struct ast_frame *f = NULL;
150         struct ast_dsp *silenceDetector = NULL;
151         int dspsilence = 0, readFormat, framelength = 0;
152         int inInitialSilence = 1;
153         int inGreeting = 0;
154         int voiceDuration = 0;
155         int silenceDuration = 0;
156         int iTotalTime = 0;
157         int iWordsCount = 0;
158         int currentState = STATE_IN_WORD;
159         int previousState = STATE_IN_SILENCE;
160         int consecutiveVoiceDuration = 0;
161         char amdCause[256] = "", amdStatus[256] = "";
162         char *parse = ast_strdupa(data);
163
164         /* Lets set the initial values of the variables that will control the algorithm.
165            The initial values are the default ones. If they are passed as arguments
166            when invoking the application, then the default values will be overwritten
167            by the ones passed as parameters. */
168         int initialSilence       = dfltInitialSilence;
169         int greeting             = dfltGreeting;
170         int afterGreetingSilence = dfltAfterGreetingSilence;
171         int totalAnalysisTime    = dfltTotalAnalysisTime;
172         int minimumWordLength    = dfltMinimumWordLength;
173         int betweenWordsSilence  = dfltBetweenWordsSilence;
174         int maximumNumberOfWords = dfltMaximumNumberOfWords;
175         int silenceThreshold     = dfltSilenceThreshold;
176         int maximumWordLength    = dfltMaximumWordLength;
177         int maxWaitTimeForFrame  = dfltMaxWaitTimeForFrame;
178
179         AST_DECLARE_APP_ARGS(args,
180                 AST_APP_ARG(argInitialSilence);
181                 AST_APP_ARG(argGreeting);
182                 AST_APP_ARG(argAfterGreetingSilence);
183                 AST_APP_ARG(argTotalAnalysisTime);
184                 AST_APP_ARG(argMinimumWordLength);
185                 AST_APP_ARG(argBetweenWordsSilence);
186                 AST_APP_ARG(argMaximumNumberOfWords);
187                 AST_APP_ARG(argSilenceThreshold);
188                 AST_APP_ARG(argMaximumWordLength);
189         );
190
191         ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", chan->name, chan->cid.cid_ani,
192                 chan->redirecting.from.number, ast_getformatname(chan->readformat));
193
194         /* Lets parse the arguments. */
195         if (!ast_strlen_zero(parse)) {
196                 /* Some arguments have been passed. Lets parse them and overwrite the defaults. */
197                 AST_STANDARD_APP_ARGS(args, parse);
198                 if (!ast_strlen_zero(args.argInitialSilence))
199                         initialSilence = atoi(args.argInitialSilence);
200                 if (!ast_strlen_zero(args.argGreeting))
201                         greeting = atoi(args.argGreeting);
202                 if (!ast_strlen_zero(args.argAfterGreetingSilence))
203                         afterGreetingSilence = atoi(args.argAfterGreetingSilence);
204                 if (!ast_strlen_zero(args.argTotalAnalysisTime))
205                         totalAnalysisTime = atoi(args.argTotalAnalysisTime);
206                 if (!ast_strlen_zero(args.argMinimumWordLength))
207                         minimumWordLength = atoi(args.argMinimumWordLength);
208                 if (!ast_strlen_zero(args.argBetweenWordsSilence))
209                         betweenWordsSilence = atoi(args.argBetweenWordsSilence);
210                 if (!ast_strlen_zero(args.argMaximumNumberOfWords))
211                         maximumNumberOfWords = atoi(args.argMaximumNumberOfWords);
212                 if (!ast_strlen_zero(args.argSilenceThreshold))
213                         silenceThreshold = atoi(args.argSilenceThreshold);
214                 if (!ast_strlen_zero(args.argMaximumWordLength))
215                         maximumWordLength = atoi(args.argMaximumWordLength);
216         } else {
217                 ast_debug(1, "AMD using the default parameters.\n");
218         }
219
220         /* Find lowest ms value, that will be max wait time for a frame */
221         if (maxWaitTimeForFrame > initialSilence)
222                 maxWaitTimeForFrame = initialSilence;
223         if (maxWaitTimeForFrame > greeting)
224                 maxWaitTimeForFrame = greeting;
225         if (maxWaitTimeForFrame > afterGreetingSilence)
226                 maxWaitTimeForFrame = afterGreetingSilence;
227         if (maxWaitTimeForFrame > totalAnalysisTime)
228                 maxWaitTimeForFrame = totalAnalysisTime;
229         if (maxWaitTimeForFrame > minimumWordLength)
230                 maxWaitTimeForFrame = minimumWordLength;
231         if (maxWaitTimeForFrame > betweenWordsSilence)
232                 maxWaitTimeForFrame = betweenWordsSilence;
233
234         /* Now we're ready to roll! */
235         ast_verb(3, "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
236                 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d] \n",
237                                 initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
238                                 minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold, maximumWordLength);
239
240         /* Set read format to signed linear so we get signed linear frames in */
241         readFormat = chan->readformat;
242         if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0 ) {
243                 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", chan->name );
244                 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
245                 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
246                 return;
247         }
248
249         /* Create a new DSP that will detect the silence */
250         if (!(silenceDetector = ast_dsp_new())) {
251                 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", chan->name );
252                 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
253                 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
254                 return;
255         }
256
257         /* Set silence threshold to specified value */
258         ast_dsp_set_threshold(silenceDetector, silenceThreshold);
259
260         /* Now we go into a loop waiting for frames from the channel */
261         while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
262
263                 /* If we fail to read in a frame, that means they hung up */
264                 if (!(f = ast_read(chan))) {
265                         ast_verb(3, "AMD: Channel [%s]. HANGUP\n", chan->name);
266                         ast_debug(1, "Got hangup\n");
267                         strcpy(amdStatus, "HANGUP");
268                         res = 1;
269                         break;
270                 }
271
272                 if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_NULL || f->frametype == AST_FRAME_CNG) {
273                         /* If the total time exceeds the analysis time then give up as we are not too sure */
274                         if (f->frametype == AST_FRAME_VOICE)
275                                 framelength = (ast_codec_get_samples(f) / DEFAULT_SAMPLES_PER_MS);
276                         else
277                                 framelength += 2 * maxWaitTimeForFrame;
278
279                         iTotalTime += framelength;
280                         if (iTotalTime >= totalAnalysisTime) {
281                                 ast_verb(3, "AMD: Channel [%s]. Too long...\n", chan->name );
282                                 ast_frfree(f);
283                                 strcpy(amdStatus , "NOTSURE");
284                                 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
285                                 break;
286                         }
287
288                         /* Feed the frame of audio into the silence detector and see if we get a result */
289                         if (f->frametype != AST_FRAME_VOICE)
290                                 dspsilence += 2 * maxWaitTimeForFrame;
291                         else {
292                                 dspsilence = 0;
293                                 ast_dsp_silence(silenceDetector, f, &dspsilence);
294                         }
295
296                         if (dspsilence > 0) {
297                                 silenceDuration = dspsilence;
298                                 
299                                 if (silenceDuration >= betweenWordsSilence) {
300                                         if (currentState != STATE_IN_SILENCE ) {
301                                                 previousState = currentState;
302                                                 ast_verb(3, "AMD: Channel [%s]. Changed state to STATE_IN_SILENCE\n", chan->name);
303                                         }
304                                         /* Find words less than word duration */
305                                         if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){
306                                                 ast_verb(3, "AMD: Channel [%s]. Short Word Duration: %d\n", chan->name, consecutiveVoiceDuration);
307                                         }
308                                         currentState  = STATE_IN_SILENCE;
309                                         consecutiveVoiceDuration = 0;
310                                 }
311
312                                 if (inInitialSilence == 1  && silenceDuration >= initialSilence) {
313                                         ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
314                                                 chan->name, silenceDuration, initialSilence);
315                                         ast_frfree(f);
316                                         strcpy(amdStatus , "MACHINE");
317                                         sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
318                                         res = 1;
319                                         break;
320                                 }
321                                 
322                                 if (silenceDuration >= afterGreetingSilence  &&  inGreeting == 1) {
323                                         ast_verb(3, "AMD: Channel [%s]. HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
324                                                 chan->name, silenceDuration, afterGreetingSilence);
325                                         ast_frfree(f);
326                                         strcpy(amdStatus , "HUMAN");
327                                         sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
328                                         res = 1;
329                                         break;
330                                 }
331                                 
332                         } else {
333                                 consecutiveVoiceDuration += framelength;
334                                 voiceDuration += framelength;
335
336                                 /* If I have enough consecutive voice to say that I am in a Word, I can only increment the
337                                    number of words if my previous state was Silence, which means that I moved into a word. */
338                                 if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
339                                         iWordsCount++;
340                                         ast_verb(3, "AMD: Channel [%s]. Word detected. iWordsCount:%d\n", chan->name, iWordsCount);
341                                         previousState = currentState;
342                                         currentState = STATE_IN_WORD;
343                                 }
344                                 if (consecutiveVoiceDuration >= maximumWordLength){
345                                         ast_verb(3, "AMD: Channel [%s]. Maximum Word Length detected. [%d]\n", chan->name, consecutiveVoiceDuration);
346                                         ast_frfree(f);
347                                         strcpy(amdStatus , "MACHINE");
348                                         sprintf(amdCause , "MAXWORDLENGTH-%d", consecutiveVoiceDuration);
349                                         break;
350                                 }
351                                 if (iWordsCount >= maximumNumberOfWords) {
352                                         ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: iWordsCount:%d\n", chan->name, iWordsCount);
353                                         ast_frfree(f);
354                                         strcpy(amdStatus , "MACHINE");
355                                         sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
356                                         res = 1;
357                                         break;
358                                 }
359
360                                 if (inGreeting == 1 && voiceDuration >= greeting) {
361                                         ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", chan->name, voiceDuration, greeting);
362                                         ast_frfree(f);
363                                         strcpy(amdStatus , "MACHINE");
364                                         sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
365                                         res = 1;
366                                         break;
367                                 }
368
369                                 if (voiceDuration >= minimumWordLength ) {
370                                         if (silenceDuration > 0)
371                                                 ast_verb(3, "AMD: Channel [%s]. Detected Talk, previous silence duration: %d\n", chan->name, silenceDuration);
372                                         silenceDuration = 0;
373                                 }
374                                 if (consecutiveVoiceDuration >= minimumWordLength && inGreeting == 0) {
375                                         /* Only go in here once to change the greeting flag when we detect the 1st word */
376                                         if (silenceDuration > 0)
377                                                 ast_verb(3, "AMD: Channel [%s]. Before Greeting Time:  silenceDuration: %d voiceDuration: %d\n", chan->name, silenceDuration, voiceDuration);
378                                         inInitialSilence = 0;
379                                         inGreeting = 1;
380                                 }
381                                 
382                         }
383                 }
384                 ast_frfree(f);
385         }
386         
387         if (!res) {
388                 /* It took too long to get a frame back. Giving up. */
389                 ast_verb(3, "AMD: Channel [%s]. Too long...\n", chan->name);
390                 strcpy(amdStatus , "NOTSURE");
391                 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
392         }
393
394         /* Set the status and cause on the channel */
395         pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus);
396         pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause);
397
398         /* Restore channel read format */
399         if (readFormat && ast_set_read_format(chan, readFormat))
400                 ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", chan->name);
401
402         /* Free the DSP used to detect silence */
403         ast_dsp_free(silenceDetector);
404
405         return;
406 }
407
408
409 static int amd_exec(struct ast_channel *chan, const char *data)
410 {
411         isAnsweringMachine(chan, data);
412
413         return 0;
414 }
415
416 static int load_config(int reload)
417 {
418         struct ast_config *cfg = NULL;
419         char *cat = NULL;
420         struct ast_variable *var = NULL;
421         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
422
423         dfltSilenceThreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
424
425         if (!(cfg = ast_config_load("amd.conf", config_flags))) {
426                 ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
427                 return -1;
428         } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
429                 return 0;
430         } else if (cfg == CONFIG_STATUS_FILEINVALID) {
431                 ast_log(LOG_ERROR, "Config file amd.conf is in an invalid format.  Aborting.\n");
432                 return -1;
433         }
434
435         cat = ast_category_browse(cfg, NULL);
436
437         while (cat) {
438                 if (!strcasecmp(cat, "general") ) {
439                         var = ast_variable_browse(cfg, cat);
440                         while (var) {
441                                 if (!strcasecmp(var->name, "initial_silence")) {
442                                         dfltInitialSilence = atoi(var->value);
443                                 } else if (!strcasecmp(var->name, "greeting")) {
444                                         dfltGreeting = atoi(var->value);
445                                 } else if (!strcasecmp(var->name, "after_greeting_silence")) {
446                                         dfltAfterGreetingSilence = atoi(var->value);
447                                 } else if (!strcasecmp(var->name, "silence_threshold")) {
448                                         dfltSilenceThreshold = atoi(var->value);
449                                 } else if (!strcasecmp(var->name, "total_analysis_time")) {
450                                         dfltTotalAnalysisTime = atoi(var->value);
451                                 } else if (!strcasecmp(var->name, "min_word_length")) {
452                                         dfltMinimumWordLength = atoi(var->value);
453                                 } else if (!strcasecmp(var->name, "between_words_silence")) {
454                                         dfltBetweenWordsSilence = atoi(var->value);
455                                 } else if (!strcasecmp(var->name, "maximum_number_of_words")) {
456                                         dfltMaximumNumberOfWords = atoi(var->value);
457                                 } else if (!strcasecmp(var->name, "maximum_word_length")) {
458                                         dfltMaximumWordLength = atoi(var->value);
459
460                                 } else {
461                                         ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
462                                                 app, cat, var->name, var->lineno);
463                                 }
464                                 var = var->next;
465                         }
466                 }
467                 cat = ast_category_browse(cfg, cat);
468         }
469
470         ast_config_destroy(cfg);
471
472         ast_verb(3, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
473                 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
474                 dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
475                 dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength);
476
477         return 0;
478 }
479
480 static int unload_module(void)
481 {
482         return ast_unregister_application(app);
483 }
484
485 static int load_module(void)
486 {
487         if (load_config(0))
488                 return AST_MODULE_LOAD_DECLINE;
489         if (ast_register_application_xml(app, amd_exec))
490                 return AST_MODULE_LOAD_FAILURE;
491         return AST_MODULE_LOAD_SUCCESS;
492 }
493
494 static int reload(void)
495 {
496         if (load_config(1))
497                 return AST_MODULE_LOAD_DECLINE;
498         return AST_MODULE_LOAD_SUCCESS;
499 }
500
501 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
502                 .load = load_module,
503                 .unload = unload_module,
504                 .reload = reload,
505 );