Sat Aug 6 00:39:33 2011

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"

Go to the source code of this file.

Defines

#define STATE_IN_SILENCE   2
#define STATE_IN_WORD   1

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int amd_exec (struct ast_channel *chan, void *data)
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 struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "Answering Machine Detection Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, .reload = reload, }
static char * app = "AMD"
static const struct ast_module_infoast_module_info = &__mod_info
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 dfltMaxWaitTimeForFrame = 50
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 void __reg_module ( void   )  [static]

Definition at line 432 of file app_amd.c.

static void __unreg_module ( void   )  [static]

Definition at line 432 of file app_amd.c.

static int amd_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 345 of file app_amd.c.

References ast_module_user_add, ast_module_user_remove, ast_module_user::chan, and isAnsweringMachine().

Referenced by load_module().

00346 {
00347    struct ast_module_user *u = NULL;
00348 
00349    u = ast_module_user_add(chan);
00350    isAnsweringMachine(chan, data);
00351    ast_module_user_remove(u);
00352 
00353    return 0;
00354 }

static void isAnsweringMachine ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 92 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_CNG, AST_FRAME_NULL, 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, ast_channel::name, 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().

00093 {
00094    int res = 0;
00095    struct ast_frame *f = NULL;
00096    struct ast_dsp *silenceDetector = NULL;
00097    int dspsilence = 0, readFormat, framelength = 0;
00098    int inInitialSilence = 1;
00099    int inGreeting = 0;
00100    int voiceDuration = 0;
00101    int silenceDuration = 0;
00102    int iTotalTime = 0;
00103    int iWordsCount = 0;
00104    int currentState = STATE_IN_WORD;
00105    int previousState = STATE_IN_SILENCE;
00106    int consecutiveVoiceDuration = 0;
00107    char amdCause[256] = "", amdStatus[256] = "";
00108    char *parse = ast_strdupa(data);
00109 
00110    /* Lets set the initial values of the variables that will control the algorithm.
00111       The initial values are the default ones. If they are passed as arguments
00112       when invoking the application, then the default values will be overwritten
00113       by the ones passed as parameters. */
00114    int initialSilence       = dfltInitialSilence;
00115    int greeting             = dfltGreeting;
00116    int afterGreetingSilence = dfltAfterGreetingSilence;
00117    int totalAnalysisTime    = dfltTotalAnalysisTime;
00118    int minimumWordLength    = dfltMinimumWordLength;
00119    int betweenWordsSilence  = dfltBetweenWordsSilence;
00120    int maximumNumberOfWords = dfltMaximumNumberOfWords;
00121    int silenceThreshold     = dfltSilenceThreshold;
00122    int maxWaitTimeForFrame  = dfltMaxWaitTimeForFrame;
00123 
00124    AST_DECLARE_APP_ARGS(args,
00125               AST_APP_ARG(argInitialSilence);
00126               AST_APP_ARG(argGreeting);
00127               AST_APP_ARG(argAfterGreetingSilence);
00128               AST_APP_ARG(argTotalAnalysisTime);
00129               AST_APP_ARG(argMinimumWordLength);
00130               AST_APP_ARG(argBetweenWordsSilence);
00131               AST_APP_ARG(argMaximumNumberOfWords);
00132               AST_APP_ARG(argSilenceThreshold);
00133    );
00134 
00135    if (option_verbose > 2)
00136       ast_verbose(VERBOSE_PREFIX_3 "AMD: %s %s %s (Fmt: %d)\n", chan->name ,chan->cid.cid_ani, chan->cid.cid_rdnis, chan->readformat);
00137 
00138    /* Lets parse the arguments. */
00139    if (!ast_strlen_zero(parse)) {
00140       /* Some arguments have been passed. Lets parse them and overwrite the defaults. */
00141       AST_STANDARD_APP_ARGS(args, parse);
00142       if (!ast_strlen_zero(args.argInitialSilence))
00143          initialSilence = atoi(args.argInitialSilence);
00144       if (!ast_strlen_zero(args.argGreeting))
00145          greeting = atoi(args.argGreeting);
00146       if (!ast_strlen_zero(args.argAfterGreetingSilence))
00147          afterGreetingSilence = atoi(args.argAfterGreetingSilence);
00148       if (!ast_strlen_zero(args.argTotalAnalysisTime))
00149          totalAnalysisTime = atoi(args.argTotalAnalysisTime);
00150       if (!ast_strlen_zero(args.argMinimumWordLength))
00151          minimumWordLength = atoi(args.argMinimumWordLength);
00152       if (!ast_strlen_zero(args.argBetweenWordsSilence))
00153          betweenWordsSilence = atoi(args.argBetweenWordsSilence);
00154       if (!ast_strlen_zero(args.argMaximumNumberOfWords))
00155          maximumNumberOfWords = atoi(args.argMaximumNumberOfWords);
00156       if (!ast_strlen_zero(args.argSilenceThreshold))
00157          silenceThreshold = atoi(args.argSilenceThreshold);
00158    } else if (option_debug)
00159       ast_log(LOG_DEBUG, "AMD using the default parameters.\n");
00160 
00161    /* Find lowest ms value, that will be max wait time for a frame */
00162    if (maxWaitTimeForFrame > initialSilence)
00163       maxWaitTimeForFrame = initialSilence;
00164    if (maxWaitTimeForFrame > greeting)
00165       maxWaitTimeForFrame = greeting;
00166    if (maxWaitTimeForFrame > afterGreetingSilence)
00167       maxWaitTimeForFrame = afterGreetingSilence;
00168    if (maxWaitTimeForFrame > totalAnalysisTime)
00169       maxWaitTimeForFrame = totalAnalysisTime;
00170    if (maxWaitTimeForFrame > minimumWordLength)
00171       maxWaitTimeForFrame = minimumWordLength;
00172    if (maxWaitTimeForFrame > betweenWordsSilence)
00173       maxWaitTimeForFrame = betweenWordsSilence;
00174 
00175    /* Now we're ready to roll! */
00176    if (option_verbose > 2)
00177       ast_verbose(VERBOSE_PREFIX_3 "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
00178       "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] \n",
00179             initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
00180             minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold );
00181 
00182    /* Set read format to signed linear so we get signed linear frames in */
00183    readFormat = chan->readformat;
00184    if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0 ) {
00185       ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", chan->name );
00186       pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
00187       pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
00188       return;
00189    }
00190 
00191    /* Create a new DSP that will detect the silence */
00192    if (!(silenceDetector = ast_dsp_new())) {
00193       ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", chan->name );
00194       pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
00195       pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
00196       return;
00197    }
00198 
00199    /* Set silence threshold to specified value */
00200    ast_dsp_set_threshold(silenceDetector, silenceThreshold);
00201 
00202    /* Now we go into a loop waiting for frames from the channel */
00203    while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
00204 
00205       /* If we fail to read in a frame, that means they hung up */
00206       if (!(f = ast_read(chan))) {
00207          if (option_verbose > 2)
00208             ast_verbose(VERBOSE_PREFIX_3 "AMD: HANGUP\n");
00209          if (option_debug)
00210             ast_log(LOG_DEBUG, "Got hangup\n");
00211          strcpy(amdStatus, "HANGUP");
00212          res = 1;
00213          break;
00214       }
00215 
00216       if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_NULL || f->frametype == AST_FRAME_CNG) {
00217          /* If the total time exceeds the analysis time then give up as we are not too sure */
00218          if (f->frametype == AST_FRAME_VOICE) {
00219             framelength = (ast_codec_get_samples(f) / DEFAULT_SAMPLES_PER_MS);
00220          } else {
00221             framelength = 2 * maxWaitTimeForFrame;
00222          }
00223 
00224          iTotalTime += framelength;
00225          if (iTotalTime >= totalAnalysisTime) {
00226             if (option_verbose > 2) 
00227                ast_verbose(VERBOSE_PREFIX_3 "AMD: Channel [%s]. Too long...\n", chan->name );
00228             ast_frfree(f);
00229             strcpy(amdStatus , "NOTSURE");
00230             sprintf(amdCause , "TOOLONG-%d", iTotalTime);
00231             break;
00232          }
00233 
00234          /* Feed the frame of audio into the silence detector and see if we get a result */
00235          if (f->frametype != AST_FRAME_VOICE)
00236             dspsilence += 2 * maxWaitTimeForFrame;
00237          else {
00238             dspsilence = 0;
00239             ast_dsp_silence(silenceDetector, f, &dspsilence);
00240          }
00241 
00242          if (dspsilence > 0) {
00243             silenceDuration = dspsilence;
00244             
00245             if (silenceDuration >= betweenWordsSilence) {
00246                if (currentState != STATE_IN_SILENCE ) {
00247                   previousState = currentState;
00248                   if (option_verbose > 2)
00249                      ast_verbose(VERBOSE_PREFIX_3 "AMD: Changed state to STATE_IN_SILENCE\n");
00250                }
00251                currentState  = STATE_IN_SILENCE;
00252                consecutiveVoiceDuration = 0;
00253             }
00254             
00255             if (inInitialSilence == 1  && silenceDuration >= initialSilence) {
00256                if (option_verbose > 2)
00257                   ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
00258                          silenceDuration, initialSilence);
00259                ast_frfree(f);
00260                strcpy(amdStatus , "MACHINE");
00261                sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
00262                res = 1;
00263                break;
00264             }
00265             
00266             if (silenceDuration >= afterGreetingSilence  &&  inGreeting == 1) {
00267                if (option_verbose > 2)
00268                   ast_verbose(VERBOSE_PREFIX_3 "AMD: HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
00269                          silenceDuration, afterGreetingSilence);
00270                ast_frfree(f);
00271                strcpy(amdStatus , "HUMAN");
00272                sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
00273                res = 1;
00274                break;
00275             }
00276             
00277          } else {
00278             consecutiveVoiceDuration += framelength;
00279             voiceDuration += framelength;
00280             
00281             /* If I have enough consecutive voice to say that I am in a Word, I can only increment the
00282                number of words if my previous state was Silence, which means that I moved into a word. */
00283             if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
00284                iWordsCount++;
00285                if (option_verbose > 2)
00286                   ast_verbose(VERBOSE_PREFIX_3 "AMD: Word detected. iWordsCount:%d\n", iWordsCount);
00287                previousState = currentState;
00288                currentState = STATE_IN_WORD;
00289             }
00290             
00291             if (iWordsCount >= maximumNumberOfWords) {
00292                if (option_verbose > 2)
00293                   ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: iWordsCount:%d\n", iWordsCount);
00294                ast_frfree(f);
00295                strcpy(amdStatus , "MACHINE");
00296                sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
00297                res = 1;
00298                break;
00299             }
00300             
00301             if (inGreeting == 1 && voiceDuration >= greeting) {
00302                if (option_verbose > 2)
00303                   ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", voiceDuration, greeting);
00304                ast_frfree(f);
00305                strcpy(amdStatus , "MACHINE");
00306                sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
00307                res = 1;
00308                break;
00309             }
00310             
00311             if (voiceDuration >= minimumWordLength ) {
00312                silenceDuration = 0;
00313                inInitialSilence = 0;
00314                inGreeting = 1;
00315             }
00316             
00317          }
00318       }
00319       ast_frfree(f);
00320    }
00321    
00322    if (!res) {
00323       /* It took too long to get a frame back. Giving up. */
00324       if (option_verbose > 2)
00325          ast_verbose(VERBOSE_PREFIX_3 "AMD: Channel [%s]. Too long...\n", chan->name);
00326       strcpy(amdStatus , "NOTSURE");
00327       sprintf(amdCause , "TOOLONG-%d", iTotalTime);
00328    }
00329 
00330    /* Set the status and cause on the channel */
00331    pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus);
00332    pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause);
00333 
00334    /* Restore channel read format */
00335    if (readFormat && ast_set_read_format(chan, readFormat))
00336       ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", chan->name);
00337 
00338    /* Free the DSP used to detect silence */
00339    ast_dsp_free(silenceDetector);
00340 
00341    return;
00342 }

