git migration: Refactor the ASTERISK_FILE_VERSION macro
[asterisk/asterisk.git] / apps / app_amd.c
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 ASTERISK_REGISTER_FILE()
47
48 #include "asterisk/module.h"
49 #include "asterisk/lock.h"
50 #include "asterisk/channel.h"
51 #include "asterisk/dsp.h"
52 #include "asterisk/pbx.h"
53 #include "asterisk/config.h"
54 #include "asterisk/app.h"
55 #include "asterisk/format_cache.h"
56
57 /*** DOCUMENTATION
58         <application name="AMD" language="en_US">
59                 <synopsis>
60                         Attempt to detect answering machines.
61                 </synopsis>
62                 <syntax>
63                         <parameter name="initialSilence" required="false">
64                                 <para>Is maximum initial silence duration before greeting.</para>
65                                 <para>If this is exceeded set as MACHINE</para>
66                         </parameter>
67                         <parameter name="greeting" required="false">
68                                 <para>is the maximum length of a greeting.</para>
69                                 <para>If this is exceeded set as MACHINE</para>
70                         </parameter>
71                         <parameter name="afterGreetingSilence" required="false">
72                                 <para>Is the silence after detecting a greeting.</para>
73                                 <para>If this is exceeded set as HUMAN</para>
74                         </parameter>
75                         <parameter name="totalAnalysis Time" required="false">
76                                 <para>Is the maximum time allowed for the algorithm</para>
77                                 <para>to decide HUMAN or MACHINE</para>
78                         </parameter>
79                         <parameter name="miniumWordLength" required="false">
80                                 <para>Is the minimum duration of Voice considered to be a word</para>
81                         </parameter>
82                         <parameter name="betweenWordSilence" required="false">
83                                 <para>Is the minimum duration of silence after a word to
84                                 consider the audio that follows to be a new word</para>
85                         </parameter>
86                         <parameter name="maximumNumberOfWords" required="false">
87                                 <para>Is the maximum number of words in a greeting</para>
88                                 <para>If this is exceeded set as MACHINE</para>
89                         </parameter>
90                         <parameter name="silenceThreshold" required="false">
91                                 <para>How long do we consider silence</para>
92                         </parameter>
93                         <parameter name="maximumWordLength" required="false">
94                                 <para>Is the maximum duration of a word to accept.</para>
95                                 <para>If exceeded set as MACHINE</para>
96                         </parameter>
97                 </syntax>
98                 <description>
99                         <para>This application attempts to detect answering machines at the beginning
100                         of outbound calls. Simply call this application after the call
101                         has been answered (outbound only, of course).</para>
102                         <para>When loaded, AMD reads amd.conf and uses the parameters specified as
103                         default values. Those default values get overwritten when the calling AMD
104                         with parameters.</para>
105                         <para>This application sets the following channel variables:</para>
106                         <variablelist>
107                                 <variable name="AMDSTATUS">
108                                         <para>This is the status of the answering machine detection</para>
109                                         <value name="MACHINE" />
110                                         <value name="HUMAN" />
111                                         <value name="NOTSURE" />
112                                         <value name="HANGUP" />
113                                 </variable>
114                                 <variable name="AMDCAUSE">
115                                         <para>Indicates the cause that led to the conclusion</para>
116                                         <value name="TOOLONG">
117                                                 Total Time.
118                                         </value>
119                                         <value name="INITIALSILENCE">
120                                                 Silence Duration - Initial Silence.
121                                         </value>
122                                         <value name="HUMAN">
123                                                 Silence Duration - afterGreetingSilence.
124                                         </value>
125                                         <value name="LONGGREETING">
126                                                 Voice Duration - Greeting.
127                                         </value>
128                                         <value name="MAXWORDLENGTH">
129                                                 Word Length - max length of a single word.
130                                         </value>
131                                         <value name="MAXWORDS">
132                                                 Word Count - maximum number of words.
133                                         </value>        
134                                 </variable>
135                         </variablelist>
136                 </description>
137                 <see-also>
138                         <ref type="application">WaitForSilence</ref>
139                         <ref type="application">WaitForNoise</ref>
140                 </see-also>
141         </application>
142
143  ***/
144
145 static const char app[] = "AMD";
146
147 #define STATE_IN_WORD       1
148 #define STATE_IN_SILENCE    2
149
150 /* Some default values for the algorithm parameters. These defaults will be overwritten from amd.conf */
151 static int dfltInitialSilence       = 2500;
152 static int dfltGreeting             = 1500;
153 static int dfltAfterGreetingSilence = 800;
154 static int dfltTotalAnalysisTime    = 5000;
155 static int dfltMinimumWordLength    = 100;
156 static int dfltBetweenWordsSilence  = 50;
157 static int dfltMaximumNumberOfWords = 3;
158 static int dfltSilenceThreshold     = 256;
159 static int dfltMaximumWordLength    = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
160
161 /* Set to the lowest ms value provided in amd.conf or application parameters */
162 static int dfltMaxWaitTimeForFrame  = 50;
163
164 static void isAnsweringMachine(struct ast_channel *chan, const char *data)
165 {
166         int res = 0;
167         struct ast_frame *f = NULL;
168         struct ast_dsp *silenceDetector = NULL;
169         int dspsilence = 0, framelength = 0;
170         RAII_VAR(struct ast_format *, readFormat, NULL, ao2_cleanup);
171         int inInitialSilence = 1;
172         int inGreeting = 0;
173         int voiceDuration = 0;
174         int silenceDuration = 0;
175         int iTotalTime = 0;
176         int iWordsCount = 0;
177         int currentState = STATE_IN_WORD;
178         int consecutiveVoiceDuration = 0;
179         char amdCause[256] = "", amdStatus[256] = "";
180         char *parse = ast_strdupa(data);
181
182         /* Lets set the initial values of the variables that will control the algorithm.
183            The initial values are the default ones. If they are passed as arguments
184            when invoking the application, then the default values will be overwritten
185            by the ones passed as parameters. */
186         int initialSilence       = dfltInitialSilence;
187         int greeting             = dfltGreeting;
188         int afterGreetingSilence = dfltAfterGreetingSilence;
189         int totalAnalysisTime    = dfltTotalAnalysisTime;
190         int minimumWordLength    = dfltMinimumWordLength;
191         int betweenWordsSilence  = dfltBetweenWordsSilence;
192         int maximumNumberOfWords = dfltMaximumNumberOfWords;
193         int silenceThreshold     = dfltSilenceThreshold;
194         int maximumWordLength    = dfltMaximumWordLength;
195         int maxWaitTimeForFrame  = dfltMaxWaitTimeForFrame;
196
197         AST_DECLARE_APP_ARGS(args,
198                 AST_APP_ARG(argInitialSilence);
199                 AST_APP_ARG(argGreeting);
200                 AST_APP_ARG(argAfterGreetingSilence);
201                 AST_APP_ARG(argTotalAnalysisTime);
202                 AST_APP_ARG(argMinimumWordLength);
203                 AST_APP_ARG(argBetweenWordsSilence);
204                 AST_APP_ARG(argMaximumNumberOfWords);
205                 AST_APP_ARG(argSilenceThreshold);
206                 AST_APP_ARG(argMaximumWordLength);
207         );
208
209         ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", ast_channel_name(chan),
210                 S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, "(N/A)"),
211                 S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, "(N/A)"),
212                 ast_format_get_name(ast_channel_readformat(chan)));
213
214         /* Lets parse the arguments. */
215         if (!ast_strlen_zero(parse)) {
216                 /* Some arguments have been passed. Lets parse them and overwrite the defaults. */
217                 AST_STANDARD_APP_ARGS(args, parse);
218                 if (!ast_strlen_zero(args.argInitialSilence))
219                         initialSilence = atoi(args.argInitialSilence);
220                 if (!ast_strlen_zero(args.argGreeting))
221                         greeting = atoi(args.argGreeting);
222                 if (!ast_strlen_zero(args.argAfterGreetingSilence))
223                         afterGreetingSilence = atoi(args.argAfterGreetingSilence);
224                 if (!ast_strlen_zero(args.argTotalAnalysisTime))
225                         totalAnalysisTime = atoi(args.argTotalAnalysisTime);
226                 if (!ast_strlen_zero(args.argMinimumWordLength))
227                         minimumWordLength = atoi(args.argMinimumWordLength);
228                 if (!ast_strlen_zero(args.argBetweenWordsSilence))
229                         betweenWordsSilence = atoi(args.argBetweenWordsSilence);
230                 if (!ast_strlen_zero(args.argMaximumNumberOfWords))
231                         maximumNumberOfWords = atoi(args.argMaximumNumberOfWords);
232                 if (!ast_strlen_zero(args.argSilenceThreshold))
233                         silenceThreshold = atoi(args.argSilenceThreshold);
234                 if (!ast_strlen_zero(args.argMaximumWordLength))
235                         maximumWordLength = atoi(args.argMaximumWordLength);
236         } else {
237                 ast_debug(1, "AMD using the default parameters.\n");
238         }
239
240         /* Find lowest ms value, that will be max wait time for a frame */
241         if (maxWaitTimeForFrame > initialSilence)
242                 maxWaitTimeForFrame = initialSilence;
243         if (maxWaitTimeForFrame > greeting)
244                 maxWaitTimeForFrame = greeting;
245         if (maxWaitTimeForFrame > afterGreetingSilence)
246                 maxWaitTimeForFrame = afterGreetingSilence;
247         if (maxWaitTimeForFrame > totalAnalysisTime)
248                 maxWaitTimeForFrame = totalAnalysisTime;
249         if (maxWaitTimeForFrame > minimumWordLength)
250                 maxWaitTimeForFrame = minimumWordLength;
251         if (maxWaitTimeForFrame > betweenWordsSilence)
252                 maxWaitTimeForFrame = betweenWordsSilence;
253
254         /* Now we're ready to roll! */
255         ast_verb(3, "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
256                 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d] \n",
257                                 initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
258                                 minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold, maximumWordLength);
259
260         /* Set read format to signed linear so we get signed linear frames in */
261         readFormat = ao2_bump(ast_channel_readformat(chan));
262         if (ast_set_read_format(chan, ast_format_slin) < 0 ) {
263                 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", ast_channel_name(chan));
264                 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
265                 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
266                 return;
267         }
268
269         /* Create a new DSP that will detect the silence */
270         if (!(silenceDetector = ast_dsp_new())) {
271                 ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", ast_channel_name(chan));
272                 pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
273                 pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
274                 return;
275         }
276
277         /* Set silence threshold to specified value */
278         ast_dsp_set_threshold(silenceDetector, silenceThreshold);
279
280         /* Now we go into a loop waiting for frames from the channel */
281         while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
282
283                 /* If we fail to read in a frame, that means they hung up */
284                 if (!(f = ast_read(chan))) {
285                         ast_verb(3, "AMD: Channel [%s]. HANGUP\n", ast_channel_name(chan));
286                         ast_debug(1, "Got hangup\n");
287                         strcpy(amdStatus, "HANGUP");
288                         res = 1;
289                         break;
290                 }
291
292                 if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_NULL || f->frametype == AST_FRAME_CNG) {
293                         /* If the total time exceeds the analysis time then give up as we are not too sure */
294                         if (f->frametype == AST_FRAME_VOICE) {
295                                 framelength = (ast_codec_samples_count(f) / DEFAULT_SAMPLES_PER_MS);
296                         } else {
297                                 framelength = 2 * maxWaitTimeForFrame;
298                         }
299
300                         iTotalTime += framelength;
301                         if (iTotalTime >= totalAnalysisTime) {
302                                 ast_verb(3, "AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
303                                 ast_frfree(f);
304                                 strcpy(amdStatus , "NOTSURE");
305                                 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
306                                 break;
307                         }
308
309                         /* Feed the frame of audio into the silence detector and see if we get a result */
310                         if (f->frametype != AST_FRAME_VOICE)
311                                 dspsilence += 2 * maxWaitTimeForFrame;
312                         else {
313                                 dspsilence = 0;
314                                 ast_dsp_silence(silenceDetector, f, &dspsilence);
315                         }
316
317                         if (dspsilence > 0) {
318                                 silenceDuration = dspsilence;
319                                 
320                                 if (silenceDuration >= betweenWordsSilence) {
321                                         if (currentState != STATE_IN_SILENCE ) {
322                                                 ast_verb(3, "AMD: Channel [%s]. Changed state to STATE_IN_SILENCE\n", ast_channel_name(chan));
323                                         }
324                                         /* Find words less than word duration */
325                                         if (consecutiveVoiceDuration < minimumWordLength && consecutiveVoiceDuration > 0){
326                                                 ast_verb(3, "AMD: Channel [%s]. Short Word Duration: %d\n", ast_channel_name(chan), consecutiveVoiceDuration);
327                                         }
328                                         currentState  = STATE_IN_SILENCE;
329                                         consecutiveVoiceDuration = 0;
330                                 }
331
332                                 if (inInitialSilence == 1  && silenceDuration >= initialSilence) {
333                                         ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
334                                                 ast_channel_name(chan), silenceDuration, initialSilence);
335                                         ast_frfree(f);
336                                         strcpy(amdStatus , "MACHINE");
337                                         sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
338                                         res = 1;
339                                         break;
340                                 }
341                                 
342                                 if (silenceDuration >= afterGreetingSilence  &&  inGreeting == 1) {
343                                         ast_verb(3, "AMD: Channel [%s]. HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
344                                                 ast_channel_name(chan), silenceDuration, afterGreetingSilence);
345                                         ast_frfree(f);
346                                         strcpy(amdStatus , "HUMAN");
347                                         sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
348                                         res = 1;
349                                         break;
350                                 }
351                                 
352                         } else {
353                                 consecutiveVoiceDuration += framelength;
354                                 voiceDuration += framelength;
355
356                                 /* If I have enough consecutive voice to say that I am in a Word, I can only increment the
357                                    number of words if my previous state was Silence, which means that I moved into a word. */
358                                 if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
359                                         iWordsCount++;
360                                         ast_verb(3, "AMD: Channel [%s]. Word detected. iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
361                                         currentState = STATE_IN_WORD;
362                                 }
363                                 if (consecutiveVoiceDuration >= maximumWordLength){
364                                         ast_verb(3, "AMD: Channel [%s]. Maximum Word Length detected. [%d]\n", ast_channel_name(chan), consecutiveVoiceDuration);
365                                         ast_frfree(f);
366                                         strcpy(amdStatus , "MACHINE");
367                                         sprintf(amdCause , "MAXWORDLENGTH-%d", consecutiveVoiceDuration);
368                                         break;
369                                 }
370                                 if (iWordsCount >= maximumNumberOfWords) {
371                                         ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: iWordsCount:%d\n", ast_channel_name(chan), iWordsCount);
372                                         ast_frfree(f);
373                                         strcpy(amdStatus , "MACHINE");
374                                         sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
375                                         res = 1;
376                                         break;
377                                 }
378
379                                 if (inGreeting == 1 && voiceDuration >= greeting) {
380                                         ast_verb(3, "AMD: Channel [%s]. ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", ast_channel_name(chan), voiceDuration, greeting);
381                                         ast_frfree(f);
382                                         strcpy(amdStatus , "MACHINE");
383                                         sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
384                                         res = 1;
385                                         break;
386                                 }
387
388                                 if (voiceDuration >= minimumWordLength ) {
389                                         if (silenceDuration > 0)
390                                                 ast_verb(3, "AMD: Channel [%s]. Detected Talk, previous silence duration: %d\n", ast_channel_name(chan), silenceDuration);
391                                         silenceDuration = 0;
392                                 }
393                                 if (consecutiveVoiceDuration >= minimumWordLength && inGreeting == 0) {
394                                         /* Only go in here once to change the greeting flag when we detect the 1st word */
395                                         if (silenceDuration > 0)
396                                                 ast_verb(3, "AMD: Channel [%s]. Before Greeting Time:  silenceDuration: %d voiceDuration: %d\n", ast_channel_name(chan), silenceDuration, voiceDuration);
397                                         inInitialSilence = 0;
398                                         inGreeting = 1;
399                                 }
400                                 
401                         }
402                 }
403                 ast_frfree(f);
404         }
405         
406         if (!res) {
407                 /* It took too long to get a frame back. Giving up. */
408                 ast_verb(3, "AMD: Channel [%s]. Too long...\n", ast_channel_name(chan));
409                 strcpy(amdStatus , "NOTSURE");
410                 sprintf(amdCause , "TOOLONG-%d", iTotalTime);
411         }
412
413         /* Set the status and cause on the channel */
414         pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus);
415         pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause);
416
417         /* Restore channel read format */
418         if (readFormat && ast_set_read_format(chan, readFormat))
419                 ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", ast_channel_name(chan));
420
421         /* Free the DSP used to detect silence */
422         ast_dsp_free(silenceDetector);
423
424         return;
425 }
426
427
428 static int amd_exec(struct ast_channel *chan, const char *data)
429 {
430         isAnsweringMachine(chan, data);
431
432         return 0;
433 }
434
435 static int load_config(int reload)
436 {
437         struct ast_config *cfg = NULL;
438         char *cat = NULL;
439         struct ast_variable *var = NULL;
440         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
441
442         dfltSilenceThreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
443
444         if (!(cfg = ast_config_load("amd.conf", config_flags))) {
445                 ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
446                 return -1;
447         } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
448                 return 0;
449         } else if (cfg == CONFIG_STATUS_FILEINVALID) {
450                 ast_log(LOG_ERROR, "Config file amd.conf is in an invalid format.  Aborting.\n");
451                 return -1;
452         }
453
454         cat = ast_category_browse(cfg, NULL);
455
456         while (cat) {
457                 if (!strcasecmp(cat, "general") ) {
458                         var = ast_variable_browse(cfg, cat);
459                         while (var) {
460                                 if (!strcasecmp(var->name, "initial_silence")) {
461                                         dfltInitialSilence = atoi(var->value);
462                                 } else if (!strcasecmp(var->name, "greeting")) {
463                                         dfltGreeting = atoi(var->value);
464                                 } else if (!strcasecmp(var->name, "after_greeting_silence")) {
465                                         dfltAfterGreetingSilence = atoi(var->value);
466                                 } else if (!strcasecmp(var->name, "silence_threshold")) {
467                                         dfltSilenceThreshold = atoi(var->value);
468                                 } else if (!strcasecmp(var->name, "total_analysis_time")) {
469                                         dfltTotalAnalysisTime = atoi(var->value);
470                                 } else if (!strcasecmp(var->name, "min_word_length")) {
471                                         dfltMinimumWordLength = atoi(var->value);
472                                 } else if (!strcasecmp(var->name, "between_words_silence")) {
473                                         dfltBetweenWordsSilence = atoi(var->value);
474                                 } else if (!strcasecmp(var->name, "maximum_number_of_words")) {
475                                         dfltMaximumNumberOfWords = atoi(var->value);
476                                 } else if (!strcasecmp(var->name, "maximum_word_length")) {
477                                         dfltMaximumWordLength = atoi(var->value);
478
479                                 } else {
480                                         ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
481                                                 app, cat, var->name, var->lineno);
482                                 }
483                                 var = var->next;
484                         }
485                 }
486                 cat = ast_category_browse(cfg, cat);
487         }
488
489         ast_config_destroy(cfg);
490
491         ast_verb(3, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
492                 "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
493                 dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
494                 dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength);
495
496         return 0;
497 }
498
499 static int unload_module(void)
500 {
501         return ast_unregister_application(app);
502 }
503
504 /*!
505  * \brief Load the module
506  *
507  * Module loading including tests for configuration or dependencies.
508  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
509  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
510  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the 
511  * configuration file or other non-critical problem return 
512  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
513  */
514 static int load_module(void)
515 {
516         if (load_config(0) || ast_register_application_xml(app, amd_exec)) {
517                 return AST_MODULE_LOAD_DECLINE;
518         }
519
520         return AST_MODULE_LOAD_SUCCESS;
521 }
522
523 static int reload(void)
524 {
525         if (load_config(1))
526                 return AST_MODULE_LOAD_DECLINE;
527         return AST_MODULE_LOAD_SUCCESS;
528 }
529
530 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
531                 .support_level = AST_MODULE_SUPPORT_EXTENDED,
532                 .load = load_module,
533                 .unload = unload_module,
534                 .reload = reload,
535 );