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