Asterisk - The Open Source Telephony Project GIT-master-8f1982c
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
Macros | Functions | Variables
app_amd.c File Reference

Answering machine detection. More...

#include "asterisk.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/dsp.h"
#include "asterisk/pbx.h"
#include "asterisk/config.h"
#include "asterisk/app.h"
#include "asterisk/format_cache.h"
Include dependency graph for app_amd.c:

Go to the source code of this file.

Macros

#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, const char *data)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static void isAnsweringMachine (struct ast_channel *chan, const char *data)
 
static int load_config (int reload)
 
static int load_module (void)
 Load the module. More...
 
static int reload (void)
 
static int unload_module (void)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .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 = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .reload = reload, }
 
static const char app [] = "AMD"
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static ast_mutex_t config_lock
 
static int dfltAfterGreetingSilence = 800
 
static char * dfltAudioFile = NULL
 
static int dfltBetweenWordsSilence = 50
 
static int dfltGreeting = 1500
 
static int dfltInitialSilence = 2500
 
static int dfltMaximumNumberOfWords = 2
 
static int dfltMaximumWordLength = 5000
 
static int dfltMaxWaitTimeForFrame = 50
 
static int dfltMinimumWordLength = 100
 
static int dfltSilenceThreshold = 256
 
static int dfltTotalAnalysisTime = 5000
 

Detailed Description

Answering machine detection.

Author
Claude Klimos (claud.nosp@m.e.kl.nosp@m.imos@.nosp@m.ahee.nosp@m.va.co.nosp@m.m)

Definition in file app_amd.c.

Macro Definition Documentation

◆ STATE_IN_SILENCE

#define STATE_IN_SILENCE   2

Definition at line 155 of file app_amd.c.

◆ STATE_IN_WORD

#define STATE_IN_WORD   1

Definition at line 154 of file app_amd.c.

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 624 of file app_amd.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 624 of file app_amd.c.

◆ amd_exec()

static int amd_exec ( struct ast_channel chan,
const char *  data 
)
static

Definition at line 501 of file app_amd.c.

502{
503 isAnsweringMachine(chan, data);
504
505 return 0;
506}
static void isAnsweringMachine(struct ast_channel *chan, const char *data)
Definition: app_amd.c:174

References isAnsweringMachine().

Referenced by load_module().

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 624 of file app_amd.c.

◆ isAnsweringMachine()

static void isAnsweringMachine ( struct ast_channel chan,
const char *  data 
)
static

Definition at line 174 of file app_amd.c.

