#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 | 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 311 of file app_amd.c.
References ast_module_user_add, ast_module_user_remove, and isAnsweringMachine().
Referenced by load_module().
00312 { 00313 struct ast_module_user *u = NULL; 00314 00315 u = ast_module_user_add(chan); 00316 isAnsweringMachine(chan, data); 00317 ast_module_user_remove(u); 00318 00319 return 0; 00320 }
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 89 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_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().
00090 { 00091 int res = 0; 00092 struct ast_frame *f = NULL; 00093 struct ast_dsp *silenceDetector = NULL; 00094 int dspsilence = 0, readFormat, framelength; 00095 int inInitialSilence = 1; 00096 int inGreeting = 0; 00097 int voiceDuration = 0; 00098 int silenceDuration = 0; 00099 int iTotalTime = 0; 00100 int iWordsCount = 0; 00101 int currentState = STATE_IN_SILENCE; 00102 int previousState = STATE_IN_SILENCE; 00103 int consecutiveVoiceDuration = 0; 00104 char amdCause[256] = "", amdStatus[256] = ""; 00105 char *parse = ast_strdupa(data); 00106 00107 /* Lets set the initial values of the variables that will control the algorithm. 00108 The initial values are the default ones. If they are passed as arguments 00109 when invoking the application, then the default values will be overwritten 00110 by the ones passed as parameters. */ 00111 int initialSilence = dfltInitialSilence; 00112 int greeting = dfltGreeting; 00113 int afterGreetingSilence = dfltAfterGreetingSilence; 00114 int totalAnalysisTime = dfltTotalAnalysisTime; 00115 int minimumWordLength = dfltMinimumWordLength; 00116 int betweenWordsSilence = dfltBetweenWordsSilence; 00117 int maximumNumberOfWords = dfltMaximumNumberOfWords; 00118 int silenceThreshold = dfltSilenceThreshold; 00119 00120 AST_DECLARE_APP_ARGS(args, 00121 AST_APP_ARG(argInitialSilence); 00122 AST_APP_ARG(argGreeting); 00123 AST_APP_ARG(argAfterGreetingSilence); 00124 AST_APP_ARG(argTotalAnalysisTime); 00125 AST_APP_ARG(argMinimumWordLength); 00126 AST_APP_ARG(argBetweenWordsSilence); 00127 AST_APP_ARG(argMaximumNumberOfWords); 00128 AST_APP_ARG(argSilenceThreshold); 00129 ); 00130 00131 if (option_verbose > 2) 00132 ast_verbose(VERBOSE_PREFIX_3 "AMD: %s %s %s (Fmt: %d)\n", chan->name ,chan->cid.cid_ani, chan->cid.cid_rdnis, chan->readformat); 00133 00134 /* Lets parse the arguments. */ 00135 if (!ast_strlen_zero(parse)) { 00136 /* Some arguments have been passed. Lets parse them and overwrite the defaults. */ 00137 AST_STANDARD_APP_ARGS(args, parse); 00138 if (!ast_strlen_zero(args.argInitialSilence)) 00139 initialSilence = atoi(args.argInitialSilence); 00140 if (!ast_strlen_zero(args.argGreeting)) 00141 greeting = atoi(args.argGreeting); 00142 if (!ast_strlen_zero(args.argAfterGreetingSilence)) 00143 afterGreetingSilence = atoi(args.argAfterGreetingSilence); 00144 if (!ast_strlen_zero(args.argTotalAnalysisTime)) 00145 totalAnalysisTime = atoi(args.argTotalAnalysisTime); 00146 if (!ast_strlen_zero(args.argMinimumWordLength)) 00147 minimumWordLength = atoi(args.argMinimumWordLength); 00148 if (!ast_strlen_zero(args.argBetweenWordsSilence)) 00149 betweenWordsSilence = atoi(args.argBetweenWordsSilence); 00150 if (!ast_strlen_zero(args.argMaximumNumberOfWords)) 00151 maximumNumberOfWords = atoi(args.argMaximumNumberOfWords); 00152 if (!ast_strlen_zero(args.argSilenceThreshold)) 00153 silenceThreshold = atoi(args.argSilenceThreshold); 00154 } else if (option_debug) 00155 ast_log(LOG_DEBUG, "AMD using the default parameters.\n"); 00156 00157 /* Now we're ready to roll! */ 00158 if (option_verbose > 2) 00159 ast_verbose(VERBOSE_PREFIX_3 "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] " 00160 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] \n", 00161 initialSilence, greeting, afterGreetingSilence, totalAnalysisTime, 00162 minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold ); 00163 00164 /* Set read format to signed linear so we get signed linear frames in */ 00165 readFormat = chan->readformat; 00166 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0 ) { 00167 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", chan->name ); 00168 pbx_builtin_setvar_helper(chan , "AMDSTATUS", ""); 00169 pbx_builtin_setvar_helper(chan , "AMDCAUSE", ""); 00170 return; 00171 } 00172 00173 /* Create a new DSP that will detect the silence */ 00174 if (!(silenceDetector = ast_dsp_new())) { 00175 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", chan->name ); 00176 pbx_builtin_setvar_helper(chan , "AMDSTATUS", ""); 00177 pbx_builtin_setvar_helper(chan , "AMDCAUSE", ""); 00178 return; 00179 } 00180 00181 /* Set silence threshold to specified value */ 00182 ast_dsp_set_threshold(silenceDetector, silenceThreshold); 00183 00184 /* Now we go into a loop waiting for frames from the channel */ 00185 while ((res = ast_waitfor(chan, totalAnalysisTime)) > -1) { 00186 /* If we fail to read in a frame, that means they hung up */ 00187 if (!(f = ast_read(chan))) { 00188 if (option_verbose > 2) 00189 ast_verbose(VERBOSE_PREFIX_3 "AMD: HANGUP\n"); 00190 if (option_debug) 00191 ast_log(LOG_DEBUG, "Got hangup\n"); 00192 strcpy(amdStatus, "HANGUP"); 00193 break; 00194 } 00195 00196 if (f->frametype == AST_FRAME_VOICE) { 00197 /* If the total time exceeds the analysis time then give up as we are not too sure */ 00198 framelength = (ast_codec_get_samples(f) / DEFAULT_SAMPLES_PER_MS); 00199 iTotalTime += framelength; 00200 if (iTotalTime >= totalAnalysisTime) { 00201 if (option_verbose > 2) 00202 ast_verbose(VERBOSE_PREFIX_3 "AMD: Channel [%s]. Too long...\n", chan->name ); 00203 ast_frfree(f); 00204 strcpy(amdStatus , "NOTSURE"); 00205 sprintf(amdCause , "TOOLONG-%d", iTotalTime); 00206 break; 00207 } 00208 00209 /* Feed the frame of audio into the silence detector and see if we get a result */ 00210 dspsilence = 0; 00211 ast_dsp_silence(silenceDetector, f, &dspsilence); 00212 if (dspsilence) { 00213 silenceDuration = dspsilence; 00214 00215 if (silenceDuration >= betweenWordsSilence) { 00216 if (currentState != STATE_IN_SILENCE ) { 00217 previousState = currentState; 00218 if (option_verbose > 2) 00219 ast_verbose(VERBOSE_PREFIX_3 "AMD: Changed state to STATE_IN_SILENCE\n"); 00220 } 00221 currentState = STATE_IN_SILENCE; 00222 consecutiveVoiceDuration = 0; 00223 } 00224 00225 if (inInitialSilence == 1 && silenceDuration >= initialSilence) { 00226 if (option_verbose > 2) 00227 ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n", 00228 silenceDuration, initialSilence); 00229 ast_frfree(f); 00230 strcpy(amdStatus , "MACHINE"); 00231 sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence); 00232 break; 00233 } 00234 00235 if (silenceDuration >= afterGreetingSilence && inGreeting == 1) { 00236 if (option_verbose > 2) 00237 ast_verbose(VERBOSE_PREFIX_3 "AMD: HUMAN: silenceDuration:%d afterGreetingSilence:%d\n", 00238 silenceDuration, afterGreetingSilence); 00239 ast_frfree(f); 00240 strcpy(amdStatus , "HUMAN"); 00241 sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence); 00242 break; 00243 } 00244 00245 } else { 00246 consecutiveVoiceDuration += framelength; 00247 voiceDuration += framelength; 00248 00249 /* If I have enough consecutive voice to say that I am in a Word, I can only increment the 00250 number of words if my previous state was Silence, which means that I moved into a word. */ 00251 if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) { 00252 iWordsCount++; 00253 if (option_verbose > 2) 00254 ast_verbose(VERBOSE_PREFIX_3 "AMD: Word detected. iWordsCount:%d\n", iWordsCount); 00255 previousState = currentState; 00256 currentState = STATE_IN_WORD; 00257 } 00258 00259 if (iWordsCount >= maximumNumberOfWords) { 00260 if (option_verbose > 2) 00261 ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: iWordsCount:%d\n", iWordsCount); 00262 ast_frfree(f); 00263 strcpy(amdStatus , "MACHINE"); 00264 sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords); 00265 break; 00266 } 00267 00268 if (inGreeting == 1 && voiceDuration >= greeting) { 00269 if (option_verbose > 2) 00270 ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", voiceDuration, greeting); 00271 ast_frfree(f); 00272 strcpy(amdStatus , "MACHINE"); 00273 sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting); 00274 break; 00275 } 00276 00277 if (voiceDuration >= minimumWordLength ) { 00278 silenceDuration = 0; 00279 inInitialSilence = 0; 00280 inGreeting = 1; 00281 } 00282 00283 } 00284 } 00285 ast_frfree(f); 00286 } 00287 00288 if (!res) { 00289 /* It took too long to get a frame back. Giving up. */ 00290 if (option_verbose > 2) 00291 ast_verbose(VERBOSE_PREFIX_3 "AMD: Channel [%s]. Too long...\n", chan->name); 00292 strcpy(amdStatus , "NOTSURE"); 00293 sprintf(amdCause , "TOOLONG-%d", iTotalTime); 00294 } 00295 00296 /* Set the status and cause on the channel */ 00297 pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus); 00298 pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause); 00299 00300 /* Restore channel read format */ 00301 if (readFormat && ast_set_read_format(chan, readFormat)) 00302 ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", chan->name); 00303 00304 /* Free the DSP used to detect silence */ 00305 ast_dsp_free(silenceDetector); 00306 00307 return; 00308 }
static void load_config | ( | void | ) | [static] |
Definition at line 322 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.
00323 { 00324 struct ast_config *cfg = NULL; 00325 char *cat = NULL; 00326 struct ast_variable *var = NULL; 00327 00328 if (!(cfg = ast_config_load("amd.conf"))) { 00329 ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n"); 00330 return; 00331 } 00332 00333 cat = ast_category_browse(cfg, NULL); 00334 00335 while (cat) { 00336 if (!strcasecmp(cat, "general") ) { 00337 var = ast_variable_browse(cfg, cat); 00338 while (var) { 00339 if (!strcasecmp(var->name, "initial_silence")) { 00340 dfltInitialSilence = atoi(var->value); 00341 } else if (!strcasecmp(var->name, "greeting")) { 00342 dfltGreeting = atoi(var->value); 00343 } else if (!strcasecmp(var->name, "after_greeting_silence")) { 00344 dfltAfterGreetingSilence = atoi(var->value); 00345 } else if (!strcasecmp(var->name, "silence_threshold")) { 00346 dfltSilenceThreshold = atoi(var->value); 00347 } else if (!strcasecmp(var->name, "total_analysis_time")) { 00348 dfltTotalAnalysisTime = atoi(var->value); 00349 } else if (!strcasecmp(var->name, "min_word_length")) { 00350 dfltMinimumWordLength = atoi(var->value); 00351 } else if (!strcasecmp(var->name, "between_words_silence")) { 00352 dfltBetweenWordsSilence = atoi(var->value); 00353 } else if (!strcasecmp(var->name, "maximum_number_of_words")) { 00354 dfltMaximumNumberOfWords = atoi(var->value); 00355 } else { 00356 ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n", 00357 app, cat, var->name, var->lineno); 00358 } 00359 var = var->next; 00360 } 00361 } 00362 cat = ast_category_browse(cfg, cat); 00363 } 00364 00365 ast_config_destroy(cfg); 00366 00367 if (option_verbose > 2) 00368 ast_verbose(VERBOSE_PREFIX_3 "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] " 00369 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] \n", 00370 dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime, 00371 dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold ); 00372 00373 return; 00374 }
static int load_module | ( | void | ) | [static] |
Definition at line 382 of file app_amd.c.
References amd_exec(), ast_register_application(), and load_config().
00383 { 00384 load_config(); 00385 return ast_register_application(app, amd_exec, synopsis, descrip); 00386 }
static int reload | ( | void | ) | [static] |
Definition at line 388 of file app_amd.c.
References load_config().
00389 { 00390 load_config(); 00391 return 0; 00392 }
static int unload_module | ( | void | ) | [static] |
Definition at line 376 of file app_amd.c.
References ast_module_user_hangup_all, and ast_unregister_application().
00377 { 00378 ast_module_user_hangup_all(); 00379 return ast_unregister_application(app); 00380 }
int dfltAfterGreetingSilence = 800 [static] |
int dfltBetweenWordsSilence = 50 [static] |
int dfltGreeting = 1500 [static] |
int dfltInitialSilence = 2500 [static] |
int dfltMaximumNumberOfWords = 3 [static] |
int dfltMinimumWordLength = 100 [static] |
int dfltSilenceThreshold = 256 [static] |
int dfltTotalAnalysisTime = 5000 [static] |
char* synopsis = "Attempts to detect answering machines" [static] |