Asterisk - The Open Source Telephony Project GIT-master-f36a736
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 152 of file app_amd.c.

◆ STATE_IN_WORD

#define STATE_IN_WORD   1

Definition at line 151 of file app_amd.c.

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 621 of file app_amd.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 621 of file app_amd.c.

◆ amd_exec()

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

Definition at line 498 of file app_amd.c.

499{
500 isAnsweringMachine(chan, data);
501
502 return 0;
503}
static void isAnsweringMachine(struct ast_channel *chan, const char *data)
Definition: app_amd.c:171

References isAnsweringMachine().

Referenced by load_module().

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 621 of file app_amd.c.

◆ isAnsweringMachine()

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

Definition at line 171 of file app_amd.c.

172{
173 int res = 0;
174 int audioFrameCount = 0;
175 struct ast_frame *f = NULL;
176 struct ast_dsp *silenceDetector = NULL;
177 struct timeval amd_tvstart;
178 int dspsilence = 0, framelength = 0;
179 RAII_VAR(struct ast_format *, readFormat, NULL, ao2_cleanup);
180 int inInitialSilence = 1;
181 int inGreeting = 0;
182 int voiceDuration = 0;
183 int silenceDuration = 0;
184 int iTotalTime = 0;
185 int iWordsCount = 0;
186 int currentState = STATE_IN_WORD;
187 int consecutiveVoiceDuration = 0;
188 char amdCause[256] = "", amdStatus[256] = "";
189 char *parse = ast_strdupa(data);
190
191 /* Let's set the initial values of the variables that will control the algorithm.
192 The initial values are the default ones. If they are passed as arguments
193 when invoking the application, then the default values will be overwritten
194 by the ones passed as parameters. */
195 int initialSilence = dfltInitialSilence;
196 int greeting = dfltGreeting;
197 int afterGreetingSilence = dfltAfterGreetingSilence;
198 int totalAnalysisTime = dfltTotalAnalysisTime;
199 int minimumWordLength = dfltMinimumWordLength;
200 int betweenWordsSilence = dfltBetweenWordsSilence;
201 int maximumNumberOfWords = dfltMaximumNumberOfWords;
202 int silenceThreshold = dfltSilenceThreshold;
203 int maximumWordLength = dfltMaximumWordLength;
204 int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame;
205 const char *audioFile = NULL;
206
208 AST_APP_ARG(argInitialSilence);
209 AST_APP_ARG(argGreeting);
210 AST_APP_ARG(argAfterGreetingSilence);
211 AST_APP_ARG(argTotalAnalysisTime);
212 AST_APP_ARG(argMinimumWordLength);
213 AST_APP_ARG(argBetweenWordsSilence);
214 AST_APP_ARG(argMaximumNumberOfWords);
215 AST_APP_ARG(argSilenceThreshold);
216 AST_APP_ARG(argMaximumWordLength);
217 AST_APP_ARG(audioFile);
218 );
219
222 audioFile = ast_strdupa(dfltAudioFile);
223 }
225
226 ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", ast_channel_name(chan),
227 S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, "(N/A)"),
228 S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, "(N/A)"),
230
231 /* Lets parse the arguments. */
232 if (!ast_strlen_zero(parse)) {
233 /* Some arguments have been passed. Lets parse them and overwrite the defaults. */
235 if (!ast_strlen_zero(args.argInitialSilence))
236 initialSilence = atoi(args.argInitialSilence);
237 if (!ast_strlen_zero(args.argGreeting))
238 greeting = atoi(args.argGreeting);
239 if (!ast_strlen_zero(args.argAfterGreetingSilence))
240 afterGreetingSilence = atoi(args.argAfterGreetingSilence);
241 if (!ast_strlen_zero(args.argTotalAnalysisTime))
242 totalAnalysisTime = atoi(args.argTotalAnalysisTime);
243 if (!ast_strlen_zero(args.argMinimumWordLength))
244 minimumWordLength = atoi(args.argMinimumWordLength);
245 if (!ast_strlen_zero(args.argBetweenWordsSilence))
246 betweenWordsSilence = atoi(args.argBetweenWordsSilence);
247 if (!ast_strlen_zero(args.argMaximumNumberOfWords))
248 maximumNumberOfWords = atoi(args.argMaximumNumberOfWords);
249 if (!ast_strlen_zero(args.argSilenceThreshold))
250 silenceThreshold = atoi(args.argSilenceThreshold);
251 if (!ast_strlen_zero(args.argMaximumWordLength))
252 maximumWordLength = atoi(args.argMaximumWordLength);
253 if (!ast_strlen_zero(args.audioFile)) {
254 audioFile = args.audioFile;
255 }
256 } else {
257 ast_debug(1, "AMD using the default parameters.\n");
258 }
259
260 /* Find lowest ms value, that will be max wait time for a frame */
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;
273
274 /* Now we're ready to roll! */
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);
279
280 /* Set read format to signed linear so we get signed linear frames in */
281 readFormat = ao2_bump(ast_channel_readformat(chan));
282 if (ast_set_read_format(chan, ast_format_slin) < 0 ) {
283 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", ast_channel_name(chan));
284 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
285 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
286 return;
287 }
288
289 /* Create a new DSP that will detect the silence */
290 if (!(silenceDetector = ast_dsp_new())) {
291 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", ast_channel_name(chan));
292 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
293 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
294 return;
295 }
296
297 /* Set silence threshold to specified value */
298 ast_dsp_set_threshold(silenceDetector, silenceThreshold);
299
300 /* Set our start time so we can tie the loop to real world time and not RTP updates */
301 amd_tvstart = ast_tvnow();
302
303 /* Optional audio file to play to caller while AMD is doing its thing. */
304 if (!ast_strlen_zero(audioFile)) {
305 ast_streamfile(chan, audioFile, ast_channel_language(chan));
306 }
307
308 /* Now we go into a loop waiting for frames from the channel */
309 while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
310 int ms = 0;
311
312 /* Figure out how long we waited */
313 if (res >= 0) {
314 ms = 2 * maxWaitTimeForFrame - res;
315 }
316
317 /* If we fail to read in a frame, that means they hung up */
318 if (!(f = ast_read(chan))) {
319 ast_verb(3, "AMD: Channel [%s]. HANGUP\n", ast_channel_name(chan));
320 ast_debug(1, "Got hangup\n");
321 strcpy(amdStatus, "HANGUP");
322 res = 1;
323 break;
324 }
325
326 /* Check to make sure we haven't gone over our real-world timeout in case frames get stalled for whatever reason */
327 if ( (ast_tvdiff_ms(ast_tvnow(), amd_tvstart)) > totalAnalysisTime ) {
328 ast_frfree(f);
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);
333 break;
334 }
335 ast_verb(3, "AMD: Channel [%s]. Timeout...\n", ast_channel_name(chan));
336 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
337 break;
338 }
339
341 /* keep track of the number of audio frames we get */
342 audioFrameCount++;
343
344 /* Figure out how long the frame is in milliseconds */
345 if (f->frametype == AST_FRAME_VOICE) {
347 } else {
348 framelength = ms;
349 }
350
351 iTotalTime += framelength;
352
353 ast_debug(1, "AMD: Channel [%s] frametype [%s] iTotalTime [%d] framelength [%d] totalAnalysisTime [%d]\n",
354 ast_channel_name(chan),
355 f->frametype == AST_FRAME_VOICE ? "AST_FRAME_VOICE" : "AST_FRAME_CNG",
356 iTotalTime, framelength, totalAnalysisTime);
357
358 /* If the total time exceeds the analysis time then give up as we are not too sure */
359 if (iTotalTime >= totalAnalysisTime) {
360 ast_verb(3, "AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
361 ast_frfree(f);
362 strcpy(amdStatus , "NOTSURE");
363 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
364 break;
365 }
366
367 /* Feed the frame of audio into the silence detector and see if we get a result */
368 if (f->frametype != AST_FRAME_VOICE)
369 dspsilence += framelength;
370 else {
371 dspsilence = 0;
372 ast_dsp_silence(silenceDetector, f, &dspsilence);
373 }
374
375 if (dspsilence > 0) {
376 silenceDuration = dspsilence;
377
378 if (silenceDuration >= betweenWordsSilence) {
379 if (currentState != STATE_IN_SILENCE ) {
380 ast_verb(3, "AMD: Channel [%s]. Changed state to STATE_IN_SILENCE\n", ast_channel_name(chan));
381 }
382 /* Find words less than word duration */
383 if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){
384 ast_verb(3, "AMD: Channel [%s]. Short Word Duration: %d\n", ast_channel_name(chan), consecutiveVoiceDuration);
385 }
386 currentState = STATE_IN_SILENCE;
387 consecutiveVoiceDuration = 0;
388 }
389
390 if (inInitialSilence == 1 && silenceDuration >= initialSilence) {
391 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
392 ast_channel_name(chan), silenceDuration, initialSilence);
393 ast_frfree(f);
394 strcpy(amdStatus , "MACHINE");
395 sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
396 res = 1;
397 break;
398 }
399
400 if (silenceDuration >= afterGreetingSilence && inGreeting == 1) {
401 ast_verb(3, "AMD: Channel [%s]. HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
402 ast_channel_name(chan), silenceDuration, afterGreetingSilence);
403 ast_frfree(f);
404 strcpy(amdStatus , "HUMAN");
405 sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
406 res = 1;
407 break;
408 }
409
410 } else {
411 consecutiveVoiceDuration += framelength;
412 voiceDuration += framelength;
413
414 /* If I have enough consecutive voice to say that I am in a Word, I can only increment the
415 number of words if my previous state was Silence, which means that I moved into a word. */
416 if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
417 iWordsCount++;
418 ast_verb(3, "AMD: Channel [%s]. Word detected. iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
419 currentState = STATE_IN_WORD;
420 }
421 if (consecutiveVoiceDuration >= maximumWordLength){
422 ast_verb(3, "AMD: Channel [%s]. Maximum Word Length detected. [%d]\n", ast_channel_name(chan), consecutiveVoiceDuration);
423 ast_frfree(f);
424 strcpy(amdStatus , "MACHINE");
425 sprintf(amdCause , "MAXWORDLENGTH-%d", consecutiveVoiceDuration);
426 break;
427 }
428 if (iWordsCount > maximumNumberOfWords) {
429 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
430 ast_frfree(f);
431 strcpy(amdStatus , "MACHINE");
432 sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
433 res = 1;
434 break;
435 }
436
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);
439 ast_frfree(f);
440 strcpy(amdStatus , "MACHINE");
441 sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
442 res = 1;
443 break;
444 }
445
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);
449 silenceDuration = 0;
450 }
451 if (consecutiveVoiceDuration >= minimumWordLength && inGreeting == 0) {
452 /* Only go in here once to change the greeting flag when we detect the 1st word */
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;
456 inGreeting = 1;
457 }
458
459 }
460 } else {
461 iTotalTime += ms;
462 if (iTotalTime >= totalAnalysisTime) {
463 ast_frfree(f);
464 strcpy(amdStatus , "NOTSURE");
465 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
466 break;
467 }
468 }
469 ast_frfree(f);
470 }
471
472 if (!res) {
473 /* It took too long to get a frame back. Giving up. */
474 ast_verb(3, "AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
475 strcpy(amdStatus , "NOTSURE");
476 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
477 }
478
479 /* Set the status and cause on the channel */
480 pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus);
481 pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause);
482
483 /* Restore channel read format */
484 if (readFormat && ast_set_read_format(chan, readFormat))
485 ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", ast_channel_name(chan));
486
487 /* Free the DSP used to detect silence */
488 ast_dsp_free(silenceDetector);
489
490 /* If we were playing something to pass the time, stop it now. */
491 if (!ast_strlen_zero(audioFile)) {
492 ast_stopstream(chan);
493 }
494
495 return;
496}
static int dfltBetweenWordsSilence
Definition: app_amd.c:160
static int dfltInitialSilence
Definition: app_amd.c:155
static int dfltMaximumNumberOfWords
Definition: app_amd.c:161
#define STATE_IN_SILENCE
Definition: app_amd.c:152
static int dfltGreeting
Definition: app_amd.c:156
static ast_mutex_t config_lock
Definition: app_amd.c:166
#define STATE_IN_WORD
Definition: app_amd.c:151
static int dfltMinimumWordLength
Definition: app_amd.c:159
static int dfltMaximumWordLength
Definition: app_amd.c:163
static int dfltTotalAnalysisTime
Definition: app_amd.c:158
static char * dfltAudioFile
Definition: app_amd.c:164
static int dfltAfterGreetingSilence
Definition: app_amd.c:157
static int dfltMaxWaitTimeForFrame
Definition: app_amd.c:169
static int dfltSilenceThreshold
Definition: app_amd.c:162
#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:3181
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4276
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
Definition: channel.c:5781
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:1293
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:190
#define ast_mutex_lock(a)
Definition: lock.h:189
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 505 of file app_amd.c.

