Asterisk - The Open Source Telephony Project GIT-master-545c459
Loading...
Searching...
No Matches
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="totalAnalysisTime" 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="minimumWordLength" required="false">
81 <para>Is the minimum duration of Voice considered to be a word</para>
82 </parameter>
83 <parameter name="betweenWordsSilence" 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 continuous voice to accept as a single word.</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 calling AMD
111 with parameters. All durations are specified in milliseconds.</para>
112 <para>This application sets the following channel variables, unless the detection
113 could not be performed at all, in which case both variables are set to the empty
114 string:</para>
115 <variablelist>
116 <variable name="AMDSTATUS">
117 <para>This is the status of the answering machine detection</para>
118 <value name="MACHINE" />
119 <value name="HUMAN" />
120 <value name="NOTSURE" />
121 <value name="HANGUP" />
122 </variable>
123 <variable name="AMDCAUSE">
124 <para>Indicates the cause that led to the conclusion. The measurement
125 which triggered it, and for most causes the parameter value it
126 breached, are appended to the cause name, separated by <literal>-</literal>.</para>
127 <value name="TOOLONG">
128 Total Time.
129 </value>
130 <value name="NOAUDIODATA">
131 Total Time.
132 </value>
133 <value name="INITIALSILENCE">
134 Silence Duration - initialSilence.
135 </value>
136 <value name="HUMAN">
137 Silence Duration - afterGreetingSilence.
138 </value>
139 <value name="LONGGREETING">
140 Voice Duration - greeting.
141 </value>
142 <value name="MAXWORDLENGTH">
143 Consecutive Voice Duration.
144 </value>
145 <value name="MAXWORDS">
146 Word Count - maximumNumberOfWords.
147 </value>
148 </variable>
149 </variablelist>
150 </description>
151 <see-also>
152 <ref type="application">WaitForSilence</ref>
153 <ref type="application">WaitForNoise</ref>
154 </see-also>
155 </application>
156
157 ***/
158
159static const char app[] = "AMD";
160
161#define STATE_IN_WORD 1
162#define STATE_IN_SILENCE 2
163
164/* Some default values for the algorithm parameters. These defaults will be overwritten from amd.conf */
165static int dfltInitialSilence = 2500;
166static int dfltGreeting = 1500;
168static int dfltTotalAnalysisTime = 5000;
169static int dfltMinimumWordLength = 100;
172static int dfltSilenceThreshold = 256;
173static int dfltMaximumWordLength = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
174static char *dfltAudioFile = NULL;
175
177
178/* Set to the lowest ms value provided in amd.conf or application parameters */
180
181static void isAnsweringMachine(struct ast_channel *chan, const char *data)
182{
183 int res = 0;
184 int audioFrameCount = 0;
185 struct ast_frame *f = NULL;
186 struct ast_dsp *silenceDetector = NULL;
187 struct timeval amd_tvstart;
188 int dspsilence = 0, framelength = 0;
189 RAII_VAR(struct ast_format *, readFormat, NULL, ao2_cleanup);
190 int inInitialSilence = 1;
191 int inGreeting = 0;
192 int voiceDuration = 0;
193 int silenceDuration = 0;
194 int iTotalTime = 0;
195 int iWordsCount = 0;
196 int currentState = STATE_IN_WORD;
197 int consecutiveVoiceDuration = 0;
198 char amdCause[256] = "", amdStatus[256] = "";
199 char *parse = ast_strdupa(data);
200
201 /* Let's set the initial values of the variables that will control the algorithm.
202 The initial values are the default ones. If they are passed as arguments
203 when invoking the application, then the default values will be overwritten
204 by the ones passed as parameters. */
205 int initialSilence = dfltInitialSilence;
206 int greeting = dfltGreeting;
207 int afterGreetingSilence = dfltAfterGreetingSilence;
208 int totalAnalysisTime = dfltTotalAnalysisTime;
209 int minimumWordLength = dfltMinimumWordLength;
210 int betweenWordsSilence = dfltBetweenWordsSilence;
211 int maximumNumberOfWords = dfltMaximumNumberOfWords;
212 int silenceThreshold = dfltSilenceThreshold;
213 int maximumWordLength = dfltMaximumWordLength;
214 int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame;
215 const char *audioFile = NULL;
216
218 AST_APP_ARG(argInitialSilence);
219 AST_APP_ARG(argGreeting);
220 AST_APP_ARG(argAfterGreetingSilence);
221 AST_APP_ARG(argTotalAnalysisTime);
222 AST_APP_ARG(argMinimumWordLength);
223 AST_APP_ARG(argBetweenWordsSilence);
224 AST_APP_ARG(argMaximumNumberOfWords);
225 AST_APP_ARG(argSilenceThreshold);
226 AST_APP_ARG(argMaximumWordLength);
227 AST_APP_ARG(audioFile);
228 );
229
232 audioFile = ast_strdupa(dfltAudioFile);
233 }
235
236 ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", ast_channel_name(chan),
237 S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, "(N/A)"),
238 S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, "(N/A)"),
240
241 /* Lets parse the arguments. */
242 if (!ast_strlen_zero(parse)) {
243 /* Some arguments have been passed. Lets parse them and overwrite the defaults. */
245 if (!ast_strlen_zero(args.argInitialSilence))
246 initialSilence = atoi(args.argInitialSilence);
247 if (!ast_strlen_zero(args.argGreeting))
248 greeting = atoi(args.argGreeting);
249 if (!ast_strlen_zero(args.argAfterGreetingSilence))
250 afterGreetingSilence = atoi(args.argAfterGreetingSilence);
251 if (!ast_strlen_zero(args.argTotalAnalysisTime))
252 totalAnalysisTime = atoi(args.argTotalAnalysisTime);
253 if (!ast_strlen_zero(args.argMinimumWordLength))
254 minimumWordLength = atoi(args.argMinimumWordLength);
255 if (!ast_strlen_zero(args.argBetweenWordsSilence))
256 betweenWordsSilence = atoi(args.argBetweenWordsSilence);
257 if (!ast_strlen_zero(args.argMaximumNumberOfWords))
258 maximumNumberOfWords = atoi(args.argMaximumNumberOfWords);
259 if (!ast_strlen_zero(args.argSilenceThreshold))
260 silenceThreshold = atoi(args.argSilenceThreshold);
261 if (!ast_strlen_zero(args.argMaximumWordLength))
262 maximumWordLength = atoi(args.argMaximumWordLength);
263 if (!ast_strlen_zero(args.audioFile)) {
264 audioFile = args.audioFile;
265 }
266 } else {
267 ast_debug(1, "AMD using the default parameters.\n");
268 }
269
270 /* Find lowest ms value, that will be max wait time for a frame */
271 if (maxWaitTimeForFrame > initialSilence)
272 maxWaitTimeForFrame = initialSilence;
273 if (maxWaitTimeForFrame > greeting)
274 maxWaitTimeForFrame = greeting;
275 if (maxWaitTimeForFrame > afterGreetingSilence)
276 maxWaitTimeForFrame = afterGreetingSilence;
277 if (maxWaitTimeForFrame > totalAnalysisTime)
278 maxWaitTimeForFrame = totalAnalysisTime;
279 if (maxWaitTimeForFrame > minimumWordLength)
280 maxWaitTimeForFrame = minimumWordLength;
281 if (maxWaitTimeForFrame > betweenWordsSilence)
282 maxWaitTimeForFrame = betweenWordsSilence;
283
284 /* Now we're ready to roll! */
285 ast_verb(3, "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
286 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d] \n",
287 initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
288 minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold, maximumWordLength);
289
290 /* Set read format to signed linear so we get signed linear frames in */
291 readFormat = ao2_bump(ast_channel_readformat(chan));
292 if (ast_set_read_format(chan, ast_format_slin) < 0 ) {
293 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", ast_channel_name(chan));
294 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
295 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
296 return;
297 }
298
299 /* Create a new DSP that will detect the silence */
300 if (!(silenceDetector = ast_dsp_new())) {
301 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", ast_channel_name(chan));
302 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
303 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
304 return;
305 }
306
307 /* Set silence threshold to specified value */
308 ast_dsp_set_threshold(silenceDetector, silenceThreshold);
309
310 /* Set our start time so we can tie the loop to real world time and not RTP updates */
311 amd_tvstart = ast_tvnow();
312
313 /* Optional audio file to play to caller while AMD is doing its thing. */
314 if (!ast_strlen_zero(audioFile)) {
315 ast_streamfile(chan, audioFile, ast_channel_language(chan));
316 }
317
318 /* Now we go into a loop waiting for frames from the channel */
319 while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
320 int ms = 0;
321
322 /* Figure out how long we waited */
323 if (res >= 0) {
324 ms = 2 * maxWaitTimeForFrame - res;
325 }
326
327 /* If we fail to read in a frame, that means they hung up */
328 if (!(f = ast_read(chan))) {
329 ast_verb(3, "AMD: Channel [%s]. HANGUP\n", ast_channel_name(chan));
330 ast_debug(1, "Got hangup\n");
331 strcpy(amdStatus, "HANGUP");
332 res = 1;
333 break;
334 }
335
336 /* Check to make sure we haven't gone over our real-world timeout in case frames get stalled for whatever reason */
337 if ( (ast_tvdiff_ms(ast_tvnow(), amd_tvstart)) > totalAnalysisTime ) {
338 ast_frfree(f);
339 strcpy(amdStatus , "NOTSURE");
340 if ( audioFrameCount == 0 ) {
341 ast_verb(3, "AMD: Channel [%s]. No audio data received in [%d] seconds.\n", ast_channel_name(chan), totalAnalysisTime);
342 sprintf(amdCause , "NOAUDIODATA-%d", iTotalTime);
343 break;
344 }
345 ast_verb(3, "AMD: Channel [%s]. Timeout...\n", ast_channel_name(chan));
346 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
347 break;
348 }
349
351 /* keep track of the number of audio frames we get */
352 audioFrameCount++;
353
354 /* Figure out how long the frame is in milliseconds */
355 if (f->frametype == AST_FRAME_VOICE) {
357 } else {
358 framelength = ms;
359 }
360
361 iTotalTime += framelength;
362
363 ast_debug(1, "AMD: Channel [%s] frametype [%s] iTotalTime [%d] framelength [%d] totalAnalysisTime [%d]\n",
364 ast_channel_name(chan),
365 f->frametype == AST_FRAME_VOICE ? "AST_FRAME_VOICE" : "AST_FRAME_CNG",
366 iTotalTime, framelength, totalAnalysisTime);
367
368 /* If the total time exceeds the analysis time then give up as we are not too sure */
369 if (iTotalTime >= totalAnalysisTime) {
370 ast_verb(3, "AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
371 ast_frfree(f);
372 strcpy(amdStatus , "NOTSURE");
373 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
374 break;
375 }
376
377 /* Feed the frame of audio into the silence detector and see if we get a result */
378 if (f->frametype != AST_FRAME_VOICE)
379 dspsilence += framelength;
380 else {
381 dspsilence = 0;
382 ast_dsp_silence(silenceDetector, f, &dspsilence);
383 }
384
385 if (dspsilence > 0) {
386 silenceDuration = dspsilence;
387
388 if (silenceDuration >= betweenWordsSilence) {
389 if (currentState != STATE_IN_SILENCE ) {
390 ast_verb(3, "AMD: Channel [%s]. Changed state to STATE_IN_SILENCE\n", ast_channel_name(chan));
391 }
392 /* Find words less than word duration */
393 if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){
394 ast_verb(3, "AMD: Channel [%s]. Short Word Duration: %d\n", ast_channel_name(chan), consecutiveVoiceDuration);
395 }
396 currentState = STATE_IN_SILENCE;
397 consecutiveVoiceDuration = 0;
398 }
399
400 if (inInitialSilence == 1 && silenceDuration >= initialSilence) {
401 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
402 ast_channel_name(chan), silenceDuration, initialSilence);
403 ast_frfree(f);
404 strcpy(amdStatus , "MACHINE");
405 sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
406 res = 1;
407 break;
408 }
409
410 if (silenceDuration >= afterGreetingSilence && inGreeting == 1) {
411 ast_verb(3, "AMD: Channel [%s]. HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
412 ast_channel_name(chan), silenceDuration, afterGreetingSilence);
413 ast_frfree(f);
414 strcpy(amdStatus , "HUMAN");
415 sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
416 res = 1;
417 break;
418 }
419
420 } else {
421 consecutiveVoiceDuration += framelength;
422 voiceDuration += framelength;
423
424 /* If I have enough consecutive voice to say that I am in a Word, I can only increment the
425 number of words if my previous state was Silence, which means that I moved into a word. */
426 if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
427 iWordsCount++;
428 ast_verb(3, "AMD: Channel [%s]. Word detected. iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
429 currentState = STATE_IN_WORD;
430 }
431 if (consecutiveVoiceDuration >= maximumWordLength){
432 ast_verb(3, "AMD: Channel [%s]. Maximum Word Length detected. [%d]\n", ast_channel_name(chan), consecutiveVoiceDuration);
433 ast_frfree(f);
434 strcpy(amdStatus , "MACHINE");
435 sprintf(amdCause , "MAXWORDLENGTH-%d", consecutiveVoiceDuration);
436 break;
437 }
438 if (iWordsCount > maximumNumberOfWords) {
439 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
440 ast_frfree(f);
441 strcpy(amdStatus , "MACHINE");
442 sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
443 res = 1;
444 break;
445 }
446
447 if (inGreeting == 1 && voiceDuration >= greeting) {
448 ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", ast_channel_name(chan), voiceDuration, greeting);
449 ast_frfree(f);
450 strcpy(amdStatus , "MACHINE");
451 sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
452 res = 1;
453 break;
454 }
455
456 if (voiceDuration >= minimumWordLength ) {
457 if (silenceDuration > 0)
458 ast_verb(3, "AMD: Channel [%s]. Detected Talk, previous silence duration: %d\n", ast_channel_name(chan), silenceDuration);
459 silenceDuration = 0;
460 }
461 if (consecutiveVoiceDuration >= minimumWordLength && inGreeting == 0) {
462 /* Only go in here once to change the greeting flag when we detect the 1st word */
463 if (silenceDuration > 0)
464 ast_verb(3, "AMD: Channel [%s]. Before Greeting Time: silenceDuration: %d voiceDuration: %d\n", ast_channel_name(chan), silenceDuration, voiceDuration);
465 inInitialSilence = 0;
466 inGreeting = 1;
467 }
468
469 }
470 } else {
471 iTotalTime += ms;
472 if (iTotalTime >= totalAnalysisTime) {
473 ast_frfree(f);
474 strcpy(amdStatus , "NOTSURE");
475 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
476 break;
477 }
478 }
479 ast_frfree(f);
480 }
481
482 if (!res) {
483 /* It took too long to get a frame back. Giving up. */
484 ast_verb(3, "AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
485 strcpy(amdStatus , "NOTSURE");
486 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
487 }
488
489 /* Set the status and cause on the channel */
490 pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus);
491 pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause);
492
493 /* Restore channel read format */
494 if (readFormat && ast_set_read_format(chan, readFormat))
495 ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", ast_channel_name(chan));
496
497 /* Free the DSP used to detect silence */
498 ast_dsp_free(silenceDetector);
499
500 /* If we were playing something to pass the time, stop it now. */
501 if (!ast_strlen_zero(audioFile)) {
502 ast_stopstream(chan);
503 }
504
505 return;
506}
507
508static int amd_exec(struct ast_channel *chan, const char *data)
509{
510 isAnsweringMachine(chan, data);
511
512 return 0;
513}
514
515static int load_config(int reload)
516{
517 struct ast_config *cfg = NULL;
518 char *cat = NULL;
519 struct ast_variable *var = NULL;
520 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
521
523
524 if (!(cfg = ast_config_load("amd.conf", config_flags))) {
525 ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
526 return -1;
527 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
528 return 0;
529 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
530 ast_log(LOG_ERROR, "Config file amd.conf is in an invalid format. Aborting.\n");
531 return -1;
532 }
533
534 cat = ast_category_browse(cfg, NULL);
535
536 while (cat) {
537 if (!strcasecmp(cat, "general") ) {
538 var = ast_variable_browse(cfg, cat);
539 while (var) {
540 if (!strcasecmp(var->name, "initial_silence")) {
541 dfltInitialSilence = atoi(var->value);
542 } else if (!strcasecmp(var->name, "greeting")) {
543 dfltGreeting = atoi(var->value);
544 } else if (!strcasecmp(var->name, "after_greeting_silence")) {
545 dfltAfterGreetingSilence = atoi(var->value);
546 } else if (!strcasecmp(var->name, "silence_threshold")) {
547 dfltSilenceThreshold = atoi(var->value);
548 } else if (!strcasecmp(var->name, "total_analysis_time")) {
549 dfltTotalAnalysisTime = atoi(var->value);
550 } else if (!strcasecmp(var->name, "min_word_length")) {
551 dfltMinimumWordLength = atoi(var->value);
552 } else if (!strcasecmp(var->name, "between_words_silence")) {
553 dfltBetweenWordsSilence = atoi(var->value);
554 } else if (!strcasecmp(var->name, "maximum_number_of_words")) {
555 dfltMaximumNumberOfWords = atoi(var->value);
556 } else if (!strcasecmp(var->name, "maximum_word_length")) {
557 dfltMaximumWordLength = atoi(var->value);
558 } else if (!strcasecmp(var->name, "playback_file")) {
560 if (dfltAudioFile) {
563 }
564 if (!ast_strlen_zero(var->value)) {
565 dfltAudioFile = ast_strdup(var->value);
566 }
568 } else {
569 ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
570 app, cat, var->name, var->lineno);
571 }
572 var = var->next;
573 }
574 }
575 cat = ast_category_browse(cfg, cat);
576 }
577
579
580 ast_verb(5, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
581 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
584
585 return 0;
586}
587
598
599/*!
600 * \brief Load the module
601 *
602 * Module loading including tests for configuration or dependencies.
603 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
604 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
605 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
606 * configuration file or other non-critical problem return
607 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
608 */
609static int load_module(void)
610{
614 }
615
617}
618
619static int reload(void)
620{
621 if (load_config(1))
624}
625
626AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
627 .support_level = AST_MODULE_SUPPORT_EXTENDED,
628 .load = load_module,
629 .unload = unload_module,
630 .reload = reload,
static int load_config(void)
static int dfltBetweenWordsSilence
Definition app_amd.c:170
static int amd_exec(struct ast_channel *chan, const char *data)
Definition app_amd.c:508
static int dfltInitialSilence
Definition app_amd.c:165
static int dfltMaximumNumberOfWords
Definition app_amd.c:171
static const char app[]
Definition app_amd.c:159
#define STATE_IN_SILENCE
Definition app_amd.c:162
static int dfltGreeting
Definition app_amd.c:166
static ast_mutex_t config_lock
Definition app_amd.c:176
#define STATE_IN_WORD
Definition app_amd.c:161
static int dfltMinimumWordLength
Definition app_amd.c:169
static int dfltMaximumWordLength
Definition app_amd.c:173
static int load_module(void)
Load the module.
Definition app_amd.c:609
static int dfltTotalAnalysisTime
Definition app_amd.c:168
static char * dfltAudioFile
Definition app_amd.c:174
static int unload_module(void)
Definition app_amd.c:588
static int reload(void)
Definition app_amd.c:619
static void isAnsweringMachine(struct ast_channel *chan, const char *data)
Definition app_amd.c:181
static int dfltAfterGreetingSilence
Definition app_amd.c:167
static int dfltMaxWaitTimeForFrame
Definition app_amd.c:179
static int dfltSilenceThreshold
Definition app_amd.c:172
#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:3166
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition channel.c:4278
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
Definition channel.c:5785
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:1973
void ast_dsp_free(struct ast_dsp *dsp)
Definition dsp.c:1968
@ THRESHOLD_SILENCE
Definition dsp.h:75
int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence)
Process the audio frame for silence.
Definition dsp.c:1672
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Definition dsp.c:2196
struct ast_dsp * ast_dsp_new(void)
Allocates a new dsp, assumes 8khz for internal sample rate.
Definition dsp.c:1943
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition file.c:223
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition file.c:1320
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.
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.
@ CONFIG_FLAG_FILEUNCHANGED
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition extconf.c:3324
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition extconf.c:1287
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition extconf.c:1213
#define ast_frfree(fr)
#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:193
#define ast_mutex_unlock(a)
Definition lock.h:197
#define ast_mutex_destroy(a)
Definition lock.h:195
#define ast_mutex_lock(a)
Definition lock.h:196
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:404
@ 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.
static struct @523 args
#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:421
Structure used to handle boolean flags.
Definition utils.h:220
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:142
Structure for variables, used for configurations and for channel variables.
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:981