175{
176 int res = 0;
177 int audioFrameCount = 0;
178 struct ast_frame *f = NULL;
179 struct ast_dsp *silenceDetector = NULL;
180 struct timeval amd_tvstart;
181 int dspsilence = 0, framelength = 0;
182 RAII_VAR(struct ast_format *, readFormat, NULL, ao2_cleanup);
183 int inInitialSilence = 1;
184 int inGreeting = 0;
185 int voiceDuration = 0;
186 int silenceDuration = 0;
187 int iTotalTime = 0;
188 int iWordsCount = 0;
189 int currentState = STATE_IN_WORD;
190 int consecutiveVoiceDuration = 0;
191 char amdCause[256] = "", amdStatus[256] = "";
192 char *parse = ast_strdupa(data);
193
194 /* Let's set the initial values of the variables that will control the algorithm.
195 The initial values are the default ones. If they are passed as arguments
196 when invoking the application, then the default values will be overwritten
197 by the ones passed as parameters. */
198 int initialSilence = dfltInitialSilence;
199 int greeting = dfltGreeting;
200 int afterGreetingSilence = dfltAfterGreetingSilence;
201 int totalAnalysisTime = dfltTotalAnalysisTime;
202 int minimumWordLength = dfltMinimumWordLength;
203 int betweenWordsSilence = dfltBetweenWordsSilence;
204 int maximumNumberOfWords = dfltMaximumNumberOfWords;
205 int silenceThreshold = dfltSilenceThreshold;
206 int maximumWordLength = dfltMaximumWordLength;
207 int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame;
208 const char *audioFile = NULL;
209
211 AST_APP_ARG(argInitialSilence);
212 AST_APP_ARG(argGreeting);
213 AST_APP_ARG(argAfterGreetingSilence);
214 AST_APP_ARG(argTotalAnalysisTime);
215 AST_APP_ARG(argMinimumWordLength);
216 AST_APP_ARG(argBetweenWordsSilence);
217 AST_APP_ARG(argMaximumNumberOfWords);
218 AST_APP_ARG(argSilenceThreshold);
219 AST_APP_ARG(argMaximumWordLength);
220 AST_APP_ARG(audioFile);
221 );
222
225 audioFile = ast_strdupa(dfltAudioFile);
226 }
228
229 ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", ast_channel_name(chan),
230 S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, "(N/A)"),
231 S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, "(N/A)"),
233
234 /* Lets parse the arguments. */
235 if (!ast_strlen_zero(parse)) {
236 /* Some arguments have been passed. Lets parse them and overwrite the defaults. */
238 if (!ast_strlen_zero(args.argInitialSilence))
239 initialSilence = atoi(args.argInitialSilence);
240 if (!ast_strlen_zero(args.argGreeting))
241 greeting = atoi(args.argGreeting);
242 if (!ast_strlen_zero(args.argAfterGreetingSilence))
243 afterGreetingSilence = atoi(args.argAfterGreetingSilence);
244 if (!ast_strlen_zero(args.argTotalAnalysisTime))
245 totalAnalysisTime = atoi(args.argTotalAnalysisTime);
246 if (!ast_strlen_zero(args.argMinimumWordLength))
247 minimumWordLength = atoi(args.argMinimumWordLength);
248 if (!ast_strlen_zero(args.argBetweenWordsSilence))
249 betweenWordsSilence = atoi(args.argBetweenWordsSilence);
250 if (!ast_strlen_zero(args.argMaximumNumberOfWords))
251 maximumNumberOfWords = atoi(args.argMaximumNumberOfWords);
252 if (!ast_strlen_zero(args.argSilenceThreshold))
253 silenceThreshold = atoi(args.argSilenceThreshold);
254 if (!ast_strlen_zero(args.argMaximumWordLength))
255 maximumWordLength = atoi(args.argMaximumWordLength);
256 if (!ast_strlen_zero(args.audioFile)) {
257 audioFile = args.audioFile;
258 }
259 } else {
260 ast_debug(1, "AMD using the default parameters.\n");
261 }
262
263 /* Find lowest ms value, that will be max wait time for a frame */
264 if (maxWaitTimeForFrame > initialSilence)
265 maxWaitTimeForFrame = initialSilence;
266 if (maxWaitTimeForFrame > greeting)
267 maxWaitTimeForFrame = greeting;
268 if (maxWaitTimeForFrame > afterGreetingSilence)
269 maxWaitTimeForFrame = afterGreetingSilence;
270 if (maxWaitTimeForFrame > totalAnalysisTime)
271 maxWaitTimeForFrame = totalAnalysisTime;
272 if (maxWaitTimeForFrame > minimumWordLength)
273 maxWaitTimeForFrame = minimumWordLength;
274 if (maxWaitTimeForFrame > betweenWordsSilence)
275 maxWaitTimeForFrame = betweenWordsSilence;
276
277 /* Now we're ready to roll! */
278 ast_verb(3, "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
279 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d] \n",
280 initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
281 minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold, maximumWordLength);
282
283 /* Set read format to signed linear so we get signed linear frames in */
284 readFormat = ao2_bump(ast_channel_readformat(chan));
285 if (ast_set_read_format(chan, ast_format_slin) < 0 ) {
286 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", ast_channel_name(chan));
287 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
288 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
289 return;
290 }
291
292 /* Create a new DSP that will detect the silence */
293 if (!(silenceDetector = ast_dsp_new())) {
294 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", ast_channel_name(chan));
295 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
296 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
297 return;
298 }
299
300 /* Set silence threshold to specified value */
301 ast_dsp_set_threshold(silenceDetector, silenceThreshold);
302
303 /* Set our start time so we can tie the loop to real world time and not RTP updates */
304 amd_tvstart = ast_tvnow();
305
306 /* Optional audio file to play to caller while AMD is doing its thing. */
307 if (!ast_strlen_zero(audioFile)) {
308 ast_streamfile(chan, audioFile, ast_channel_language(chan));
309 }
310
311 /* Now we go into a loop waiting for frames from the channel */
312 while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
313 int ms = 0;
314
315 /* Figure out how long we waited */
316 if (res >= 0) {
317 ms = 2 * maxWaitTimeForFrame - res;
318 }
319
320 /* If we fail to read in a frame, that means they hung up */
321 if (!(f = ast_read(chan))) {
322 ast_verb(3, "AMD: Channel [%s]. HANGUP\n", ast_channel_name(chan));
323 ast_debug(1, "Got hangup\n");
324 strcpy(amdStatus, "HANGUP");
325 res = 1;
326 break;
327 }
328
329 /* Check to make sure we haven't gone over our real-world timeout in case frames get stalled for whatever reason */
330 if ( (ast_tvdiff_ms(ast_tvnow(), amd_tvstart)) > totalAnalysisTime ) {
331 ast_frfree(f);
332 strcpy(amdStatus , "NOTSURE");
333 if ( audioFrameCount == 0 ) {
334 ast_verb(3, "AMD: Channel [%s]. No audio data received in [%d] seconds.\n", ast_channel_name(chan), totalAnalysisTime);
335 sprintf(amdCause , "NOAUDIODATA-%d", iTotalTime);
336 break;
337 }
338 ast_verb(3, "AMD: Channel [%s]. Timeout...\n", ast_channel_name(chan));
339 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
340 break;
341 }
342
344 /* keep track of the number of audio frames we get */
345 audioFrameCount++;
346
347 /* Figure out how long the frame is in milliseconds */
348 if (f->frametype == AST_FRAME_VOICE) {
350 } else {
351 framelength = ms;
352 }
353
354 iTotalTime += framelength;
355
356 ast_debug(1, "AMD: Channel [%s] frametype [%s] iTotalTime [%d] framelength [%d] totalAnalysisTime [%d]\n",
357 ast_channel_name(chan),
358 f->frametype == AST_FRAME_VOICE ? "AST_FRAME_VOICE" : "AST_FRAME_CNG",
359 iTotalTime, framelength, totalAnalysisTime);
360
361 /* If the total time exceeds the analysis time then give up as we are not too sure */
362 if (iTotalTime >= totalAnalysisTime) {
363 ast_verb(3, "AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
364 ast_frfree(f);
365 strcpy(amdStatus , "NOTSURE");
366 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
367 break;
368 }
369
370 /* Feed the frame of audio into the silence detector and see if we get a result */
371 if (f->frametype != AST_FRAME_VOICE)
372 dspsilence += framelength;
373 else {
374 dspsilence = 0;
375 ast_dsp_silence(silenceDetector, f, &dspsilence);
376 }
377
378 if (dspsilence > 0) {
379 silenceDuration = dspsilence;
380
381 if (silenceDuration >= betweenWordsSilence) {
382 if (currentState != STATE_IN_SILENCE ) {
383 ast_verb(3, "AMD: Channel [%s]. Changed state to STATE_IN_SILENCE\n", ast_channel_name(chan));
384 }
385 /* Find words less than word duration */
386 if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){
387 ast_verb(3, "AMD: Channel [%s]. Short Word Duration: %d\n", ast_channel_name(chan), consecutiveVoiceDuration);
388 }
389 currentState = STATE_IN_SILENCE;
390 consecutiveVoiceDuration = 0;
391 }
392
393 if (inInitialSilence == 1 && silenceDuration >= initialSilence) {
394 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
395 ast_channel_name(chan), silenceDuration, initialSilence);
396 ast_frfree(f);
397 strcpy(amdStatus , "MACHINE");
398 sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
399 res = 1;
400 break;
401 }
402
403 if (silenceDuration >= afterGreetingSilence && inGreeting == 1) {
404 ast_verb(3, "AMD: Channel [%s]. HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
405 ast_channel_name(chan), silenceDuration, afterGreetingSilence);
406 ast_frfree(f);
407 strcpy(amdStatus , "HUMAN");
408 sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
409 res = 1;
410 break;
411 }
412
413 } else {
414 consecutiveVoiceDuration += framelength;
415 voiceDuration += framelength;
416
417 /* If I have enough consecutive voice to say that I am in a Word, I can only increment the
418 number of words if my previous state was Silence, which means that I moved into a word. */
419 if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
420 iWordsCount++;
421 ast_verb(3, "AMD: Channel [%s]. Word detected. iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
422 currentState = STATE_IN_WORD;
423 }
424 if (consecutiveVoiceDuration >= maximumWordLength){
425 ast_verb(3, "AMD: Channel [%s]. Maximum Word Length detected. [%d]\n", ast_channel_name(chan), consecutiveVoiceDuration);
426 ast_frfree(f);
427 strcpy(amdStatus , "MACHINE");
428 sprintf(amdCause , "MAXWORDLENGTH-%d", consecutiveVoiceDuration);
429 break;
430 }
431 if (iWordsCount > maximumNumberOfWords) {
432 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
433 ast_frfree(f);
434 strcpy(amdStatus , "MACHINE");
435 sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
436 res = 1;
437 break;
438 }
439
440 if (inGreeting == 1 && voiceDuration >= greeting) {
441 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", ast_channel_name(chan), voiceDuration, greeting);
442 ast_frfree(f);
443 strcpy(amdStatus , "MACHINE");
444 sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
445 res = 1;
446 break;
447 }
448
449 if (voiceDuration >= minimumWordLength ) {
450 if (silenceDuration > 0)
451 ast_verb(3, "AMD: Channel [%s]. Detected Talk, previous silence duration: %d\n", ast_channel_name(chan), silenceDuration);
452 silenceDuration = 0;
453 }
454 if (consecutiveVoiceDuration >= minimumWordLength && inGreeting == 0) {
455 /* Only go in here once to change the greeting flag when we detect the 1st word */
456 if (silenceDuration > 0)
457 ast_verb(3, "AMD: Channel [%s]. Before Greeting Time: silenceDuration: %d voiceDuration: %d\n", ast_channel_name(chan), silenceDuration, voiceDuration);
458 inInitialSilence = 0;
459 inGreeting = 1;
460 }
461
462 }
463 } else {
464 iTotalTime += ms;
465 if (iTotalTime >= totalAnalysisTime) {
466 ast_frfree(f);
467 strcpy(amdStatus , "NOTSURE");
468 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
469 break;
470 }
471 }
472 ast_frfree(f);
473 }
474
475 if (!res) {
476 /* It took too long to get a frame back. Giving up. */
477 ast_verb(3, "AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
478 strcpy(amdStatus , "NOTSURE");
479 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
480 }
481
482 /* Set the status and cause on the channel */
483 pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus);
484 pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause);
485
486 /* Restore channel read format */
487 if (readFormat && ast_set_read_format(chan, readFormat))
488 ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", ast_channel_name(chan));
489
490 /* Free the DSP used to detect silence */
491 ast_dsp_free(silenceDetector);
492
493 /* If we were playing something to pass the time, stop it now. */
494 if (!ast_strlen_zero(audioFile)) {
495 ast_stopstream(chan);
496 }
497
498 return;
499}
static int dfltBetweenWordsSilence
Definition: app_amd.c:163
static int dfltInitialSilence
Definition: app_amd.c:158
static int dfltMaximumNumberOfWords
Definition: app_amd.c:164
#define STATE_IN_SILENCE
Definition: app_amd.c:155
static int dfltGreeting
Definition: app_amd.c:159
static ast_mutex_t config_lock
Definition: app_amd.c:169
#define STATE_IN_WORD
Definition: app_amd.c:154
static int dfltMinimumWordLength
Definition: app_amd.c:162
static int dfltMaximumWordLength
Definition: app_amd.c:166
static int dfltTotalAnalysisTime
Definition: app_amd.c:161
static char * dfltAudioFile
Definition: app_amd.c:167
static int dfltAfterGreetingSilence
Definition: app_amd.c:160
static int dfltMaxWaitTimeForFrame
Definition: app_amd.c:172
static int dfltSilenceThreshold
Definition: app_amd.c:165
#define DEFAULT_SAMPLES_PER_MS
Definition: asterisk.h:49
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
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.
Definition: channel.c:3130
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4214
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
Definition: channel.c:5721
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.
Definition: codec.c:379
void ast_dsp_set_threshold(struct ast_dsp *dsp, int threshold)
Set the minimum average magnitude threshold to determine talking by the DSP.
Definition: dsp.c:1788
void ast_dsp_free(struct ast_dsp *dsp)
Definition: dsp.c:1783
int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence)
Process the audio frame for silence.
Definition: dsp.c:1488
struct ast_dsp * ast_dsp_new(void)
Allocates a new dsp, assumes 8khz for internal sample rate.
Definition: dsp.c:1758
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1301
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition: format.c:334
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
#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.
#define ast_frfree(fr)
@ AST_FRAME_VOICE
#define ast_debug(level,...)
Log a DEBUG message.
#define ast_verb(level,...)
#define LOG_WARNING
#define ast_mutex_unlock(a)
Definition: lock.h:197
#define ast_mutex_lock(a)
Definition: lock.h:196
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 NULL
Definition: resample.c:96
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:87
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Definition: dsp.c:407
Definition of a media format.
Definition: format.c:43
Data structure associated with a single frame of data.
enum ast_frame_type frametype
const char * args
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941

