Asterisk - The Open Source Telephony Project  21.4.1
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"
53 #include "asterisk/format_cache.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 
149 static 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 */
155 static int dfltInitialSilence = 2500;
156 static int dfltGreeting = 1500;
157 static int dfltAfterGreetingSilence = 800;
158 static int dfltTotalAnalysisTime = 5000;
159 static int dfltMinimumWordLength = 100;
160 static int dfltBetweenWordsSilence = 50;
161 static int dfltMaximumNumberOfWords = 2;
162 static int dfltSilenceThreshold = 256;
163 static int dfltMaximumWordLength = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
164 static char *dfltAudioFile = NULL;
165 
166 static ast_mutex_t config_lock;
167 
168 /* Set to the lowest ms value provided in amd.conf or application parameters */
169 static int dfltMaxWaitTimeForFrame = 50;
170 
171 static 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 
220  ast_mutex_lock(&config_lock);
221  if (!ast_strlen_zero(dfltAudioFile)) {
222  audioFile = ast_strdupa(dfltAudioFile);
223  }
224  ast_mutex_unlock(&config_lock);
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)"),
229  ast_format_get_name(ast_channel_readformat(chan)));
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. */
234  AST_STANDARD_APP_ARGS(args, parse);
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 
340  if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_CNG) {
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) {
346  framelength = (ast_codec_samples_count(f) / DEFAULT_SAMPLES_PER_MS);
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 
498 static int amd_exec(struct ast_channel *chan, const char *data)
499 {
500  isAnsweringMachine(chan, data);
501 
502  return 0;
503 }
504 
505 static 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 
512  dfltSilenceThreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
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")) {
549  ast_mutex_lock(&config_lock);
550  if (dfltAudioFile) {
551  ast_free(dfltAudioFile);
552  dfltAudioFile = NULL;
553  }
554  if (!ast_strlen_zero(var->value)) {
555  dfltAudioFile = ast_strdup(var->value);
556  }
557  ast_mutex_unlock(&config_lock);
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 
568  ast_config_destroy(cfg);
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",
572  dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
573  dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength);
574 
575  return 0;
576 }
577 
578 static int unload_module(void)
579 {
580  ast_mutex_lock(&config_lock);
581  if (dfltAudioFile) {
582  ast_free(dfltAudioFile);
583  }
584  ast_mutex_unlock(&config_lock);
585  ast_mutex_destroy(&config_lock);
586  return ast_unregister_application(app);
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  */
599 static int load_module(void)
600 {
601  ast_mutex_init(&config_lock);
602  if (load_config(0) || ast_register_application_xml(app, amd_exec)) {
604  }
605 
607 }
608 
609 static int reload(void)
610 {
611  if (load_config(1))
614 }
615 
616 AST_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,
621 );
struct ast_variable * next
Main Channel structure associated with a channel.
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1293
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
Convenient Signal Processing routines.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
struct ast_dsp * ast_dsp_new(void)
Allocates a new dsp, assumes 8khz for internal sample rate.
Definition: dsp.c:1758
Structure for variables, used for configurations and for channel variables.
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4257
Definition of a media format.
Definition: format.c:43
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition: format.c:334
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
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
General Asterisk PBX channel definitions.
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
Definition: channel.c:5762
Definition: dsp.c:407
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#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
unsigned int ast_codec_samples_count(struct ast_frame *frame)
Get the number of samples contained within a frame.
Definition: codec.c:379
#define ast_debug(level,...)
Log a DEBUG message.
Core PBX routines and definitions.
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
static int load_module(void)
Load the module.
Definition: app_amd.c:599
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Structure used to handle boolean flags.
Definition: utils.h:199
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...
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_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3162
Data structure associated with a single frame of data.
enum ast_frame_type frametype
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Definition: dsp.c:2009
Asterisk module definitions.
#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
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
Structure for mutex and tracking information.
Definition: lock.h:135
Media Format Cache API.
#define AST_APP_ARG(name)
Define an application argument.