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
00031
00032
00033 #include "asterisk.h"
00034
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328209 $")
00036
00037 #include "asterisk/module.h"
00038 #include "asterisk/lock.h"
00039 #include "asterisk/channel.h"
00040 #include "asterisk/dsp.h"
00041 #include "asterisk/pbx.h"
00042 #include "asterisk/config.h"
00043 #include "asterisk/app.h"
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130 static const char app[] = "AMD";
00131
00132 #define STATE_IN_WORD 1
00133 #define STATE_IN_SILENCE 2
00134
00135
00136 static int dfltInitialSilence = 2500;
00137 static int dfltGreeting = 1500;
00138 static int dfltAfterGreetingSilence = 800;
00139 static int dfltTotalAnalysisTime = 5000;
00140 static int dfltMinimumWordLength = 100;
00141 static int dfltBetweenWordsSilence = 50;
00142 static int dfltMaximumNumberOfWords = 3;
00143 static int dfltSilenceThreshold = 256;
00144 static int dfltMaximumWordLength = 5000;
00145
00146
00147 static int dfltMaxWaitTimeForFrame = 50;
00148
00149 static void isAnsweringMachine(struct ast_channel *chan, const char *data)
00150 {
00151 int res = 0;
00152 struct ast_frame *f = NULL;
00153 struct ast_dsp *silenceDetector = NULL;
00154 int dspsilence = 0, readFormat, framelength = 0;
00155 int inInitialSilence = 1;
00156 int inGreeting = 0;
00157 int voiceDuration = 0;
00158 int silenceDuration = 0;
00159 int iTotalTime = 0;
00160 int iWordsCount = 0;
00161 int currentState = STATE_IN_WORD;
00162 int consecutiveVoiceDuration = 0;
00163 char amdCause[256] = "", amdStatus[256] = "";
00164 char *parse = ast_strdupa(data);
00165
00166
00167
00168
00169
00170 int initialSilence = dfltInitialSilence;
00171 int greeting = dfltGreeting;
00172 int afterGreetingSilence = dfltAfterGreetingSilence;
00173 int totalAnalysisTime = dfltTotalAnalysisTime;
00174 int minimumWordLength = dfltMinimumWordLength;
00175 int betweenWordsSilence = dfltBetweenWordsSilence;
00176 int maximumNumberOfWords = dfltMaximumNumberOfWords;
00177 int silenceThreshold = dfltSilenceThreshold;
00178 int maximumWordLength = dfltMaximumWordLength;
00179 int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame;
00180
00181 AST_DECLARE_APP_ARGS(args,
00182 AST_APP_ARG(argInitialSilence);
00183 AST_APP_ARG(argGreeting);
00184 AST_APP_ARG(argAfterGreetingSilence);
00185 AST_APP_ARG(argTotalAnalysisTime);
00186 AST_APP_ARG(argMinimumWordLength);
00187 AST_APP_ARG(argBetweenWordsSilence);
00188 AST_APP_ARG(argMaximumNumberOfWords);
00189 AST_APP_ARG(argSilenceThreshold);
00190 AST_APP_ARG(argMaximumWordLength);
00191 );
00192
00193 ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", chan->name,
00194 S_COR(chan->caller.ani.number.valid, chan->caller.ani.number.str, "(N/A)"),
00195 S_COR(chan->redirecting.from.number.valid, chan->redirecting.from.number.str, "(N/A)"),
00196 ast_getformatname(chan->readformat));
00197
00198
00199 if (!ast_strlen_zero(parse)) {
00200
00201 AST_STANDARD_APP_ARGS(args, parse);
00202 if (!ast_strlen_zero(args.argInitialSilence))
00203 initialSilence = atoi(args.argInitialSilence);
00204 if (!ast_strlen_zero(args.argGreeting))
00205 greeting = atoi(args.argGreeting);
00206 if (!ast_strlen_zero(args.argAfterGreetingSilence))
00207 afterGreetingSilence = atoi(args.argAfterGreetingSilence);
00208 if (!ast_strlen_zero(args.argTotalAnalysisTime))
00209 totalAnalysisTime = atoi(args.argTotalAnalysisTime);
00210 if (!ast_strlen_zero(args.argMinimumWordLength))
00211 minimumWordLength = atoi(args.argMinimumWordLength);
00212 if (!ast_strlen_zero(args.argBetweenWordsSilence))
00213 betweenWordsSilence = atoi(args.argBetweenWordsSilence);
00214 if (!ast_strlen_zero(args.argMaximumNumberOfWords))
00215 maximumNumberOfWords = atoi(args.argMaximumNumberOfWords);
00216 if (!ast_strlen_zero(args.argSilenceThreshold))
00217 silenceThreshold = atoi(args.argSilenceThreshold);
00218 if (!ast_strlen_zero(args.argMaximumWordLength))
00219 maximumWordLength = atoi(args.argMaximumWordLength);
00220 } else {
00221 ast_debug(1, "AMD using the default parameters.\n");
00222 }
00223
00224
00225 if (maxWaitTimeForFrame > initialSilence)
00226 maxWaitTimeForFrame = initialSilence;
00227 if (maxWaitTimeForFrame > greeting)
00228 maxWaitTimeForFrame = greeting;
00229 if (maxWaitTimeForFrame > afterGreetingSilence)
00230 maxWaitTimeForFrame = afterGreetingSilence;
00231 if (maxWaitTimeForFrame > totalAnalysisTime)
00232 maxWaitTimeForFrame = totalAnalysisTime;
00233 if (maxWaitTimeForFrame > minimumWordLength)
00234 maxWaitTimeForFrame = minimumWordLength;
00235 if (maxWaitTimeForFrame > betweenWordsSilence)
00236 maxWaitTimeForFrame = betweenWordsSilence;
00237
00238
00239 ast_verb(3, "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
00240 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d] \n",
00241 initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
00242 minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold, maximumWordLength);
00243
00244
00245 readFormat = chan->readformat;
00246 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0 ) {
00247 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", chan->name );
00248 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
00249 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
00250 return;
00251 }
00252
00253
00254 if (!(silenceDetector = ast_dsp_new())) {
00255 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", chan->name );
00256 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
00257 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
00258 return;
00259 }
00260
00261
00262 ast_dsp_set_threshold(silenceDetector, silenceThreshold);
00263
00264
00265 while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
00266
00267
00268 if (!(f = ast_read(chan))) {
00269 ast_verb(3, "AMD: Channel [%s]. HANGUP\n", chan->name);
00270 ast_debug(1, "Got hangup\n");
00271 strcpy(amdStatus, "HANGUP");
00272 res = 1;
00273 break;
00274 }
00275
00276 if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_NULL || f->frametype == AST_FRAME_CNG) {
00277
00278 if (f->frametype == AST_FRAME_VOICE) {
00279 framelength = (ast_codec_get_samples(f) / DEFAULT_SAMPLES_PER_MS);
00280 } else {
00281 framelength = 2 * maxWaitTimeForFrame;
00282 }
00283
00284 iTotalTime += framelength;
00285 if (iTotalTime >= totalAnalysisTime) {
00286 ast_verb(3, "AMD: Channel [%s]. Too long...\n", chan->name );
00287 ast_frfree(f);
00288 strcpy(amdStatus , "NOTSURE");
00289 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
00290 break;
00291 }
00292
00293
00294 if (f->frametype != AST_FRAME_VOICE)
00295 dspsilence += 2 * maxWaitTimeForFrame;
00296 else {
00297 dspsilence = 0;
00298 ast_dsp_silence(silenceDetector, f, &dspsilence);
00299 }
00300
00301 if (dspsilence > 0) {
00302 silenceDuration = dspsilence;
00303
00304 if (silenceDuration >= betweenWordsSilence) {
00305 if (currentState != STATE_IN_SILENCE ) {
00306 ast_verb(3, "AMD: Channel [%s]. Changed state to STATE_IN_SILENCE\n", chan->name);
00307 }
00308
00309 if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){
00310 ast_verb(3, "AMD: Channel [%s]. Short Word Duration: %d\n", chan->name, consecutiveVoiceDuration);
00311 }
00312 currentState = STATE_IN_SILENCE;
00313 consecutiveVoiceDuration = 0;
00314 }
00315
00316 if (inInitialSilence == 1 && silenceDuration >= initialSilence) {
00317 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
00318 chan->name, silenceDuration, initialSilence);
00319 ast_frfree(f);
00320 strcpy(amdStatus , "MACHINE");
00321 sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
00322 res = 1;
00323 break;
00324 }
00325
00326 if (silenceDuration >= afterGreetingSilence && inGreeting == 1) {
00327 ast_verb(3, "AMD: Channel [%s]. HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
00328 chan->name, silenceDuration, afterGreetingSilence);
00329 ast_frfree(f);
00330 strcpy(amdStatus , "HUMAN");
00331 sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
00332 res = 1;
00333 break;
00334 }
00335
00336 } else {
00337 consecutiveVoiceDuration += framelength;
00338 voiceDuration += framelength;
00339
00340
00341
00342 if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
00343 iWordsCount++;
00344 ast_verb(3, "AMD: Channel [%s]. Word detected. iWordsCount:%d\n", chan->name, iWordsCount);
00345 currentState = STATE_IN_WORD;
00346 }
00347 if (consecutiveVoiceDuration >= maximumWordLength){
00348 ast_verb(3, "AMD: Channel [%s]. Maximum Word Length detected. [%d]\n", chan->name, consecutiveVoiceDuration);
00349 ast_frfree(f);
00350 strcpy(amdStatus , "MACHINE");
00351 sprintf(amdCause , "MAXWORDLENGTH-%d", consecutiveVoiceDuration);
00352 break;
00353 }
00354 if (iWordsCount >= maximumNumberOfWords) {
00355 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: iWordsCount:%d\n", chan->name, iWordsCount);
00356 ast_frfree(f);
00357 strcpy(amdStatus , "MACHINE");
00358 sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
00359 res = 1;
00360 break;
00361 }
00362
00363 if (inGreeting == 1 && voiceDuration >= greeting) {
00364 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", chan->name, voiceDuration, greeting);
00365 ast_frfree(f);
00366 strcpy(amdStatus , "MACHINE");
00367 sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
00368 res = 1;
00369 break;
00370 }
00371
00372 if (voiceDuration >= minimumWordLength ) {
00373 if (silenceDuration > 0)
00374 ast_verb(3, "AMD: Channel [%s]. Detected Talk, previous silence duration: %d\n", chan->name, silenceDuration);
00375 silenceDuration = 0;
00376 }
00377 if (consecutiveVoiceDuration >= minimumWordLength && inGreeting == 0) {
00378
00379 if (silenceDuration > 0)
00380 ast_verb(3, "AMD: Channel [%s]. Before Greeting Time: silenceDuration: %d voiceDuration: %d\n", chan->name, silenceDuration, voiceDuration);
00381 inInitialSilence = 0;
00382 inGreeting = 1;
00383 }
00384
00385 }
00386 }
00387 ast_frfree(f);
00388 }
00389
00390 if (!res) {
00391
00392 ast_verb(3, "AMD: Channel [%s]. Too long...\n", chan->name);
00393 strcpy(amdStatus , "NOTSURE");
00394 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
00395 }
00396
00397
00398 pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus);
00399 pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause);
00400
00401
00402 if (readFormat && ast_set_read_format(chan, readFormat))
00403 ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", chan->name);
00404
00405
00406 ast_dsp_free(silenceDetector);
00407
00408 return;
00409 }
00410
00411
00412 static int amd_exec(struct ast_channel *chan, const char *data)
00413 {
00414 isAnsweringMachine(chan, data);
00415
00416 return 0;
00417 }
00418
00419 static int load_config(int reload)
00420 {
00421 struct ast_config *cfg = NULL;
00422 char *cat = NULL;
00423 struct ast_variable *var = NULL;
00424 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00425
00426 dfltSilenceThreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
00427
00428 if (!(cfg = ast_config_load("amd.conf", config_flags))) {
00429 ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
00430 return -1;
00431 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00432 return 0;
00433 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00434 ast_log(LOG_ERROR, "Config file amd.conf is in an invalid format. Aborting.\n");
00435 return -1;
00436 }
00437
00438 cat = ast_category_browse(cfg, NULL);
00439
00440 while (cat) {
00441 if (!strcasecmp(cat, "general") ) {
00442 var = ast_variable_browse(cfg, cat);
00443 while (var) {
00444 if (!strcasecmp(var->name, "initial_silence")) {
00445 dfltInitialSilence = atoi(var->value);
00446 } else if (!strcasecmp(var->name, "greeting")) {
00447 dfltGreeting = atoi(var->value);
00448 } else if (!strcasecmp(var->name, "after_greeting_silence")) {
00449 dfltAfterGreetingSilence = atoi(var->value);
00450 } else if (!strcasecmp(var->name, "silence_threshold")) {
00451 dfltSilenceThreshold = atoi(var->value);
00452 } else if (!strcasecmp(var->name, "total_analysis_time")) {
00453 dfltTotalAnalysisTime = atoi(var->value);
00454 } else if (!strcasecmp(var->name, "min_word_length")) {
00455 dfltMinimumWordLength = atoi(var->value);
00456 } else if (!strcasecmp(var->name, "between_words_silence")) {
00457 dfltBetweenWordsSilence = atoi(var->value);
00458 } else if (!strcasecmp(var->name, "maximum_number_of_words")) {
00459 dfltMaximumNumberOfWords = atoi(var->value);
00460 } else if (!strcasecmp(var->name, "maximum_word_length")) {
00461 dfltMaximumWordLength = atoi(var->value);
00462
00463 } else {
00464 ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
00465 app, cat, var->name, var->lineno);
00466 }
00467 var = var->next;
00468 }
00469 }
00470 cat = ast_category_browse(cfg, cat);
00471 }
00472
00473 ast_config_destroy(cfg);
00474
00475 ast_verb(3, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
00476 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
00477 dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
00478 dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength);
00479
00480 return 0;
00481 }
00482
00483 static int unload_module(void)
00484 {
00485 return ast_unregister_application(app);
00486 }
00487
00488 static int load_module(void)
00489 {
00490 if (load_config(0))
00491 return AST_MODULE_LOAD_DECLINE;
00492 if (ast_register_application_xml(app, amd_exec))
00493 return AST_MODULE_LOAD_FAILURE;
00494 return AST_MODULE_LOAD_SUCCESS;
00495 }
00496
00497 static int reload(void)
00498 {
00499 if (load_config(1))
00500 return AST_MODULE_LOAD_DECLINE;
00501 return AST_MODULE_LOAD_SUCCESS;
00502 }
00503
00504 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
00505 .load = load_module,
00506 .unload = unload_module,
00507 .reload = reload,
00508 );