<p>George Joseph <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/16459">View Change</a></p><div style="white-space:pre-wrap">Approvals:
George Joseph: Looks good to me, approved; Approved for Submit
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_tonedetect: Tone detection module<br><br>dsp.c contains arbitrary tone detection functionality<br>which is currently only used for fax tone recognition.<br>This change makes this functionality publicly<br>accessible so that other modules can take advantage<br>of this.<br><br>Additionally, a WaitForTone and TONE_DETECT app and<br>function are included to allow users to do their<br>own tone detection operations in the dialplan.<br><br>ASTERISK-29546<br><br>Change-Id: Ie38c395000f4fd4d04e942e8658e177f8f499b26<br>---<br>A doc/CHANGES-staging/res_tonedetect.txt<br>M include/asterisk/dsp.h<br>M main/dsp.c<br>A res/res_tonedetect.c<br>4 files changed, 719 insertions(+), 2 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/doc/CHANGES-staging/res_tonedetect.txt b/doc/CHANGES-staging/res_tonedetect.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..ddda8e8</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/res_tonedetect.txt</span><br><span>@@ -0,0 +1,5 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: res_tonedetect</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Arbitrary tone detection is now available through a</span><br><span style="color: hsl(120, 100%, 40%);">+WaitForTone application (blocking) and a TONE_DETECT</span><br><span style="color: hsl(120, 100%, 40%);">+function (non-blocking).</span><br><span>diff --git a/include/asterisk/dsp.h b/include/asterisk/dsp.h</span><br><span>index 769d3b9..b641a99 100644</span><br><span>--- a/include/asterisk/dsp.h</span><br><span>+++ b/include/asterisk/dsp.h</span><br><span>@@ -42,6 +42,7 @@</span><br><span> #define DSP_PROGRESS_CONGESTION (1 << 19) /*!< Enable congestion tone detection */</span><br><span> #define DSP_FEATURE_CALL_PROGRESS (DSP_PROGRESS_TALK | DSP_PROGRESS_RINGING | DSP_PROGRESS_BUSY | DSP_PROGRESS_CONGESTION)</span><br><span> #define DSP_FEATURE_WAITDIALTONE (1 << 20) /*!< Enable dial tone detection */</span><br><span style="color: hsl(120, 100%, 40%);">+#define DSP_FEATURE_FREQ_DETECT (1 << 21) /*!< Enable arbitrary tone detection */</span><br><span> </span><br><span> #define DSP_FAXMODE_DETECT_CNG (1 << 0)</span><br><span> #define DSP_FAXMODE_DETECT_CED (1 << 1)</span><br><span>@@ -171,6 +172,9 @@</span><br><span> */</span><br><span> int ast_dsp_set_digitmode(struct ast_dsp *dsp, int digitmode);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Set arbitrary frequency detection mode */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_dsp_set_freqmode(struct ast_dsp *dsp, int freq, int dur, int db, int squelch);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \brief Set fax mode */</span><br><span> int ast_dsp_set_faxmode(struct ast_dsp *dsp, int faxmode);</span><br><span> </span><br><span>diff --git a/main/dsp.c b/main/dsp.c</span><br><span>index 871a687..106ee9b 100644</span><br><span>--- a/main/dsp.c</span><br><span>+++ b/main/dsp.c</span><br><span>@@ -425,6 +425,7 @@</span><br><span> int tcount;</span><br><span> int digitmode;</span><br><span> int faxmode;</span><br><span style="color: hsl(120, 100%, 40%);">+ int freqmode;</span><br><span> int dtmf_began;</span><br><span> int display_inband_dtmf_warning;</span><br><span> float genergy;</span><br><span>@@ -476,7 +477,7 @@</span><br><span> /* Now calculate final block size. It will contain integer number of periods */</span><br><span> s->block_size = periods_in_block * sample_rate / freq;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- /* tone_detect is currently only used to detect fax tones and we</span><br><span style="color: hsl(120, 100%, 40%);">+ /* tone_detect is generally only used to detect fax tones and we</span><br><span> do not need squelching the fax tones */</span><br><span> s->squelch = 0;</span><br><span> </span><br><span>@@ -518,6 +519,15 @@</span><br><span> </span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void ast_freq_detect_init(struct ast_dsp *s, int freq, int dur, int db, int squelch)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ /* we can conveniently just use one of the two fax tone states */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_tone_detect_init(&s->cng_tone_state, freq, dur, db, s->sample_rate);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (s->freqmode & squelch) {</span><br><span style="color: hsl(120, 100%, 40%);">+ s->cng_tone_state.squelch = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void ast_dtmf_detect_init(dtmf_detect_state_t *s, unsigned int sample_rate)</span><br><span> {</span><br><span> int i;</span><br><span>@@ -1485,7 +1495,7 @@</span><br><span> {</span><br><span> int silence;</span><br><span> int res;</span><br><span style="color: hsl(0, 100%, 40%);">- int digit = 0, fax_digit = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int digit = 0, fax_digit = 0, custom_freq_digit = 0;</span><br><span> int x;</span><br><span> short *shortdata;</span><br><span> unsigned char *odata;</span><br><span>@@ -1558,6 +1568,12 @@</span><br><span> }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if ((dsp->features & DSP_FEATURE_FREQ_DETECT)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((dsp->freqmode) && tone_detect(dsp, &dsp->cng_tone_state, shortdata, len)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ custom_freq_digit = 'q';</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if (dsp->features & (DSP_FEATURE_DIGIT_DETECT | DSP_FEATURE_BUSY_DETECT)) {</span><br><span> if (dsp->digitmode & DSP_DIGITMODE_MF) {</span><br><span> digit = mf_detect(dsp, &dsp->digit_state, shortdata, len, (dsp->digitmode & DSP_DIGITMODE_NOQUELCH) == 0, (dsp->digitmode & DSP_DIGITMODE_RELAXDTMF));</span><br><span>@@ -1619,6 +1635,16 @@</span><br><span> goto done;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (custom_freq_digit) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Custom frequency was detected - digit is 'q' */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ memset(&dsp->f, 0, sizeof(dsp->f));</span><br><span style="color: hsl(120, 100%, 40%);">+ dsp->f.frametype = AST_FRAME_DTMF;</span><br><span style="color: hsl(120, 100%, 40%);">+ dsp->f.subclass.integer = custom_freq_digit;</span><br><span style="color: hsl(120, 100%, 40%);">+ outf = &dsp->f;</span><br><span style="color: hsl(120, 100%, 40%);">+ goto done;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if ((dsp->features & DSP_FEATURE_CALL_PROGRESS)) {</span><br><span> res = __ast_dsp_call_progress(dsp, shortdata, len);</span><br><span> if (res) {</span><br><span>@@ -1830,6 +1856,17 @@</span><br><span> return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_dsp_set_freqmode(struct ast_dsp *dsp, int freq, int dur, int db, int squelch)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (freq > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ dsp->freqmode = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_freq_detect_init(dsp, freq, dur, db, squelch);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ dsp->freqmode = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int ast_dsp_set_faxmode(struct ast_dsp *dsp, int faxmode)</span><br><span> {</span><br><span> if (dsp->faxmode != faxmode) {</span><br><span>diff --git a/res/res_tonedetect.c b/res/res_tonedetect.c</span><br><span>new file mode 100644</span><br><span>index 0000000..1d5db83</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_tonedetect.c</span><br><span>@@ -0,0 +1,671 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2021, Naveen Albert</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Naveen Albert <asterisk@phreaknet.org></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Tone detection module</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \author Naveen Albert <asterisk@phreaknet.org></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \ingroup resources</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+ <support_level>extended</support_level></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <math.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/frame.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/format_cache.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/channel.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/dsp.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/pbx.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/audiohook.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/app.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/indications.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/conversions.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** DOCUMENTATION</span><br><span style="color: hsl(120, 100%, 40%);">+ <application name="WaitForTone" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Wait for tone</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="freq" required="true"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Frequency of the tone to wait for.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="duration_ms" required="false"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Minimum duration of tone, in ms. Default is 500ms.</span><br><span style="color: hsl(120, 100%, 40%);">+ Using a minimum duration under 50ms is unlikely to produce</span><br><span style="color: hsl(120, 100%, 40%);">+ accurate results.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="timeout" required="false"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Maximum amount of time, in seconds, to wait for specified tone.</span><br><span style="color: hsl(120, 100%, 40%);">+ Default is forever.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="times" required="false"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Number of times the tone should be detected (subject to the</span><br><span style="color: hsl(120, 100%, 40%);">+ provided timeout) before returning. Default is 1.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="options" required="false"></span><br><span style="color: hsl(120, 100%, 40%);">+ <optionlist></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="d"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Custom decibel threshold to use. Default is 16.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="s"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Squelch tone.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ </optionlist></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Waits for a single-frequency tone to be detected before dialplan execution continues.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <variablelist></span><br><span style="color: hsl(120, 100%, 40%);">+ <variable name="WAITFORTONESTATUS"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>This indicates the result of the wait.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="SUCCESS"/></span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="ERROR"/></span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="TIMEOUT"/></span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="HANGUP"/></span><br><span style="color: hsl(120, 100%, 40%);">+ </variable></span><br><span style="color: hsl(120, 100%, 40%);">+ </variablelist></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ <see-also></span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">PlayTones</ref></span><br><span style="color: hsl(120, 100%, 40%);">+ </see-also></span><br><span style="color: hsl(120, 100%, 40%);">+ </application></span><br><span style="color: hsl(120, 100%, 40%);">+ <function name="TONE_DETECT" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Asynchronously detects a tone</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="freq" required="true"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Frequency of the tone to detect.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="duration_ms" required="false"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Minimum duration of tone, in ms. Default is 500ms.</span><br><span style="color: hsl(120, 100%, 40%);">+ Using a minimum duration under 50ms is unlikely to produce</span><br><span style="color: hsl(120, 100%, 40%);">+ accurate results.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="options"></span><br><span style="color: hsl(120, 100%, 40%);">+ <optionlist></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="d"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Custom decibel threshold to use. Default is 16.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="g"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Go to the specified context,exten,priority if tone is received on this channel.</span><br><span style="color: hsl(120, 100%, 40%);">+ Detection will not end automatically.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="h"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Go to the specified context,exten,priority if tone is transmitted on this channel.</span><br><span style="color: hsl(120, 100%, 40%);">+ Detection will not end automatically.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="n"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Number of times the tone should be detected (subject to the</span><br><span style="color: hsl(120, 100%, 40%);">+ provided timeout) before going to the destination provided in the <literal>g</literal></span><br><span style="color: hsl(120, 100%, 40%);">+ or <literal>h</literal> option. Default is 1.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="r"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Apply to received frames only. Default is both directions.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="s"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Squelch tone.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="t"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Apply to transmitted frames only. Default is both directions.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="x"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Destroy the detector (stop detection).</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ </optionlist></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>The TONE_DETECT function detects a single-frequency tone and keeps</span><br><span style="color: hsl(120, 100%, 40%);">+ track of how many times the tone has been detected.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>When reading this function (instead of writing), supply <literal>tx</literal></span><br><span style="color: hsl(120, 100%, 40%);">+ to get the number of times a tone has been detected in the TX direction and</span><br><span style="color: hsl(120, 100%, 40%);">+ <literal>rx</literal> to get the number of times a tone has been detected in the</span><br><span style="color: hsl(120, 100%, 40%);">+ RX direction.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <example title="intercept2600"></span><br><span style="color: hsl(120, 100%, 40%);">+ same => n,Set(TONE_DETECT(2600,1000,g(got-2600,s,1))=)</span><br><span style="color: hsl(120, 100%, 40%);">+ same => n,Wait(15)</span><br><span style="color: hsl(120, 100%, 40%);">+ same => n,NoOp(${TONE_DETECT(rx)})</span><br><span style="color: hsl(120, 100%, 40%);">+ </example></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </function></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct detect_information {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_dsp *dsp;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_audiohook audiohook;</span><br><span style="color: hsl(120, 100%, 40%);">+ int freq1;</span><br><span style="color: hsl(120, 100%, 40%);">+ int freq2;</span><br><span style="color: hsl(120, 100%, 40%);">+ int duration;</span><br><span style="color: hsl(120, 100%, 40%);">+ int db;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *gototx;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *gotorx;</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned short int squelch;</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned short int tx;</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned short int rx;</span><br><span style="color: hsl(120, 100%, 40%);">+ int txcount;</span><br><span style="color: hsl(120, 100%, 40%);">+ int rxcount;</span><br><span style="color: hsl(120, 100%, 40%);">+ int hitsrequired;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum td_opts {</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_TX = (1 << 1),</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_RX = (1 << 2),</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_END_FILTER = (1 << 3),</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_GOTO_RX = (1 << 4),</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_GOTO_TX = (1 << 5),</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_DECIBEL = (1 << 6),</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_SQUELCH = (1 << 7),</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_HITS_REQ = (1 << 8),</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum {</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_ARG_DECIBEL,</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_ARG_GOTO_RX,</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_ARG_GOTO_TX,</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_ARG_HITS_REQ,</span><br><span style="color: hsl(120, 100%, 40%);">+ /* note: this entry _MUST_ be the last one in the enum */</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_ARG_ARRAY_SIZE,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_APP_OPTIONS(td_opts, {</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION_ARG('d', OPT_DECIBEL, OPT_ARG_DECIBEL),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION_ARG('g', OPT_GOTO_RX, OPT_ARG_GOTO_RX),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION_ARG('h', OPT_GOTO_TX, OPT_ARG_GOTO_TX),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION_ARG('n', OPT_HITS_REQ, OPT_ARG_HITS_REQ),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('s', OPT_SQUELCH),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('t', OPT_TX),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('r', OPT_RX),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('x', OPT_END_FILTER),</span><br><span style="color: hsl(120, 100%, 40%);">+});</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void destroy_callback(void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct detect_information *di = data;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_dsp_free(di->dsp);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (di->gotorx) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(di->gotorx);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (di->gototx) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(di->gototx);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_lock(&di->audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_detach(&di->audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_unlock(&di->audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_destroy(&di->audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(di);</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct ast_datastore_info detect_datastore = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .type = "detect",</span><br><span style="color: hsl(120, 100%, 40%);">+ .destroy = destroy_callback</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int detect_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_datastore *datastore = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct detect_information *di = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Grab datastore which contains our gain information */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(datastore = ast_channel_datastore_find(chan, &detect_datastore, NULL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ di = datastore->data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!frame || frame->frametype != AST_FRAME_VOICE) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(direction == AST_AUDIOHOOK_DIRECTION_READ ? &di->rx : &di->tx)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* ast_dsp_process may free the frame and return a new one */</span><br><span style="color: hsl(120, 100%, 40%);">+ frame = ast_frdup(frame);</span><br><span style="color: hsl(120, 100%, 40%);">+ frame = ast_dsp_process(chan, di->dsp, frame);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (frame->frametype == AST_FRAME_DTMF) {</span><br><span style="color: hsl(120, 100%, 40%);">+ char result = frame->subclass.integer;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (result == 'q') {</span><br><span style="color: hsl(120, 100%, 40%);">+ int now;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (direction == AST_AUDIOHOOK_DIRECTION_READ) {</span><br><span style="color: hsl(120, 100%, 40%);">+ di->rxcount = di->rxcount + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ now = di->rxcount;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ di->txcount = di->txcount + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ now = di->txcount;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "TONE_DETECT just got a hit (#%d in this direction, waiting for %d total)\n", now, di->hitsrequired);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (now >= di->hitsrequired) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (direction == AST_AUDIOHOOK_DIRECTION_READ && di->gotorx) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_async_parseable_goto(chan, di->gotorx);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (di->gototx) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_async_parseable_goto(chan, di->gototx);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ /* this could be the duplicated frame or a new one, doesn't matter */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_frfree(frame);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int remove_detect(struct ast_channel *chan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_datastore *datastore = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct detect_information *data;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPED_CHANNELLOCK(chan_lock, chan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ datastore = ast_channel_datastore_find(chan, &detect_datastore, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!datastore) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(AST_LOG_WARNING, "Cannot remove TONE_DETECT from %s: TONE_DETECT not currently enabled\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ data = datastore->data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_audiohook_remove(chan, &data->audiohook)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(AST_LOG_WARNING, "Failed to remove TONE_DETECT audiohook from channel %s\n", ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_channel_datastore_remove(chan, datastore)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(AST_LOG_WARNING, "Failed to remove TONE_DETECT datastore from channel %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_datastore_free(datastore);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int freq_parser(char *freqs, int *freq1, int *freq2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ char *f1, *f2, *f3;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(freqs)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "No frequency specified\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ f3 = ast_strdupa(freqs);</span><br><span style="color: hsl(120, 100%, 40%);">+ f1 = strsep(&f3, "+");</span><br><span style="color: hsl(120, 100%, 40%);">+ f2 = strsep(&f3, "+");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(f3)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Only up to 2 frequencies may be specified: %s\n", freqs);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_str_to_int(f1, freq1)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Frequency must be an integer: %s\n", f1);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (*freq1 < 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Sorry, positive frequencies only: %d\n", *freq1);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(f2)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Sorry, currently only 1 frequency is supported\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* not supported just yet, but possibly will be in the future */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_str_to_int(f2, freq2)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Frequency must be an integer: %s\n", f2);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (*freq2 < 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Sorry, positive frequencies only: %d\n", *freq2);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char* goto_parser(struct ast_channel *chan, char *loc) {</span><br><span style="color: hsl(120, 100%, 40%);">+ char *exten, *pri, *context, *parse;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *dest;</span><br><span style="color: hsl(120, 100%, 40%);">+ int size;</span><br><span style="color: hsl(120, 100%, 40%);">+ parse = ast_strdupa(loc);</span><br><span style="color: hsl(120, 100%, 40%);">+ context = strsep(&parse, ",");</span><br><span style="color: hsl(120, 100%, 40%);">+ exten = strsep(&parse, ",");</span><br><span style="color: hsl(120, 100%, 40%);">+ pri = strsep(&parse, ",");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!exten) {</span><br><span style="color: hsl(120, 100%, 40%);">+ pri = context;</span><br><span style="color: hsl(120, 100%, 40%);">+ exten = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ context = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (!pri) {</span><br><span style="color: hsl(120, 100%, 40%);">+ pri = exten;</span><br><span style="color: hsl(120, 100%, 40%);">+ exten = context;</span><br><span style="color: hsl(120, 100%, 40%);">+ context = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_lock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(exten)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ exten = ast_strdupa(ast_channel_exten(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(context)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ context = ast_strdupa(ast_channel_context(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* size + 3: for 1 null terminator + 2 commas */</span><br><span style="color: hsl(120, 100%, 40%);">+ size = strlen(context) + strlen(exten) + strlen(pri) + 3;</span><br><span style="color: hsl(120, 100%, 40%);">+ dest = ast_malloc(size + 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!dest) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to parse goto: %s,%s,%s\n", context, exten, pri);</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(dest, size, "%s,%s,%s", context, exten, pri);</span><br><span style="color: hsl(120, 100%, 40%);">+ return dest;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int detect_read(struct ast_channel *chan, const char *cmd, char *data, char *buffer, size_t buflen)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_datastore *datastore = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct detect_information *di = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!chan) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_lock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(datastore = ast_channel_datastore_find(chan, &detect_datastore, NULL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1; /* function not initiated yet, so nothing to read */</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ di = datastore->data;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strchr(data, 't')) {</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(buffer, buflen, "%d", di->txcount);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (strchr(data, 'r')) {</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(buffer, buflen, "%d", di->rxcount);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid direction: %s\n", data);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int detect_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char *parse;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_datastore *datastore = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct detect_information *di = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_flags flags = { 0 };</span><br><span style="color: hsl(120, 100%, 40%);">+ char *opt_args[OPT_ARG_ARRAY_SIZE];</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_dsp *dsp;</span><br><span style="color: hsl(120, 100%, 40%);">+ int freq1 = 0, freq2 = 0, duration = 500, db = 16, squelch = 0, hitsrequired = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DECLARE_APP_ARGS(args,</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(freqs);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(duration);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(options);</span><br><span style="color: hsl(120, 100%, 40%);">+ );</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!chan) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ parse = ast_strdupa(data);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STANDARD_APP_ARGS(args, parse);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag(&flags, OPT_END_FILTER)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return remove_detect(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(args.options)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_parse_options(td_opts, &flags, opt_args, args.options);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (freq_parser(args.freqs, &freq1, &freq2)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(args.duration) && (ast_str_to_int(args.duration, &duration) || duration < 1)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid duration: %s\n", args.duration);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag(&flags, OPT_HITS_REQ) && !ast_strlen_zero(opt_args[OPT_ARG_HITS_REQ])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((ast_str_to_int(opt_args[OPT_ARG_HITS_REQ], &hitsrequired) || hitsrequired < 1)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid number hits required: %s\n", opt_args[OPT_ARG_HITS_REQ]);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag(&flags, OPT_DECIBEL) && !ast_strlen_zero(opt_args[OPT_ARG_DECIBEL])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((ast_str_to_int(opt_args[OPT_ARG_DECIBEL], &db) || db < 1)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid decibel level: %s\n", opt_args[OPT_ARG_DECIBEL]);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_lock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(datastore = ast_channel_datastore_find(chan, &detect_datastore, NULL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(datastore = ast_datastore_alloc(&detect_datastore, NULL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(di = ast_calloc(1, sizeof(*di)))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_datastore_free(datastore);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_init(&di->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Tone Detector", AST_AUDIOHOOK_MANIPULATE_ALL_RATES);</span><br><span style="color: hsl(120, 100%, 40%);">+ di->audiohook.manipulate_callback = detect_callback;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(dsp = ast_dsp_new())) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_datastore_free(datastore);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Unable to allocate DSP!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_dsp_set_features(dsp, DSP_FEATURE_FREQ_DETECT);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_dsp_set_freqmode(dsp, freq1, duration, db, squelch);</span><br><span style="color: hsl(120, 100%, 40%);">+ di->dsp = dsp;</span><br><span style="color: hsl(120, 100%, 40%);">+ di->txcount = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ di->rxcount = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Keeping our ears open for %s Hz, %d db\n", args.freqs, db);</span><br><span style="color: hsl(120, 100%, 40%);">+ datastore->data = di;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_datastore_add(chan, datastore);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_audiohook_attach(chan, &di->audiohook);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ di = datastore->data;</span><br><span style="color: hsl(120, 100%, 40%);">+ dsp = di->dsp;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_dsp_set_freqmode(dsp, freq1, duration, db, squelch);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ di->duration = duration;</span><br><span style="color: hsl(120, 100%, 40%);">+ di->gotorx = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ di->gototx = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* resolve gotos now, in case a full context,exten,pri wasn't specified */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag(&flags, OPT_GOTO_RX) && !ast_strlen_zero(opt_args[OPT_ARG_GOTO_RX])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ di->gotorx = goto_parser(chan, opt_args[OPT_ARG_GOTO_RX]);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag(&flags, OPT_GOTO_TX) && !ast_strlen_zero(opt_args[OPT_ARG_GOTO_TX])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ di->gototx = goto_parser(chan, opt_args[OPT_ARG_GOTO_TX]);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ di->db = db;</span><br><span style="color: hsl(120, 100%, 40%);">+ di->hitsrequired = hitsrequired;</span><br><span style="color: hsl(120, 100%, 40%);">+ di->squelch = ast_test_flag(&flags, OPT_SQUELCH);</span><br><span style="color: hsl(120, 100%, 40%);">+ di->tx = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ di->rx = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(args.options) || ast_test_flag(&flags, OPT_TX)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ di->tx = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ di->rx = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(args.options) || ast_test_flag(&flags, OPT_RX)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ di->rx = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ di->tx = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum {</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_APP_DECIBEL = (1 << 0),</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_APP_SQUELCH = (1 << 1),</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum {</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_APP_ARG_DECIBEL,</span><br><span style="color: hsl(120, 100%, 40%);">+ /* note: this entry _MUST_ be the last one in the enum */</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_APP_ARG_ARRAY_SIZE,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_APP_OPTIONS(wait_exec_options, BEGIN_OPTIONS</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION_ARG('d', OPT_APP_DECIBEL, OPT_APP_ARG_DECIBEL),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('s', OPT_APP_SQUELCH),</span><br><span style="color: hsl(120, 100%, 40%);">+END_OPTIONS);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int wait_exec(struct ast_channel *chan, const char *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char *appdata;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_flags flags = {0};</span><br><span style="color: hsl(120, 100%, 40%);">+ char *opt_args[OPT_APP_ARG_ARRAY_SIZE];</span><br><span style="color: hsl(120, 100%, 40%);">+ double timeoutf = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int freq1 = 0, freq2 = 0, timeout = 0, duration = 500, times = 1, db = 16, squelch = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_frame *frame = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_dsp *dsp;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct timeval start;</span><br><span style="color: hsl(120, 100%, 40%);">+ int remaining_time = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int hits = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DECLARE_APP_ARGS(args,</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(freqs);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(duration);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(times);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(options);</span><br><span style="color: hsl(120, 100%, 40%);">+ );</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ appdata = ast_strdupa(data);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STANDARD_APP_ARGS(args, appdata);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(args.options)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_parse_options(wait_exec_options, &flags, opt_args, args.options);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (freq_parser(args.freqs, &freq1, &freq2)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "ERROR");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(args.timeout) && (sscanf(args.timeout, "%30lf", &timeoutf) != 1 || timeout < 0)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid timeout: %s\n", args.timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "ERROR");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ timeout = 1000 * timeoutf;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(args.duration) && (ast_str_to_int(args.duration, &duration) || duration < 1)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid duration: %s\n", args.duration);</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "ERROR");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(args.times) && (ast_str_to_int(args.times, ×) || times < 1)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid number of times: %s\n", args.times);</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "ERROR");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag(&flags, OPT_APP_DECIBEL) && !ast_strlen_zero(opt_args[OPT_APP_ARG_DECIBEL])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((ast_str_to_int(opt_args[OPT_APP_ARG_DECIBEL], &db) || db < 1)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid decibel level: %s\n", opt_args[OPT_APP_ARG_DECIBEL]);</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "ERROR");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ squelch = ast_test_flag(&flags, OPT_APP_SQUELCH);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(dsp = ast_dsp_new())) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Unable to allocate DSP!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "ERROR");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_dsp_set_features(dsp, DSP_FEATURE_FREQ_DETECT);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_dsp_set_freqmode(dsp, freq1, duration, db, squelch);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Waiting for %s Hz, %d time(s), timeout %d ms, %d db\n", args.freqs, times, timeout, db);</span><br><span style="color: hsl(120, 100%, 40%);">+ start = ast_tvnow();</span><br><span style="color: hsl(120, 100%, 40%);">+ do {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (timeout > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ remaining_time = ast_remaining_ms(start, timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (remaining_time <= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "TIMEOUT");</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_waitfor(chan, 1000) > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(frame = ast_read(chan))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "HANGUP");</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (frame->frametype == AST_FRAME_VOICE) {</span><br><span style="color: hsl(120, 100%, 40%);">+ frame = ast_dsp_process(chan, dsp, frame);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (frame->frametype == AST_FRAME_DTMF) {</span><br><span style="color: hsl(120, 100%, 40%);">+ char result = frame->subclass.integer;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (result == 'q') {</span><br><span style="color: hsl(120, 100%, 40%);">+ hits++;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "We just detected %s Hz (hit #%d)\n", args.freqs, hits);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (hits >= times) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_frfree(frame);</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "SUCCESS");</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_frfree(frame);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "HANGUP");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ } while (timeout == 0 || remaining_time > 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_dsp_free(dsp);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *waitapp = "WaitForTone";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_custom_function detect_function = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .name = "TONE_DETECT",</span><br><span style="color: hsl(120, 100%, 40%);">+ .read = detect_read,</span><br><span style="color: hsl(120, 100%, 40%);">+ .write = detect_write,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_unregister_application(waitapp);</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_custom_function_unregister(&detect_function);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_register_application_xml(waitapp, wait_exec);</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_custom_function_register(&detect_function);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Tone detection module");</span><br><span></span><br></pre><div style="white-space:pre-wrap"></div><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/16459">change 16459</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/16459"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 19 </div>
<div style="display:none"> Gerrit-Change-Id: Ie38c395000f4fd4d04e942e8658e177f8f499b26 </div>
<div style="display:none"> Gerrit-Change-Number: 16459 </div>
<div style="display:none"> Gerrit-PatchSet: 2 </div>
<div style="display:none"> Gerrit-Owner: N A <mail@interlinked.x10host.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>