static void load_config ( void   )  [static]

Definition at line 356 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.

00357 {
00358    struct ast_config *cfg = NULL;
00359    char *cat = NULL;
00360    struct ast_variable *var = NULL;
00361 
00362    if (!(cfg = ast_config_load("amd.conf"))) {
00363       ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
00364       return;
00365    }
00366 
00367    cat = ast_category_browse(cfg, NULL);
00368 
00369    while (cat) {
00370       if (!strcasecmp(cat, "general") ) {
00371          var = ast_variable_browse(cfg, cat);
00372          while (var) {
00373             if (!strcasecmp(var->name, "initial_silence")) {
00374                dfltInitialSilence = atoi(var->value);
00375             } else if (!strcasecmp(var->name, "greeting")) {
00376                dfltGreeting = atoi(var->value);
00377             } else if (!strcasecmp(var->name, "after_greeting_silence")) {
00378                dfltAfterGreetingSilence = atoi(var->value);
00379             } else if (!strcasecmp(var->name, "silence_threshold")) {
00380                dfltSilenceThreshold = atoi(var->value);
00381             } else if (!strcasecmp(var->name, "total_analysis_time")) {
00382                dfltTotalAnalysisTime = atoi(var->value);
00383             } else if (!strcasecmp(var->name, "min_word_length")) {
00384                dfltMinimumWordLength = atoi(var->value);
00385             } else if (!strcasecmp(var->name, "between_words_silence")) {
00386                dfltBetweenWordsSilence = atoi(var->value);
00387             } else if (!strcasecmp(var->name, "maximum_number_of_words")) {
00388                dfltMaximumNumberOfWords = atoi(var->value);
00389             } else {
00390                ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
00391                   app, cat, var->name, var->lineno);
00392             }
00393             var = var->next;
00394          }
00395       }
00396       cat = ast_category_browse(cfg, cat);
00397    }
00398 
00399    ast_config_destroy(cfg);
00400 
00401    if (option_verbose > 2)
00402       ast_verbose(VERBOSE_PREFIX_3 "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
00403       "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] \n",
00404             dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
00405             dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold );
00406 
00407    return;
00408 }

static int load_module ( void   )  [static]

Definition at line 416 of file app_amd.c.

References amd_exec(), ast_register_application(), and load_config().

00417 {
00418    load_config();
00419    return ast_register_application(app, amd_exec, synopsis, descrip);
00420 }

static int reload ( void   )  [static]

Definition at line 422 of file app_amd.c.

References load_config().

00423 {
00424    load_config();
00425    return 0;
00426 }

static int unload_module ( void   )  [static]

Definition at line 410 of file app_amd.c.

References ast_module_user_hangup_all, and ast_unregister_application().

00411 {
00412    ast_module_user_hangup_all();
00413    return ast_unregister_application(app);
00414 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "Answering Machine Detection Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 432 of file app_amd.c.

char* app = "AMD" [static]

Definition at line 39 of file app_amd.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 432 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 dfltMaxWaitTimeForFrame = 50 [static]

Definition at line 90 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 Sat Aug 6 00:39:33 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7