#include "asterisk.h"
#include <stdio.h>
#include <stdlib.h>
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/options.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 int | amd_exec (struct ast_channel *chan, void *data) |
AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,"Answering Machine Detection Application",.load=load_module,.unload=unload_module,.reload=reload,) | |
static void | isAnsweringMachine (struct ast_channel *chan, void *data) |
static void | load_config (void) |
static int | load_module (void) |
static int | reload (void) |
static int | unload_module (void) |
Variables | |
static char * | app = "AMD" |
static char * | descrip |
static int | dfltAfterGreetingSilence = 800 |
static int | dfltBetweenWordsSilence = 50 |
static int | dfltGreeting = 1500 |
static int | dfltInitialSilence = 2500 |
static int | dfltMaximumNumberOfWords = 3 |
static int | dfltMaxWaitTimeForFrame = 50 |
static int | dfltMinimumWordLength = 100 |
static int | dfltSilenceThreshold = 256 |
static int | dfltTotalAnalysisTime = 5000 |
static char * | synopsis = "Attempts to detect answering machines" |
#define STATE_IN_SILENCE 2 |
#define STATE_IN_WORD 1 |
static int amd_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 343 of file app_amd.c.
References ast_module_user_add, ast_module_user_remove, and isAnsweringMachine().
Referenced by load_module().
00344 { 00345 struct ast_module_user *u = NULL; 00346 00347 u = ast_module_user_add(chan); 00348 isAnsweringMachine(chan, data); 00349 ast_module_user_remove(u); 00350 00351 return 0; 00352 }
AST_MODULE_INFO | ( | ASTERISK_GPL_KEY | , | |
AST_MODFLAG_DEFAULT | , | |||
"Answering Machine Detection Application" | , | |||
. | load = load_module , |
|||
. | unload = unload_module , |
|||
. | reload = reload | |||
) |
static void isAnsweringMachine | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 92 of file app_amd.c.
References AST_APP_ARG, ast_codec_get_samples(), 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_verbose(), ast_waitfor(), ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_rdnis, DEFAULT_SAMPLES_PER_MS, f, ast_frame::frametype, LOG_DEBUG, LOG_WARNING, option_debug, option_verbose, parse(), pbx_builtin_setvar_helper(), ast_channel::readformat, STATE_IN_SILENCE, STATE_IN_WORD, and VERBOSE_PREFIX_3.
Referenced by amd_exec().
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 /* Lets set the initial values of the variables that will control the algorithm. 00111 The initial values are the default ones. If they are passed as arguments 00112 when invoking the application, then the default values will be overwritten 00113 by the ones passed as parameters. */ 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 /* Lets parse the arguments. */ 00139 if (!ast_strlen_zero(parse)) { 00140 /* Some arguments have been passed. Lets parse them and overwrite the defaults. */ 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 /* Find lowest ms value, that will be max wait time for a frame */ 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 /* Now we're ready to roll! */ 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 /* Set read format to signed linear so we get signed linear frames in */ 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 /* Create a new DSP that will detect the silence */ 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 /* Set silence threshold to specified value */ 00200 ast_dsp_set_threshold(silenceDetector, silenceThreshold); 00201 00202 /* Now we go into a loop waiting for frames from the channel */ 00203 while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) { 00204 00205 /* If we fail to read in a frame, that means they hung up */ 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 break; 00213 } 00214 00215 if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_NULL || f->frametype == AST_FRAME_CNG) { 00216 /* If the total time exceeds the analysis time then give up as we are not too sure */ 00217 if (f->frametype == AST_FRAME_VOICE) 00218 framelength = (ast_codec_get_samples(f) / DEFAULT_SAMPLES_PER_MS); 00219 else 00220 framelength += 2 * maxWaitTimeForFrame; 00221 00222 iTotalTime += framelength; 00223 if (iTotalTime >= totalAnalysisTime) { 00224 if (option_verbose > 2) 00225 ast_verbose(VERBOSE_PREFIX_3 "AMD: Channel [%s]. Too long...\n", chan->name ); 00226 ast_frfree(f); 00227 strcpy(amdStatus , "NOTSURE"); 00228 sprintf(amdCause , "TOOLONG-%d", iTotalTime); 00229 break; 00230 } 00231 00232 /* Feed the frame of audio into the silence detector and see if we get a result */ 00233 if (f->frametype != AST_FRAME_VOICE) 00234 dspsilence += 2 * maxWaitTimeForFrame; 00235 else { 00236 dspsilence = 0; 00237 ast_dsp_silence(silenceDetector, f, &dspsilence); 00238 } 00239 00240 if (dspsilence > 0) { 00241 silenceDuration = dspsilence; 00242 00243 if (silenceDuration >= betweenWordsSilence) { 00244 if (currentState != STATE_IN_SILENCE ) { 00245 previousState = currentState; 00246 if (option_verbose > 2) 00247 ast_verbose(VERBOSE_PREFIX_3 "AMD: Changed state to STATE_IN_SILENCE\n"); 00248 } 00249 currentState = STATE_IN_SILENCE; 00250 consecutiveVoiceDuration = 0; 00251 } 00252 00253 if (inInitialSilence == 1 && silenceDuration >= initialSilence) { 00254 if (option_verbose > 2) 00255 ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n", 00256 silenceDuration, initialSilence); 00257 ast_frfree(f); 00258 strcpy(amdStatus , "MACHINE"); 00259 sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence); 00260 res = 1; 00261 break; 00262 } 00263 00264 if (silenceDuration >= afterGreetingSilence && inGreeting == 1) { 00265 if (option_verbose > 2) 00266 ast_verbose(VERBOSE_PREFIX_3 "AMD: HUMAN: silenceDuration:%d afterGreetingSilence:%d\n", 00267 silenceDuration, afterGreetingSilence); 00268 ast_frfree(f); 00269 strcpy(amdStatus , "HUMAN"); 00270 sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence); 00271 res = 1; 00272 break; 00273 } 00274 00275 } else { 00276 consecutiveVoiceDuration += framelength; 00277 voiceDuration += framelength; 00278 00279 /* If I have enough consecutive voice to say that I am in a Word, I can only increment the 00280 number of words if my previous state was Silence, which means that I moved into a word. */ 00281 if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) { 00282 iWordsCount++; 00283 if (option_verbose > 2) 00284 ast_verbose(VERBOSE_PREFIX_3 "AMD: Word detected. iWordsCount:%d\n", iWordsCount); 00285 previousState = currentState; 00286 currentState = STATE_IN_WORD; 00287 } 00288 00289 if (iWordsCount >= maximumNumberOfWords) { 00290 if (option_verbose > 2) 00291 ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: iWordsCount:%d\n", iWordsCount); 00292 ast_frfree(f); 00293 strcpy(amdStatus , "MACHINE"); 00294 sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords); 00295 res = 1; 00296 break; 00297 } 00298 00299 if (inGreeting == 1 && voiceDuration >= greeting) { 00300 if (option_verbose > 2) 00301 ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", voiceDuration, greeting); 00302 ast_frfree(f); 00303 strcpy(amdStatus , "MACHINE"); 00304 sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting); 00305 res = 1; 00306 break; 00307 } 00308 00309 if (voiceDuration >= minimumWordLength ) { 00310 silenceDuration = 0; 00311 inInitialSilence = 0; 00312 inGreeting = 1; 00313 } 00314 00315 } 00316 } 00317 ast_frfree(f); 00318 } 00319 00320 if (!res) { 00321 /* It took too long to get a frame back. Giving up. */ 00322 if (option_verbose > 2) 00323 ast_verbose(VERBOSE_PREFIX_3 "AMD: Channel [%s]. Too long...\n", chan->name); 00324 strcpy(amdStatus , "NOTSURE"); 00325 sprintf(amdCause , "TOOLONG-%d", iTotalTime); 00326 } 00327 00328 /* Set the status and cause on the channel */ 00329 pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus); 00330 pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause); 00331 00332 /* Restore channel read format */ 00333 if (readFormat && ast_set_read_format(chan, readFormat)) 00334 ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", chan->name); 00335 00336 /* Free the DSP used to detect silence */ 00337 ast_dsp_free(silenceDetector); 00338 00339 return; 00340 }
static void load_config | ( | void | ) | [static] |
Definition at line 354 of file app_amd.c.
References ast_category_browse(), ast_config_destroy(), ast_config_load(), ast_log(), ast_variable_browse(), ast_verbose(), ast_variable::lineno, LOG_ERROR, LOG_WARNING, ast_variable::name, ast_variable::next, option_verbose, ast_variable::value, var, and VERBOSE_PREFIX_3.
00355 { 00356 struct ast_config *cfg = NULL; 00357 char *cat = NULL; 00358 struct ast_variable *var = NULL; 00359 00360 if (!(cfg = ast_config_load("amd.conf"))) { 00361 ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n"); 00362 return; 00363 } 00364 00365 cat = ast_category_browse(cfg, NULL); 00366 00367 while (cat) { 00368 if (!strcasecmp(cat, "general") ) { 00369 var = ast_variable_browse(cfg, cat); 00370 while (var) { 00371 if (!strcasecmp(var->name, "initial_silence")) { 00372 dfltInitialSilence = atoi(var->value); 00373 } else if (!strcasecmp(var->name, "greeting")) { 00374 dfltGreeting = atoi(var->value); 00375 } else if (!strcasecmp(var->name, "after_greeting_silence")) { 00376 dfltAfterGreetingSilence = atoi(var->value); 00377 } else if (!strcasecmp(var->name, "silence_threshold")) { 00378 dfltSilenceThreshold = atoi(var->value); 00379 } else if (!strcasecmp(var->name, "total_analysis_time")) { 00380 dfltTotalAnalysisTime = atoi(var->value); 00381 } else if (!strcasecmp(var->name, "min_word_length")) { 00382 dfltMinimumWordLength = atoi(var->value); 00383 } else if (!strcasecmp(var->name, "between_words_silence")) { 00384 dfltBetweenWordsSilence = atoi(var->value); 00385 } else if (!strcasecmp(var->name, "maximum_number_of_words")) { 00386 dfltMaximumNumberOfWords = atoi(var->value); 00387 } else { 00388 ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n", 00389 app, cat, var->name, var->lineno); 00390 } 00391 var = var->next; 00392 } 00393 } 00394 cat = ast_category_browse(cfg, cat); 00395 } 00396 00397 ast_config_destroy(cfg); 00398 00399 if (option_verbose > 2) 00400 ast_verbose(VERBOSE_PREFIX_3 "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] " 00401 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] \n", 00402 dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime, 00403 dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold ); 00404 00405 return; 00406 }
static int load_module | ( | void | ) | [static] |
Definition at line 414 of file app_amd.c.
References amd_exec(), ast_register_application(), and load_config().
00415 { 00416 load_config(); 00417 return ast_register_application(app, amd_exec, synopsis, descrip); 00418 }
static int reload | ( | void | ) | [static] |
Definition at line 420 of file app_amd.c.
References load_config().
00421 { 00422 load_config(); 00423 return 0; 00424 }
static int unload_module | ( | void | ) | [static] |
Definition at line 408 of file app_amd.c.
References ast_module_user_hangup_all, and ast_unregister_application().
00409 { 00410 ast_module_user_hangup_all(); 00411 return ast_unregister_application(app); 00412 }
int dfltAfterGreetingSilence = 800 [static] |
int dfltBetweenWordsSilence = 50 [static] |
int dfltGreeting = 1500 [static] |
int dfltInitialSilence = 2500 [static] |
int dfltMaximumNumberOfWords = 3 [static] |
int dfltMaxWaitTimeForFrame = 50 [static] |
int dfltMinimumWordLength = 100 [static] |
int dfltSilenceThreshold = 256 [static] |
int dfltTotalAnalysisTime = 5000 [static] |
char* synopsis = "Attempts to detect answering machines" [static] |