Skip to main content

mod_avmd

About

Advanced Voice Mail Detection module is designed to detect the single-frequency constant-amplitude tone in audio signal. Common usage is to detect "beep" sound at the end of the voicemail or answering machine greetings (similar to this 1000 Hz beep [raw] shown above, see also "Dialplan examples" below). This is useful in cases where you wish to leave a recorded message on the recipient's message system but don't want to wait for a pause to complete that is common when using wait_for_silence, and you are looking for a beep sound to start recording. Avmd may (should) also be used on the outbound calls made by dialer, simultaneously with answering machine detection module (AMD), because the detection of voicemail is strong indication of the call being answered by machine and AMD doesn't detect sounds (though in AMD case you often want to be able to detect machine as soon as possible, more quickly than at the end of the voicemail greetings).

Click to expand Table of Contents

How it works and how hungry it is

AVMD uses DESA-2 algorithm to separate estimations of instantaneous frequency and amplitude from TKEO energy operator of an audio signal. DESA-2 uses three sample derivative estimation. This algorithm is fast, computationally simple and a little inaccurate in presence of noise, though good enough for almost all practical uses.

AVMD offers three modes of detection, so that it can be used to detect three types of sound:

  • detection of constant amplitude
  • detection of constant frequency (this may be used to detect dtmf sound)
  • detection of constant amplitude and frequency (default)

The call to AVMD's start method results in launching of detection threads processing audio frames with different resolutions and/or offsets. The avmd::start event is fired after successful start of the AVMD session. The FreeSWITCH core then calls AVMD on each incoming audio frame and all detection threads process incoming frame according to their resolution, offset and lag. Once the signal has been qualified as meeting the specified criteria (of constant amplitude, frequency or both) the avmd::beep event is raised. Application should then stop AVMD running on the call which results in avmd::stop event being fired.

There are known also other energy separation algorithms including bot not limited to DESA-1, variance-covariance method, PRONY, etc. These algorithms differ in a way energy of differentiated signal is estimated and/or in other adjustments. They are more computationally-expensive methods which have been reported to yield better results in terms of robustness and accuracy, at least at some conditions, by some researchers. Therefore it is possible avmd will be extended in the future to implement some of them. At this moment, avmd uses DESA-2 and this results in a single digit percentage error in a production environments.

Details

This UML diagram depicts processing of audio frames by each of detection threads running in AVMD session, after thread is signaled to start processing by the AVMD main thread in avmd_process function.

