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: 357542 $")
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, framelength = 0;
00155 struct ast_format readFormat;
00156 int inInitialSilence = 1;
00157 int inGreeting = 0;
00158 int voiceDuration = 0;
00159 int silenceDuration = 0;
00160 int iTotalTime = 0;
00161 int iWordsCount = 0;
00162 int currentState = STATE_IN_WORD;
00163 int consecutiveVoiceDuration = 0;
00164 char amdCause[256] = "", amdStatus[256] = "";
00165 char *parse = ast_strdupa(data);
00166
00167
00168
00169
00170
00171 int initialSilence = dfltInitialSilence;
00172 int greeting = dfltGreeting;
00173 int afterGreetingSilence = dfltAfterGreetingSilence;
00174 int totalAnalysisTime = dfltTotalAnalysisTime;
00175 int minimumWordLength = dfltMinimumWordLength;
00176 int betweenWordsSilence = dfltBetweenWordsSilence;
00177 int maximumNumberOfWords = dfltMaximumNumberOfWords;
00178 int silenceThreshold = dfltSilenceThreshold;
00179 int maximumWordLength = dfltMaximumWordLength;
00180 int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame;
00181
00182 AST_DECLARE_APP_ARGS(args,
00183 AST_APP_ARG(argInitialSilence);
00184 AST_APP_ARG(argGreeting);
00185 AST_APP_ARG(argAfterGreetingSilence);
00186 AST_APP_ARG(argTotalAnalysisTime);
00187 AST_APP_ARG(argMinimumWordLength);
00188 AST_APP_ARG(argBetweenWordsSilence);
00189 AST_APP_ARG(argMaximumNumberOfWords);
00190 AST_APP_ARG(argSilenceThreshold);
00191 AST_APP_ARG(argMaximumWordLength);
00192 );
00193
00194 ast_format_clear(&readFormat);
00195 ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", ast_channel_name(chan),
00196 S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, "(N/A)"),
00197 S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, "(N/A)"),
00198 ast_getformatname(ast_channel_readformat(chan)));
00199
00200
00201 if (!ast_strlen_zero(parse)) {
00202
00203 AST_STANDARD_APP_ARGS(args, parse);
00204 if (!ast_strlen_zero(args.argInitialSilence))
00205 initialSilence = atoi(args.argInitialSilence);
00206 if (!ast_strlen_zero(args.argGreeting))
00207 greeting = atoi(args.argGreeting);
00208 if (!ast_strlen_zero(args.argAfterGreetingSilence))
00209 afterGreetingSilence = atoi(args.argAfterGreetingSilence);
00210 if (!ast_strlen_zero(args.argTotalAnalysisTime))
00211 totalAnalysisTime = atoi(args.argTotalAnalysisTime);
00212 if (!ast_strlen_zero(args.argMinimumWordLength))
00213 minimumWordLength = atoi(args.argMinimumWordLength);
00214 if (!ast_strlen_zero(args.argBetweenWordsSilence))
00215 betweenWordsSilence = atoi(args.argBetweenWordsSilence);
00216 if (!ast_strlen_zero(args.argMaximumNumberOfWords))
00217 maximumNumberOfWords = atoi(args.argMaximumNumberOfWords);
00218 if (!ast_strlen_zero(args.argSilenceThreshold))
00219 silenceThreshold = atoi(args.argSilenceThreshold);
00220 if (!ast_strlen_zero(args.argMaximumWordLength))
00221 maximumWordLength = atoi(args.argMaximumWordLength);
00222 } else {
00223 ast_debug(1, "AMD using the default parameters.\n");
00224 }
00225
00226
00227 if (maxWaitTimeForFrame > initialSilence)
00228 maxWaitTimeForFrame = initialSilence;
00229 if (maxWaitTimeForFrame > greeting)
00230 maxWaitTimeForFrame = greeting;
00231 if (maxWaitTimeForFrame > afterGreetingSilence)
00232 maxWaitTimeForFrame = afterGreetingSilence;
00233 if (maxWaitTimeForFrame > totalAnalysisTime)
00234 maxWaitTimeForFrame = totalAnalysisTime;
00235 if (maxWaitTimeForFrame > minimumWordLength)
00236 maxWaitTimeForFrame = minimumWordLength;
00237 if (maxWaitTimeForFrame > betweenWordsSilence)
00238 maxWaitTimeForFrame = betweenWordsSilence;
00239
00240
00241 ast_verb(3, "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
00242 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d] \n",
00243 initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
00244 minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold, maximumWordLength);
00245
00246
00247 ast_format_copy(&readFormat, ast_channel_readformat(chan));
00248 if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR) < 0 ) {
00249 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", ast_channel_name(chan));
00250 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
00251 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
00252 return;
00253 }
00254
00255
00256 if (!(silenceDetector = ast_dsp_new())) {
00257 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", ast_channel_name(chan));
00258 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
00259 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
00260 return;
00261 }
00262
00263
00264 ast_dsp_set_threshold(silenceDetector, silenceThreshold);
00265
00266
00267 while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
00268
00269
00270 if (!(f = ast_read(chan))) {
00271 ast_verb(3, "AMD: Channel [%s]. HANGUP\n", ast_channel_name(chan));
00272 ast_debug(1, "Got hangup\n");
00273 strcpy(amdStatus, "HANGUP");
00274 res = 1;
00275 break;
00276 }
00277
00278 if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_NULL || f->frametype == AST_FRAME_CNG) {
00279
00280 if (f->frametype == AST_FRAME_VOICE) {
00281 framelength = (ast_codec_get_samples(f) / DEFAULT_SAMPLES_PER_MS);
00282 } else {
00283 framelength = 2 * maxWaitTimeForFrame;
00284 }
00285
00286 iTotalTime += framelength;
00287 if (iTotalTime >= totalAnalysisTime) {
00288 ast_verb(3, "AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
00289 ast_frfree(f);
00290 strcpy(amdStatus , "NOTSURE");
00291 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
00292 break;
00293 }
00294
00295
00296 if (f->frametype != AST_FRAME_VOICE)
00297 dspsilence += 2 * maxWaitTimeForFrame;
00298 else {
00299 dspsilence = 0;
00300 ast_dsp_silence(silenceDetector, f, &dspsilence);
00301 }
00302
00303 if (dspsilence > 0) {
00304 silenceDuration = dspsilence;
00305
00306 if (silenceDuration >= betweenWordsSilence) {
00307 if (currentState != STATE_IN_SILENCE ) {
00308 ast_verb(3, "AMD: Channel [%s]. Changed state to STATE_IN_SILENCE\n", ast_channel_name(chan));
00309 }
00310
00311 if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){
00312 ast_verb(3, "AMD: Channel [%s]. Short Word Duration: %d\n", ast_channel_name(chan), consecutiveVoiceDuration);
00313 }
00314 currentState = STATE_IN_SILENCE;
00315 consecutiveVoiceDuration = 0;
00316 }
00317
00318 if (inInitialSilence == 1 && silenceDuration >= initialSilence) {
00319 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
00320 ast_channel_name(chan), silenceDuration, initialSilence);
00321 ast_frfree(f);
00322 strcpy(amdStatus , "MACHINE");
00323 sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
00324 res = 1;
00325 break;
00326 }
00327
00328 if (silenceDuration >= afterGreetingSilence && inGreeting == 1) {
00329 ast_verb(3, "AMD: Channel [%s]. HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
00330 ast_channel_name(chan), silenceDuration, afterGreetingSilence);
00331 ast_frfree(f);
00332 strcpy(amdStatus , "HUMAN");
00333 sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
00334 res = 1;
00335 break;
00336 }
00337
00338 } else {
00339 consecutiveVoiceDuration += framelength;
00340 voiceDuration += framelength;
00341
00342
00343
00344 if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
00345 iWordsCount++;
00346 ast_verb(3, "AMD: Channel [%s]. Word detected. iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
00347 currentState = STATE_IN_WORD;
00348 }
00349 if (consecutiveVoiceDuration >= maximumWordLength){
00350 ast_verb(3, "AMD: Channel [%s]. Maximum Word Length detected. [%d]\n", ast_channel_name(chan), consecutiveVoiceDuration);
00351 ast_frfree(f);
00352 strcpy(amdStatus , "MACHINE");
00353 sprintf(amdCause , "MAXWORDLENGTH-%d", consecutiveVoiceDuration);
00354 break;
00355 }
00356 if (iWordsCount >= maximumNumberOfWords) {
00357 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
00358 ast_frfree(f);
00359 strcpy(amdStatus , "MACHINE");
00360 sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
00361 res = 1;
00362 break;
00363 }
00364
00365 if (inGreeting == 1 && voiceDuration >= greeting) {
00366 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", ast_channel_name(chan), voiceDuration, greeting);
00367 ast_frfree(f);
00368 strcpy(amdStatus , "MACHINE");
00369 sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
00370 res = 1;
00371 break;
00372 }
00373
00374 if (voiceDuration >= minimumWordLength ) {
00375 if (silenceDuration > 0)
00376 ast_verb(3, "AMD: Channel [%s]. Detected Talk, previous silence duration: %d\n", ast_channel_name(chan), silenceDuration);
00377 silenceDuration = 0;
00378 }
00379 if (consecutiveVoiceDuration >= minimumWordLength && inGreeting == 0) {
00380
00381 if (silenceDuration > 0)
00382 ast_verb(3, "AMD: Channel [%s]. Before Greeting Time: silenceDuration: %d voiceDuration: %d\n", ast_channel_name(chan), silenceDuration, voiceDuration);
00383 inInitialSilence = 0;
00384 inGreeting = 1;
00385 }
00386
00387 }
00388 }
00389 ast_frfree(f);
00390 }
00391
00392 if (!res) {
00393
00394 ast_verb(3, "AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
00395 strcpy(amdStatus , "NOTSURE");
00396 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
00397 }
00398
00399
00400 pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus);
00401 pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause);
00402
00403
00404 if (readFormat.id && ast_set_read_format(chan, &readFormat))
00405 ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", ast_channel_name(chan));
00406
00407
00408 ast_dsp_free(silenceDetector);
00409
00410 return;
00411 }
00412
00413
00414 static int amd_exec(struct ast_channel *chan, const char *data)
00415 {
00416 isAnsweringMachine(chan, data);
00417
00418 return 0;
00419 }
00420
00421 static int load_config(int reload)
00422 {
00423 struct ast_config *cfg = NULL;
00424 char *cat = NULL;
00425 struct ast_variable *var = NULL;
00426 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00427
00428 dfltSilenceThreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
00429
00430 if (!(cfg = ast_config_load("amd.conf", config_flags))) {
00431 ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
00432 return -1;
00433 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00434 return 0;
00435 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00436 ast_log(LOG_ERROR, "Config file amd.conf is in an invalid format. Aborting.\n");
00437 return -1;
00438 }
00439
00440 cat = ast_category_browse(cfg, NULL);
00441
00442 while (cat) {
00443 if (!strcasecmp(cat, "general") ) {
00444 var = ast_variable_browse(cfg, cat);
00445 while (var) {
00446 if (!strcasecmp(var->name, "initial_silence")) {
00447 dfltInitialSilence = atoi(var->value);
00448 } else if (!strcasecmp(var->name, "greeting")) {
00449 dfltGreeting = atoi(var->value);
00450 } else if (!strcasecmp(var->name, "after_greeting_silence")) {
00451 dfltAfterGreetingSilence = atoi(var->value);
00452 } else if (!strcasecmp(var->name, "silence_threshold")) {
00453 dfltSilenceThreshold = atoi(var->value);
00454 } else if (!strcasecmp(var->name, "total_analysis_time")) {
00455 dfltTotalAnalysisTime = atoi(var->value);
00456 } else if (!strcasecmp(var->name, "min_word_length")) {
00457 dfltMinimumWordLength = atoi(var->value);
00458 } else if (!strcasecmp(var->name, "between_words_silence")) {
00459 dfltBetweenWordsSilence = atoi(var->value);
00460 } else if (!strcasecmp(var->name, "maximum_number_of_words")) {
00461 dfltMaximumNumberOfWords = atoi(var->value);
00462 } else if (!strcasecmp(var->name, "maximum_word_length")) {
00463 dfltMaximumWordLength = atoi(var->value);
00464
00465 } else {
00466 ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
00467 app, cat, var->name, var->lineno);
00468 }
00469 var = var->next;
00470 }
00471 }
00472 cat = ast_category_browse(cfg, cat);
00473 }
00474
00475 ast_config_destroy(cfg);
00476
00477 ast_verb(3, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
00478 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
00479 dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
00480 dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength);
00481
00482 return 0;
00483 }
00484
00485 static int unload_module(void)
00486 {
00487 return ast_unregister_application(app);
00488 }
00489
00490 static int load_module(void)
00491 {
00492 if (load_config(0))
00493 return AST_MODULE_LOAD_DECLINE;
00494 if (ast_register_application_xml(app, amd_exec))
00495 return AST_MODULE_LOAD_FAILURE;
00496 return AST_MODULE_LOAD_SUCCESS;
00497 }
00498
00499 static int reload(void)
00500 {
00501 if (load_config(1))
00502 return AST_MODULE_LOAD_DECLINE;
00503 return AST_MODULE_LOAD_SUCCESS;
00504 }
00505
00506 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
00507 .load = load_module,
00508 .unload = unload_module,
00509 .reload = reload,
00510 );