#include "asterisk.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/dsp.h"
#include "asterisk/pbx.h"
#include "asterisk/config.h"
#include "asterisk/app.h"

Go to the source code of this file.
Defines | |
| #define | STATE_IN_SILENCE 2 |
| #define | STATE_IN_WORD 1 |
Functions | |
| static void | __reg_module (void) |
| static void | __unreg_module (void) |
| static int | amd_exec (struct ast_channel *chan, const char *data) |
| static void | isAnsweringMachine (struct ast_channel *chan, const char *data) |
| static int | load_config (int reload) |
| static int | load_module (void) |
| static int | reload (void) |
| static int | unload_module (void) |
Variables | |
| static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Answering Machine Detection Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, } |
| static const char | app [] = "AMD" |
| static struct ast_module_info * | ast_module_info = &__mod_info |
| static int | dfltAfterGreetingSilence = 800 |
| static int | dfltBetweenWordsSilence = 50 |
| static int | dfltGreeting = 1500 |
| static int | dfltInitialSilence = 2500 |
| static int | dfltMaximumNumberOfWords = 3 |
| static int | dfltMaximumWordLength = 5000 |
| static int | dfltMaxWaitTimeForFrame = 50 |
| static int | dfltMinimumWordLength = 100 |
| static int | dfltSilenceThreshold = 256 |
| static int | dfltTotalAnalysisTime = 5000 |
Definition in file app_amd.c.
| #define STATE_IN_SILENCE 2 |
| #define STATE_IN_WORD 1 |
| static int amd_exec | ( | struct ast_channel * | chan, | |
| const char * | data | |||
| ) | [static] |
Definition at line 407 of file app_amd.c.
References isAnsweringMachine().
Referenced by load_module().
00408 { 00409 isAnsweringMachine(chan, data); 00410 00411 return 0; 00412 }
| static void isAnsweringMachine | ( | struct ast_channel * | chan, | |
| const char * | data | |||
| ) | [static] |
Definition at line 146 of file app_amd.c.
References AST_APP_ARG, ast_codec_get_samples(), ast_debug, AST_DECLARE_APP_ARGS, ast_dsp_free(), ast_dsp_new(), ast_dsp_set_threshold(), ast_dsp_silence(), AST_FORMAT_SLINEAR, AST_FRAME_CNG, AST_FRAME_NULL, AST_FRAME_VOICE, ast_frfree, ast_log(), ast_read(), ast_set_read_format(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_verb, ast_waitfor(), ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_rdnis, DEFAULT_SAMPLES_PER_MS, f, ast_frame::frametype, LOG_WARNING, ast_channel::name, parse(), pbx_builtin_setvar_helper(), ast_channel::readformat, STATE_IN_SILENCE, and STATE_IN_WORD.
Referenced by amd_exec().
00147 { 00148 int res = 0; 00149 struct ast_frame *f = NULL; 00150 struct ast_dsp *silenceDetector = NULL; 00151 int dspsilence = 0, readFormat, framelength = 0; 00152 int inInitialSilence = 1; 00153 int inGreeting = 0; 00154 int voiceDuration = 0; 00155 int silenceDuration = 0; 00156 int iTotalTime = 0; 00157 int iWordsCount = 0; 00158 int currentState = STATE_IN_WORD; 00159 int previousState = STATE_IN_SILENCE; 00160 int consecutiveVoiceDuration = 0; 00161 char amdCause[256] = "", amdStatus[256] = ""; 00162 char *parse = ast_strdupa(data); 00163 00164 /* Lets set the initial values of the variables that will control the algorithm. 00165 The initial values are the default ones. If they are passed as arguments 00166 when invoking the application, then the default values will be overwritten 00167 by the ones passed as parameters. */ 00168 int initialSilence = dfltInitialSilence; 00169 int greeting = dfltGreeting; 00170 int afterGreetingSilence = dfltAfterGreetingSilence; 00171 int totalAnalysisTime = dfltTotalAnalysisTime; 00172 int minimumWordLength = dfltMinimumWordLength; 00173 int betweenWordsSilence = dfltBetweenWordsSilence; 00174 int maximumNumberOfWords = dfltMaximumNumberOfWords; 00175 int silenceThreshold = dfltSilenceThreshold; 00176 int maximumWordLength = dfltMaximumWordLength; 00177 int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame; 00178 00179 AST_DECLARE_APP_ARGS(args, 00180 AST_APP_ARG(argInitialSilence); 00181 AST_APP_ARG(argGreeting); 00182 AST_APP_ARG(argAfterGreetingSilence); 00183 AST_APP_ARG(argTotalAnalysisTime); 00184 AST_APP_ARG(argMinimumWordLength); 00185 AST_APP_ARG(argBetweenWordsSilence); 00186 AST_APP_ARG(argMaximumNumberOfWords); 00187 AST_APP_ARG(argSilenceThreshold); 00188 AST_APP_ARG(argMaximumWordLength); 00189 ); 00190 00191 ast_verb(3, "AMD: %s %s %s (Fmt: %d)\n", chan->name ,chan->cid.cid_ani, chan->cid.cid_rdnis, chan->readformat); 00192 00193 /* Lets parse the arguments. */ 00194 if (!ast_strlen_zero(parse)) { 00195 /* Some arguments have been passed. Lets parse them and overwrite the defaults. */ 00196 AST_STANDARD_APP_ARGS(args, parse); 00197 if (!ast_strlen_zero(args.argInitialSilence)) 00198 initialSilence = atoi(args.argInitialSilence); 00199 if (!ast_strlen_zero(args.argGreeting)) 00200 greeting = atoi(args.argGreeting); 00201 if (!ast_strlen_zero(args.argAfterGreetingSilence)) 00202 afterGreetingSilence = atoi(args.argAfterGreetingSilence); 00203 if (!ast_strlen_zero(args.argTotalAnalysisTime)) 00204 totalAnalysisTime = atoi(args.argTotalAnalysisTime); 00205 if (!ast_strlen_zero(args.argMinimumWordLength)) 00206 minimumWordLength = atoi(args.argMinimumWordLength); 00207 if (!ast_strlen_zero(args.argBetweenWordsSilence)) 00208 betweenWordsSilence = atoi(args.argBetweenWordsSilence); 00209 if (!ast_strlen_zero(args.argMaximumNumberOfWords)) 00210 maximumNumberOfWords = atoi(args.argMaximumNumberOfWords); 00211 if (!ast_strlen_zero(args.argSilenceThreshold)) 00212 silenceThreshold = atoi(args.argSilenceThreshold); 00213 if (!ast_strlen_zero(args.argMaximumWordLength)) 00214 maximumWordLength = atoi(args.argMaximumWordLength); 00215 } else { 00216 ast_debug(1, "AMD using the default parameters.\n"); 00217 } 00218 00219 /* Find lowest ms value, that will be max wait time for a frame */ 00220 if (maxWaitTimeForFrame > initialSilence) 00221 maxWaitTimeForFrame = initialSilence; 00222 if (maxWaitTimeForFrame > greeting) 00223 maxWaitTimeForFrame = greeting; 00224 if (maxWaitTimeForFrame > afterGreetingSilence) 00225 maxWaitTimeForFrame = afterGreetingSilence; 00226 if (maxWaitTimeForFrame > totalAnalysisTime) 00227 maxWaitTimeForFrame = totalAnalysisTime; 00228 if (maxWaitTimeForFrame > minimumWordLength) 00229 maxWaitTimeForFrame = minimumWordLength; 00230 if (maxWaitTimeForFrame > betweenWordsSilence) 00231 maxWaitTimeForFrame = betweenWordsSilence; 00232 00233 /* Now we're ready to roll! */ 00234 ast_verb(3, "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] " 00235 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d] \n", 00236 initialSilence, greeting, afterGreetingSilence, totalAnalysisTime, 00237 minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold, maximumWordLength); 00238 00239 /* Set read format to signed linear so we get signed linear frames in */ 00240 readFormat = chan->readformat; 00241 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0 ) { 00242 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", chan->name ); 00243 pbx_builtin_setvar_helper(chan , "AMDSTATUS", ""); 00244 pbx_builtin_setvar_helper(chan , "AMDCAUSE", ""); 00245 return; 00246 } 00247 00248 /* Create a new DSP that will detect the silence */ 00249 if (!(silenceDetector = ast_dsp_new())) { 00250 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", chan->name ); 00251 pbx_builtin_setvar_helper(chan , "AMDSTATUS", ""); 00252 pbx_builtin_setvar_helper(chan , "AMDCAUSE", ""); 00253 return; 00254 } 00255 00256 /* Set silence threshold to specified value */ 00257 ast_dsp_set_threshold(silenceDetector, silenceThreshold); 00258 00259 /* Now we go into a loop waiting for frames from the channel */ 00260 while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) { 00261 00262 /* If we fail to read in a frame, that means they hung up */ 00263 if (!(f = ast_read(chan))) { 00264 ast_verb(3, "AMD: Channel [%s]. HANGUP\n", chan->name); 00265 ast_debug(1, "Got hangup\n"); 00266 strcpy(amdStatus, "HANGUP"); 00267 break; 00268 } 00269 00270 if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_NULL || f->frametype == AST_FRAME_CNG) { 00271 /* If the total time exceeds the analysis time then give up as we are not too sure */ 00272 if (f->frametype == AST_FRAME_VOICE) 00273 framelength = (ast_codec_get_samples(f) / DEFAULT_SAMPLES_PER_MS); 00274 else 00275 framelength += 2 * maxWaitTimeForFrame; 00276 00277 iTotalTime += framelength; 00278 if (iTotalTime >= totalAnalysisTime) { 00279 ast_verb(3, "AMD: Channel [%s]. Too long...\n", chan->name ); 00280 ast_frfree(f); 00281 strcpy(amdStatus , "NOTSURE"); 00282 sprintf(amdCause , "TOOLONG-%d", iTotalTime); 00283 break; 00284 } 00285 00286 /* Feed the frame of audio into the silence detector and see if we get a result */ 00287 if (f->frametype != AST_FRAME_VOICE) 00288 dspsilence += 2 * maxWaitTimeForFrame; 00289 else { 00290 dspsilence = 0; 00291 ast_dsp_silence(silenceDetector, f, &dspsilence); 00292 } 00293 00294 if (dspsilence > 0) { 00295 silenceDuration = dspsilence; 00296 00297 if (silenceDuration >= betweenWordsSilence) { 00298 if (currentState != STATE_IN_SILENCE ) { 00299 previousState = currentState; 00300 ast_verb(3, "AMD: Channel [%s]. Changed state to STATE_IN_SILENCE\n", chan->name); 00301 } 00302 /* Find words less than word duration */ 00303 if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){ 00304 ast_verb(3, "AMD: Channel [%s]. Short Word Duration: %d\n", chan->name, consecutiveVoiceDuration); 00305 } 00306 currentState = STATE_IN_SILENCE; 00307 consecutiveVoiceDuration = 0; 00308 } 00309 00310 if (inInitialSilence == 1 && silenceDuration >= initialSilence) { 00311 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n", 00312 chan->name, silenceDuration, initialSilence); 00313 ast_frfree(f); 00314 strcpy(amdStatus , "MACHINE"); 00315 sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence); 00316 res = 1; 00317 break; 00318 } 00319 00320 if (silenceDuration >= afterGreetingSilence && inGreeting == 1) { 00321 ast_verb(3, "AMD: Channel [%s]. HUMAN: silenceDuration:%d afterGreetingSilence:%d\n", 00322 chan->name, silenceDuration, afterGreetingSilence); 00323 ast_frfree(f); 00324 strcpy(amdStatus , "HUMAN"); 00325 sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence); 00326 res = 1; 00327 break; 00328 } 00329 00330 } else { 00331 consecutiveVoiceDuration += framelength; 00332 voiceDuration += framelength; 00333 00334 /* If I have enough consecutive voice to say that I am in a Word, I can only increment the 00335 number of words if my previous state was Silence, which means that I moved into a word. */ 00336 if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) { 00337 iWordsCount++; 00338 ast_verb(3, "AMD: Channel [%s]. Word detected. iWordsCount:%d\n", chan->name, iWordsCount); 00339 previousState = currentState; 00340 currentState = STATE_IN_WORD; 00341 } 00342 if (consecutiveVoiceDuration >= maximumWordLength){ 00343 ast_verb(3, "AMD: Channel [%s]. Maximum Word Length detected. [%d]\n", chan->name, consecutiveVoiceDuration); 00344 ast_frfree(f); 00345 strcpy(amdStatus , "MACHINE"); 00346 sprintf(amdCause , "MAXWORDLENGTH-%d", consecutiveVoiceDuration); 00347 break; 00348 } 00349 if (iWordsCount >= maximumNumberOfWords) { 00350 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: iWordsCount:%d\n", chan->name, iWordsCount); 00351 ast_frfree(f); 00352 strcpy(amdStatus , "MACHINE"); 00353 sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords); 00354 res = 1; 00355 break; 00356 } 00357 00358 if (inGreeting == 1 && voiceDuration >= greeting) { 00359 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", chan->name, voiceDuration, greeting); 00360 ast_frfree(f); 00361 strcpy(amdStatus , "MACHINE"); 00362 sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting); 00363 res = 1; 00364 break; 00365 } 00366 00367 if (voiceDuration >= minimumWordLength ) { 00368 if (silenceDuration > 0) 00369 ast_verb(3, "AMD: Channel [%s]. Detected Talk, previous silence duration: %d\n", chan->name, silenceDuration); 00370 silenceDuration = 0; 00371 } 00372 if (consecutiveVoiceDuration >= minimumWordLength && inGreeting == 0) { 00373 /* Only go in here once to change the greeting flag when we detect the 1st word */ 00374 if (silenceDuration > 0) 00375 ast_verb(3, "AMD: Channel [%s]. Before Greeting Time: silenceDuration: %d voiceDuration: %d\n", chan->name, silenceDuration, voiceDuration); 00376 inInitialSilence = 0; 00377 inGreeting = 1; 00378 } 00379 00380 } 00381 } 00382 ast_frfree(f); 00383 } 00384 00385 if (!res) { 00386 /* It took too long to get a frame back. Giving up. */ 00387 ast_verb(3, "AMD: Channel [%s]. Too long...\n", chan->name); 00388 strcpy(amdStatus , "NOTSURE"); 00389 sprintf(amdCause , "TOOLONG-%d", iTotalTime); 00390 } 00391 00392 /* Set the status and cause on the channel */ 00393 pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus); 00394 pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause); 00395 00396 /* Restore channel read format */ 00397 if (readFormat && ast_set_read_format(chan, readFormat)) 00398 ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", chan->name); 00399 00400 /* Free the DSP used to detect silence */ 00401 ast_dsp_free(silenceDetector); 00402 00403 return; 00404 }
| static int load_config | ( | int | reload | ) | [static] |
Definition at line 414 of file app_amd.c.
References ast_category_browse(), ast_config_destroy(), ast_config_load, ast_dsp_get_threshold_from_settings(), ast_log(), ast_variable_browse(), ast_verb, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, ast_variable::lineno, LOG_ERROR, LOG_WARNING, ast_variable::name, ast_variable::next, THRESHOLD_SILENCE, ast_variable::value, and var.
00415 { 00416 struct ast_config *cfg = NULL; 00417 char *cat = NULL; 00418 struct ast_variable *var = NULL; 00419 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 00420 00421 dfltSilenceThreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE); 00422 00423 if (!(cfg = ast_config_load("amd.conf", config_flags))) { 00424 ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n"); 00425 return -1; 00426 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { 00427 return 0; 00428 } else if (cfg == CONFIG_STATUS_FILEINVALID) { 00429 ast_log(LOG_ERROR, "Config file amd.conf is in an invalid format. Aborting.\n"); 00430 return -1; 00431 } 00432 00433 cat = ast_category_browse(cfg, NULL); 00434 00435 while (cat) { 00436 if (!strcasecmp(cat, "general") ) { 00437 var = ast_variable_browse(cfg, cat); 00438 while (var) { 00439 if (!strcasecmp(var->name, "initial_silence")) { 00440 dfltInitialSilence = atoi(var->value); 00441 } else if (!strcasecmp(var->name, "greeting")) { 00442 dfltGreeting = atoi(var->value); 00443 } else if (!strcasecmp(var->name, "after_greeting_silence")) { 00444 dfltAfterGreetingSilence = atoi(var->value); 00445 } else if (!strcasecmp(var->name, "silence_threshold")) { 00446 dfltSilenceThreshold = atoi(var->value); 00447 } else if (!strcasecmp(var->name, "total_analysis_time")) { 00448 dfltTotalAnalysisTime = atoi(var->value); 00449 } else if (!strcasecmp(var->name, "min_word_length")) { 00450 dfltMinimumWordLength = atoi(var->value); 00451 } else if (!strcasecmp(var->name, "between_words_silence")) { 00452 dfltBetweenWordsSilence = atoi(var->value); 00453 } else if (!strcasecmp(var->name, "maximum_number_of_words")) { 00454 dfltMaximumNumberOfWords = atoi(var->value); 00455 } else if (!strcasecmp(var->name, "maximum_word_length")) { 00456 dfltMaximumWordLength = atoi(var->value); 00457 00458 } else { 00459 ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n", 00460 app, cat, var->name, var->lineno); 00461 } 00462 var = var->next; 00463 } 00464 } 00465 cat = ast_category_browse(cfg, cat); 00466 } 00467 00468 ast_config_destroy(cfg); 00469 00470 ast_verb(3, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] " 00471 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n", 00472 dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime, 00473 dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength); 00474 00475 return 0; 00476 }
| static int load_module | ( | void | ) | [static] |
Definition at line 483 of file app_amd.c.
References amd_exec(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_SUCCESS, ast_register_application_xml, and load_config().
00484 { 00485 if (load_config(0)) 00486 return AST_MODULE_LOAD_DECLINE; 00487 if (ast_register_application_xml(app, amd_exec)) 00488 return AST_MODULE_LOAD_FAILURE; 00489 return AST_MODULE_LOAD_SUCCESS; 00490 }
| static int reload | ( | void | ) | [static] |
Definition at line 492 of file app_amd.c.
References AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, and load_config().
00493 { 00494 if (load_config(1)) 00495 return AST_MODULE_LOAD_DECLINE; 00496 return AST_MODULE_LOAD_SUCCESS; 00497 }
| static int unload_module | ( | void | ) | [static] |
Definition at line 478 of file app_amd.c.
References ast_unregister_application().
00479 { 00480 return ast_unregister_application(app); 00481 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Answering Machine Detection Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, } [static] |
struct ast_module_info* ast_module_info = &__mod_info [static] |
int dfltAfterGreetingSilence = 800 [static] |
int dfltBetweenWordsSilence = 50 [static] |
int dfltGreeting = 1500 [static] |
int dfltInitialSilence = 2500 [static] |
int dfltMaximumNumberOfWords = 3 [static] |
int dfltMaximumWordLength = 5000 [static] |
int dfltMaxWaitTimeForFrame = 50 [static] |
int dfltMinimumWordLength = 100 [static] |
int dfltSilenceThreshold = 256 [static] |
int dfltTotalAnalysisTime = 5000 [static] |
1.5.6