References ao2_bump, ao2_cleanup, args, AST_APP_ARG, ast_channel_caller(), ast_channel_language(), ast_channel_name(), ast_channel_readformat(), ast_channel_redirecting(), ast_codec_samples_count(), ast_debug, AST_DECLARE_APP_ARGS, ast_dsp_free(), ast_dsp_new(), ast_dsp_set_threshold(), ast_dsp_silence(), ast_format_get_name(), ast_format_slin, AST_FRAME_CNG, AST_FRAME_VOICE, ast_frfree, ast_log, ast_mutex_lock, ast_mutex_unlock, ast_read(), ast_set_read_format(), AST_STANDARD_APP_ARGS, ast_stopstream(), ast_strdupa, ast_streamfile(), ast_strlen_zero(), ast_tvdiff_ms(), ast_tvnow(), ast_verb, ast_waitfor(), config_lock, DEFAULT_SAMPLES_PER_MS, dfltAfterGreetingSilence, dfltAudioFile, dfltBetweenWordsSilence, dfltGreeting, dfltInitialSilence, dfltMaximumNumberOfWords, dfltMaximumWordLength, dfltMaxWaitTimeForFrame, dfltMinimumWordLength, dfltSilenceThreshold, dfltTotalAnalysisTime, ast_frame::frametype, LOG_WARNING, NULL, pbx_builtin_setvar_helper(), RAII_VAR, S_COR, STATE_IN_SILENCE, and STATE_IN_WORD.

