149 static const char app[] =
"AMD";
151 #define STATE_IN_WORD 1
152 #define STATE_IN_SILENCE 2
155 static int dfltInitialSilence = 2500;
156 static int dfltGreeting = 1500;
157 static int dfltAfterGreetingSilence = 800;
158 static int dfltTotalAnalysisTime = 5000;
159 static int dfltMinimumWordLength = 100;
160 static int dfltBetweenWordsSilence = 50;
161 static int dfltMaximumNumberOfWords = 2;
162 static int dfltSilenceThreshold = 256;
163 static int dfltMaximumWordLength = 5000;
164 static char *dfltAudioFile = NULL;
169 static int dfltMaxWaitTimeForFrame = 50;
171 static void isAnsweringMachine(
struct ast_channel *chan,
const char *data)
174 int audioFrameCount = 0;
176 struct ast_dsp *silenceDetector = NULL;
177 struct timeval amd_tvstart;
178 int dspsilence = 0, framelength = 0;
180 int inInitialSilence = 1;
182 int voiceDuration = 0;
183 int silenceDuration = 0;
186 int currentState = STATE_IN_WORD;
187 int consecutiveVoiceDuration = 0;
188 char amdCause[256] =
"", amdStatus[256] =
"";
195 int initialSilence = dfltInitialSilence;
196 int greeting = dfltGreeting;
197 int afterGreetingSilence = dfltAfterGreetingSilence;
198 int totalAnalysisTime = dfltTotalAnalysisTime;
199 int minimumWordLength = dfltMinimumWordLength;
200 int betweenWordsSilence = dfltBetweenWordsSilence;
201 int maximumNumberOfWords = dfltMaximumNumberOfWords;
202 int silenceThreshold = dfltSilenceThreshold;
203 int maximumWordLength = dfltMaximumWordLength;
204 int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame;
205 const char *audioFile = NULL;
220 ast_mutex_lock(&config_lock);
221 if (!ast_strlen_zero(dfltAudioFile)) {
224 ast_mutex_unlock(&config_lock);
226 ast_verb(3,
"AMD: %s %s %s (Fmt: %s)\n", ast_channel_name(chan),
227 S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str,
"(N/A)"),
228 S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str,
"(N/A)"),
232 if (!ast_strlen_zero(parse)) {
235 if (!ast_strlen_zero(args.argInitialSilence))
236 initialSilence = atoi(args.argInitialSilence);
237 if (!ast_strlen_zero(args.argGreeting))
238 greeting = atoi(args.argGreeting);
239 if (!ast_strlen_zero(args.argAfterGreetingSilence))
240 afterGreetingSilence = atoi(args.argAfterGreetingSilence);
241 if (!ast_strlen_zero(args.argTotalAnalysisTime))
242 totalAnalysisTime = atoi(args.argTotalAnalysisTime);
243 if (!ast_strlen_zero(args.argMinimumWordLength))
244 minimumWordLength = atoi(args.argMinimumWordLength);
245 if (!ast_strlen_zero(args.argBetweenWordsSilence))
246 betweenWordsSilence = atoi(args.argBetweenWordsSilence);
247 if (!ast_strlen_zero(args.argMaximumNumberOfWords))
248 maximumNumberOfWords = atoi(args.argMaximumNumberOfWords);
249 if (!ast_strlen_zero(args.argSilenceThreshold))
250 silenceThreshold = atoi(args.argSilenceThreshold);
251 if (!ast_strlen_zero(args.argMaximumWordLength))
252 maximumWordLength = atoi(args.argMaximumWordLength);
253 if (!ast_strlen_zero(args.audioFile)) {
254 audioFile = args.audioFile;
257 ast_debug(1,
"AMD using the default parameters.\n");
261 if (maxWaitTimeForFrame > initialSilence)
262 maxWaitTimeForFrame = initialSilence;
263 if (maxWaitTimeForFrame > greeting)
264 maxWaitTimeForFrame = greeting;
265 if (maxWaitTimeForFrame > afterGreetingSilence)
266 maxWaitTimeForFrame = afterGreetingSilence;
267 if (maxWaitTimeForFrame > totalAnalysisTime)
268 maxWaitTimeForFrame = totalAnalysisTime;
269 if (maxWaitTimeForFrame > minimumWordLength)
270 maxWaitTimeForFrame = minimumWordLength;
271 if (maxWaitTimeForFrame > betweenWordsSilence)
272 maxWaitTimeForFrame = betweenWordsSilence;
275 ast_verb(3,
"AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
276 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d] \n",
277 initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
278 minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold, maximumWordLength);
281 readFormat =
ao2_bump(ast_channel_readformat(chan));
283 ast_log(LOG_WARNING,
"AMD: Channel [%s]. Unable to set to linear mode, giving up\n", ast_channel_name(chan));
291 ast_log(LOG_WARNING,
"AMD: Channel [%s]. Unable to create silence detector :(\n", ast_channel_name(chan));
304 if (!ast_strlen_zero(audioFile)) {
309 while ((res =
ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
314 ms = 2 * maxWaitTimeForFrame - res;
319 ast_verb(3,
"AMD: Channel [%s]. HANGUP\n", ast_channel_name(chan));
321 strcpy(amdStatus,
"HANGUP");
329 strcpy(amdStatus ,
"NOTSURE");
330 if ( audioFrameCount == 0 ) {
331 ast_verb(3,
"AMD: Channel [%s]. No audio data received in [%d] seconds.\n", ast_channel_name(chan), totalAnalysisTime);
332 sprintf(amdCause ,
"NOAUDIODATA-%d", iTotalTime);
335 ast_verb(3,
"AMD: Channel [%s]. Timeout...\n", ast_channel_name(chan));
336 sprintf(amdCause ,
"TOOLONG-%d", iTotalTime);
351 iTotalTime += framelength;
353 ast_debug(1,
"AMD: Channel [%s] frametype [%s] iTotalTime [%d] framelength [%d] totalAnalysisTime [%d]\n",
354 ast_channel_name(chan),
356 iTotalTime, framelength, totalAnalysisTime);
359 if (iTotalTime >= totalAnalysisTime) {
360 ast_verb(3,
"AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
362 strcpy(amdStatus ,
"NOTSURE");
363 sprintf(amdCause ,
"TOOLONG-%d", iTotalTime);
369 dspsilence += framelength;
375 if (dspsilence > 0) {
376 silenceDuration = dspsilence;
378 if (silenceDuration >= betweenWordsSilence) {
379 if (currentState != STATE_IN_SILENCE ) {
380 ast_verb(3,
"AMD: Channel [%s]. Changed state to STATE_IN_SILENCE\n", ast_channel_name(chan));
383 if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){
384 ast_verb(3,
"AMD: Channel [%s]. Short Word Duration: %d\n", ast_channel_name(chan), consecutiveVoiceDuration);
386 currentState = STATE_IN_SILENCE;
387 consecutiveVoiceDuration = 0;
390 if (inInitialSilence == 1 && silenceDuration >= initialSilence) {
391 ast_verb(3,
"AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
392 ast_channel_name(chan), silenceDuration, initialSilence);
394 strcpy(amdStatus ,
"MACHINE");
395 sprintf(amdCause ,
"INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
400 if (silenceDuration >= afterGreetingSilence && inGreeting == 1) {
401 ast_verb(3,
"AMD: Channel [%s]. HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
402 ast_channel_name(chan), silenceDuration, afterGreetingSilence);
404 strcpy(amdStatus ,
"HUMAN");
405 sprintf(amdCause ,
"HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
411 consecutiveVoiceDuration += framelength;
412 voiceDuration += framelength;
416 if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
418 ast_verb(3,
"AMD: Channel [%s]. Word detected. iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
419 currentState = STATE_IN_WORD;
421 if (consecutiveVoiceDuration >= maximumWordLength){
422 ast_verb(3,
"AMD: Channel [%s]. Maximum Word Length detected. [%d]\n", ast_channel_name(chan), consecutiveVoiceDuration);
424 strcpy(amdStatus ,
"MACHINE");
425 sprintf(amdCause ,
"MAXWORDLENGTH-%d", consecutiveVoiceDuration);
428 if (iWordsCount > maximumNumberOfWords) {
429 ast_verb(3,
"AMD: Channel [%s]. ANSWERING MACHINE: iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
431 strcpy(amdStatus ,
"MACHINE");
432 sprintf(amdCause ,
"MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
437 if (inGreeting == 1 && voiceDuration >= greeting) {
438 ast_verb(3,
"AMD: Channel [%s]. ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", ast_channel_name(chan), voiceDuration, greeting);
440 strcpy(amdStatus ,
"MACHINE");
441 sprintf(amdCause ,
"LONGGREETING-%d-%d", voiceDuration, greeting);
446 if (voiceDuration >= minimumWordLength ) {
447 if (silenceDuration > 0)
448 ast_verb(3,
"AMD: Channel [%s]. Detected Talk, previous silence duration: %d\n", ast_channel_name(chan), silenceDuration);
451 if (consecutiveVoiceDuration >= minimumWordLength && inGreeting == 0) {
453 if (silenceDuration > 0)
454 ast_verb(3,
"AMD: Channel [%s]. Before Greeting Time: silenceDuration: %d voiceDuration: %d\n", ast_channel_name(chan), silenceDuration, voiceDuration);
455 inInitialSilence = 0;
462 if (iTotalTime >= totalAnalysisTime) {
464 strcpy(amdStatus ,
"NOTSURE");
465 sprintf(amdCause ,
"TOOLONG-%d", iTotalTime);
474 ast_verb(3,
"AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
475 strcpy(amdStatus ,
"NOTSURE");
476 sprintf(amdCause ,
"TOOLONG-%d", iTotalTime);
485 ast_log(LOG_WARNING,
"AMD: Unable to restore read format on '%s'\n", ast_channel_name(chan));
488 ast_dsp_free(silenceDetector);
491 if (!ast_strlen_zero(audioFile)) {
498 static int amd_exec(
struct ast_channel *chan,
const char *data)
500 isAnsweringMachine(chan, data);
505 static int load_config(
int reload)
515 ast_log(LOG_ERROR,
"Configuration file amd.conf missing.\n");
517 }
else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
519 }
else if (cfg == CONFIG_STATUS_FILEINVALID) {
520 ast_log(LOG_ERROR,
"Config file amd.conf is in an invalid format. Aborting.\n");
527 if (!strcasecmp(cat,
"general") ) {
528 var = ast_variable_browse(cfg, cat);
530 if (!strcasecmp(var->
name,
"initial_silence")) {
531 dfltInitialSilence = atoi(var->
value);
532 }
else if (!strcasecmp(var->
name,
"greeting")) {
533 dfltGreeting = atoi(var->
value);
534 }
else if (!strcasecmp(var->
name,
"after_greeting_silence")) {
535 dfltAfterGreetingSilence = atoi(var->
value);
536 }
else if (!strcasecmp(var->
name,
"silence_threshold")) {
537 dfltSilenceThreshold = atoi(var->
value);
538 }
else if (!strcasecmp(var->
name,
"total_analysis_time")) {
539 dfltTotalAnalysisTime = atoi(var->
value);
540 }
else if (!strcasecmp(var->
name,
"min_word_length")) {
541 dfltMinimumWordLength = atoi(var->
value);
542 }
else if (!strcasecmp(var->
name,
"between_words_silence")) {
543 dfltBetweenWordsSilence = atoi(var->
value);
544 }
else if (!strcasecmp(var->
name,
"maximum_number_of_words")) {
545 dfltMaximumNumberOfWords = atoi(var->
value);
546 }
else if (!strcasecmp(var->
name,
"maximum_word_length")) {
547 dfltMaximumWordLength = atoi(var->
value);
548 }
else if (!strcasecmp(var->
name,
"playback_file")) {
549 ast_mutex_lock(&config_lock);
551 ast_free(dfltAudioFile);
552 dfltAudioFile = NULL;
554 if (!ast_strlen_zero(var->
value)) {
557 ast_mutex_unlock(&config_lock);
559 ast_log(LOG_WARNING,
"%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
560 app, cat, var->
name, var->lineno);
570 ast_verb(5,
"AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
571 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
572 dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
573 dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength);
578 static int unload_module(
void)
580 ast_mutex_lock(&config_lock);
582 ast_free(dfltAudioFile);
584 ast_mutex_unlock(&config_lock);
585 ast_mutex_destroy(&config_lock);
601 ast_mutex_init(&config_lock);
609 static int reload(
void)
616 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,
"Answering Machine Detection Application",
617 .support_level = AST_MODULE_SUPPORT_EXTENDED,
619 .unload = unload_module,
struct ast_variable * next
Main Channel structure associated with a channel.
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
Convenient Signal Processing routines.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
struct ast_dsp * ast_dsp_new(void)
Allocates a new dsp, assumes 8khz for internal sample rate.
Structure for variables, used for configurations and for channel variables.
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
#define ast_strdup(str)
A wrapper for strdup()
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
int ast_unregister_application(const char *app)
Unregister an application.
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
General Asterisk PBX channel definitions.
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
#define ast_strdupa(s)
duplicate a string in memory from the stack
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
unsigned int ast_codec_samples_count(struct ast_frame *frame)
Get the number of samples contained within a frame.
#define ast_debug(level,...)
Log a DEBUG message.
Core PBX routines and definitions.
void ast_dsp_set_threshold(struct ast_dsp *dsp, int threshold)
Set the minimum average magnitude threshold to determine talking by the DSP.
static int load_module(void)
Load the module.
Module has failed to load, may be in an inconsistent state.
Structure used to handle boolean flags.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence)
Process the audio frame for silence.
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Data structure associated with a single frame of data.
enum ast_frame_type frametype
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
#define ASTERISK_GPL_KEY
The text the key() function should return.
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
int ast_stopstream(struct ast_channel *c)
Stops a stream.
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Structure for mutex and tracking information.
#define AST_APP_ARG(name)
Define an application argument.