159static const char app[] =
"AMD";
161#define STATE_IN_WORD 1
162#define STATE_IN_SILENCE 2
184 int audioFrameCount = 0;
187 struct timeval amd_tvstart;
188 int dspsilence = 0, framelength = 0;
190 int inInitialSilence = 1;
192 int voiceDuration = 0;
193 int silenceDuration = 0;
197 int consecutiveVoiceDuration = 0;
198 char amdCause[256] =
"", amdStatus[256] =
"";
215 const char *audioFile =
NULL;
246 initialSilence = atoi(
args.argInitialSilence);
248 greeting = atoi(
args.argGreeting);
250 afterGreetingSilence = atoi(
args.argAfterGreetingSilence);
252 totalAnalysisTime = atoi(
args.argTotalAnalysisTime);
254 minimumWordLength = atoi(
args.argMinimumWordLength);
256 betweenWordsSilence = atoi(
args.argBetweenWordsSilence);
258 maximumNumberOfWords = atoi(
args.argMaximumNumberOfWords);
260 silenceThreshold = atoi(
args.argSilenceThreshold);
262 maximumWordLength = atoi(
args.argMaximumWordLength);
264 audioFile =
args.audioFile;
267 ast_debug(1,
"AMD using the default parameters.\n");
271 if (maxWaitTimeForFrame > initialSilence)
272 maxWaitTimeForFrame = initialSilence;
273 if (maxWaitTimeForFrame > greeting)
274 maxWaitTimeForFrame = greeting;
275 if (maxWaitTimeForFrame > afterGreetingSilence)
276 maxWaitTimeForFrame = afterGreetingSilence;
277 if (maxWaitTimeForFrame > totalAnalysisTime)
278 maxWaitTimeForFrame = totalAnalysisTime;
279 if (maxWaitTimeForFrame > minimumWordLength)
280 maxWaitTimeForFrame = minimumWordLength;
281 if (maxWaitTimeForFrame > betweenWordsSilence)
282 maxWaitTimeForFrame = betweenWordsSilence;
285 ast_verb(3,
"AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
286 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d] \n",
287 initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
288 minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold, maximumWordLength);
319 while ((res =
ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
324 ms = 2 * maxWaitTimeForFrame - res;
331 strcpy(amdStatus,
"HANGUP");
339 strcpy(amdStatus ,
"NOTSURE");
340 if ( audioFrameCount == 0 ) {
341 ast_verb(3,
"AMD: Channel [%s]. No audio data received in [%d] seconds.\n",
ast_channel_name(chan), totalAnalysisTime);
342 sprintf(amdCause ,
"NOAUDIODATA-%d", iTotalTime);
346 sprintf(amdCause ,
"TOOLONG-%d", iTotalTime);
361 iTotalTime += framelength;
363 ast_debug(1,
"AMD: Channel [%s] frametype [%s] iTotalTime [%d] framelength [%d] totalAnalysisTime [%d]\n",
366 iTotalTime, framelength, totalAnalysisTime);
369 if (iTotalTime >= totalAnalysisTime) {
372 strcpy(amdStatus ,
"NOTSURE");
373 sprintf(amdCause ,
"TOOLONG-%d", iTotalTime);
379 dspsilence += framelength;
385 if (dspsilence > 0) {
386 silenceDuration = dspsilence;
388 if (silenceDuration >= betweenWordsSilence) {
393 if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){
397 consecutiveVoiceDuration = 0;
400 if (inInitialSilence == 1 && silenceDuration >= initialSilence) {
401 ast_verb(3,
"AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
404 strcpy(amdStatus ,
"MACHINE");
405 sprintf(amdCause ,
"INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
410 if (silenceDuration >= afterGreetingSilence && inGreeting == 1) {
411 ast_verb(3,
"AMD: Channel [%s]. HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
414 strcpy(amdStatus ,
"HUMAN");
415 sprintf(amdCause ,
"HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
421 consecutiveVoiceDuration += framelength;
422 voiceDuration += framelength;
426 if (consecutiveVoiceDuration >= minimumWordLength && currentState ==
STATE_IN_SILENCE) {
431 if (consecutiveVoiceDuration >= maximumWordLength){
432 ast_verb(3,
"AMD: Channel [%s]. Maximum Word Length detected. [%d]\n",
ast_channel_name(chan), consecutiveVoiceDuration);
434 strcpy(amdStatus ,
"MACHINE");
435 sprintf(amdCause ,
"MAXWORDLENGTH-%d", consecutiveVoiceDuration);
438 if (iWordsCount > maximumNumberOfWords) {
441 strcpy(amdStatus ,
"MACHINE");
442 sprintf(amdCause ,
"MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
447 if (inGreeting == 1 && voiceDuration >= greeting) {
448 ast_verb(3,
"AMD: Channel [%s]. ANSWERING MACHINE: voiceDuration:%d greeting:%d\n",
ast_channel_name(chan), voiceDuration, greeting);
450 strcpy(amdStatus ,
"MACHINE");
451 sprintf(amdCause ,
"LONGGREETING-%d-%d", voiceDuration, greeting);
456 if (voiceDuration >= minimumWordLength ) {
457 if (silenceDuration > 0)
458 ast_verb(3,
"AMD: Channel [%s]. Detected Talk, previous silence duration: %d\n",
ast_channel_name(chan), silenceDuration);
461 if (consecutiveVoiceDuration >= minimumWordLength && inGreeting == 0) {
463 if (silenceDuration > 0)
464 ast_verb(3,
"AMD: Channel [%s]. Before Greeting Time: silenceDuration: %d voiceDuration: %d\n",
ast_channel_name(chan), silenceDuration, voiceDuration);
465 inInitialSilence = 0;
472 if (iTotalTime >= totalAnalysisTime) {
474 strcpy(amdStatus ,
"NOTSURE");
475 sprintf(amdCause ,
"TOOLONG-%d", iTotalTime);
485 strcpy(amdStatus ,
"NOTSURE");
486 sprintf(amdCause ,
"TOOLONG-%d", iTotalTime);
530 ast_log(
LOG_ERROR,
"Config file amd.conf is in an invalid format. Aborting.\n");
537 if (!strcasecmp(cat,
"general") ) {
540 if (!strcasecmp(
var->name,
"initial_silence")) {
542 }
else if (!strcasecmp(
var->name,
"greeting")) {
544 }
else if (!strcasecmp(
var->name,
"after_greeting_silence")) {
546 }
else if (!strcasecmp(
var->name,
"silence_threshold")) {
548 }
else if (!strcasecmp(
var->name,
"total_analysis_time")) {
550 }
else if (!strcasecmp(
var->name,
"min_word_length")) {
552 }
else if (!strcasecmp(
var->name,
"between_words_silence")) {
554 }
else if (!strcasecmp(
var->name,
"maximum_number_of_words")) {
556 }
else if (!strcasecmp(
var->name,
"maximum_word_length")) {
558 }
else if (!strcasecmp(
var->name,
"playback_file")) {
580 ast_verb(5,
"AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
581 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
static int load_config(void)
static int dfltBetweenWordsSilence
static int amd_exec(struct ast_channel *chan, const char *data)
static int dfltInitialSilence
static int dfltMaximumNumberOfWords
static ast_mutex_t config_lock
static int dfltMinimumWordLength
static int dfltMaximumWordLength
static int load_module(void)
Load the module.
static int dfltTotalAnalysisTime
static char * dfltAudioFile
static int unload_module(void)
static void isAnsweringMachine(struct ast_channel *chan, const char *data)
static int dfltAfterGreetingSilence
static int dfltMaxWaitTimeForFrame
static int dfltSilenceThreshold
Asterisk main include file. File version handling, generic pbx functions.
#define DEFAULT_SAMPLES_PER_MS
#define ast_strdup(str)
A wrapper for strdup()
#define ast_strdupa(s)
duplicate a string in memory from the stack
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
struct ast_party_redirecting * ast_channel_redirecting(struct ast_channel *chan)
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
const char * ast_channel_language(const struct ast_channel *chan)
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
struct ast_format * ast_channel_readformat(struct ast_channel *chan)
unsigned int ast_codec_samples_count(struct ast_frame *frame)
Get the number of samples contained within a frame.
Convenient Signal Processing routines.
void ast_dsp_set_threshold(struct ast_dsp *dsp, int threshold)
Set the minimum average magnitude threshold to determine talking by the DSP.
void ast_dsp_free(struct ast_dsp *dsp)
int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence)
Process the audio frame for silence.
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
struct ast_dsp * ast_dsp_new(void)
Allocates a new dsp, assumes 8khz for internal sample rate.
int ast_stopstream(struct ast_channel *c)
Stops a stream.
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
@ CONFIG_FLAG_FILEUNCHANGED
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
#define ast_debug(level,...)
Log a DEBUG message.
#define ast_verb(level,...)
Asterisk locking-related definitions:
#define ast_mutex_init(pmutex)
#define ast_mutex_unlock(a)
#define ast_mutex_destroy(a)
#define ast_mutex_lock(a)
Asterisk module definitions.
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
@ AST_MODULE_SUPPORT_EXTENDED
#define ASTERISK_GPL_KEY
The text the key() function should return.
int ast_unregister_application(const char *app)
Unregister an application.
@ AST_MODULE_LOAD_SUCCESS
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Core PBX routines and definitions.
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.
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Main Channel structure associated with a channel.
Structure used to handle boolean flags.
Data structure associated with a single frame of data.
enum ast_frame_type frametype
Structure for mutex and tracking information.
Structure for variables, used for configurations and for channel variables.
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.