Referenced by amd_exec().

◆ load_config()

static int load_config ( int  reload)
static

Definition at line 508 of file app_amd.c.

509{
510 struct ast_config *cfg = NULL;
511 char *cat = NULL;
512 struct ast_variable *var = NULL;
513 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
514
516
517 if (!(cfg = ast_config_load("amd.conf", config_flags))) {
518 ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
519 return -1;
520 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
521 return 0;
522 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
523 ast_log(LOG_ERROR, "Config file amd.conf is in an invalid format. Aborting.\n");
524 return -1;
525 }
526
527 cat = ast_category_browse(cfg, NULL);
528
529 while (cat) {
530 if (!strcasecmp(cat, "general") ) {
531 var = ast_variable_browse(cfg, cat);
532 while (var) {
533 if (!strcasecmp(var->name, "initial_silence")) {
534 dfltInitialSilence = atoi(var->value);
535 } else if (!strcasecmp(var->name, "greeting")) {
536 dfltGreeting = atoi(var->value);
537 } else if (!strcasecmp(var->name, "after_greeting_silence")) {
538 dfltAfterGreetingSilence = atoi(var->value);
539 } else if (!strcasecmp(var->name, "silence_threshold")) {
540 dfltSilenceThreshold = atoi(var->value);
541 } else if (!strcasecmp(var->name, "total_analysis_time")) {
542 dfltTotalAnalysisTime = atoi(var->value);
543 } else if (!strcasecmp(var->name, "min_word_length")) {
544 dfltMinimumWordLength = atoi(var->value);
545 } else if (!strcasecmp(var->name, "between_words_silence")) {
546 dfltBetweenWordsSilence = atoi(var->value);
547 } else if (!strcasecmp(var->name, "maximum_number_of_words")) {
548 dfltMaximumNumberOfWords = atoi(var->value);
549 } else if (!strcasecmp(var->name, "maximum_word_length")) {
550 dfltMaximumWordLength = atoi(var->value);
551 } else if (!strcasecmp(var->name, "playback_file")) {
553 if (dfltAudioFile) {
556 }
557 if (!ast_strlen_zero(var->value)) {
558 dfltAudioFile = ast_strdup(var->value);
559 }
561 } else {
562 ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
563 app, cat, var->name, var->lineno);
564 }
565 var = var->next;
566 }
567 }
568 cat = ast_category_browse(cfg, cat);
569 }
570
572
573 ast_verb(5, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
574 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
577
578 return 0;
579}
static const char app[]
Definition: app_amd.c:152
static int reload(void)
Definition: app_amd.c:612
#define var
Definition: ast_expr2f.c:605
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
@ THRESHOLD_SILENCE
Definition: dsp.h:73
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Definition: dsp.c:2009
#define ast_config_load(filename, flags)
Load a config file.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
@ CONFIG_FLAG_FILEUNCHANGED
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1215
#define LOG_ERROR
Structure used to handle boolean flags.
Definition: utils.h:199
Structure for variables, used for configurations and for channel variables.

