Fri Aug 24 02:22:26 2007

Asterisk developer's documentation


app_amd.c File Reference

#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"

Include dependency graph for app_amd.c:

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 Documentation

#define STATE_IN_SILENCE   2

Definition at line 77 of file app_amd.c.

Referenced by isAnsweringMachine().

#define STATE_IN_WORD   1

Definition at line 76 of file app_amd.c.

Referenced by isAnsweringMachine().


Function Documentation

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, ast_module_user::chan, 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, 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(), LOG_ERROR, LOG_WARNING, option_verbose, 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 }


Variable Documentation

char* app = "AMD" [static]

Definition at line 39 of file app_amd.c.

char* descrip [static]

Definition at line 41 of file app_amd.c.

int dfltAfterGreetingSilence = 800 [static]

Definition at line 82 of file app_amd.c.

int dfltBetweenWordsSilence = 50 [static]

Definition at line 85 of file app_amd.c.

int dfltGreeting = 1500 [static]

Definition at line 81 of file app_amd.c.

int dfltInitialSilence = 2500 [static]

Definition at line 80 of file app_amd.c.

int dfltMaximumNumberOfWords = 3 [static]

Definition at line 86 of file app_amd.c.

int dfltMinimumWordLength = 100 [static]

Definition at line 84 of file app_amd.c.

int dfltSilenceThreshold = 256 [static]

Definition at line 87 of file app_amd.c.

int dfltTotalAnalysisTime = 5000 [static]

Definition at line 83 of file app_amd.c.

char* synopsis = "Attempts to detect answering machines" [static]

Definition at line 40 of file app_amd.c.


Generated on Fri Aug 24 02:22:27 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1