506{
507 struct ast_config *cfg = NULL;
508 char *cat = NULL;
509 struct ast_variable *var = NULL;
510 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
511
513
514 if (!(cfg = ast_config_load("amd.conf", config_flags))) {
515 ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
516 return -1;
517 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
518 return 0;
519 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
520 ast_log(LOG_ERROR, "Config file amd.conf is in an invalid format. Aborting.\n");
521 return -1;
522 }
523
524 cat = ast_category_browse(cfg, NULL);
525
526 while (cat) {
527 if (!strcasecmp(cat, "general") ) {
528 var = ast_variable_browse(cfg, cat);
529 while (var) {
530 if (!strcasecmp(var->name, "initial_silence")) {
531 dfltInitialSilence = atoi(var->value);
532 } else if (!strcasecmp(var->name, "greeting")) {
533 dfltGreeting = atoi(var->value);
534 } else if (!strcasecmp(var->name, "after_greeting_silence")) {
535 dfltAfterGreetingSilence = atoi(var->value);
536 } else if (!strcasecmp(var->name, "silence_threshold")) {
537 dfltSilenceThreshold = atoi(var->value);
538 } else if (!strcasecmp(var->name, "total_analysis_time")) {
539 dfltTotalAnalysisTime = atoi(var->value);
540 } else if (!strcasecmp(var->name, "min_word_length")) {
541 dfltMinimumWordLength = atoi(var->value);
542 } else if (!strcasecmp(var->name, "between_words_silence")) {
543 dfltBetweenWordsSilence = atoi(var->value);
544 } else if (!strcasecmp(var->name, "maximum_number_of_words")) {
545 dfltMaximumNumberOfWords = atoi(var->value);
546 } else if (!strcasecmp(var->name, "maximum_word_length")) {
547 dfltMaximumWordLength = atoi(var->value);
548 } else if (!strcasecmp(var->name, "playback_file")) {
550 if (dfltAudioFile) {
553 }
554 if (!ast_strlen_zero(var->value)) {
555 dfltAudioFile = ast_strdup(var->value);
556 }
558 } else {
559 ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
560 app, cat, var->name, var->lineno);
561 }
562 var = var->next;
563 }
564 }
565 cat = ast_category_browse(cfg, cat);
566 }
567
569
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",
574
575 return 0;
576}
static const char app[]
Definition: app_amd.c:149
static int reload(void)
Definition: app_amd.c:609
#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
#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
@ CONFIG_FLAG_FILEUNCHANGED
#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 599 of file app_amd.c.