References app, ast_category_browse(), ast_config_destroy(), ast_config_load, ast_dsp_get_threshold_from_settings(), ast_free, ast_log, ast_mutex_lock, ast_mutex_unlock, ast_strdup, ast_strlen_zero(), ast_variable_browse(), ast_verb, CONFIG_FLAG_FILEUNCHANGED, config_lock, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, dfltAfterGreetingSilence, dfltAudioFile, dfltBetweenWordsSilence, dfltGreeting, dfltInitialSilence, dfltMaximumNumberOfWords, dfltMaximumWordLength, dfltMinimumWordLength, dfltSilenceThreshold, dfltTotalAnalysisTime, LOG_ERROR, LOG_WARNING, NULL, reload(), THRESHOLD_SILENCE, and var.

Referenced by load_module(), and reload().

◆ load_module()

static int load_module ( void  )
static

Load the module.

Module loading including tests for configuration or dependencies. This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails tests return AST_MODULE_LOAD_FAILURE. If the module can not load the configuration file or other non-critical problem return AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.

Definition at line 602 of file app_amd.c.

603{
607 }
608
610}
static int amd_exec(struct ast_channel *chan, const char *data)
Definition: app_amd.c:501
static int load_config(int reload)
Definition: app_amd.c:508
#define ast_mutex_init(pmutex)
Definition: lock.h:193
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640