Features added in version 1.6.11

  • multithreaded detectors and lagged detectors

  • three modes of detection (amplitude only, frequency only, both simultaneously)

  • detection mode setting added to configuration XML

  • number of detection threads and the number of lagged detectors settings added to configuration XML

  • extended event API (more information about detection session and about it's outcome added to the events)

Compared to call playing simply playback music on answer, running avmd (in default configuration of 36 detectors) consumes about 10 times more CPU. Also there is a tradeoff between detection quality (which increases with number of detection threads) and CPU usage. Please adjust detectors number in AVMD configuration XML or per session to fit your needs and computing resources.

CPU usage (how much CPU it needs)

Tests made on 8-core Intel's i7-4790K CPU indicate that compared to a call simply playing playback music on answer, running avmd (in default configuration of 36 detectors) makes your CPU about 10 times more busy. In a table below we show % of total CPU power used by FreeSWICTH when multiple calls are dialed to extensions, one of them is just playing back the music, other one is running avmd in addition to that.

Results were obtained from output of top command (top -p `pidof freeswitch`).

extension/# of calls1050100150200
playback music1.253.256.257.99.75
playback music + avmd12.932.1347.863.7595 (GUI lagging)

Usage

It is as simple as could only be.

  • You "start" the detection on the particular voice channel.

  • The avmd::beep indicating that beep has been detected is delivered to your ESL socket listener, to command prompt and written to logs. Avmd also sets the channel variable (see "Channel variables").

  • You (optionally) "stop" the detection on the particular voice channel.

Therefore you will see output similar to this in the console:

Console log (running avmd on given example answering_machine_sound_effect_8000.raw)

2016-10-13 15:37:06.765346 [INFO] mod_avmd.c:1113 Avmd dynamic configuration: debug [0], report_status [1], fast_math [0], require_continuous_streak [1], sample_n_continuous_streak [3], sample_n_to_skip [0], require_continuous_streak_amp [1], sample_n_continuous_streak_amp [3], simplified_estimation [1], inbound_channel [1], outbound_channel [0], detection_mode [2], detectors_n [36], detectors_lagged_n [1]
2016-10-13 15:37:06.765346 [INFO] mod_avmd.c:610 Avmd session initialized, [8000] samples/s
2016-10-13 15:37:06.765346 [INFO] mod_avmd.c:1411 Avmd on channel [loopback/1706-b] started!
2016-10-13 15:37:13.885357 [INFO] mod_avmd.c:1901 <<< AVMD - Beep Detected [2][1][0][36]: f = [999.589033] variance = [0.000003], amplitude = [11976.009917](max [15783.276141]) variance = [871.936721], detection time [7099987] [us] >>>
2016-10-13 15:37:15.265359 [INFO] mod_avmd.c:1467 Avmd on channel [loopback/1706-b] stopped, beep status: [DETECTED], total running time [8500013] [us]

API interface

This interface is exposed for calls from mod_event_socket.

start

Starts avmd detection on a given channel.

Usage: <uuid> start

<uuid> - channel unique identifier

Examples:

afcbe4db-cdb3-4e1c-98c5-c12fea8474cc start

In all of this documentation we omit "api avmd" (used in calls from ESL, telnet client), "avmd" (used in calls from fs_cli, dialplan). It is however assumed that this prefix is always there. In all of this documentation by <x> we mean parameter that is required for a given command and when command is issued the <x> becomes just x. For example, above uuid is channel unique identifier and <uuid> should be understood as "insert uuid of the channel here" (obligatory parameter) when issuing start/stop commands. Extending this example, given the resulting event shown in "Example event" (see below) the following command must have been executed from ESL:

api avmd afcbe4db-cdb3-4e1c-98c5-c12fea8474cc start

or alternatively this form from fs_cli:

avmd afcbe4db-cdb3-4e1c-98c5-c12fea8474cc start

stop

Stops avmd detection on a given channel.

Usage: <uuid> stop

<uuid> - channel unique identifier

Examples:

afcbe4db-cdb3-4e1c-98c5-c12fea8474cc stop

All below commands are available since 1.6.8 release.

show

Prints to console current default/global avmd settings.

Usage: show

Examples:

show

reload

Reloads avmd XML configuration settings to what FS has in RAM.

Usage: reload

Examples:

reload

The settings are loaded from FS memory. This means new data will be a copy of what was loaded to FS from $CONFDIR when it was started or when reloadxml command was called last time. If you change XML file and want avmd to load this, it is enough to call FS command reloadxml. But you can also make avmd to load the XML settings file that was cached by FS when reloadxml was called last time. This avmd's reload method will do it, it always restores settings to what FS has in it's RAM, and what FS has in RAM is the version of XML file corresponding to most recent reloadxml command.

set

Sets detection on internal/external channel.

Usage: set inbound | outbound | default

inbound - set detection on internal channel

outboud - set detection on external channel

default - set avmd settings to default factory settings (see "Dynamic configuration of avmd session")

Examples:

set inbound

set outbound

set default

load

Loads avmd XML settings from RAM and sets detection on internal/external channel.

Usage: load inbound | outbound

inbound - set detection on internal channel

outboud - set detection on external channel

Examples:

load inbound

load outbound

This command is equivalent to reload + set. load inbound == reload + set inbound, load outbound == reload + set outbound.

Sometimes you may want to start avmd a few seconds after answer. In that case, once you get an answer event, issue this command

api sched_api +3 none avmd <uuid> start

For best results in getting accurate detection as well a saving system resources (CPU & memory), you should do the following:

  • Wait until AFTER the call has been answered before starting AVMD. In many cases you may find it best to wait a few seconds after the call is answered before starting AVMD. Starting AVMD before the call is answered will result in waste of CPU resources.
  • Once you have received the AVMD event, you should explicitly stop AVMD, even though it will not return more events.

APP interface

Commands

This interface is exposed for calls from dialplan and scripts (LUA, js). As from 1.6.8 this version of APP should be used:

Available since 1.6.9 release.

APP (command name)Dynamic parameters?Usage (from dialplan)DescriptionNotes
avmd_startno<action application="avmd_start"/>starts avmd session with default parameters
avmd_startyes<action application="avmd_start" data=""/>starts avmd session with default parametersdata="" is legal, though useless
avmd_startyes<action application="avmd_start" data= "dynamic parameters: comma separated list of key=value options"/>(e.g. <action application="avmd_start" data="inbound_channel=0,outbound_channel=1, sample_n_continuous_streak=19,sample_n_to_skip=18,debug=0,detectors_n=3,detectors_lagged_n=1,report_status=1"/>)starts avmd session with parameters set, value of the parameters which have not been specified here is set to default value from XML config
avmd_stopno<action application="avmd_stop"/>stops avmd session

When dynamic passing of parameters is used avmd module will validate the comma separated list (which may be empty, that is data="") of key=value pairs for name and value correctness and will set found keys to specified values if validation succeeds. If it doesn't succeed avmd session parameters are set to currently stored default (which means it is set specified in last call to avmd set/ avmd load or otherwise, if none such call has been made - taken from XML config or from default factory settings if config load failed [there is no config or it is invalid]).

Below APP calls will be deprecated since 1.6.8 version but they will still work until the next release for not to break the backward compatibility. When you call one of these methods the warning "YOU ARE USING DEPRECATED APP INTERFACE" is printed to the logs.

APP (command name)DescriptionUsage (from dialplan)Dynamic parameters?Notes
avmdstarts/stops avmd session dependant on data argument<action application="avmd" data="start"/><action application="avmd" data="stop"/>noDEPRECATED since 1.6.8 version

Dialplan examples

Dialplan example 1 (running avmd on given example answering_machine_sound_effect_8000.raw) Expand source

<extension name="1706">
<condition field="destination_number" expression="^1706$">
<action application="avmd_start"/>
<action application="playback" data="voicemail/8000/answering_machine_sound_effect_8000.raw"/>
<action application="avmd_stop"/>
<action application="hangup"/>
</condition>
</extension>

Available since 1.6.9 release.

Dialplan example 2 (running avmd on given example answering_machine_sound_effect_8000.raw) Expand source

<extension name="1706">
<condition field="destination_number" expression="^1706$">
<action application="avmd_start" data="simplified_estimation=0,inbound_channel=1,outbound_channel=1,sample_n_continuous_streak=25,sample_n_to_skip=18,debug=0,report_status=0"/> <!-- this configuration sets more samples to skip at the beginning of each audio frame and requires more than default number of continuous good estimations to validate detection */
<action application="playback" data="voicemail/8000/answering_machine_sound_effect_8000.raw"/>
<action application="avmd_stop"/>
<action application="hangup"/>
</condition>
</extension>

Dialplan example 3 (running avmd on given example answering_machine_sound_effect_8000.raw) Expand source

<extension name="1706_playback">
<condition field="destination_number" expression="^1706$">
<action application="avmd_start" data="debug=1,sample_n_to_skip=19,inbound_channel=1,outbound_channel=0"/> <!-- inbound configuration, good if you start calls from script and playback audio from FS -->
<action application="playback" data="voicemail/8000/answering_machine_sound_effect_8000.raw"/>
<action application="avmd_stop"/>
<action application="hangup"/>
</condition>
</extension>

Dialplan example 4 (running avmd on given example answering_machine_sound_effect_8000.raw in amplitude only mode) Expand source

<extension name="1706_amplitude_only">
<condition field="destination_number" expression="^1706$">
<action application="avmd_start" data="detection_mode=0"/> <!-- set AVMD to detect sound of constant amplitude (and neglect frequency) -->
<action application="playback" data="voicemail/8000/answering_machine_sound_effect_8000.raw"/>
<action application="avmd_stop"/>
<action application="hangup"/>
</condition>
</extension>

Dialplan example 5 (running avmd on given example answering_machine_sound_effect_8000.raw in frequency only mode) Expand source

<extension name="1706_frequency_only">
<condition field="destination_number" expression="^1706$">
<action application="avmd_start" data="detection_mode=1"/> <!-- set AVMD to detect sound of constant frequency (and neglect amplitude) -->
<action application="playback" data="voicemail/8000/answering_machine_sound_effect_8000.raw"/>
<action application="avmd_stop"/>
<action application="hangup"/>
</condition>
</extension>

DEPRECATED since 1.6.8 version

Dialplan Example 6 (running avmd on given example answering_machine_sound_effect_8000.raw) Expand source

<extension name="1706">
<condition field="destination_number" expression="^1706$">
<action application="avmd" data="start"/>
<action application="playback" data="voicemail/8000/answering_machine_sound_effect_8000.raw"/>
<action application="avmd" data="stop"/>
<action application="hangup"/>
</condition>
</extension>

Dynamic configuration of avmd session

When avmd session is started it's parameters are configured with values that are currently stored in FreeSWITCH's RAM. These values are taken from avmd XML configuration file avmd_conf.xml and can be altered by configuration commands (set/load/reload). There is also a possibility to set each parameter of avmd session's individually, per session. It is done by passing comma separated list of parameters and their values with avmd start command from dialplan (as of 1.6.9 version - it is not available from APP interface, e.g. event socket).

Parameters explained

The meaning of parameters which can be set dynamically is explained in the table below.

ParameterScopeDescriptionData typeDefault valueNotes
debugGLOBALenables/disables logging of avmd intermediate computations to loguint8_t0
report_statusGLOBALenables/disables verbose logging (and reporting to the console)of detection status and other diagnostics like parameters avmdsession has been started with, change of configuration parameters,beep detection status after session ended (stop event is firedindependently of this setting and beep status included there)uint8_t1
fast_mathGLOBALenables/disables faster computation of arcus cosine - table is created withmapping of floats to integers and returning arc cos values given theseinteger indices into tableuint8_t0not available on Windows(setting is ignored)
require_continuous_streakPER SESSIONused in frequency estimation:enables/disables check of the required number of consecutive elements(given in sample_n_continuous_streak) in the SMA bufferwithout reset needed to classify beep detection as validuint16_t1
sample_n_continuous_streakPER SESSIONused in frequency estimation:specifies the required number of consecutive elements in the SMA bufferwithout reset needed to classify beep detection as valid.uint16_t3This parameter helps to avoid false beeps, bigger this value is - smallerthe probability of getting false detection but also smaller the chance of gettingdetections of true beeps.
require_continuous_streak_ampPER SESSIONused in amplitude estimation:enables/disables check of the required number of consecutive elements(given in sample_n_continuous_streak) in the SMA bufferwithout reset needed to classify beep detection as validuint16_t1
sample_n_continuous_streak_ampPER SESSIONused in amplitude estimation:specifies the required number of consecutive elements in the SMA bufferwithout reset needed to classify beep detection as valid.uint16_t3This parameter helps to avoid false beeps, bigger this value is - smallerthe probability of getting false detection but also smaller the chance of gettingdetections of true beeps.
sample_n_to_skipPER SESSIONspecifies the number of samples to skip starting from the beginning of the frame and/or after reset has happened.uint8_t0This parameter serves the purpose of skipping first few estimations in each frame,as these estimations may be inaccurate. Increasing value of this parameter(up to certain limit of about 60) may help to give more robust detections in some cases.
simplified_estimationPER SESSIONenables/disables simplified estimation of frequency based on approximationof sin(x) with (x) in the range x=[0,PI/2]uint8_t1Since 1.6.10 release.In releases before this parameter is ignored.This would be used only if DESA original algorithm is usedin which arcus cosine computation is neededper each estimation, but as of 1.6.10 version we use tweaked DESAalgorithm to avoid computation of cosine at all.
inbound_channelPER SESSIONenables/disables avmd on internal channeluint8_t0
outbound_channelPER SESSIONenables/disables avmd on external channeluint8_t1
detection_modePER SESSIONsets the characteristics of the sound AVMD will detect, this is the constantamplitude sound or constant frequency sound or sound characterizedby both constant amplitude and frequency simultaneously(0 = AVMD_DETECT_AMP, 1 = AVMD_DETECT_FREQ,2 = AVMD_DETECT_BOTH)uint8_t2default value is 2 which means AVMD_DETECT_BOTH
detectors_nPER SESSIONsets the number of detection threads running at different resolution and/oroffset within the avmd sessionuint8_t36increasing these numbers gives better detection qualitybut can make CPU very busy, it may be needed to decreasedetectors_n value on slow machines. The default setting allowsto handle about 300 sessions peak on 16GB machine with 8 coresIntel(R) Core(TM) i7-4790K CPU @ 4.00GHz
detectors_lagged_nPER SESSIONsets the number of detection threads which process audio skipping fewframes at the beginning of voice streamuint8_t1

Events

Event types

There exist following types of avmd events:

Event nameDescriptionCustom fieldsUsage (how to subscribe)Notes
AVMD_EVENT_BEEPconstant frequency signal has been detectedUnique-IDCall-commandBeep-StatusFrequencyFrequency-varianceAmplitudeAmplitude-varianceDetection-timeDetector-resolutionDetector-offsetDetector-indexevent plain CUSTOM avmd::beepThis event is fired only if beep has been detected
AVMD_EVENT_SESSION_STARTavmd session has been startedUnique-IDCall-commandStart-timeevent plain CUSTOM avmd::start
AVMD_EVENT_SESSION_STOPavmd session has been stoppedUnique-IDCall-commandBeep statusTotal-timeevent plain CUSTOM avmd::stop

To get all avmd events you can subscribe to all FS events and filter them by custom avmd field, a calling command "Call-command":

event all
filter Call-command avmd

Event examples

Example avmd session start event (from running above dialplan examples)

Example avmd session start event (from running above dialplan examples) Expand source

Event-Subclass: avmd%3A%3Astart
Event-Name: CUSTOM
Core-UUID: 9c7e6daf-cdc6-4f1d-931e-fcb886cffde9
FreeSWITCH-Hostname: westernst
FreeSWITCH-Switchname: westernst
FreeSWITCH-IPv4: 192.168.1.60
FreeSWITCH-IPv6: %3A%3A1
Event-Date-Local: 2016-10-13%2015%3A37%3A06
Event-Date-GMT: Thu,%2013%20Oct%202016%2014%3A37%3A06%20GMT
Event-Date-Timestamp: 1476369426765346
Event-Calling-File: mod_avmd.c
Event-Calling-Function: avmd_fire_event
Event-Calling-Line-Number: 678
Event-Sequence: 5967
Unique-ID: ffbeebb0-8480-4422-9337-bd666d15358d
Call-command: avmd
Start-time: 1476369426765346

Example beep event (from running above dialplan examples)

Example avmd beep event (from running above dialplan examples) Expand source

Event-Subclass: avmd%3A%3Abeep
Event-Name: CUSTOM
Core-UUID: 9c7e6daf-cdc6-4f1d-931e-fcb886cffde9
FreeSWITCH-Hostname: westernst
FreeSWITCH-Switchname: westernst
FreeSWITCH-IPv4: 192.168.1.60
FreeSWITCH-IPv6: %3A%3A1
Event-Date-Local: 2016-10-13%2015%3A37%3A13
Event-Date-GMT: Thu,%2013%20Oct%202016%2014%3A37%3A13%20GMT
Event-Date-Timestamp: 1476369433885357
Event-Calling-File: mod_avmd.c
Event-Calling-Function: avmd_fire_event
Event-Calling-Line-Number: 678
Event-Sequence: 5971
Unique-ID: ffbeebb0-8480-4422-9337-bd666d15358d
Call-command: avmd
Beep-Status: DETECTED
Frequency: 999.589033
Frequency-variance: 0.000003
Amplitude: 11976.009917
Amplitude-variance: 871.936721
Detection-time: 7099987
Detector-resolution: 1
Detector-offset: 0
Detector-index: 36

Example avmd session stop (from running above dialplan examples)

Example avmd session stop (from running above dialplan examples) Expand source

Event-Subclass: avmd%3A%3Astop
Event-Name: CUSTOM
Core-UUID: 9c7e6daf-cdc6-4f1d-931e-fcb886cffde9
FreeSWITCH-Hostname: westernst
FreeSWITCH-Switchname: westernst
FreeSWITCH-IPv4: 192.168.1.60
FreeSWITCH-IPv6: %3A%3A1
Event-Date-Local: 2016-10-13%2015%3A37%3A15
Event-Date-GMT: Thu,%2013%20Oct%202016%2014%3A37%3A15%20GMT
Event-Date-Timestamp: 1476369435265359
Event-Calling-File: mod_avmd.c
Event-Calling-Function: avmd_fire_event
Event-Calling-Line-Number: 678
Event-Sequence: 5975
Unique-ID: ffbeebb0-8480-4422-9337-bd666d15358d
Call-command: avmd
Beep-Status: DETECTED
Total-time: 8500013

Channel variables

avmd_detect

When avmd session starts on a given channel, it's channel variable "avmd_detect" is set to FALSE. Avmd sets this variable to TRUE just before AVMD_EVENT_BEEP is fired, so detection result can be checked by inspecting the value of this variable on a given channel. This is not however the supported way of getting detection indications. You should instead subscribe to and process avmd::beep event or check value of Beep-status field in avmd::stop event. Please take a look at "Start AVMD on the call and check detection result" below for more details.

Scripts

Few useful scripts written in Perl can be found in AVMD source folder (src/mod/applications/mod_avmd/scripts).

Start AVMD on the call and get events

mod_avmd Start AVMD session on the originated call

#!/usr/bin/perl -w


#brief Subscribe to avmd events and print them to the console.
#author Piotr Gregor <piotr@dataandsignal.com>
#date 13 Sept 2016 09:44 PM


$|++; # turn on autoflush
use strict;
use warnings;
require ESL;


my $host = "127.0.0.1";
my $port = "8021";
my $pass = "ClueCon";

my $format = "plain";

if ($#ARGV + 1 eq 1) {
$format = $ARGV[0];
print "Using format: [" .$format ."]\n";
}

my $con = new ESL::ESLconnection($host, $port, $pass);
if (!$con) {
die "Unable to establish connection to $host:$port\n";
}
if ($con->connected()) {
print "OK, Connected.\n";
} else {
die "Conenction failure.\n";
}

print "Subscribing to avmd events...\n";
$con->events("plain", "CUSTOM avmd::start");
$con->events("plain", "CUSTOM avmd::stop");
$con->events("plain", "CUSTOM avmd::beep");

print "Waiting for the events...\n";
while($con->connected()) {
my $e = $con->recvEvent();
my $avmd_event_type = "";
$avmd_event_type = $e->getHeader("Event-Subclass");
if ($avmd_event_type eq 'avmd::start') { # mark nicely the start of new session and event streak - most likely there will be other events from this session coming after this one
print "\n--------------------\n\n";
}
if ($e) {
my $body = $e->serialize($format);
print $body;
print "\n\n";
}
}

print "Disconnected.\n\n";

Start AVMD on the call and check detection result

mod_avmd Start AVMD session on the originated call and check detection result

#!/usr/bin/perl -w


#brief Test module avmd by calling voicemails from avmd test suite and print detection results to the console.
#author Piotr Gregor <piotr@dataandsignal.com>
#details If you are testing serving voicemails from dialplan then avmd must be set to inbound mode, either globally (by avmd set inbound
# in fs_cli) or in dialplan settings (<action application="avmd_start" data="inbound_channel=1,outbound_channel=0") or dynamically per call.
#date 15 Sept 2016 03:00 PM

$|++; # turn on autoflush
use strict;
use warnings;
require ESL;
use POSIX;
use Time::HiRes;

# Hashtable of <destination number : test result expectation> pairs
my %numbers = (
503 => "NOTDETECTED", # dual frequency (similar to single freq with varying amplitude), mode [0] AVMD_DETECT_AMP
504 => "NOTDETECTED",
505 => "NOTDETECTED",
506 => "NOTDETECTED",
507 => "NOTDETECTED",
508 => "NOTDETECTED",
509 => "NOTDETECTED",
510 => "NOTDETECTED",
511 => "NOTDETECTED",
512 => "NOTDETECTED",
513 => "NOTDETECTED",
514 => "NOTDETECTED",
515 => "NOTDETECTED",
516 => "NOTDETECTED",
517 => "NOTDETECTED",
518 => "NOTDETECTED",
519 => "NOTDETECTED",
520 => "NOTDETECTED",
521 => "NOTDETECTED",
522 => "NOTDETECTED",
523 => "NOTDETECTED",
603 => "DETECTED", # dual frequency (similar to single freq with varying amplitude), mode [1] AVMD_DETECT_FREQ
604 => "DETECTED",
605 => "DETECTED",
606 => "DETECTED",
607 => "DETECTED",
608 => "DETECTED",
609 => "DETECTED",
610 => "DETECTED",
611 => "DETECTED",
612 => "DETECTED",
613 => "DETECTED",
614 => "DETECTED",
615 => "DETECTED",
616 => "DETECTED",
617 => "DETECTED",
618 => "DETECTED",
619 => "DETECTED",
620 => "DETECTED",
621 => "DETECTED",
622 => "DETECTED",
623 => "DETECTED",
703 => "NOTDETECTED", # dual frequency (similar to single freq with varying amplitude), mode [2] AVMD_DETECT_BOTH
704 => "NOTDETECTED",
705 => "NOTDETECTED",
706 => "NOTDETECTED",
707 => "NOTDETECTED",
708 => "NOTDETECTED",
709 => "NOTDETECTED",
710 => "NOTDETECTED",
711 => "NOTDETECTED",
712 => "NOTDETECTED",
713 => "NOTDETECTED",
714 => "NOTDETECTED",
715 => "NOTDETECTED",
716 => "NOTDETECTED",
717 => "NOTDETECTED",
718 => "NOTDETECTED",
719 => "NOTDETECTED",
720 => "NOTDETECTED",
721 => "NOTDETECTED",
722 => "NOTDETECTED",
723 => "NOTDETECTED",
840531000 => "DETECTED", # obscure voicemails, mode AVMD_DETECT_BOTH
840531001 => "DETECTED",
840531002 => "DETECTED",
840531003 => "DETECTED",
840531004 => "DETECTED",
840531005 => "DETECTED",
840531006 => "DETECTED",
840531007 => "DETECTED",
840531008 => "DETECTED",
840531009 => "DETECTED",
840531010 => "DETECTED",
840531011 => "DETECTED",
840531012 => "DETECTED",
840531013 => "DETECTED",
840531014 => "DETECTED",
840531200 => "DETECTED", # obscure voicemails, mode AVMD_DETECT_FREQ
840531201 => "DETECTED",
840531202 => "DETECTED",
840531203 => "DETECTED",
840531204 => "DETECTED",
840531205 => "DETECTED",
840531206 => "DETECTED",
840531207 => "DETECTED",
840531208 => "DETECTED",
840531209 => "DETECTED",
840531210 => "DETECTED",
840531211 => "DETECTED",
840531212 => "DETECTED",
840531213 => "DETECTED",
840531214 => "DETECTED",
840531400 => "DETECTED", # obscure voicemails ATT pack
840531401 => "DETECTED",
840531402 => "DETECTED",
840531403 => "DETECTED",
840531404 => "DETECTED",
840531405 => "DETECTED",
840531051 => "NOTDETECTED", # fragment of "Save tonight" by Eagle-Eye Cherry covered by D-Lete-Funk-K
);
my $host = "127.0.0.1";
my $port = "8021";
my $pass = "ClueCon";
my $playback = 'local_stream://moh';
my $context = 'default';
my $endpoint;
my $dest;
my $expectation;
my $callerid;
my $passed = 0;
my $failed = 0;
my $hanguped = 0;

if ($#ARGV + 1 eq 1) {
$callerid = $ARGV[0];
print "\nDialing as [" .$callerid ."]\n";
} elsif ($#ARGV + 1 > 1) {
die "Please specify single caller id.\n";
} else {
die "Please specify caller id.\n";
}

print "Connecting...\t";
my $con = new ESL::ESLconnection($host, $port, $pass);
if (!$con) {
die "Unable to establish connection to $host:$port\n";
}
if ($con->connected()) {
print "OK.\n";
} else {
die "Connection failure.\n";
}
print "Subscribing to avmd events...\t";
$con->events("plain", "CUSTOM avmd::start");
$con->events("plain", "CUSTOM avmd::stop");
$con->events("plain", "CUSTOM avmd::beep");
$con->events("plain", "CHANNEL_CALLSTATE");
$con->events("plain", "CHANNEL_HANGUP");
print "OK.\n\n";
printf("\nRunning [" .keys(%numbers) ."] tests.\n\n");
printf("outbound uuid | destination number | timestamp | expectation | test result | freq | f-variance | amplitude | a-variance | resolution | offset | idx\n\n");
foreach $dest (sort keys %numbers) {
if (!$con->connected()) {
last;
}
$expectation = $numbers{$dest};
test_once($dest, $callerid, $expectation);
}
print "Disconnected.\n\n";
if (($failed == 0) && ($hanguped == 0)) {
printf("\n\nOK. All PASS [%s]\n\n", $passed);
} else {
printf("PASS [%s], FAIL [%s], HANGUP [%s]\n\n", $passed, $failed, $hanguped);
}
sub test_once {
my ($dest, $callerid, $expectation) = @_;
my $originate_string =
'originate ' .
'{ignore_early_media=true,' .
'origination_uuid=%s,' .
'originate_timeout=60,' .
'origination_caller_id_number=' . $callerid . ',' .
'origination_caller_id_name=' . $callerid . '}';
my $outcome = "";
my $result = "";
my $event_uuid = "N/A";
my $uuid_in = "";
my $freq = "N/A";
my $freq_var = "N/A";
my $amp = "N/A";
my $amp_var = "N/A";
my $resolution = "N/A";
my $offset = "N/A";
my $idx = "N/A";
if(defined($endpoint)) {
$originate_string .= $endpoint;
} else {
$originate_string .= 'loopback/' . $dest . '/' . $context;
}
$originate_string .= ' ' . '&playback(' . $playback . ')';
my $uuid_out = $con->api('create_uuid')->getBody();
my ($time_epoch, $time_hires) = Time::HiRes::gettimeofday();
printf("[%s] [%s]", $uuid_out, $dest);
$con->bgapi(sprintf($originate_string, $uuid_out));
while($con->connected()) {
my $e = $con->recvEvent();
if ($e) {
my $event_name = $e->getHeader("Event-Name");
if ($event_name eq 'CUSTOM') {
my $avmd_event_type = $e->getHeader("Event-Subclass");
if ($avmd_event_type eq 'avmd::start') {
$uuid_in = $e->getHeader("Unique-ID");
} elsif (!($uuid_in eq "") && (($avmd_event_type eq 'avmd::beep') || ($avmd_event_type eq 'avmd::stop'))) {
$event_uuid = $e->getHeader("Unique-ID");
if ($event_uuid eq $uuid_in) {
if ($avmd_event_type eq 'avmd::beep') {
$freq = $e->getHeader("Frequency");
$freq_var = $e->getHeader("Frequency-variance");
$amp = $e->getHeader("Amplitude");
$amp_var = $e->getHeader("Amplitude-variance");
$resolution = $e->getHeader("Detector-resolution");
$offset = $e->getHeader("Detector-offset");
$idx = $e->getHeader("Detector-index");
}
$outcome = $e->getHeader("Beep-Status");
if ($outcome eq $expectation) {
$result = "PASS";
$passed++;
} else {
$result = "FAIL";
$failed++;
}
last;
}
}
} elsif ($event_name eq 'CHANNEL_HANGUP') {
$event_uuid = $e->getHeader("variable_origination_uuid");
if ((defined $event_uuid) && ($event_uuid eq $uuid_out)) {
$outcome = "HANGUP";
$result = "HANGUP";
$hanguped++;
last;
}
}
}
}
printf("\t[%s]\t[%s]\t\t[%s]\t[%s]HZ\t[%s]\t[%s]\t[%s]\t[%s][%s][%s]\n", POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime($time_epoch)), $expectation, $result, $freq, $freq_var, $amp, $amp_var, $resolution, $offset, $idx);
Time::HiRes::sleep(0.5); # avoid switch_core_session.c:2265 Throttle Error! 33, switch_time.c:1227 Over Session Rate of 30!
}

