Asterisk - The Open Source Telephony Project GIT-master-8924258
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
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 <since>
58 <version>1.4.0</version>
59 </since>
60 <synopsis>
61 Attempt to detect answering machines.
62 </synopsis>
63 <syntax>
64 <parameter name="initialSilence" required="false">
65 <para>Is maximum initial silence duration before greeting.</para>
66 <para>If this is exceeded, the result is detection as a MACHINE</para>
67 </parameter>
68 <parameter name="greeting" required="false">
69 <para>is the maximum length of a greeting.</para>
70 <para>If this is exceeded, the result is detection as a MACHINE</para>
71 </parameter>
72 <parameter name="afterGreetingSilence" required="false">
73 <para>Is the silence after detecting a greeting.</para>
74 <para>If this is exceeded, the result is detection as a HUMAN</para>
75 </parameter>
76 <parameter name="totalAnalysis Time" required="false">
77 <para>Is the maximum time allowed for the algorithm</para>
78 <para>to decide on whether the audio represents a HUMAN, or a MACHINE</para>
79 </parameter>
80 <parameter name="miniumWordLength" required="false">
81 <para>Is the minimum duration of Voice considered to be a word</para>
82 </parameter>
83 <parameter name="betweenWordSilence" required="false">
84 <para>Is the minimum duration of silence after a word to
85 consider the audio that follows to be a new word</para>
86 </parameter>
87 <parameter name="maximumNumberOfWords" required="false">
88 <para>Is the maximum number of words in a greeting</para>
89 <para>If this is exceeded, then the result is detection as a MACHINE</para>
90 </parameter>
91 <parameter name="silenceThreshold" required="false">
92 <para>What is the average level of noise from 0 to 32767 which if not exceeded, should be considered silence?</para>
93 </parameter>
94 <parameter name="maximumWordLength" required="false">
95 <para>Is the maximum duration of a word to accept.</para>
96 <para>If exceeded, then the result is detection as a MACHINE</para>
97 </parameter>
98 <parameter name="audioFile" required="false">
99 <para>Is an audio file to play to the caller while AMD is in progress.</para>
100 <para>By default, no audio file is played.</para>
101 <para>If an audio file is configured in amd.conf, then that file will be used
102 if one is not specified here. That file may be overridden by this argument.</para>
103 </parameter>
104 </syntax>
105 <description>
106 <para>This application attempts to detect answering machines at the beginning
107 of outbound calls. Simply call this application after the call
108 has been answered (outbound only, of course).</para>
109 <para>When loaded, AMD reads amd.conf and uses the parameters specified as
110 default values. Those default values get overwritten when the calling AMD
111 with parameters.</para>
112 <para>This application sets the following channel variables:</para>
113 <variablelist>
114 <variable name="AMDSTATUS">
115 <para>This is the status of the answering machine detection</para>
116 <value name="MACHINE" />
117 <value name="HUMAN" />
118 <value name="NOTSURE" />
119 <value name="HANGUP" />
120 </variable>
121 <variable name="AMDCAUSE">
122 <para>Indicates the cause that led to the conclusion</para>
123 <value name="TOOLONG">
124 Total Time.
125 </value>
126 <value name="INITIALSILENCE">
127 Silence Duration - Initial Silence.
128 </value>
129 <value name="HUMAN">
130 Silence Duration - afterGreetingSilence.
131 </value>
132 <value name="LONGGREETING">
133 Voice Duration - Greeting.
134 </value>
135 <value name="MAXWORDLENGTH">
136 Word Length - max length of a single word.
137 </value>
138 <value name="MAXWORDS">
139 Word Count - maximum number of words.
140 </value>
141 </variable>
142 </variablelist>
143 </description>
144 <see-also>
145 <ref type="application">WaitForSilence</ref>
146 <ref type="application">WaitForNoise</ref>
147 </see-also>
148 </application>
149
150 ***/
151
152static const char app[] = "AMD";
153
154#define STATE_IN_WORD 1
155#define STATE_IN_SILENCE 2
156
157/* Some default values for the algorithm parameters. These defaults will be overwritten from amd.conf */
158static int dfltInitialSilence = 2500;
159static int dfltGreeting = 1500;
161static int dfltTotalAnalysisTime = 5000;
162static int dfltMinimumWordLength = 100;
165static int dfltSilenceThreshold = 256;
166static int dfltMaximumWordLength = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
167static char *dfltAudioFile = NULL;
168
170
171/* Set to the lowest ms value provided in amd.conf or application parameters */
173
174static void isAnsweringMachine(struct ast_channel *chan, const char *data)
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}
500
501static int amd_exec(struct ast_channel *chan, const char *data)
502{
503 isAnsweringMachine(chan, data);
504
505 return 0;
506}
507
508static int load_config(int reload)
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}
580
581static int unload_module(void)
582{
584 if (dfltAudioFile) {
586 }
590}
591
592/*!
593 * \brief Load the module
594 *
595 * Module loading including tests for configuration or dependencies.
596 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
597 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
598 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
599 * configuration file or other non-critical problem return
600 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
601 */
602static int load_module(void)
603{
607 }
608
610}
611
612static int reload(void)
613{
614 if (load_config(1))
617}
618
619AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
620 .support_level = AST_MODULE_SUPPORT_EXTENDED,
621 .load = load_module,
622 .unload = unload_module,
623 .reload = reload,
static int dfltBetweenWordsSilence
Definition: app_amd.c:163
static int amd_exec(struct ast_channel *chan, const char *data)
Definition: app_amd.c:501
static int dfltInitialSilence
Definition: app_amd.c:158
static int dfltMaximumNumberOfWords
Definition: app_amd.c:164
static const char app[]
Definition: app_amd.c:152
#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 load_module(void)
Load the module.
Definition: app_amd.c:602
static int dfltTotalAnalysisTime
Definition: app_amd.c:161
static char * dfltAudioFile
Definition: app_amd.c:167
static int unload_module(void)
Definition: app_amd.c:581
static int load_config(int reload)
Definition: app_amd.c:508
static int reload(void)
Definition: app_amd.c:612
static void isAnsweringMachine(struct ast_channel *chan, const char *data)
Definition: app_amd.c:174
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 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:3190
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4274
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
Definition: channel.c:5779
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:1301
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
@ 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 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:190
#define ast_mutex_unlock(a)
Definition: lock.h:194
#define ast_mutex_destroy(a)
Definition: lock.h:192
#define ast_mutex_lock(a)
Definition: lock.h:193
Asterisk module definitions.
@ AST_MODFLAG_DEFAULT
Definition: module.h:329
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ 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:640
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:139
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