References amd_exec(), app, AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_mutex_init, ast_register_application_xml, config_lock, and load_config().

◆ reload()

static int reload ( void  )
static

Definition at line 612 of file app_amd.c.

613{
614 if (load_config(1))
617}

References AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, and load_config().

Referenced by load_config().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 581 of file app_amd.c.

582{
584 if (dfltAudioFile) {
586 }
590}
#define ast_mutex_destroy(a)
Definition: lock.h:195
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392

References app, ast_free, ast_mutex_destroy, ast_mutex_lock, ast_mutex_unlock, ast_unregister_application(), config_lock, and dfltAudioFile.

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .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 = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .reload = reload, }
static

Definition at line 624 of file app_amd.c.

◆ app

const char app[] = "AMD"
static

Definition at line 152 of file app_amd.c.

Referenced by load_config(), load_module(), and unload_module().

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 624 of file app_amd.c.

◆ config_lock

ast_mutex_t config_lock
static

Definition at line 169 of file app_amd.c.

Referenced by isAnsweringMachine(), load_config(), load_module(), and unload_module().

◆ dfltAfterGreetingSilence

int dfltAfterGreetingSilence = 800
static

Definition at line 160 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().

◆ dfltAudioFile

char* dfltAudioFile = NULL
static

Definition at line 167 of file app_amd.c.

Referenced by isAnsweringMachine(), load_config(), and unload_module().

◆ dfltBetweenWordsSilence

int dfltBetweenWordsSilence = 50
static

Definition at line 163 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().

◆ dfltGreeting

int dfltGreeting = 1500
static

Definition at line 159 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().

◆ dfltInitialSilence

int dfltInitialSilence = 2500
static

Definition at line 158 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().

◆ dfltMaximumNumberOfWords

int dfltMaximumNumberOfWords = 2
static

Definition at line 164 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().

◆ dfltMaximumWordLength

int dfltMaximumWordLength = 5000
static

Definition at line 166 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().

◆ dfltMaxWaitTimeForFrame

int dfltMaxWaitTimeForFrame = 50
static

Definition at line 172 of file app_amd.c.

Referenced by isAnsweringMachine().

◆ dfltMinimumWordLength

int dfltMinimumWordLength = 100
static

Definition at line 162 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().

◆ dfltSilenceThreshold

int dfltSilenceThreshold = 256
static

Definition at line 165 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().

◆ dfltTotalAnalysisTime

int dfltTotalAnalysisTime = 5000
static

Definition at line 161 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().