<p>George Joseph <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/16457">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) || 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/+/16457">change 16457</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/+/16457"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 16 </div>
<div style="display:none"> Gerrit-Change-Id: Ie38c395000f4fd4d04e942e8658e177f8f499b26 </div>
<div style="display:none"> Gerrit-Change-Number: 16457 </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>