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