600{
604 }
605
607}
static int amd_exec(struct ast_channel *chan, const char *data)
Definition: app_amd.c:498
static int load_config(int reload)
Definition: app_amd.c:505
#define ast_mutex_init(pmutex)
Definition: lock.h:186
@ 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 609 of file app_amd.c.

610{
611 if (load_config(1))
614}

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 578 of file app_amd.c.

579{
581 if (dfltAudioFile) {
583 }
587}
#define ast_mutex_destroy(a)
Definition: lock.h:188
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 621 of file app_amd.c.

◆ app

const char app[] = "AMD"
static

Definition at line 149 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 621 of file app_amd.c.

◆ config_lock

ast_mutex_t config_lock
static

Definition at line 166 of file app_amd.c.

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

◆ dfltAfterGreetingSilence

int dfltAfterGreetingSilence = 800
static

Definition at line 157 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().

◆ dfltAudioFile

char* dfltAudioFile = NULL
static

Definition at line 164 of file app_amd.c.

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

◆ dfltBetweenWordsSilence

int dfltBetweenWordsSilence = 50
static

Definition at line 160 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().

◆ dfltGreeting

int dfltGreeting = 1500
static

Definition at line 156 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().

◆ dfltInitialSilence

int dfltInitialSilence = 2500
static

Definition at line 155 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().

◆ dfltMaximumNumberOfWords

int dfltMaximumNumberOfWords = 2
static

Definition at line 161 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().

◆ dfltMaximumWordLength

int dfltMaximumWordLength = 5000
static

Definition at line 163 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().

◆ dfltMaxWaitTimeForFrame

int dfltMaxWaitTimeForFrame = 50
static

Definition at line 169 of file app_amd.c.

Referenced by isAnsweringMachine().

◆ dfltMinimumWordLength

int dfltMinimumWordLength = 100
static

Definition at line 159 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().

◆ dfltSilenceThreshold

int dfltSilenceThreshold = 256
static

Definition at line 162 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().

◆ dfltTotalAnalysisTime

int dfltTotalAnalysisTime = 5000
static

Definition at line 158 of file app_amd.c.

Referenced by isAnsweringMachine(), and load_config().