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