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: 232355 $")
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 iTotalTime += framelength;
00224 if (iTotalTime >= totalAnalysisTime) {
00225 if (option_verbose > 2)
00226 ast_verbose(VERBOSE_PREFIX_3 "AMD: Channel [%s]. Too long...\n", chan->name );
00227 ast_frfree(f);
00228 strcpy(amdStatus , "NOTSURE");
00229 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
00230 break;
00231 }
00232
00233
00234 if (f->frametype != AST_FRAME_VOICE)
00235 dspsilence += 2 * maxWaitTimeForFrame;
00236 else {
00237 dspsilence = 0;
00238 ast_dsp_silence(silenceDetector, f, &dspsilence);
00239 }
00240
00241 if (dspsilence > 0) {
00242 silenceDuration = dspsilence;
00243
00244 if (silenceDuration >= betweenWordsSilence) {
00245 if (currentState != STATE_IN_SILENCE ) {
00246 previousState = currentState;
00247 if (option_verbose > 2)
00248 ast_verbose(VERBOSE_PREFIX_3 "AMD: Changed state to STATE_IN_SILENCE\n");
00249 }
00250 currentState = STATE_IN_SILENCE;
00251 consecutiveVoiceDuration = 0;
00252 }
00253
00254 if (inInitialSilence == 1 && silenceDuration >= initialSilence) {
00255 if (option_verbose > 2)
00256 ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
00257 silenceDuration, initialSilence);
00258 ast_frfree(f);
00259 strcpy(amdStatus , "MACHINE");
00260 sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
00261 res = 1;
00262 break;
00263 }
00264
00265 if (silenceDuration >= afterGreetingSilence && inGreeting == 1) {
00266 if (option_verbose > 2)
00267 ast_verbose(VERBOSE_PREFIX_3 "AMD: HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
00268 silenceDuration, afterGreetingSilence);
00269 ast_frfree(f);
00270 strcpy(amdStatus , "HUMAN");
00271 sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
00272 res = 1;
00273 break;
00274 }
00275
00276 } else {
00277 consecutiveVoiceDuration += framelength;
00278 voiceDuration += framelength;
00279
00280
00281
00282 if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
00283 iWordsCount++;
00284 if (option_verbose > 2)
00285 ast_verbose(VERBOSE_PREFIX_3 "AMD: Word detected. iWordsCount:%d\n", iWordsCount);
00286 previousState = currentState;
00287 currentState = STATE_IN_WORD;
00288 }
00289
00290 if (iWordsCount >= maximumNumberOfWords) {
00291 if (option_verbose > 2)
00292 ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: iWordsCount:%d\n", iWordsCount);
00293 ast_frfree(f);
00294 strcpy(amdStatus , "MACHINE");
00295 sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
00296 res = 1;
00297 break;
00298 }
00299
00300 if (inGreeting == 1 && voiceDuration >= greeting) {
00301 if (option_verbose > 2)
00302 ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", voiceDuration, greeting);
00303 ast_frfree(f);
00304 strcpy(amdStatus , "MACHINE");
00305 sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
00306 res = 1;
00307 break;
00308 }
00309
00310 if (voiceDuration >= minimumWordLength ) {
00311 silenceDuration = 0;
00312 inInitialSilence = 0;
00313 inGreeting = 1;
00314 }
00315
00316 }
00317 }
00318 ast_frfree(f);
00319 }
00320
00321 if (!res) {
00322
00323 if (option_verbose > 2)
00324 ast_verbose(VERBOSE_PREFIX_3 "AMD: Channel [%s]. Too long...\n", chan->name);
00325 strcpy(amdStatus , "NOTSURE");
00326 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
00327 }
00328
00329
00330 pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus);
00331 pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause);
00332
00333
00334 if (readFormat && ast_set_read_format(chan, readFormat))
00335 ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", chan->name);
00336
00337
00338 ast_dsp_free(silenceDetector);
00339
00340 return;
00341 }
00342
00343
00344 static int amd_exec(struct ast_channel *chan, void *data)
00345 {
00346 struct ast_module_user *u = NULL;
00347
00348 u = ast_module_user_add(chan);
00349 isAnsweringMachine(chan, data);
00350 ast_module_user_remove(u);
00351
00352 return 0;
00353 }
00354
00355 static void load_config(void)
00356 {
00357 struct ast_config *cfg = NULL;
00358 char *cat = NULL;
00359 struct ast_variable *var = NULL;
00360
00361 if (!(cfg = ast_config_load("amd.conf"))) {
00362 ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
00363 return;
00364 }
00365
00366 cat = ast_category_browse(cfg, NULL);
00367
00368 while (cat) {
00369 if (!strcasecmp(cat, "general") ) {
00370 var = ast_variable_browse(cfg, cat);
00371 while (var) {
00372 if (!strcasecmp(var->name, "initial_silence")) {
00373 dfltInitialSilence = atoi(var->value);
00374 } else if (!strcasecmp(var->name, "greeting")) {
00375 dfltGreeting = atoi(var->value);
00376 } else if (!strcasecmp(var->name, "after_greeting_silence")) {
00377 dfltAfterGreetingSilence = atoi(var->value);
00378 } else if (!strcasecmp(var->name, "silence_threshold")) {
00379 dfltSilenceThreshold = atoi(var->value);
00380 } else if (!strcasecmp(var->name, "total_analysis_time")) {
00381 dfltTotalAnalysisTime = atoi(var->value);
00382 } else if (!strcasecmp(var->name, "min_word_length")) {
00383 dfltMinimumWordLength = atoi(var->value);
00384 } else if (!strcasecmp(var->name, "between_words_silence")) {
00385 dfltBetweenWordsSilence = atoi(var->value);
00386 } else if (!strcasecmp(var->name, "maximum_number_of_words")) {
00387 dfltMaximumNumberOfWords = atoi(var->value);
00388 } else {
00389 ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
00390 app, cat, var->name, var->lineno);
00391 }
00392 var = var->next;
00393 }
00394 }
00395 cat = ast_category_browse(cfg, cat);
00396 }
00397
00398 ast_config_destroy(cfg);
00399
00400 if (option_verbose > 2)
00401 ast_verbose(VERBOSE_PREFIX_3 "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
00402 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] \n",
00403 dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
00404 dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold );
00405
00406 return;
00407 }
00408
00409 static int unload_module(void)
00410 {
00411 ast_module_user_hangup_all();
00412 return ast_unregister_application(app);
00413 }
00414
00415 static int load_module(void)
00416 {
00417 load_config();
00418 return ast_register_application(app, amd_exec, synopsis, descrip);
00419 }
00420
00421 static int reload(void)
00422 {
00423 load_config();
00424 return 0;
00425 }
00426
00427 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
00428 .load = load_module,
00429 .unload = unload_module,
00430 .reload = reload,
00431 );