<p>N A has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/16481">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">app_mf: Add full MF/SF support<br><br>Adds tech agnostic support for MF and SF signaling.<br>The Dial application is also extended to support<br>these signal types.<br><br>ASTERISK-29496<br><br>Change-Id: I05ba61ce49092d3f131ade9dc4d97dcc9887fa1d<br>---<br>M apps/app_dial.c<br>A apps/app_mf.c<br>A doc/CHANGES-staging/app_mf_full.txt<br>M include/asterisk/app.h<br>M include/asterisk/channel.h<br>M main/app.c<br>M main/channel.c<br>7 files changed, 1,316 insertions(+), 18 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/81/16481/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/apps/app_dial.c b/apps/app_dial.c</span><br><span>index 51f5f45..a61cfa0 100644</span><br><span>--- a/apps/app_dial.c</span><br><span>+++ b/apps/app_dial.c</span><br><span>@@ -156,6 +156,10 @@</span><br><span> <argument name="called" /></span><br><span> <argument name="calling" /></span><br><span> <argument name="progress" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <argument name="mfprogress" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <argument name="mfwink" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <argument name="sfprogress" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <argument name="sfwink" /></span><br><span> <para>Send the specified DTMF strings <emphasis>after</emphasis> the called</span><br><span> party has answered, but before the call gets bridged. The</span><br><span> <replaceable>called</replaceable> DTMF string is sent to the called party, and the</span><br><span>@@ -163,6 +167,20 @@</span><br><span> can be used alone. If <replaceable>progress</replaceable> is specified, its DTMF is sent</span><br><span> to the called party immediately after receiving a <literal>PROGRESS</literal> message.</para></span><br><span> <para>See <literal>SendDTMF</literal> for valid digits.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>If <replaceable>mfprogress</replaceable> is specified, its MF is sent</span><br><span style="color: hsl(120, 100%, 40%);">+ to the called party immediately after receiving a <literal>PROGRESS</literal> message.</span><br><span style="color: hsl(120, 100%, 40%);">+ If <replaceable>mfwink</replaceable> is specified, its MF is sent</span><br><span style="color: hsl(120, 100%, 40%);">+ to the called party immediately after receiving a <literal>WINK</literal> message.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>See <literal>SendMF</literal> for valid digits.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>If <replaceable>sfprogress</replaceable> is specified, its SF is sent</span><br><span style="color: hsl(120, 100%, 40%);">+ to the called party immediately after receiving a <literal>PROGRESS</literal> message.</span><br><span style="color: hsl(120, 100%, 40%);">+ If <replaceable>sfwink</replaceable> is specified, its SF is sent</span><br><span style="color: hsl(120, 100%, 40%);">+ to the called party immediately after receiving a <literal>WINK</literal> message.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>See <literal>SendSF</literal> for valid digits.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="E"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Enable echoing of sent MF or SF digits back to caller (e.g. "hearpulsing").</span><br><span style="color: hsl(120, 100%, 40%);">+ Used in conjunction with the D option.</para></span><br><span> </option></span><br><span> <option name="e"></span><br><span> <para>Execute the <literal>h</literal> extension for peer after the call ends</para></span><br><span>@@ -499,7 +517,6 @@</span><br><span> answered, if it has not already been answered. These two channels will then</span><br><span> be active in a bridged call. All other channels that were requested will then</span><br><span> be hung up.</para></span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> <para>Unless there is a timeout specified, the Dial application will wait</span><br><span> indefinitely until one of the called channels answers, the user hangs up, or</span><br><span> if all of the called channels are busy or unavailable. Dialplan execution will</span><br><span>@@ -512,7 +529,6 @@</span><br><span> If the <variable>OUTBOUND_GROUP_ONCE</variable> variable is set, all peer channels created by this</span><br><span> application will be put into that group (as in <literal>Set(GROUP()=...</literal>). Unlike <variable>OUTBOUND_GROUP</variable>,</span><br><span> however, the variable will be unset after use.</para></span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> <example title="Dial with 30 second timeout"></span><br><span> same => n,Dial(PJSIP/alice,30)</span><br><span> </example></span><br><span>@@ -534,28 +550,22 @@</span><br><span> </example></span><br><span> <example title="Dial with pre-dial subroutines"></span><br><span> [default]</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> exten => callee_channel,1,NoOp(ARG1=${ARG1} ARG2=${ARG2})</span><br><span> same => n,Log(NOTICE, I'm called on channel ${CHANNEL} prior to it starting the dial attempt)</span><br><span> same => n,Return()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> exten => called_channel,1,NoOp(ARG1=${ARG1} ARG2=${ARG2})</span><br><span> same => n,Log(NOTICE, I'm called on outbound channel ${CHANNEL} prior to it being used to dial someone)</span><br><span> same => n,Return()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> exten => _X.,1,NoOp()</span><br><span> same => n,Dial(PJSIP/alice,,b(default^called_channel^1(my_gosub_arg1^my_gosub_arg2))B(default^callee_channel^1(my_gosub_arg1^my_gosub_arg2)))</span><br><span> same => n,Hangup()</span><br><span> </example></span><br><span> <example title="Dial with post-answer subroutine executed on outbound channel"></span><br><span> [my_gosub_routine]</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> exten => s,1,NoOp(ARG1=${ARG1} ARG2=${ARG2})</span><br><span> same => n,Playback(hello)</span><br><span> same => n,Return()</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> [default]</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> exten => _X.,1,NoOp()</span><br><span> same => n,Dial(PJSIP/alice,,U(my_gosub_routine^my_gosub_arg1^my_gosub_arg2))</span><br><span> same => n,Hangup()</span><br><span>@@ -717,6 +727,7 @@</span><br><span> #define OPT_PREDIAL_CALLER (1LLU << 42)</span><br><span> #define OPT_RING_WITH_EARLY_MEDIA (1LLU << 43)</span><br><span> #define OPT_HANGUPCAUSE (1LLU << 44)</span><br><span style="color: hsl(120, 100%, 40%);">+#define OPT_HEARPULSING (1LLU << 45)</span><br><span> </span><br><span> enum {</span><br><span> OPT_ARG_ANNOUNCE = 0,</span><br><span>@@ -752,6 +763,7 @@</span><br><span> AST_APP_OPTION('c', OPT_CANCEL_ELSEWHERE),</span><br><span> AST_APP_OPTION('d', OPT_DTMF_EXIT),</span><br><span> AST_APP_OPTION_ARG('D', OPT_SENDDTMF, OPT_ARG_SENDDTMF),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('E', OPT_HEARPULSING),</span><br><span> AST_APP_OPTION('e', OPT_PEER_H),</span><br><span> AST_APP_OPTION_ARG('f', OPT_FORCECLID, OPT_ARG_FORCECLID),</span><br><span> AST_APP_OPTION_ARG('F', OPT_CALLEE_GO_ON, OPT_ARG_CALLEE_GO_ON),</span><br><span>@@ -1209,6 +1221,8 @@</span><br><span> char *opt_args[],</span><br><span> struct privacy_args *pa,</span><br><span> const struct cause_args *num_in, int *result, char *dtmf_progress,</span><br><span style="color: hsl(120, 100%, 40%);">+ char *mf_progress, char *mf_wink, char *sf_progress, char *sf_wink,</span><br><span style="color: hsl(120, 100%, 40%);">+ const int hearpulsing,</span><br><span> const int ignore_cc,</span><br><span> struct ast_party_id *forced_clid, struct ast_party_id *stored_clid,</span><br><span> struct ast_bridge_config *config)</span><br><span>@@ -1228,7 +1242,7 @@</span><br><span> int cc_frame_received = 0;</span><br><span> int num_ringing = 0;</span><br><span> int sent_ring = 0;</span><br><span style="color: hsl(0, 100%, 40%);">- int sent_progress = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int sent_progress = 0, sent_wink = 0;</span><br><span> struct timeval start = ast_tvnow();</span><br><span> SCOPE_ENTER(3, "%s\n", ast_channel_name(in));</span><br><span> </span><br><span>@@ -1566,6 +1580,22 @@</span><br><span> ast_channel_unlock(in);</span><br><span> sent_progress = 1;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(mf_progress)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verb(3,</span><br><span style="color: hsl(120, 100%, 40%);">+ "Sending MF '%s' to %s as result of "</span><br><span style="color: hsl(120, 100%, 40%);">+ "receiving a PROGRESS message.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ mf_progress, hearpulsing ? "parties" : "called party");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mf_stream(c, (hearpulsing ? NULL : in),</span><br><span style="color: hsl(120, 100%, 40%);">+ (hearpulsing ? in : NULL), mf_progress, 50, 55, 120, 65, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(sf_progress)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verb(3,</span><br><span style="color: hsl(120, 100%, 40%);">+ "Sending SF '%s' to %s as result of "</span><br><span style="color: hsl(120, 100%, 40%);">+ "receiving a PROGRESS message.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ sf_progress, (hearpulsing ? "parties" : "called party"));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sf_stream(c, (hearpulsing ? NULL : in),</span><br><span style="color: hsl(120, 100%, 40%);">+ (hearpulsing ? in : NULL), sf_progress, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> if (!ast_strlen_zero(dtmf_progress)) {</span><br><span> ast_verb(3,</span><br><span> "Sending DTMF '%s' to the called party as result of "</span><br><span>@@ -1576,6 +1606,28 @@</span><br><span> }</span><br><span> ast_channel_publish_dial(in, c, NULL, "PROGRESS");</span><br><span> break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case AST_CONTROL_WINK:</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verb(3, "%s winked, passing it to %s\n", ast_channel_name(c), ast_channel_name(in));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!sent_wink) {</span><br><span style="color: hsl(120, 100%, 40%);">+ sent_wink = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(mf_wink)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verb(3,</span><br><span style="color: hsl(120, 100%, 40%);">+ "Sending MF '%s' to %s as result of "</span><br><span style="color: hsl(120, 100%, 40%);">+ "receiving a WINK message.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ mf_wink, (hearpulsing ? "parties" : "called party"));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mf_stream(c, (hearpulsing ? NULL : in),</span><br><span style="color: hsl(120, 100%, 40%);">+ (hearpulsing ? in : NULL), mf_wink, 50, 55, 120, 65, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(sf_wink)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verb(3,</span><br><span style="color: hsl(120, 100%, 40%);">+ "Sending SF '%s' to %s as result of "</span><br><span style="color: hsl(120, 100%, 40%);">+ "receiving a WINK message.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ sf_wink, (hearpulsing ? "parties" : "called party"));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sf_stream(c, (hearpulsing ? NULL : in),</span><br><span style="color: hsl(120, 100%, 40%);">+ (hearpulsing ? in : NULL), sf_wink, 0, 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%);">+ break;</span><br><span> case AST_CONTROL_VIDUPDATE:</span><br><span> case AST_CONTROL_SRCUPDATE:</span><br><span> case AST_CONTROL_SRCCHANGE:</span><br><span>@@ -2130,9 +2182,7 @@</span><br><span> /* the file doesn't exist yet. Let the caller submit his</span><br><span> vocal intro for posterity */</span><br><span> /* priv-recordintro script:</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> "At the tone, please say your name:"</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> */</span><br><span> int silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);</span><br><span> ast_answer(chan);</span><br><span>@@ -2249,7 +2299,8 @@</span><br><span> </span><br><span> struct ast_bridge_config config = { { 0, } };</span><br><span> struct timeval calldurationlimit = { 0, };</span><br><span style="color: hsl(0, 100%, 40%);">- char *dtmfcalled = NULL, *dtmfcalling = NULL, *dtmf_progress=NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *dtmfcalled = NULL, *dtmfcalling = NULL, *dtmf_progress = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *mf_progress = NULL, *mf_wink = NULL, *sf_progress = NULL, *sf_wink = NULL;</span><br><span> struct privacy_args pa = {</span><br><span> .sentringing = 0,</span><br><span> .privdb_val = 0,</span><br><span>@@ -2384,9 +2435,13 @@</span><br><span> }</span><br><span> </span><br><span> if (ast_test_flag64(&opts, OPT_SENDDTMF) && !ast_strlen_zero(opt_args[OPT_ARG_SENDDTMF])) {</span><br><span style="color: hsl(0, 100%, 40%);">- dtmf_progress = opt_args[OPT_ARG_SENDDTMF];</span><br><span style="color: hsl(0, 100%, 40%);">- dtmfcalled = strsep(&dtmf_progress, ":");</span><br><span style="color: hsl(0, 100%, 40%);">- dtmfcalling = strsep(&dtmf_progress, ":");</span><br><span style="color: hsl(120, 100%, 40%);">+ sf_wink = opt_args[OPT_ARG_SENDDTMF];</span><br><span style="color: hsl(120, 100%, 40%);">+ dtmfcalled = strsep(&sf_wink, ":");</span><br><span style="color: hsl(120, 100%, 40%);">+ dtmfcalling = strsep(&sf_wink, ":");</span><br><span style="color: hsl(120, 100%, 40%);">+ dtmf_progress = strsep(&sf_wink, ":");</span><br><span style="color: hsl(120, 100%, 40%);">+ mf_progress = strsep(&sf_wink, ":");</span><br><span style="color: hsl(120, 100%, 40%);">+ mf_wink = strsep(&sf_wink, ":");</span><br><span style="color: hsl(120, 100%, 40%);">+ sf_progress = strsep(&sf_wink, ":");</span><br><span> }</span><br><span> </span><br><span> if (ast_test_flag64(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) {</span><br><span>@@ -2863,7 +2918,8 @@</span><br><span> }</span><br><span> </span><br><span> peer = wait_for_answer(chan, &out_chans, &to, peerflags, opt_args, &pa, &num, &result,</span><br><span style="color: hsl(0, 100%, 40%);">- dtmf_progress, ignore_cc, &forced_clid, &stored_clid, &config);</span><br><span style="color: hsl(120, 100%, 40%);">+ dtmf_progress, mf_progress, mf_wink, sf_progress, sf_wink, (ast_test_flag64(&opts, OPT_HEARPULSING) ? 1 : 0),</span><br><span style="color: hsl(120, 100%, 40%);">+ ignore_cc, &forced_clid, &stored_clid, &config);</span><br><span> </span><br><span> if (!peer) {</span><br><span> if (result) {</span><br><span>@@ -3536,4 +3592,4 @@</span><br><span> .load = load_module,</span><br><span> .unload = unload_module,</span><br><span> .requires = "ccss",</span><br><span style="color: hsl(0, 100%, 40%);">-);</span><br><span style="color: hsl(120, 100%, 40%);">+);</span><br><span>\ No newline at end of file</span><br><span>diff --git a/apps/app_mf.c b/apps/app_mf.c</span><br><span>new file mode 100644</span><br><span>index 0000000..d68f180</span><br><span>--- /dev/null</span><br><span>+++ b/apps/app_mf.c</span><br><span>@@ -0,0 +1,839 @@</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 MF sender and receiver applications
</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 applications
</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 "asterisk/file.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/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/app.h"
</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.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="ReceiveSF" language="en_US">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>
</span><br><span style="color: hsl(120, 100%, 40%);">+ Detects SF digits on a channel and saves them to a variable.
</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="variable" required="true">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>The input digits will be stored in the given
</span><br><span style="color: hsl(120, 100%, 40%);">+ <replaceable>variable</replaceable> name.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="digits" required="false">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Maximum number of digits to read. Default is unlimited.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="timeout">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>The number of seconds to wait for all digits, if greater
</span><br><span style="color: hsl(120, 100%, 40%);">+ than <literal>0</literal>. Can be floating point. Default
</span><br><span style="color: hsl(120, 100%, 40%);">+ is no timeout.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="frequency">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>The frequency for which to detect pulsed digits.
</span><br><span style="color: hsl(120, 100%, 40%);">+ Default is 2600 Hz.</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>Delay audio by a frame to try to extra quelch.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </option>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="e">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Allow receiving extra pulses 11 through 16.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </option>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="m">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Mute conference.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </option>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="q">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Quelch SF from in-band.</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>"Radio" mode (relaxed SF).</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>Reads SF digits from the user in to the given
</span><br><span style="color: hsl(120, 100%, 40%);">+ <replaceable>variable</replaceable>.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>This application does not automatically answer the channel and
</span><br><span style="color: hsl(120, 100%, 40%);">+ should be preceded with <literal>Answer</literal> or
</span><br><span style="color: hsl(120, 100%, 40%);">+ <literal>Progress</literal> as needed.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <variablelist>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <variable name="RECEIVESFSTATUS">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>This is the status of the read operation.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="START" />
</span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="ERROR" />
</span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="HANGUP" />
</span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="TIMEOUT" />
</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">ReceiveMF</ref>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SendMF</ref>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">Read</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%);">+ <application name="ReceiveMF" language="en_US">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>
</span><br><span style="color: hsl(120, 100%, 40%);">+ Detects MF digits on a channel and saves them to a variable.
</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="variable" required="true">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>The input digits will be stored in the given
</span><br><span style="color: hsl(120, 100%, 40%);">+ <replaceable>variable</replaceable> name.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="timeout">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>The number of seconds to wait for all digits, if greater
</span><br><span style="color: hsl(120, 100%, 40%);">+ than <literal>0</literal>. Can be floating point. Default
</span><br><span style="color: hsl(120, 100%, 40%);">+ is no timeout.</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>Delay audio by a frame to try to extra quelch.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </option>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="l">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Receive digits even if a key pulse (KP) has not yet
</span><br><span style="color: hsl(120, 100%, 40%);">+ been received. By default, this application will ignore
</span><br><span style="color: hsl(120, 100%, 40%);">+ all other digits until a KP has been received.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </option>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="k">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Do not return a character for the KP digit.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </option>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="m">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Mute conference.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </option>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="o">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Enable override. Repeated KPs will clear all previous digits.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </option>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="q">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Quelch MF from in-band.</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>"Radio" mode (relaxed MF).</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>Do not return a character for ST digits.</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>Reads a ST, STP, ST2P, or ST3P-terminated string of MF digits from
</span><br><span style="color: hsl(120, 100%, 40%);">+ the user in to the given <replaceable>variable</replaceable>.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>This application does not automatically answer the channel and
</span><br><span style="color: hsl(120, 100%, 40%);">+ should be preceded with <literal>Answer</literal> or
</span><br><span style="color: hsl(120, 100%, 40%);">+ <literal>Progress</literal> as needed.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <variablelist>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <variable name="RECEIVEMFSTATUS">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>This is the status of the read operation.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="START" />
</span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="ERROR" />
</span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="HANGUP" />
</span><br><span style="color: hsl(120, 100%, 40%);">+ <value name="TIMEOUT" />
</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">Read</ref>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">ReceiveSF</ref>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SendMF</ref>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SendSF</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%);">+ <application name="SendSF" language="en_US">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>
</span><br><span style="color: hsl(120, 100%, 40%);">+ Sends arbitrary SF digits on the current or specified channel.
</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="digits" required="true">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>List of digits 0-9 to send; also f or F for a flash-hook
</span><br><span style="color: hsl(120, 100%, 40%);">+ if the channel supports flash-hook, and w or W for a wink if the channel
</span><br><span style="color: hsl(120, 100%, 40%);">+ supports wink.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="frequency" required="false">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Frequency to use. (defaults to 2600 Hz).</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="channel" required="false">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Channel where digits will be played</para>
</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>It will send all digits or terminate if it encounters an error.</para>
</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">SendDTMF</ref>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SendMF</ref>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">ReceiveMF</ref>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">ReceiveSF</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%);">+ <application name="SendMF" language="en_US">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>
</span><br><span style="color: hsl(120, 100%, 40%);">+ Sends arbitrary MF digits on the current or specified channel.
</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="digits" required="true">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>List of digits 0-9,*#ABC to send; also f or F for a flash-hook
</span><br><span style="color: hsl(120, 100%, 40%);">+ if the channel supports flash-hook, and w or W for a wink if the channel
</span><br><span style="color: hsl(120, 100%, 40%);">+ supports wink.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Key pulse and start digits are not included automatically.
</span><br><span style="color: hsl(120, 100%, 40%);">+ * is used for KP, # for ST, A for STP, B for ST2P, and C for ST3P.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="timeout_ms" required="false">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Amount of time to wait in ms between tones. (defaults to 50ms).</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>Duration of each numeric digit (defaults to 55ms).</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="duration_ms_kp" required="false">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Duration of KP digits (defaults to 120ms).</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="duration_ms_st" required="false">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Duration of ST, STP, ST2P, and ST3P digits (defaults to 65ms).</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="channel" required="false">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Channel where digits will be played</para>
</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>It will send all digits or terminate if it encounters an error.</para>
</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">ReceiveMF</ref>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SendDTMF</ref>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <ref type="application">SendMF</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%);">+ <manager name="PlayMF" language="en_US">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis>
</span><br><span style="color: hsl(120, 100%, 40%);">+ Play MF digit on a specific channel.
</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%);">+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
</span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="Channel" required="true">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Channel name to send digit to.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="Digit" required="true">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>The MF digit to play.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter>
</span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="Duration" required="false">
</span><br><span style="color: hsl(120, 100%, 40%);">+ <para>The duration, in milliseconds, of the digit to be played.</para>
</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>Plays an MF digit on the specified channel.</para>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </description>
</span><br><span style="color: hsl(120, 100%, 40%);">+ </manager>
</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 read_option_flags {
</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_DELAY = (1 << 0),
</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_MUTE = (1 << 1),
</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_QUELCH = (1 << 2),
</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_RELAXED = (1 << 3),
</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_LAX_KP = (1 << 4),
</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_PROCESS = (1 << 5),
</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_NO_KP = (1 << 6),
</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_NO_ST = (1 << 7),
</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_KP_OVERRIDE = (1 << 8),
</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_EXTRAPULSES = (1 << 9),
</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(read_app_options, {
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('d', OPT_DELAY),
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('e', OPT_EXTRAPULSES), /* SF only */
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('l', OPT_LAX_KP), /* MF only */
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('k', OPT_NO_KP), /* MF only */
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('m', OPT_MUTE),
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('o', OPT_KP_OVERRIDE), /* MF only */
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('p', OPT_PROCESS), /* MF only */
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('q', OPT_QUELCH),
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('r', OPT_RELAXED),
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('s', OPT_NO_ST), /* MF only */
</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 char *readmf_name = "ReceiveMF";
</span><br><span style="color: hsl(120, 100%, 40%);">+static const char *readsf_name = "ReceiveSF";
</span><br><span style="color: hsl(120, 100%, 40%);">+static const char sendmf_name[] = "SendMF";
</span><br><span style="color: hsl(120, 100%, 40%);">+static const char sendsf_name[] = "SendSF";
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+#define MF_BETWEEN_MS 50
</span><br><span style="color: hsl(120, 100%, 40%);">+#define MF_DURATION 55
</span><br><span style="color: hsl(120, 100%, 40%);">+#define MF_KP_DURATION 120
</span><br><span style="color: hsl(120, 100%, 40%);">+#define MF_ST_DURATION 65
</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%);">+ * \brief Detects MF digits on channel using DSP, terminated by ST, STP, ST2P, or ST3P
</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return 0 if successful, -1 if unsuccessful.
</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param chan channel on which to read digits
</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param str buffer in which to store digits
</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param timeout ms to wait for all digits before giving up
</span><br><span style="color: hsl(120, 100%, 40%);">+ */
</span><br><span style="color: hsl(120, 100%, 40%);">+static int read_mf_digits(struct ast_channel *chan, char *buf, int timeout, int features, int laxkp, int override, int no_kp, int no_st) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_dsp *dsp;
</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_frame *frame = NULL;
</span><br><span style="color: hsl(120, 100%, 40%);">+ struct timeval start;
</span><br><span style="color: hsl(120, 100%, 40%);">+ int remaining_time = timeout;
</span><br><span style="color: hsl(120, 100%, 40%);">+ int digits_read = 0;
</span><br><span style="color: hsl(120, 100%, 40%);">+ int is_start_digit = 0;
</span><br><span style="color: hsl(120, 100%, 40%);">+ char *str = buf;
</span><br><span style="color: hsl(120, 100%, 40%);">+
</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, "RECEIVEMFSTATUS", "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_DIGIT_DETECT);
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_dsp_set_digitmode(dsp, DSP_DIGITMODE_MF | features);
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ start = ast_tvnow();
</span><br><span style="color: hsl(120, 100%, 40%);">+ *str = 0; /* start with empty output buffer */
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ /* based on app_read and generic_fax_exec from res_fax */
</span><br><span style="color: hsl(120, 100%, 40%);">+ while (timeout == 0 || remaining_time > 0) {
</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, "RECEIVEMFSTATUS", "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%);">+ /* ast_waitfordigit only waits for DTMF frames, we need to do DSP on voice frames */
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_waitfor(chan, 1000) > 0) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ frame = ast_read(chan);
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!frame) {
</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, "RECEIVEMFSTATUS", "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%);">+ /* AST_FRAME_DTMF is used all over the DSP code for DTMF, MF, fax, etc.
</span><br><span style="color: hsl(120, 100%, 40%);">+ It's used because we can use the frame to store the digit detected.
</span><br><span style="color: hsl(120, 100%, 40%);">+ All this means is that we received something we care about. */
</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 (digits_read == 0 && !laxkp && result != '*') {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Received MF digit, but no KP yet, ignoring: %c\n", result);
</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;
</span><br><span style="color: hsl(120, 100%, 40%);">+ }
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Received MF digit: %c\n", result);
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (result == '*') {
</span><br><span style="color: hsl(120, 100%, 40%);">+ /* We received an additional KP, start over? */
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (override && digits_read > 0) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Received another KP, starting over\n");
</span><br><span style="color: hsl(120, 100%, 40%);">+ str = buf;
</span><br><span style="color: hsl(120, 100%, 40%);">+ *str = 0;
</span><br><span style="color: hsl(120, 100%, 40%);">+ digits_read = 1; /* we just detected a KP */
</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {
</span><br><span style="color: hsl(120, 100%, 40%);">+ digits_read++;
</span><br><span style="color: hsl(120, 100%, 40%);">+ }
</span><br><span style="color: hsl(120, 100%, 40%);">+ /* if we were told not to include the KP digit in the output string, then skip it */
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (no_kp) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;
</span><br><span style="color: hsl(120, 100%, 40%);">+ }
</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {
</span><br><span style="color: hsl(120, 100%, 40%);">+ digits_read++;
</span><br><span style="color: hsl(120, 100%, 40%);">+ }
</span><br><span style="color: hsl(120, 100%, 40%);">+ is_start_digit = (strchr("#", result) || strchr("A", result) || strchr("B", result) || strchr("C", result));
</span><br><span style="color: hsl(120, 100%, 40%);">+ /* if we were told not to include the ST digit in the output string, then skip it */
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!no_st || !is_start_digit) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ *str++ = result;
</span><br><span style="color: hsl(120, 100%, 40%);">+ *str = 0;
</span><br><span style="color: hsl(120, 100%, 40%);">+ }
</span><br><span style="color: hsl(120, 100%, 40%);">+ /* we received a ST digit (ST, STP, ST2P, or ST3P), so we're done */
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (is_start_digit) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "START");
</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%);">+ } else {
</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "HANGUP");
</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_free(dsp);
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(3, "channel '%s' - event loop stopped { timeout: %d, remaining_time: %d }\n", ast_channel_name(chan), timeout, remaining_time);
</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 read_sf_digits(struct ast_channel *chan, char *buf, int timeout, int maxdigits, int freq, int features, int extrapulses) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Bell System Technical Journal 39 (Nov. 1960) */
</span><br><span style="color: hsl(120, 100%, 40%);">+ #define SF_MIN_OFF 25
</span><br><span style="color: hsl(120, 100%, 40%);">+ #define SF_ON 67
</span><br><span style="color: hsl(120, 100%, 40%);">+ #define SF_BETWEEN 600
</span><br><span style="color: hsl(120, 100%, 40%);">+ #define SF_MIN_DETECT 50
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_dsp *dsp = NULL;
</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_frame *frame = NULL;
</span><br><span style="color: hsl(120, 100%, 40%);">+ struct timeval start, pulsetimer, digittimer;
</span><br><span style="color: hsl(120, 100%, 40%);">+ int remaining_time = timeout;
</span><br><span style="color: hsl(120, 100%, 40%);">+ char *str = buf;
</span><br><span style="color: hsl(120, 100%, 40%);">+ int hits = 0, digits_read = 0;
</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned short int sf_on = 0;
</span><br><span style="color: hsl(120, 100%, 40%);">+
</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, "RECEIVESFSTATUS", "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%);">+ /* tolerance is 46 to 76% make break at 8 to 12 pps */
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_dsp_set_freqmode(dsp, freq, SF_MIN_DETECT, 16, 0);
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ start = ast_tvnow();
</span><br><span style="color: hsl(120, 100%, 40%);">+ *str = 0; /* start with empty output buffer */
</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%);">+ 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, "RECEIVESFSTATUS", "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%);">+ frame = ast_read(chan);
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!frame) {
</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, "RECEIVESFSTATUS", "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%);">+ sf_on = 1;
</span><br><span style="color: hsl(120, 100%, 40%);">+ pulsetimer = ast_tvnow(); /* reset the pulse timer */
</span><br><span style="color: hsl(120, 100%, 40%);">+ /* now, we need at least a 33ms pause to register the pulse */
</span><br><span style="color: hsl(120, 100%, 40%);">+ }
</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (sf_on) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ int timeleft = ast_remaining_ms(pulsetimer, SF_MIN_OFF);
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (timeleft <= 0) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ sf_on = 0;
</span><br><span style="color: hsl(120, 100%, 40%);">+ /* The pulse needs to end no more than 30ms after we detected it */
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (timeleft > -30) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ hits++;
</span><br><span style="color: hsl(120, 100%, 40%);">+ digittimer = ast_tvnow(); /* reset the digit timer */
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(5, "Detected SF pulse (pulse #%d)\n", hits);
</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, "RECEIVESFSTATUS", "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, freq, SF_MIN_DETECT, 16, 0);
</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(5, "SF noise, ignoring, time elapsed was %d ms\n", timeleft);
</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%);">+ } else if (hits > 0 && ast_remaining_ms(digittimer, SF_BETWEEN) <= 0) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ /* has the digit finished? */
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(2, "Received SF digit: %d\n", hits);
</span><br><span style="color: hsl(120, 100%, 40%);">+ digits_read++;
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (hits > 10) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (extrapulses) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ /* dahdi-base.c translates 11 to * and 12 to # */
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (hits == 11) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ hits = '*';
</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (hits == 12) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ hits = '#';
</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (hits == 13) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ hits = 'D';
</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (hits == 14) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ hits = 'C';
</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (hits == 15) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ hits = 'B';
</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (hits == 16) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ hits = 'A';
</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(3, "Got %d SF pulses, is someone playing with the phone?\n", hits);
</span><br><span style="color: hsl(120, 100%, 40%);">+ hits = 'A';
</span><br><span style="color: hsl(120, 100%, 40%);">+ }
</span><br><span style="color: hsl(120, 100%, 40%);">+ *str++ = hits;
</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(2, "Got more than 10 pulses, truncating to 10\n");
</span><br><span style="color: hsl(120, 100%, 40%);">+ hits = 0; /* 10 dial pulses = digit 0 */
</span><br><span style="color: hsl(120, 100%, 40%);">+ *str++ = hits + '0';
</span><br><span style="color: hsl(120, 100%, 40%);">+ }
</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (hits == 10) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ hits = 0; /* 10 dial pulses = digit 0 */
</span><br><span style="color: hsl(120, 100%, 40%);">+ }
</span><br><span style="color: hsl(120, 100%, 40%);">+ *str++ = hits + '0';
</span><br><span style="color: hsl(120, 100%, 40%);">+ }
</span><br><span style="color: hsl(120, 100%, 40%);">+ *str = 0;
</span><br><span style="color: hsl(120, 100%, 40%);">+ hits = 0;
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (maxdigits > 0 && digits_read >= maxdigits) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "START");
</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%);">+ } else {
</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "HANGUP");
</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 (dsp) {
</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%);">+ ast_debug(3, "channel '%s' - event loop stopped { timeout: %d, remaining_time: %d }\n", ast_channel_name(chan), timeout, remaining_time);
</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 read_sf_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 tmp[256] = "";
</span><br><span style="color: hsl(120, 100%, 40%);">+ double tosec;
</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_flags flags = {0};
</span><br><span style="color: hsl(120, 100%, 40%);">+ char *argcopy = NULL;
</span><br><span style="color: hsl(120, 100%, 40%);">+ int features = 0, digits = 0, to = 0, freq = 2600;
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DECLARE_APP_ARGS(arglist,
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(variable);
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(digits);
</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(freq);
</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 (ast_strlen_zero(data)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "ReceiveSF requires an argument (variable)\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%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ argcopy = ast_strdupa(data);
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STANDARD_APP_ARGS(arglist, argcopy);
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(arglist.options)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_parse_options(read_app_options, &flags, NULL, arglist.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 (!ast_strlen_zero(arglist.timeout)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ tosec = atof(arglist.timeout);
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (tosec <= 0) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ to = 0;
</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {
</span><br><span style="color: hsl(120, 100%, 40%);">+ to = tosec * 1000.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%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(arglist.digits) && (ast_str_to_int(arglist.digits, &digits) || digits <= 0)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid number of digits: %s\n", arglist.digits);
</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_strlen_zero(arglist.freq) && (ast_str_to_int(arglist.freq, &freq) || freq <= 0)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid freq: %s\n", arglist.freq);
</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_strlen_zero(arglist.variable)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid! Usage: ReceiveSF(variable[,timeout][,option])\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%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag(&flags, OPT_DELAY)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ features |= DSP_DIGITMODE_MUTEMAX;
</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_MUTE)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ features |= DSP_DIGITMODE_MUTECONF;
</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_QUELCH)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ features |= DSP_DIGITMODE_NOQUELCH;
</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_RELAXED)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ features |= DSP_DIGITMODE_RELAXDTMF;
</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%);">+ read_sf_digits(chan, tmp, to, digits, freq, features, ast_test_flag(&flags, OPT_EXTRAPULSES));
</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(tmp)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verb(3, "MF digits received: '%s'\n", tmp);
</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verb(3, "No MF digits received.\n");
</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 read_mf_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 tmp[256] = "";
</span><br><span style="color: hsl(120, 100%, 40%);">+ int to = 0;
</span><br><span style="color: hsl(120, 100%, 40%);">+ double tosec;
</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_flags flags = {0};
</span><br><span style="color: hsl(120, 100%, 40%);">+ char *argcopy = NULL;
</span><br><span style="color: hsl(120, 100%, 40%);">+ int features = 0;
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DECLARE_APP_ARGS(arglist,
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(variable);
</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(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 (ast_strlen_zero(data)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "ReceiveMF requires an argument (variable)\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%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ argcopy = ast_strdupa(data);
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STANDARD_APP_ARGS(arglist, argcopy);
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(arglist.options)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_parse_options(read_app_options, &flags, NULL, arglist.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 (!ast_strlen_zero(arglist.timeout)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ tosec = atof(arglist.timeout);
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (tosec <= 0) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ to = 0;
</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {
</span><br><span style="color: hsl(120, 100%, 40%);">+ to = tosec * 1000.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%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(arglist.variable)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid! Usage: ReceiveMF(variable[,timeout][,option])\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%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_test_flag(&flags, OPT_DELAY)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ features |= DSP_DIGITMODE_MUTEMAX;
</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_MUTE)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ features |= DSP_DIGITMODE_MUTECONF;
</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_QUELCH)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ features |= DSP_DIGITMODE_NOQUELCH;
</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_RELAXED)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ features |= DSP_DIGITMODE_RELAXDTMF;
</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%);">+ read_mf_digits(chan, tmp, to, features, (ast_test_flag(&flags, OPT_LAX_KP)),
</span><br><span style="color: hsl(120, 100%, 40%);">+ (ast_test_flag(&flags, OPT_KP_OVERRIDE)), (ast_test_flag(&flags, OPT_NO_KP)), (ast_test_flag(&flags, OPT_NO_ST)));
</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(tmp)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verb(3, "MF digits received: '%s'\n", tmp);
</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verb(3, "No MF digits received.\n");
</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 sendsf_exec(struct ast_channel *chan, const char *vdata)
</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%);">+ char *data;
</span><br><span style="color: hsl(120, 100%, 40%);">+ int frequency = 2600;
</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *chan_found = NULL;
</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *chan_dest = chan;
</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *chan_autoservice = NULL;
</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(digits);
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(frequency);
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(channel);
</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_strlen_zero(vdata)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "SendSF requires an argument\n");
</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%);">+ data = ast_strdupa(vdata);
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STANDARD_APP_ARGS(args, data);
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(args.digits)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "The digits argument is required (0-9,wf)\n");
</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 (!ast_strlen_zero(args.frequency) && (ast_str_to_int(args.frequency, &frequency) || frequency < 1)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Invalid duration: %s\n", args.frequency);
</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.channel)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ chan_found = ast_channel_get_by_name(args.channel);
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!chan_found) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "No such channel: %s\n", args.channel);
</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%);">+ chan_dest = chan_found;
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (chan_found != chan) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ chan_autoservice = chan;
</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%);">+ res = ast_sf_stream(chan_dest, chan_autoservice, NULL, args.digits, frequency, 0);
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_cleanup(chan_found);
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ return chan_autoservice ? 0 : 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 sendmf_exec(struct ast_channel *chan, const char *vdata)
</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%);">+ char *data;
</span><br><span style="color: hsl(120, 100%, 40%);">+ int dinterval = 0, duration = 0, durationkp = 0, durationst = 0;
</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *chan_found = NULL;
</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *chan_dest = chan;
</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *chan_autoservice = NULL;
</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(digits);
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(dinterval);
</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(durationkp);
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(durationst);
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(channel);
</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_strlen_zero(vdata)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "SendMF requires an argument\n");
</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%);">+ data = ast_strdupa(vdata);
</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STANDARD_APP_ARGS(args, data);
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(args.digits)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "The digits argument is required (0-9,*#ABC,wf)\n");
</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 (!ast_strlen_zero(args.dinterval)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_parse_timelen(args.dinterval, &dinterval, TIMELEN_MILLISECONDS);
</span><br><span style="color: hsl(120, 100%, 40%);">+ }
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(args.duration)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_parse_timelen(args.duration, &duration, TIMELEN_MILLISECONDS);
</span><br><span style="color: hsl(120, 100%, 40%);">+ }
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(args.durationkp)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_parse_timelen(args.durationkp, &durationkp, TIMELEN_MILLISECONDS);
</span><br><span style="color: hsl(120, 100%, 40%);">+ }
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(args.durationst)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_parse_timelen(args.durationst, &durationst, TIMELEN_MILLISECONDS);
</span><br><span style="color: hsl(120, 100%, 40%);">+ }
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(args.channel)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ chan_found = ast_channel_get_by_name(args.channel);
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!chan_found) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "No such channel: %s\n", args.channel);
</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%);">+ chan_dest = chan_found;
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (chan_found != chan) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ chan_autoservice = chan;
</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%);">+ res = ast_mf_stream(chan_dest, chan_autoservice, NULL, args.digits, dinterval <= 0 ? MF_BETWEEN_MS : dinterval,
</span><br><span style="color: hsl(120, 100%, 40%);">+ duration <= 0 ? MF_DURATION : duration, durationkp <= 0 ? MF_KP_DURATION : durationkp,
</span><br><span style="color: hsl(120, 100%, 40%);">+ durationst <= 0 ? MF_ST_DURATION : durationst, 0);
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_cleanup(chan_found);
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ return chan_autoservice ? 0 : 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 manager_play_mf(struct mansession *s, const struct message *m)
</span><br><span style="color: hsl(120, 100%, 40%);">+{
</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *channel = astman_get_header(m, "Channel");
</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *digit = astman_get_header(m, "Digit");
</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *duration = astman_get_header(m, "Duration");
</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *chan;
</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int duration_ms = MF_DURATION;
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(chan = ast_channel_get_by_name(channel))) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_error(s, m, "Channel not found");
</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 (ast_strlen_zero(digit)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_error(s, m, "No digit specified");
</span><br><span style="color: hsl(120, 100%, 40%);">+ chan = ast_channel_unref(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%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Override default duration with KP or ST-specific default durations */
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcmp(digit, "*"))
</span><br><span style="color: hsl(120, 100%, 40%);">+ duration_ms = MF_KP_DURATION;
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcmp(digit, "#") || !strcmp(digit, "A") || !strcmp(digit, "B") || !strcmp(digit, "C"))
</span><br><span style="color: hsl(120, 100%, 40%);">+ duration_ms = MF_ST_DURATION;
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(duration) && (sscanf(duration, "%30u", &duration_ms) != 1)) {
</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_error(s, m, "Could not convert Duration parameter");
</span><br><span style="color: hsl(120, 100%, 40%);">+ chan = ast_channel_unref(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%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mf_stream(chan, NULL, NULL, digit, 0, duration_ms, duration_ms, duration_ms, 1);
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ chan = ast_channel_unref(chan);
</span><br><span style="color: hsl(120, 100%, 40%);">+
</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_ack(s, m, "MF successfully queued");
</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 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(readsf_name);
</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_unregister_application(readmf_name);
</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_unregister_application(sendmf_name);
</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_unregister_application(sendsf_name);
</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_manager_unregister("PlayMF");
</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(readsf_name, read_sf_exec);
</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_register_application_xml(readmf_name, read_mf_exec);
</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_register_application_xml(sendmf_name, sendmf_exec);
</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_register_application_xml(sendsf_name, sendsf_exec);
</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_manager_register_xml("PlayMF", EVENT_FLAG_CALL, manager_play_mf);
</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, "MF Sender and Receiver Applications");
</span><br><span>diff --git a/doc/CHANGES-staging/app_mf_full.txt b/doc/CHANGES-staging/app_mf_full.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..256b09b</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/app_mf_full.txt</span><br><span>@@ -0,0 +1,4 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: app_mf</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Adds applications to send and receive MF and SF signaling,</span><br><span style="color: hsl(120, 100%, 40%);">+along with integration in the Dial application.</span><br><span>diff --git a/include/asterisk/app.h b/include/asterisk/app.h</span><br><span>index 7690364..5a8ca86 100644</span><br><span>--- a/include/asterisk/app.h</span><br><span>+++ b/include/asterisk/app.h</span><br><span>@@ -923,6 +923,67 @@</span><br><span> void ast_unreplace_sigchld(void);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Send a string of SF digits to a channel</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param chan The channel that will receive the SF digits</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param peer (optional) Peer channel that will be autoserviced while the</span><br><span style="color: hsl(120, 100%, 40%);">+ * primary channel is receiving SF</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param chan2 A second channel that will simultaneously receive SF digits.</span><br><span style="color: hsl(120, 100%, 40%);">+ * This option may only be used if is_external is 0.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param digits This is a string of characters representing the SF digits</span><br><span style="color: hsl(120, 100%, 40%);">+ * to be sent to the channel. Valid characters are</span><br><span style="color: hsl(120, 100%, 40%);">+ * "0123456789". Note: You can pass arguments 'f' or</span><br><span style="color: hsl(120, 100%, 40%);">+ * 'F', if you want to Flash the channel (if supported by the</span><br><span style="color: hsl(120, 100%, 40%);">+ * channel), or 'w' or 'W' to add a wink (if supported by the</span><br><span style="color: hsl(120, 100%, 40%);">+ * channel).</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param between This is the number of milliseconds to wait in between each</span><br><span style="color: hsl(120, 100%, 40%);">+ * SF digit. If zero milliseconds is specified, then the</span><br><span style="color: hsl(120, 100%, 40%);">+ * default value of 50 will be used.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param duration This is the duration that each numeric SF digit should have.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Default value is 55.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param is_external 1 if called by a thread that is not the channel's media</span><br><span style="color: hsl(120, 100%, 40%);">+ * handler thread, 0 if called by the channel's media handler</span><br><span style="color: hsl(120, 100%, 40%);">+ * thread.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 on success.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 on failure or a channel hung up.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sf_stream(struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *chan2, const char *digits, int frequency, int is_external);</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%);">+ * \brief Send a string of MF digits to a channel</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param chan The channel that will receive the MF digits.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param peer (optional) Peer channel that will be autoserviced while the</span><br><span style="color: hsl(120, 100%, 40%);">+ * primary channel is receiving MF</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param chan2 A second channel that will simultaneously receive MF digits.</span><br><span style="color: hsl(120, 100%, 40%);">+ * This option may only be used if is_external is 0.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param digits This is a string of characters representing the MF digits</span><br><span style="color: hsl(120, 100%, 40%);">+ * to be sent to the channel. Valid characters are</span><br><span style="color: hsl(120, 100%, 40%);">+ * "0123456789*#abcdABCD". Note: You can pass arguments 'f' or</span><br><span style="color: hsl(120, 100%, 40%);">+ * 'F', if you want to Flash the channel (if supported by the</span><br><span style="color: hsl(120, 100%, 40%);">+ * channel), or 'w' or 'W' to add a wink (if supported by the</span><br><span style="color: hsl(120, 100%, 40%);">+ * channel).</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param between This is the number of milliseconds to wait in between each</span><br><span style="color: hsl(120, 100%, 40%);">+ * MF digit. If zero milliseconds is specified, then the</span><br><span style="color: hsl(120, 100%, 40%);">+ * default value of 50 will be used.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param duration This is the duration that each numeric MF digit should have.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Default value is 55.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param durationkp This is the duration that each KP digit should have. Default</span><br><span style="color: hsl(120, 100%, 40%);">+ * is 120.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param durationst This is the duration that each ST, STP, ST2P, or ST3P digit</span><br><span style="color: hsl(120, 100%, 40%);">+ * should have. Default is 65.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param is_external 1 if called by a thread that is not the channel's media</span><br><span style="color: hsl(120, 100%, 40%);">+ * handler thread, 0 if called by the channel's media handler</span><br><span style="color: hsl(120, 100%, 40%);">+ * thread.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 on success.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 on failure or a channel hung up.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_mf_stream(struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *chan2, const char *digits,</span><br><span style="color: hsl(120, 100%, 40%);">+ int between, unsigned int duration, unsigned int durationkp, unsigned int durationst, int is_external);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief Send a string of DTMF digits to a channel</span><br><span> *</span><br><span> * \param chan The channel that will receive the DTMF frames</span><br><span>diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h</span><br><span>index b699c74..4a08699 100644</span><br><span>--- a/include/asterisk/channel.h</span><br><span>+++ b/include/asterisk/channel.h</span><br><span>@@ -2255,6 +2255,30 @@</span><br><span> int ast_recvchar(struct ast_channel *chan, int timeout);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief End sending an MF digit to a channel.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param chan channel to act upon</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return Returns 0 on success, -1 on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_senddigit_mf_end(struct ast_channel *chan);</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%);">+ * \brief Send an MF digit to a channel.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param chan channel to act upon</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param digit the MF digit to send, encoded in ASCII</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param duration the duration of a numeric digit ending in ms</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param duration the duration of a KP digit ending in ms</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param duration the duration of a ST, STP, ST2P, or ST3P digit ending in ms</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param is_external 1 if called by a thread that is not the channel's media</span><br><span style="color: hsl(120, 100%, 40%);">+ * handler thread, 0 if called by the channel's media handler</span><br><span style="color: hsl(120, 100%, 40%);">+ * thread.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return 0 on success, -1 on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_senddigit_mf(struct ast_channel *chan, char digit, unsigned int duration,</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int durationkp, unsigned int durationst, int is_external);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief Send a DTMF digit to a channel.</span><br><span> *</span><br><span> * \param chan channel to act upon</span><br><span>@@ -2282,6 +2306,14 @@</span><br><span> int ast_senddigit_external(struct ast_channel *chan, char digit, unsigned int duration);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Send an MF digit to a channel.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param chan channel to act upon</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param digit the MF digit to send, encoded in ASCII</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return 0 on success, -1 on failure</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_senddigit_mf_begin(struct ast_channel *chan, char digit);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief Send a DTMF digit to a channel.</span><br><span> * \param chan channel to act upon</span><br><span> * \param digit the DTMF digit to send, encoded in ASCII</span><br><span>diff --git a/main/app.c b/main/app.c</span><br><span>index 09c0123..b1cd077 100644</span><br><span>--- a/main/app.c</span><br><span>+++ b/main/app.c</span><br><span>@@ -833,6 +833,208 @@</span><br><span> return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int sf_stream(struct ast_channel *chan, struct ast_channel *chan2, const char *digits, int frequency, int is_external)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Bell System Technical Journal 39 (Nov. 1960) */</span><br><span style="color: hsl(120, 100%, 40%);">+ #define SF_ON 67</span><br><span style="color: hsl(120, 100%, 40%);">+ #define SF_OFF 33</span><br><span style="color: hsl(120, 100%, 40%);">+ #define SF_BETWEEN 600</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *ptr;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_silence_generator *silgen = NULL, *silgen2 = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *freq;</span><br><span style="color: hsl(120, 100%, 40%);">+ int (*my_sleep)(struct ast_channel *chan, int ms);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (is_external) {</span><br><span style="color: hsl(120, 100%, 40%);">+ my_sleep = external_sleep;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ my_sleep = ast_safe_sleep;</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%);">+ /* Need a quiet time before sending digits. */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_opt_transmit_silence) {</span><br><span style="color: hsl(120, 100%, 40%);">+ silgen = ast_channel_start_silence_generator(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ silgen2 = ast_channel_start_silence_generator(chan2);</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 (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_autoservice_start(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ res = my_sleep(chan, 100);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_autoservice_stop(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res) {</span><br><span style="color: hsl(120, 100%, 40%);">+ goto sf_stream_cleanup;</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%);">+ freq = ast_malloc(32);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* pauses need to send audio, so send 0 Hz */</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(freq, 31, "%d/%d,%d/%d", frequency, SF_ON, 0, SF_OFF);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (ptr = digits; *ptr; ptr++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strchr("0123456789*#ABCDabcdwWfF", *ptr)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (*ptr == 'f' || *ptr == 'F') {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* ignore return values if not supported by channel */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_indicate(chan, AST_CONTROL_FLASH);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (*ptr == 'w' || *ptr == 'W') {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* ignore return values if not supported by channel */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_indicate(chan, AST_CONTROL_WINK);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Character represents valid SF */</span><br><span style="color: hsl(120, 100%, 40%);">+ int beeps;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (*ptr == '*') {</span><br><span style="color: hsl(120, 100%, 40%);">+ beeps = 11;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (*ptr == '#') {</span><br><span style="color: hsl(120, 100%, 40%);">+ beeps = 12;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (*ptr == 'D') {</span><br><span style="color: hsl(120, 100%, 40%);">+ beeps = 13;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (*ptr == 'C') {</span><br><span style="color: hsl(120, 100%, 40%);">+ beeps = 14;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (*ptr == 'B') {</span><br><span style="color: hsl(120, 100%, 40%);">+ beeps = 15;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (*ptr == 'A') {</span><br><span style="color: hsl(120, 100%, 40%);">+ beeps = 16;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ beeps = (*ptr == '0') ? 10 : *ptr - '0';</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ while (beeps-- > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_playtones_start(chan, 0, freq, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_playtones_start(chan2, 0, freq, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_autoservice_start(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ res = my_sleep(chan, SF_ON + SF_OFF);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_senddigit_mf_end(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_autoservice_stop(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_senddigit_mf_end(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res) {</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%);">+ /* pause between digits */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_playtones_start(chan, 0, "0", 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_playtones_start(chan2, 0, "0", 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_autoservice_start(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ res = my_sleep(chan, SF_BETWEEN);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_autoservice_stop(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_senddigit_mf_end(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_senddigit_mf_end(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res) {</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%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Illegal SF character '%c' in string. (0-9A-DwWfF allowed)\n", *ptr);</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_free(freq);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+sf_stream_cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+ if (silgen) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_stop_silence_generator(chan, silgen);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (silgen2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_stop_silence_generator(chan2, silgen2);</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 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 mf_stream(struct ast_channel *chan, struct ast_channel *chan2, const char *digits, int between, unsigned int duration,</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int durationkp, unsigned int durationst, int is_external)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *ptr;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_silence_generator *silgen = NULL, *silgen2 = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ int (*my_sleep)(struct ast_channel *chan, int ms);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (is_external) {</span><br><span style="color: hsl(120, 100%, 40%);">+ my_sleep = external_sleep;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ my_sleep = ast_safe_sleep;</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 (!between) {</span><br><span style="color: hsl(120, 100%, 40%);">+ between = 100;</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%);">+ /* Need a quiet time before sending digits. */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_opt_transmit_silence) {</span><br><span style="color: hsl(120, 100%, 40%);">+ silgen = ast_channel_start_silence_generator(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ silgen2 = ast_channel_start_silence_generator(chan2);</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 (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_autoservice_start(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ res = my_sleep(chan, 100);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_autoservice_stop(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res) {</span><br><span style="color: hsl(120, 100%, 40%);">+ goto mf_stream_cleanup;</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%);">+ for (ptr = digits; *ptr; ptr++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strchr("0123456789*#ABCwWfF", *ptr)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (*ptr == 'f' || *ptr == 'F') {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* ignore return values if not supported by channel */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_indicate(chan, AST_CONTROL_FLASH);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (*ptr == 'w' || *ptr == 'W') {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* ignore return values if not supported by channel */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_indicate(chan, AST_CONTROL_WINK);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Character represents valid MF */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_senddigit_mf(chan, *ptr, duration, durationkp, durationst, is_external);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_senddigit_mf(chan2, *ptr, duration, durationkp, durationst, is_external);</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%);">+ /* pause between digits */</span><br><span style="color: hsl(120, 100%, 40%);">+ /* The DSP code in Asterisk does not currently properly receive repeated tones</span><br><span style="color: hsl(120, 100%, 40%);">+ if no audio is sent in the middle. Simply sending audio (even 0 Hz)</span><br><span style="color: hsl(120, 100%, 40%);">+ works around this limitation and guarantees the correct behavior.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_playtones_start(chan, 0, "0", 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_playtones_start(chan2, 0, "0", 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_autoservice_start(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ res = my_sleep(chan, between);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_senddigit_mf_end(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (chan2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_autoservice_stop(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_senddigit_mf_end(chan2);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res) {</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%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Illegal MF character '%c' in string. (0-9*#ABCwWfF allowed)\n", *ptr);</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%);">+mf_stream_cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+ if (silgen) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_stop_silence_generator(chan, silgen);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (silgen2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_stop_silence_generator(chan2, silgen2);</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 res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int dtmf_stream(struct ast_channel *chan, const char *digits, int between, unsigned int duration, int is_external)</span><br><span> {</span><br><span> const char *ptr;</span><br><span>@@ -901,6 +1103,36 @@</span><br><span> return res;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_sf_stream(struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *chan2, const char *digits, int frequency, int is_external)</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%);">+ if (frequency <= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ frequency = 2600;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!is_external && !chan2 && peer && ast_autoservice_start(peer)) {</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%);">+ res = sf_stream(chan, chan2, digits, frequency, is_external);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!is_external && !chan2 && peer && ast_autoservice_stop(peer)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</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%);">+int ast_mf_stream(struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *chan2, const char *digits,</span><br><span style="color: hsl(120, 100%, 40%);">+ int between, unsigned int duration, unsigned int durationkp, unsigned int durationst, int is_external)</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%);">+ if (!is_external && !chan2 && peer && ast_autoservice_start(peer)) {</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%);">+ res = mf_stream(chan, chan2, digits, between, duration, durationkp, durationst, is_external);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!is_external && !chan2 && peer && ast_autoservice_stop(peer)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</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> int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration)</span><br><span> {</span><br><span> int res;</span><br><span>diff --git a/main/channel.c b/main/channel.c</span><br><span>index 9e33fb2..31dd696 100644</span><br><span>--- a/main/channel.c</span><br><span>+++ b/main/channel.c</span><br><span>@@ -97,10 +97,14 @@</span><br><span> AST_THREADSTORAGE(state2str_threadbuf);</span><br><span> #define STATE2STR_BUFSIZE 32</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/*! Default amount of time to use when emulating a digit as a begin and end</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Default amount of time to use when emulating a DTMF digit as a begin and end</span><br><span> * 100ms */</span><br><span> #define AST_DEFAULT_EMULATE_DTMF_DURATION 100</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! Default amount of time to use when emulating an MF digit as a begin and end</span><br><span style="color: hsl(120, 100%, 40%);">+ * 55ms */</span><br><span style="color: hsl(120, 100%, 40%);">+#define DEFAULT_EMULATE_MF_DURATION 55</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #define DEFAULT_AMA_FLAGS AST_AMA_DOCUMENTATION</span><br><span> </span><br><span> /*! Minimum amount of time between the end of the last digit and the beginning</span><br><span>@@ -4864,6 +4868,45 @@</span><br><span> return rc;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_senddigit_mf_begin(struct ast_channel *chan, char digit)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ static const char * const mf_tones[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ "1300+1500", /* 0 */</span><br><span style="color: hsl(120, 100%, 40%);">+ "700+900", /* 1 */</span><br><span style="color: hsl(120, 100%, 40%);">+ "700+1100", /* 2 */</span><br><span style="color: hsl(120, 100%, 40%);">+ "900+1100", /* 3 */</span><br><span style="color: hsl(120, 100%, 40%);">+ "700+1300", /* 4 */</span><br><span style="color: hsl(120, 100%, 40%);">+ "900+1300", /* 5 */</span><br><span style="color: hsl(120, 100%, 40%);">+ "1100+1300", /* 6 */</span><br><span style="color: hsl(120, 100%, 40%);">+ "700+1500", /* 7 */</span><br><span style="color: hsl(120, 100%, 40%);">+ "900+1500", /* 8 */</span><br><span style="color: hsl(120, 100%, 40%);">+ "1100+1500", /* 9 */</span><br><span style="color: hsl(120, 100%, 40%);">+ "1100+1700", /* * (KP) */</span><br><span style="color: hsl(120, 100%, 40%);">+ "1500+1700", /* # (ST) */</span><br><span style="color: hsl(120, 100%, 40%);">+ "900+1700", /* A (STP) */</span><br><span style="color: hsl(120, 100%, 40%);">+ "1300+1700", /* B (ST2P) */</span><br><span style="color: hsl(120, 100%, 40%);">+ "700+1700" /* C (ST3P) */</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 (digit >= '0' && digit <='9') {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_playtones_start(chan, 0, mf_tones[digit-'0'], 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (digit == '*') {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_playtones_start(chan, 0, mf_tones[10], 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (digit == '#') {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_playtones_start(chan, 0, mf_tones[11], 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (digit == 'A') {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_playtones_start(chan, 0, mf_tones[12], 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (digit == 'B') {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_playtones_start(chan, 0, mf_tones[13], 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (digit == 'C') {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_playtones_start(chan, 0, mf_tones[14], 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* not handled */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Unable to generate MF tone '%c' for '%s'\n", digit, ast_channel_name(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> int ast_senddigit_begin(struct ast_channel *chan, char digit)</span><br><span> {</span><br><span> /* Device does not support DTMF tones, lets fake</span><br><span>@@ -4933,6 +4976,37 @@</span><br><span> return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_senddigit_mf_end(struct ast_channel *chan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_channel_generator(chan)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_playtones_stop(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%);">+ 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%);">+int ast_senddigit_mf(struct ast_channel *chan, char digit, unsigned int duration,</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int durationkp, unsigned int durationst, int is_external)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (duration < DEFAULT_EMULATE_MF_DURATION) {</span><br><span style="color: hsl(120, 100%, 40%);">+ duration = DEFAULT_EMULATE_MF_DURATION;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_channel_tech(chan)->send_digit_begin) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (digit == '*') {</span><br><span style="color: hsl(120, 100%, 40%);">+ duration = durationkp;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (digit == '#' || digit == 'A' || digit == 'B' || digit == 'C') {</span><br><span style="color: hsl(120, 100%, 40%);">+ duration = durationst;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_senddigit_mf_begin(chan, digit);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (is_external) {</span><br><span style="color: hsl(120, 100%, 40%);">+ usleep(duration * 1000);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_safe_sleep(chan, duration);</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 ast_senddigit_mf_end(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int ast_senddigit(struct ast_channel *chan, char digit, unsigned int duration)</span><br><span> {</span><br><span> if (duration < AST_DEFAULT_EMULATE_DTMF_DURATION) {</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/16481">change 16481</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/+/16481"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I05ba61ce49092d3f131ade9dc4d97dcc9887fa1d </div>
<div style="display:none"> Gerrit-Change-Number: 16481 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: N A <mail@interlinked.x10host.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>