00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "asterisk.h"
00023
00024 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 277182 $")
00025
00026 #include <stdio.h>
00027 #include <stdlib.h>
00028
00029 #include "asterisk/module.h"
00030 #include "asterisk/lock.h"
00031 #include "asterisk/options.h"
00032 #include "asterisk/channel.h"
00033 #include "asterisk/dsp.h"
00034 #include "asterisk/pbx.h"
00035 #include "asterisk/config.h"
00036 #include "asterisk/app.h"
00037
00038
00039 static char *app = "AMD";
00040 static char *synopsis = "Attempts to detect answering machines";
00041 static char *descrip =
00042 " AMD([initialSilence][|greeting][|afterGreetingSilence][|totalAnalysisTime]\n"
00043 " [|minimumWordLength][|betweenWordsSilence][|maximumNumberOfWords]\n"
00044 " [|silenceThreshold])\n"
00045 " This application attempts to detect answering machines at the beginning\n"
00046 " of outbound calls. Simply call this application after the call\n"
00047 " has been answered (outbound only, of course).\n"
00048 " When loaded, AMD reads amd.conf and uses the parameters specified as\n"
00049 " default values. Those default values get overwritten when calling AMD\n"
00050 " with parameters.\n"
00051 "- 'initialSilence' is the maximum silence duration before the greeting. If\n"
00052 " exceeded then MACHINE.\n"
00053 "- 'greeting' is the maximum length of a greeting. If exceeded then MACHINE.\n"
00054 "- 'afterGreetingSilence' is the silence after detecting a greeting.\n"
00055 " If exceeded then HUMAN.\n"
00056 "- 'totalAnalysisTime' is the maximum time allowed for the algorithm to decide\n"
00057 " on a HUMAN or MACHINE.\n"
00058 "- 'minimumWordLength'is the minimum duration of Voice to considered as a word.\n"
00059 "- 'betweenWordsSilence' is the minimum duration of silence after a word to \n"
00060 " consider the audio that follows as a new word.\n"
00061 "- 'maximumNumberOfWords'is the maximum number of words in the greeting. \n"
00062 " If exceeded then MACHINE.\n"
00063 "- 'silenceThreshold' is the silence threshold.\n"
00064 "This application sets the following channel variable upon completion:\n"
00065 " AMDSTATUS - This is the status of the answering machine detection.\n"
00066 " Possible values are:\n"
00067 " MACHINE | HUMAN | NOTSURE | HANGUP\n"
00068 " AMDCAUSE - Indicates the cause that led to the conclusion.\n"
00069 " Possible values are:\n"
00070 " TOOLONG-<%d total_time>\n"
00071 " INITIALSILENCE-<%d silenceDuration>-<%d initialSilence>\n"
00072 " HUMAN-<%d silenceDuration>-<%d afterGreetingSilence>\n"
00073 " MAXWORDS-<%d wordsCount>-<%d maximumNumberOfWords>\n"
00074 " LONGGREETING-<%d voiceDuration>-<%d greeting>\n";
00075
00076 #define STATE_IN_WORD 1
00077 #define STATE_IN_SILENCE 2
00078
00079
00080 static int dfltInitialSilence = 2500;
00081 static int dfltGreeting = 1500;
00082 static int dfltAfterGreetingSilence = 800;
00083 static int dfltTotalAnalysisTime = 5000;
00084 static int dfltMinimumWordLength = 100;
00085 static int dfltBetweenWordsSilence = 50;
00086 static int dfltMaximumNumberOfWords = 3;
00087 static int dfltSilenceThreshold = 256;
00088
00089
00090 static int dfltMaxWaitTimeForFrame = 50;
00091
00092 static void isAnsweringMachine(struct ast_channel *chan, void *data)
00093 {
00094 int res = 0;
00095 struct ast_frame *f = NULL;
00096 struct ast_dsp *silenceDetector = NULL;
00097 int dspsilence = 0, readFormat, framelength = 0;
00098 int inInitialSilence = 1;
00099 int inGreeting = 0;
00100 int voiceDuration = 0;
00101 int silenceDuration = 0;
00102 int iTotalTime = 0;
00103 int iWordsCount = 0;
00104 int currentState = STATE_IN_WORD;
00105 int previousState = STATE_IN_SILENCE;
00106 int consecutiveVoiceDuration = 0;
00107 char amdCause[256] = "", amdStatus[256] = "";
00108 char *parse = ast_strdupa(data);
00109
00110
00111
00112
00113
00114 int initialSilence = dfltInitialSilence;
00115 int greeting = dfltGreeting;
00116 int afterGreetingSilence = dfltAfterGreetingSilence;
00117 int totalAnalysisTime = dfltTotalAnalysisTime;
00118 int minimumWordLength = dfltMinimumWordLength;
00119 int betweenWordsSilence = dfltBetweenWordsSilence;
00120 int maximumNumberOfWords = dfltMaximumNumberOfWords;
00121 int silenceThreshold = dfltSilenceThreshold;
00122 int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame;
00123
00124 AST_DECLARE_APP_ARGS(args,
00125 AST_APP_ARG(argInitialSilence);
00126 AST_APP_ARG(argGreeting);
00127 AST_APP_ARG(argAfterGreetingSilence);
00128 AST_APP_ARG(argTotalAnalysisTime);
00129 AST_APP_ARG(argMinimumWordLength);
00130 AST_APP_ARG(argBetweenWordsSilence);
00131 AST_APP_ARG(argMaximumNumberOfWords);
00132 AST_APP_ARG(argSilenceThreshold);
00133 );
00134
00135 if (option_verbose > 2)
00136 ast_verbose(VERBOSE_PREFIX_3 "AMD: %s %s %s (Fmt: %d)\n", chan->name ,chan->cid.cid_ani, chan->cid.cid_rdnis, chan->readformat);
00137
00138
00139 if (!ast_strlen_zero(parse)) {
00140
00141 AST_STANDARD_APP_ARGS(args, parse);
00142 if (!ast_strlen_zero(args.argInitialSilence))
00143 initialSilence = atoi(args.argInitialSilence);
00144 if (!ast_strlen_zero(args.argGreeting))
00145 greeting = atoi(args.argGreeting);
00146 if (!ast_strlen_zero(args.argAfterGreetingSilence))
00147 afterGreetingSilence = atoi(args.argAfterGreetingSilence);
00148 if (!ast_strlen_zero(args.argTotalAnalysisTime))
00149 totalAnalysisTime = atoi(args.argTotalAnalysisTime);
00150 if (!ast_strlen_zero(args.argMinimumWordLength))
00151 minimumWordLength = atoi(args.argMinimumWordLength);
00152 if (!ast_strlen_zero(args.argBetweenWordsSilence))
00153 betweenWordsSilence = atoi(args.argBetweenWordsSilence);
00154 if (!ast_strlen_zero(args.argMaximumNumberOfWords))
00155 maximumNumberOfWords = atoi(args.argMaximumNumberOfWords);
00156 if (!ast_strlen_zero(args.argSilenceThreshold))
00157 silenceThreshold = atoi(args.argSilenceThreshold);
00158 } else if (option_debug)
00159 ast_log(LOG_DEBUG, "AMD using the default parameters.\n");
00160
00161
00162 if (maxWaitTimeForFrame > initialSilence)
00163 maxWaitTimeForFrame = initialSilence;
00164 if (maxWaitTimeForFrame > greeting)
00165 maxWaitTimeForFrame = greeting;
00166 if (maxWaitTimeForFrame > afterGreetingSilence)
00167 maxWaitTimeForFrame = afterGreetingSilence;
00168 if (maxWaitTimeForFrame > totalAnalysisTime)
00169 maxWaitTimeForFrame = totalAnalysisTime;
00170 if (maxWaitTimeForFrame > minimumWordLength)
00171 maxWaitTimeForFrame = minimumWordLength;
00172 if (maxWaitTimeForFrame > betweenWordsSilence)
00173 maxWaitTimeForFrame = betweenWordsSilence;
00174
00175
00176 if (option_verbose > 2)
00177 ast_verbose(VERBOSE_PREFIX_3 "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
00178 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] \n",
00179 initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
00180 minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold );
00181
00182
00183 readFormat = chan->readformat;
00184 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0 ) {
00185 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", chan->name );
00186 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
00187 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
00188 return;
00189 }
00190
00191
00192 if (!(silenceDetector = ast_dsp_new())) {
00193 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", chan->name );
00194 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
00195 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
00196 return;
00197 }
00198
00199
00200 ast_dsp_set_threshold(silenceDetector, silenceThreshold);
00201
00202
00203 while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
00204
00205
00206 if (!(f = ast_read(chan))) {
00207 if (option_verbose > 2)
00208 ast_verbose(VERBOSE_PREFIX_3 "AMD: HANGUP\n");
00209 if (option_debug)
00210 ast_log(LOG_DEBUG, "Got hangup\n");
00211 strcpy(amdStatus, "HANGUP");
00212 res = 1;
00213 break;
00214 }
00215
00216 if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_NULL || f->frametype == AST_FRAME_CNG) {
00217
00218 if (f->frametype == AST_FRAME_VOICE) {
00219 framelength = (ast_codec_get_samples(f) / DEFAULT_SAMPLES_PER_MS);
00220 } else {
00221 framelength = 2 * maxWaitTimeForFrame;
00222 }
00223
00224 iTotalTime += framelength;
00225 if (iTotalTime >= totalAnalysisTime) {
00226 if (option_verbose > 2)
00227 ast_verbose(VERBOSE_PREFIX_3 "AMD: Channel [%s]. Too long...\n", chan->name );
00228 ast_frfree(f);
00229 strcpy(amdStatus , "NOTSURE");
00230 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
00231 break;
00232 }
00233
00234
00235 if (f->frametype != AST_FRAME_VOICE)
00236 dspsilence += 2 * maxWaitTimeForFrame;
00237 else {
00238 dspsilence = 0;
00239 ast_dsp_silence(silenceDetector, f, &dspsilence);
00240 }
00241
00242 if (dspsilence > 0) {
00243 silenceDuration = dspsilence;
00244
00245 if (silenceDuration >= betweenWordsSilence) {
00246 if (currentState != STATE_IN_SILENCE ) {
00247 previousState = currentState;
00248 if (option_verbose > 2)
00249 ast_verbose(VERBOSE_PREFIX_3 "AMD: Changed state to STATE_IN_SILENCE\n");
00250 }
00251 currentState = STATE_IN_SILENCE;
00252 consecutiveVoiceDuration = 0;
00253 }
00254
00255 if (inInitialSilence == 1 && silenceDuration >= initialSilence) {
00256 if (option_verbose > 2)
00257 ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
00258 silenceDuration, initialSilence);
00259 ast_frfree(f);
00260 strcpy(amdStatus , "MACHINE");
00261 sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
00262 res = 1;
00263 break;
00264 }
00265
00266 if (silenceDuration >= afterGreetingSilence && inGreeting == 1) {
00267 if (option_verbose > 2)
00268 ast_verbose(VERBOSE_PREFIX_3 "AMD: HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
00269 silenceDuration, afterGreetingSilence);
00270 ast_frfree(f);
00271 strcpy(amdStatus , "HUMAN");
00272 sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
00273 res = 1;
00274 break;
00275 }
00276
00277 } else {
00278 consecutiveVoiceDuration += framelength;
00279 voiceDuration += framelength;
00280
00281
00282
00283 if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
00284 iWordsCount++;
00285 if (option_verbose > 2)
00286 ast_verbose(VERBOSE_PREFIX_3 "AMD: Word detected. iWordsCount:%d\n", iWordsCount);
00287 previousState = currentState;
00288 currentState = STATE_IN_WORD;
00289 }
00290
00291 if (iWordsCount >= maximumNumberOfWords) {
00292 if (option_verbose > 2)
00293 ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: iWordsCount:%d\n", iWordsCount);
00294 ast_frfree(f);
00295 strcpy(amdStatus , "MACHINE");
00296 sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
00297 res = 1;
00298 break;
00299 }
00300
00301 if (inGreeting == 1 && voiceDuration >= greeting) {
00302 if (option_verbose > 2)
00303 ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", voiceDuration, greeting);
00304 ast_frfree(f);
00305 strcpy(amdStatus , "MACHINE");
00306 sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
00307 res = 1;
00308 break;
00309 }
00310
00311 if (voiceDuration >= minimumWordLength ) {
00312 silenceDuration = 0;
00313 inInitialSilence = 0;
00314 inGreeting = 1;
00315 }
00316
00317 }
00318 }
00319 ast_frfree(f);
00320 }
00321
00322 if (!res) {
00323
00324 if (option_verbose > 2)
00325 ast_verbose(VERBOSE_PREFIX_3 "AMD: Channel [%s]. Too long...\n", chan->name);
00326 strcpy(amdStatus , "NOTSURE");
00327 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
00328 }
00329
00330
00331 pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus);
00332 pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause);
00333
00334
00335 if (readFormat && ast_set_read_format(chan, readFormat))
00336 ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", chan->name);
00337
00338
00339 ast_dsp_free(silenceDetector);
00340
00341 return;
00342 }
00343
00344
00345 static int amd_exec(struct ast_channel *chan, void *data)
00346 {
00347 struct ast_module_user *u = NULL;
00348
00349 u = ast_module_user_add(chan);
00350 isAnsweringMachine(chan, data);
00351 ast_module_user_remove(u);
00352
00353 return 0;
00354 }
00355
00356 static void load_config(void)
00357 {
00358 struct ast_config *cfg = NULL;
00359 char *cat = NULL;
00360 struct ast_variable *var = NULL;
00361
00362 if (!(cfg = ast_config_load("amd.conf"))) {
00363 ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
00364 return;
00365 }
00366
00367 cat = ast_category_browse(cfg, NULL);
00368
00369 while (cat) {
00370 if (!strcasecmp(cat, "general") ) {
00371 var = ast_variable_browse(cfg, cat);
00372 while (var) {
00373 if (!strcasecmp(var->name, "initial_silence")) {
00374 dfltInitialSilence = atoi(var->value);
00375 } else if (!strcasecmp(var->name, "greeting")) {
00376 dfltGreeting = atoi(var->value);
00377 } else if (!strcasecmp(var->name, "after_greeting_silence")) {
00378 dfltAfterGreetingSilence = atoi(var->value);
00379 } else if (!strcasecmp(var->name, "silence_threshold")) {
00380 dfltSilenceThreshold = atoi(var->value);
00381 } else if (!strcasecmp(var->name, "total_analysis_time")) {
00382 dfltTotalAnalysisTime = atoi(var->value);
00383 } else if (!strcasecmp(var->name, "min_word_length")) {
00384 dfltMinimumWordLength = atoi(var->value);
00385 } else if (!strcasecmp(var->name, "between_words_silence")) {
00386 dfltBetweenWordsSilence = atoi(var->value);
00387 } else if (!strcasecmp(var->name, "maximum_number_of_words")) {
00388 dfltMaximumNumberOfWords = atoi(var->value);
00389 } else {
00390 ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
00391 app, cat, var->name, var->lineno);
00392 }
00393 var = var->next;
00394 }
00395 }
00396 cat = ast_category_browse(cfg, cat);
00397 }
00398
00399 ast_config_destroy(cfg);
00400
00401 if (option_verbose > 2)
00402 ast_verbose(VERBOSE_PREFIX_3 "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
00403 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] \n",
00404 dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
00405 dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold );
00406
00407 return;
00408 }
00409
00410 static int unload_module(void)
00411 {
00412 ast_module_user_hangup_all();
00413 return ast_unregister_application(app);
00414 }
00415
00416 static int load_module(void)
00417 {
00418 load_config();
00419 return ast_register_application(app, amd_exec, synopsis, descrip);
00420 }
00421
00422 static int reload(void)
00423 {
00424 load_config();
00425 return 0;
00426 }
00427
00428 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
00429 .load = load_module,
00430 .unload = unload_module,
00431 .reload = reload,
00432 );