149static const char app[] =
"AMD";
151#define STATE_IN_WORD 1
152#define STATE_IN_SILENCE 2
174 int audioFrameCount = 0;
177 struct timeval amd_tvstart;
178 int dspsilence = 0, framelength = 0;
180 int inInitialSilence = 1;
182 int voiceDuration = 0;
183 int silenceDuration = 0;
187 int consecutiveVoiceDuration = 0;
188 char amdCause[256] =
"", amdStatus[256] =
"";
205 const char *audioFile =
NULL;
236 initialSilence = atoi(
args.argInitialSilence);
238 greeting = atoi(
args.argGreeting);
240 afterGreetingSilence = atoi(
args.argAfterGreetingSilence);
242 totalAnalysisTime = atoi(
args.argTotalAnalysisTime);
244 minimumWordLength = atoi(
args.argMinimumWordLength);
246 betweenWordsSilence = atoi(
args.argBetweenWordsSilence);
248 maximumNumberOfWords = atoi(
args.argMaximumNumberOfWords);
250 silenceThreshold = atoi(
args.argSilenceThreshold);
252 maximumWordLength = atoi(
args.argMaximumWordLength);
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);
309 while ((res =
ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
314 ms = 2 * maxWaitTimeForFrame - res;
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);
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",
356 iTotalTime, framelength, totalAnalysisTime);
359 if (iTotalTime >= totalAnalysisTime) {
362 strcpy(amdStatus ,
"NOTSURE");
363 sprintf(amdCause ,
"TOOLONG-%d", iTotalTime);
369 dspsilence += framelength;
375 if (dspsilence > 0) {
376 silenceDuration = dspsilence;
378 if (silenceDuration >= betweenWordsSilence) {
383 if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){
387 consecutiveVoiceDuration = 0;
390 if (inInitialSilence == 1 && silenceDuration >= initialSilence) {
391 ast_verb(3,
"AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
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",
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) {
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) {
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);
475 strcpy(amdStatus ,
"NOTSURE");
476 sprintf(amdCause ,
"TOOLONG-%d", iTotalTime);
520 ast_log(
LOG_ERROR,
"Config file amd.conf is in an invalid format. Aborting.\n");
527 if (!strcasecmp(cat,
"general") ) {
530 if (!strcasecmp(
var->name,
"initial_silence")) {
532 }
else if (!strcasecmp(
var->name,
"greeting")) {
534 }
else if (!strcasecmp(
var->name,
"after_greeting_silence")) {
536 }
else if (!strcasecmp(
var->name,
"silence_threshold")) {
538 }
else if (!strcasecmp(
var->name,
"total_analysis_time")) {
540 }
else if (!strcasecmp(
var->name,
"min_word_length")) {
542 }
else if (!strcasecmp(
var->name,
"between_words_silence")) {
544 }
else if (!strcasecmp(
var->name,
"maximum_number_of_words")) {
546 }
else if (!strcasecmp(
var->name,
"maximum_word_length")) {
548 }
else if (!strcasecmp(
var->name,
"playback_file")) {
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",
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 int load_config(int reload)
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.
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)
@ CONFIG_FLAG_FILEUNCHANGED
#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.