Asterisk - The Open Source Telephony Project GIT-master-7e7a603
app_amd.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2003 - 2006, Aheeva Technology.
5 *
6 * Claude Klimos (claude.klimos@aheeva.com)
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 *
18 * A license has been granted to Digium (via disclaimer) for the use of
19 * this code.
20 */
21
22/*! \file
23 *
24 * \brief Answering machine detection
25 *
26 * \author Claude Klimos (claude.klimos@aheeva.com)
27 *
28 * \ingroup applications
29 */
30
31/*! \li \ref app_amd.c uses the configuration file \ref amd.conf
32 * \addtogroup configuration_file Configuration Files
33 */
34
35/*!
36 * \page amd.conf amd.conf
37 * \verbinclude amd.conf.sample
38 */
39
40/*** MODULEINFO
41 <support_level>extended</support_level>
42 ***/
43
44#include "asterisk.h"
45
46#include "asterisk/module.h"
47#include "asterisk/lock.h"
48#include "asterisk/channel.h"
49#include "asterisk/dsp.h"
50#include "asterisk/pbx.h"
51#include "asterisk/config.h"
52#include "asterisk/app.h"
54
55/*** DOCUMENTATION
56 <application name="AMD" language="en_US">
57 <synopsis>
58 Attempt to detect answering machines.
59 </synopsis>
60 <syntax>
61 <parameter name="initialSilence" required="false">
62 <para>Is maximum initial silence duration before greeting.</para>
63 <para>If this is exceeded, the result is detection as a MACHINE</para>
64 </parameter>
65 <parameter name="greeting" required="false">
66 <para>is the maximum length of a greeting.</para>
67 <para>If this is exceeded, the result is detection as a MACHINE</para>
68 </parameter>
69 <parameter name="afterGreetingSilence" required="false">
70 <para>Is the silence after detecting a greeting.</para>
71 <para>If this is exceeded, the result is detection as a HUMAN</para>
72 </parameter>
73 <parameter name="totalAnalysis Time" required="false">
74 <para>Is the maximum time allowed for the algorithm</para>
75 <para>to decide on whether the audio represents a HUMAN, or a MACHINE</para>
76 </parameter>
77 <parameter name="miniumWordLength" required="false">
78 <para>Is the minimum duration of Voice considered to be a word</para>
79 </parameter>
80 <parameter name="betweenWordSilence" required="false">
81 <para>Is the minimum duration of silence after a word to
82 consider the audio that follows to be a new word</para>
83 </parameter>
84 <parameter name="maximumNumberOfWords" required="false">
85 <para>Is the maximum number of words in a greeting</para>
86 <para>If this is exceeded, then the result is detection as a MACHINE</para>
87 </parameter>
88 <parameter name="silenceThreshold" required="false">
89 <para>What is the average level of noise from 0 to 32767 which if not exceeded, should be considered silence?</para>
90 </parameter>
91 <parameter name="maximumWordLength" required="false">
92 <para>Is the maximum duration of a word to accept.</para>
93 <para>If exceeded, then the result is detection as a MACHINE</para>
94 </parameter>
95 <parameter name="audioFile" required="false">
96 <para>Is an audio file to play to the caller while AMD is in progress.</para>
97 <para>By default, no audio file is played.</para>
98 <para>If an audio file is configured in amd.conf, then that file will be used
99 if one is not specified here. That file may be overridden by this argument.</para>
100 </parameter>
101 </syntax>
102 <description>
103 <para>This application attempts to detect answering machines at the beginning
104 of outbound calls. Simply call this application after the call
105 has been answered (outbound only, of course).</para>
106 <para>When loaded, AMD reads amd.conf and uses the parameters specified as
107 default values. Those default values get overwritten when the calling AMD
108 with parameters.</para>
109 <para>This application sets the following channel variables:</para>
110 <variablelist>
111 <variable name="AMDSTATUS">
112 <para>This is the status of the answering machine detection</para>
113 <value name="MACHINE" />
114 <value name="HUMAN" />
115 <value name="NOTSURE" />
116 <value name="HANGUP" />
117 </variable>
118 <variable name="AMDCAUSE">
119 <para>Indicates the cause that led to the conclusion</para>
120 <value name="TOOLONG">
121 Total Time.
122 </value>
123 <value name="INITIALSILENCE">
124 Silence Duration - Initial Silence.
125 </value>
126 <value name="HUMAN">
127 Silence Duration - afterGreetingSilence.
128 </value>
129 <value name="LONGGREETING">
130 Voice Duration - Greeting.
131 </value>
132 <value name="MAXWORDLENGTH">
133 Word Length - max length of a single word.
134 </value>
135 <value name="MAXWORDS">
136 Word Count - maximum number of words.
137 </value>
138 </variable>
139 </variablelist>
140 </description>
141 <see-also>
142 <ref type="application">WaitForSilence</ref>
143 <ref type="application">WaitForNoise</ref>
144 </see-also>
145 </application>
146
147 ***/
148
149static const char app[] = "AMD";
150
151#define STATE_IN_WORD 1
152#define STATE_IN_SILENCE 2
153
154/* Some default values for the algorithm parameters. These defaults will be overwritten from amd.conf */
155static int dfltInitialSilence = 2500;
156static int dfltGreeting = 1500;
158static int dfltTotalAnalysisTime = 5000;
159static int dfltMinimumWordLength = 100;
162static int dfltSilenceThreshold = 256;
163static int dfltMaximumWordLength = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
164static char *dfltAudioFile = NULL;
165
167
168/* Set to the lowest ms value provided in amd.conf or application parameters */
170
171static void isAnsweringMachine(struct ast_channel *chan, const char *data)
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}
497
498static int amd_exec(struct ast_channel *chan, const char *data)
499{
500 isAnsweringMachine(chan, data);
501
502 return 0;
503}
504
505static int load_config(int reload)
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}
577
578static int unload_module(void)
579{
581 if (dfltAudioFile) {
583 }
587}
588
589/*!
590 * \brief Load the module
591 *
592 * Module loading including tests for configuration or dependencies.
593 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
594 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
595 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
596 * configuration file or other non-critical problem return
597 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
598 */
599static int load_module(void)
600{
604 }
605
607}
608
609static int reload(void)
610{
611 if (load_config(1))
614}
615
616AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
617 .support_level = AST_MODULE_SUPPORT_EXTENDED,
618 .load = load_module,
619 .unload = unload_module,
620 .reload = reload,
static int dfltBetweenWordsSilence
Definition: app_amd.c:160
static int amd_exec(struct ast_channel *chan, const char *data)
Definition: app_amd.c:498
static int dfltInitialSilence
Definition: app_amd.c:155
static int dfltMaximumNumberOfWords
Definition: app_amd.c:161
static const char app[]
Definition: app_amd.c:149
#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 load_module(void)
Load the module.
Definition: app_amd.c:599
static int dfltTotalAnalysisTime
Definition: app_amd.c:158
static char * dfltAudioFile
Definition: app_amd.c:164
static int unload_module(void)
Definition: app_amd.c:578
static int load_config(int reload)
Definition: app_amd.c:505
static int reload(void)
Definition: app_amd.c:609
static void isAnsweringMachine(struct ast_channel *chan, const char *data)
Definition: app_amd.c:171
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 var
Definition: ast_expr2f.c:605
Asterisk main include file. File version handling, generic pbx functions.
#define DEFAULT_SAMPLES_PER_MS
Definition: asterisk.h:49
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#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
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
struct ast_party_redirecting * ast_channel_redirecting(struct ast_channel *chan)
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3162
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4257
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
Definition: channel.c:5762
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
Convenient Signal Processing routines.
void ast_dsp_set_threshold(struct ast_dsp *dsp, int threshold)
Set the minimum average magnitude threshold to determine talking by the DSP.
Definition: dsp.c:1788
void ast_dsp_free(struct ast_dsp *dsp)
Definition: dsp.c:1783
@ THRESHOLD_SILENCE
Definition: dsp.h:73
int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence)
Process the audio frame for silence.
Definition: dsp.c:1488
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Definition: dsp.c:2009
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
Media Format Cache API.
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
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 ast_frfree(fr)
@ AST_FRAME_VOICE
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_WARNING
Asterisk locking-related definitions:
#define ast_mutex_init(pmutex)
Definition: lock.h:186
#define ast_mutex_unlock(a)
Definition: lock.h:190
#define ast_mutex_destroy(a)
Definition: lock.h:188
#define ast_mutex_lock(a)
Definition: lock.h:189
Asterisk module definitions.
@ AST_MODFLAG_DEFAULT
Definition: module.h:315
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODULE_SUPPORT_EXTENDED
Definition: module.h:122
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
@ 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:626
Core PBX routines and definitions.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
#define 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
Main Channel structure associated with a channel.
Definition: dsp.c:407
Structure used to handle boolean flags.
Definition: utils.h:199
Definition of a media format.
Definition: format.c:43
Data structure associated with a single frame of data.
enum ast_frame_type frametype
Structure for mutex and tracking information.
Definition: lock.h:135
Structure for variables, used for configurations and for channel variables.
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