Make multiple calls to FreeSWITCH (useful for testing CPU usage on calls running AVMD)

Make multiple calls to FreeSWITCH Expand source

#!/usr/bin/perl -w


#brief Make multiple calls. This script can be used to check CPU usage on calls running avmd (see test.txt).
#author Piotr Gregor <piotr@dataandsignal.com>
#date 15 Sept 2016 02:44 PM
#changed 27th Dec 2019


use strict;
use warnings;
require ESL;
use POSIX;
use Time::HiRes;

my $host = "127.0.0.1";
my $port = "8021";
my $pass = "ClueCon";
my $extension_base = "sofia/internal/1000\@192.168.1.1";

my $playback = 'local_stream://moh';
my $context = 'default';
#Example:
#my $endpoint = "originate {originator_codec=PCMA,origination_uuid=%s}sofia/gateway/box_b/840534002 \&park()";
#my $endpoint = "originate {originator_codec=PCMA,origination_uuid=%s}sofia/gateway/%s/%s \&park()";
my $endpoint;
my $gateway;
my $dest;
my $callerid;
my $thread_n;
my $idx = 0;


if ($#ARGV + 1 eq 3) {
$dest = $ARGV[0];
$callerid = $ARGV[1];
$thread_n = $ARGV[2];
print "Dialing [" .$thread_n ."] calls simultaneously to[" .$dest ."] as [" .$callerid ."]\n";
} else {
die "Please specify destination number, caller id and number of calls to make\n\nExample:\n./avmd_originate_multiple.pl EXTENSION CALLER NUMBER_OF_CALLS";
}

