<p>Friendly Automation <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/16080">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Kevin Harwell: Looks good to me, but someone else must approve
George Joseph: Looks good to me, approved
Friendly Automation: Approved for Submit
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">app_mf: Add channel agnostic MF sender<br><br>Adds a SendMF application and PlayMF manager<br>event to send arbitrary R1 MF tones on the<br>current or specified channel.<br><br>ASTERISK-29496<br><br>Change-Id: I5d89afdbccee3f86cc702ed96d882f3d351327a4<br>---<br>A apps/app_mf.c<br>A doc/CHANGES-staging/mf.txt<br>2 files changed, 367 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><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..671fc1c</span><br><span>--- /dev/null</span><br><span>+++ b/apps/app_mf.c</span><br><span>@@ -0,0 +1,361 @@</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 App to send MF digits</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/pbx.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/app.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/indications.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="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</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">SendDTMF</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 signal 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%);">+static const char sendmf_name[] = "SendMF";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define DEFAULT_EMULATE_MF_DURATION 35</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%);">+static int 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%);">+</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 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%);">+static int mysleep(struct ast_channel *chan, int ms, int is_external)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return is_external ? usleep(ms * 1000) : ast_safe_sleep(chan, ms);</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 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%);">+ senddigit_mf_begin(chan, digit);</span><br><span style="color: hsl(120, 100%, 40%);">+ mysleep(chan, duration, is_external);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return 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 style="color: hsl(120, 100%, 40%);">+static int mf_stream(struct ast_channel *chan, 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;</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%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ res = mysleep(chan, 100, is_external);</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%);">+ senddigit_mf(chan, *ptr, duration, durationkp, durationst, is_external);</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%);">+ res = mysleep(chan, between, is_external);</span><br><span style="color: hsl(120, 100%, 40%);">+ 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 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%);">+</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 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%);">+ if (chan_autoservice && ast_autoservice_start(chan_autoservice)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_cleanup(chan_found);</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_dest, 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%);">+ if (chan_autoservice && ast_autoservice_stop(chan_autoservice)) {</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%);">+ 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%);">+ senddigit_mf(chan, *digit, duration_ms, duration_ms, duration_ms, 1);</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(sendmf_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_manager_register_xml("PlayMF", EVENT_FLAG_CALL, manager_play_mf);</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%);">+</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, "Send MF digits Application");</span><br><span>diff --git a/doc/CHANGES-staging/mf.txt b/doc/CHANGES-staging/mf.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..644f62a</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/mf.txt</span><br><span>@@ -0,0 +1,6 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: Channel-agnostic MF support</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+A SendMF application and PlayMF manager</span><br><span style="color: hsl(120, 100%, 40%);">+application are now included to send</span><br><span style="color: hsl(120, 100%, 40%);">+arbitrary standard R1 MF tones on the</span><br><span style="color: hsl(120, 100%, 40%);">+current channel or another specified channel.</span><br><span></span><br></pre><div style="white-space:pre-wrap"></div><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/16080">change 16080</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/+/16080"/><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: I5d89afdbccee3f86cc702ed96d882f3d351327a4 </div>
<div style="display:none"> Gerrit-Change-Number: 16080 </div>
<div style="display:none"> Gerrit-PatchSet: 10 </div>
<div style="display:none"> Gerrit-Owner: N A <mail@interlinked.x10host.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@sangoma.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-CC: Sarah Autumn <sarah@endlesstemple.org> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>