app_amd: Add option to play audio during AMD.

Adds an option that will play an audio file
to the party while AMD is running on the
channel, so the called party does not just
hear silence.

ASTERISK-30179 #close

Change-Id: I4af306274552b61b3d9f0883c33f698abd4699b6
This commit is contained in:
Naveen Albert 2022-08-15 19:59:02 +00:00 committed by Friendly Automation
parent 3e7ce90f9c
commit 8c791f9a65
3 changed files with 59 additions and 4 deletions

View File

@ -92,6 +92,12 @@
<para>Is the maximum duration of a word to accept.</para>
<para>If exceeded, then the result is detection as a MACHINE</para>
</parameter>
<parameter name="audioFile" required="false">
<para>Is an audio file to play to the caller while AMD is in progress.</para>
<para>By default, no audio file is played.</para>
<para>If an audio file is configured in amd.conf, then that file will be used
if one is not specified here. That file may be overridden by this argument.</para>
</parameter>
</syntax>
<description>
<para>This application attempts to detect answering machines at the beginning
@ -155,6 +161,9 @@ static int dfltBetweenWordsSilence = 50;
static int dfltMaximumNumberOfWords = 2;
static int dfltSilenceThreshold = 256;
static int dfltMaximumWordLength = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
static char *dfltAudioFile = NULL;
static ast_mutex_t config_lock;
/* Set to the lowest ms value provided in amd.conf or application parameters */
static int dfltMaxWaitTimeForFrame = 50;
@ -179,7 +188,7 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
char amdCause[256] = "", amdStatus[256] = "";
char *parse = ast_strdupa(data);
/* Lets set the initial values of the variables that will control the algorithm.
/* Let's set the initial values of the variables that will control the algorithm.
The initial values are the default ones. If they are passed as arguments
when invoking the application, then the default values will be overwritten
by the ones passed as parameters. */
@ -193,6 +202,7 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
int silenceThreshold = dfltSilenceThreshold;
int maximumWordLength = dfltMaximumWordLength;
int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame;
const char *audioFile = NULL;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(argInitialSilence);
@ -204,8 +214,15 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
AST_APP_ARG(argMaximumNumberOfWords);
AST_APP_ARG(argSilenceThreshold);
AST_APP_ARG(argMaximumWordLength);
AST_APP_ARG(audioFile);
);
ast_mutex_lock(&config_lock);
if (!ast_strlen_zero(dfltAudioFile)) {
audioFile = ast_strdupa(dfltAudioFile);
}
ast_mutex_unlock(&config_lock);
ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", ast_channel_name(chan),
S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, "(N/A)"),
S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, "(N/A)"),
@ -233,6 +250,9 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
silenceThreshold = atoi(args.argSilenceThreshold);
if (!ast_strlen_zero(args.argMaximumWordLength))
maximumWordLength = atoi(args.argMaximumWordLength);
if (!ast_strlen_zero(args.audioFile)) {
audioFile = args.audioFile;
}
} else {
ast_debug(1, "AMD using the default parameters.\n");
}
@ -280,6 +300,11 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
/* Set our start time so we can tie the loop to real world time and not RTP updates */
amd_tvstart = ast_tvnow();
/* Optional audio file to play to caller while AMD is doing its thing. */
if (!ast_strlen_zero(audioFile)) {
ast_streamfile(chan, audioFile, ast_channel_language(chan));
}
/* Now we go into a loop waiting for frames from the channel */
while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
int ms = 0;
@ -462,10 +487,14 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
/* Free the DSP used to detect silence */
ast_dsp_free(silenceDetector);
/* If we were playing something to pass the time, stop it now. */
if (!ast_strlen_zero(audioFile)) {
ast_stopstream(chan);
}
return;
}
static int amd_exec(struct ast_channel *chan, const char *data)
{
isAnsweringMachine(chan, data);
@ -516,7 +545,16 @@ static int load_config(int reload)
dfltMaximumNumberOfWords = atoi(var->value);
} else if (!strcasecmp(var->name, "maximum_word_length")) {
dfltMaximumWordLength = atoi(var->value);
} else if (!strcasecmp(var->name, "playback_file")) {
ast_mutex_lock(&config_lock);
if (dfltAudioFile) {
ast_free(dfltAudioFile);
dfltAudioFile = NULL;
}
if (!ast_strlen_zero(var->value)) {
dfltAudioFile = ast_strdup(var->value);
}
ast_mutex_unlock(&config_lock);
} else {
ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
app, cat, var->name, var->lineno);
@ -539,6 +577,12 @@ static int load_config(int reload)
static int unload_module(void)
{
ast_mutex_lock(&config_lock);
if (dfltAudioFile) {
ast_free(dfltAudioFile);
}
ast_mutex_unlock(&config_lock);
ast_mutex_destroy(&config_lock);
return ast_unregister_application(app);
}
@ -554,6 +598,7 @@ static int unload_module(void)
*/
static int load_module(void)
{
ast_mutex_init(&config_lock);
if (load_config(0) || ast_register_application_xml(app, amd_exec)) {
return AST_MODULE_LOAD_DECLINE;
}

View File

@ -8,6 +8,11 @@ total_analysis_time = 5000 ; Maximum time allowed for the algorithm to decide
silence_threshold = 256 ; If the average level of noise in a sample does not reach
; this value, from a scale of 0 to 32767, then we will consider
; it to be silence.
;playback_file = ; Audio file to play while AMD is running, so the caller
; does not just hear silence. Note that specifying this here
; will apply to ALL AMD runs, so you may wish to set it
; in the dialplan as an argument to AMD() instead.
; Default is no audio file (not to play anything).
; Greeting ;
initial_silence = 2500 ; Maximum silence duration before the greeting.
@ -19,7 +24,7 @@ greeting = 1500 ; Maximum length of a greeting. If exceeded, then the
; Word detection ;
min_word_length = 100 ; Minimum duration of Voice to considered as a word
maximum_word_length = 5000 ; Maximum duration of a single Voice utterance allowed.
maximum_word_length = 5000 ; Maximum duration of a single Voice utterance allowed.
between_words_silence = 50 ; Minimum duration of silence after a word to consider
; the audio what follows as a new word

View File

@ -0,0 +1,5 @@
Subject: app_amd
An audio file to play during AMD processing can
now be specified to the AMD application or configured
in the amd.conf configuration file.