my $con = new ESL::ESLconnection($host, $port, $pass);
if (!$con) {
die "Unable to establish connection to $host:$port\n";
}
if ($con->connected()) {
print "OK, Connected.\n";
} else {
die "Connection failure.\n";
}

while($con->connected() && ($idx < $thread_n)) {
call_once($dest, $callerid, $idx);
$idx++;
Time::HiRes::sleep(0.11); # avoid switch_core_session.c:2265 Throttle Error! 33, switch_time.c:1227 Over Session Rate of 30!
}

print "Disconnected.\n\n";

sub call_once {
my ($dest, $callerid, $idx) = @_;
my $originate_string =
'originate ' .
'{ignore_early_media=true,' .
'originator_codec=PCMA,' .
'origination_uuid=%s,' .
'originate_timeout=60,' .
'origination_caller_id_number=' . $callerid . ',' .
'origination_caller_id_name=' . $callerid . '}';

if(defined($endpoint)) {
$originate_string = '';
$originate_string .= $endpoint;
} else {
$originate_string .= 'loopback/' . $dest . '/' . $context;
$originate_string .= ' ' . '&playback(' . $playback . ')';
}

my $uuid = $con->api('create_uuid')->getBody();
my ($time_epoch, $time_hires) = Time::HiRes::gettimeofday();
printf("[%s]\tCalling with uuid [%s] [%s]... [%s]\n", $idx + 1, $uuid, POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime($time_epoch)), $originate_string);

$con->bgapi(sprintf($originate_string, $uuid));
$con->api('uuid_setvar ' . $uuid .' execute_on_answer avmd_start');
}


Configuration

Add avmd module to 'modules.conf':

applications/mod_avmd

Rebuild and install:

sudo make sure
sudo make install

To have FreeSWITCH load it on startup simply add this to '$FS_ROOT/conf/autoload_configs/modules.conf.xml':

<load module="mod_avmd"/>

Feedback/questions

Any voicemail that avmd doesn't detect? Please do let us know by sending an email to FreeSWITCH™ users mailing list - and we will take care of it (please could you prefix the topic with 'mod_avmd' and address an email to freeswitch-users@lists.freeswitch.org). However, please do not report bugs to users list - for these please could you use jira.freeswitch.org (see Reporting Issues to GitHub).