<p>N A has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/16499">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">app_mf: Add full tech-agnostic MF support<br><br>Adds tech-agnostic support for MF signaling by adding<br>MF sender and receiver applications as well as Dial<br>integration.<br><br>ASTERISK-29496-mf #do-not-close<br><br>Change-Id: I61962b359b8ec4cfd05df877ddf9f5b8f71927a4<br>---<br>M apps/app_dial.c<br>M apps/app_mf.c<br>A doc/CHANGES-staging/app_mf_mf.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, 572 insertions(+), 194 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/99/16499/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..7d55ad5 100644</span><br><span>--- a/apps/app_dial.c</span><br><span>+++ b/apps/app_dial.c</span><br><span>@@ -156,6 +156,8 @@</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>                                   <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 +165,15 @@</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%);">+                          </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 +510,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 +522,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 +543,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 +720,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 +756,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 +1214,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,</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 +1235,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 +1573,14 @@</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>                                            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 +1591,20 @@</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%);">+                                     }</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 +2159,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 +2276,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;</span><br><span>   struct privacy_args pa = {</span><br><span>           .sentringing = 0,</span><br><span>            .privdb_val = 0,</span><br><span>@@ -2384,9 +2412,11 @@</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%);">+              mf_wink = opt_args[OPT_ARG_SENDDTMF];</span><br><span style="color: hsl(120, 100%, 40%);">+         dtmfcalled = strsep(&mf_wink, ":");</span><br><span style="color: hsl(120, 100%, 40%);">+             dtmfcalling = strsep(&mf_wink, ":");</span><br><span style="color: hsl(120, 100%, 40%);">+            dtmf_progress = strsep(&mf_wink, ":");</span><br><span style="color: hsl(120, 100%, 40%);">+          mf_progress = strsep(&mf_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 +2893,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, (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 +3567,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>index 671fc1c..293b649 100644</span><br><span>--- a/apps/app_mf.c</span><br><span>+++ b/apps/app_mf.c</span><br><span>@@ -18,7 +18,7 @@</span><br><span> </span><br><span> /*! \file</span><br><span>  *</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief App to send MF digits</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief MF sender and receiver applications</span><br><span>  *</span><br><span>  * \author Naveen Albert <asterisk@phreaknet.org></span><br><span>  *</span><br><span>@@ -31,22 +31,92 @@</span><br><span> </span><br><span> #include "asterisk.h"</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/file.h"</span><br><span> #include "asterisk/pbx.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/module.h"</span><br><span style="color: hsl(0, 100%, 40%);">-#include "asterisk/app.h"</span><br><span> #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> #include "asterisk/indications.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/conversions.h"</span><br><span> </span><br><span> /*** DOCUMENTATION</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">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>         <application name="SendMF" language="en_US"></span><br><span>               <synopsis></span><br><span style="color: hsl(0, 100%, 40%);">-                        Sends arbitrary MF digits</span><br><span style="color: hsl(120, 100%, 40%);">+                     Sends arbitrary MF digits on the current or specified channel.</span><br><span>               </synopsis></span><br><span>            <syntax></span><br><span>                       <parameter name="digits" required="true"></span><br><span style="color: hsl(0, 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(0, 100%, 40%);">-                              if the channel supports flash-hook, and w or W for a wink if the channel</span><br><span style="color: hsl(0, 100%, 40%);">-                                supports wink.</para></span><br><span style="color: hsl(120, 100%, 40%);">+                           <para>List of digits 0-9,*#ABC to send; w for a half-second pause,</span><br><span style="color: hsl(120, 100%, 40%);">+                              also f or F for a flash-hook if the channel supports flash-hook,</span><br><span style="color: hsl(120, 100%, 40%);">+                              h or H for 250 ms of 2600 Hz,</span><br><span style="color: hsl(120, 100%, 40%);">+                         and W for a wink if the channel supports wink.</para></span><br><span>                          <para>Key pulse and start digits are not included automatically.</span><br><span>                               * is used for KP, # for ST, A for STP, B for ST2P, and C for ST3P.</para></span><br><span>                      </parameter></span><br><span>@@ -70,12 +140,13 @@</span><br><span>                    <para>It will send all digits or terminate if it encounters an error.</para></span><br><span>             </description></span><br><span>                 <see-also></span><br><span style="color: hsl(120, 100%, 40%);">+                      <ref type="application">ReceiveMF</ref></span><br><span>                        <ref type="application">SendDTMF</ref></span><br><span>                 </see-also></span><br><span>    </application></span><br><span>         <manager name="PlayMF" language="en_US"></span><br><span>           <synopsis></span><br><span style="color: hsl(0, 100%, 40%);">-                        Play MF signal on a specific channel.</span><br><span style="color: hsl(120, 100%, 40%);">+                 Play MF digit on a specific channel.</span><br><span>                 </synopsis></span><br><span>            <syntax></span><br><span>                       <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" /></span><br><span>@@ -95,140 +166,199 @@</span><br><span>         </manager></span><br><span>  ***/</span><br><span> </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%);">+};</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('l', OPT_LAX_KP),</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_APP_OPTION('k', OPT_NO_KP),</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),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION('p', OPT_PROCESS),</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),</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> static const char sendmf_name[] = "SendMF";</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-#define DEFAULT_EMULATE_MF_DURATION 35</span><br><span> #define MF_BETWEEN_MS 50</span><br><span> #define MF_DURATION 55</span><br><span> #define MF_KP_DURATION 120</span><br><span> #define MF_ST_DURATION 65</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int senddigit_mf_begin(struct ast_channel *chan, char digit)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-     static const char * const mf_tones[] = {</span><br><span style="color: hsl(0, 100%, 40%);">-                "1300+1500", /* 0 */</span><br><span style="color: hsl(0, 100%, 40%);">-          "700+900",   /* 1 */</span><br><span style="color: hsl(0, 100%, 40%);">-          "700+1100",  /* 2 */</span><br><span style="color: hsl(0, 100%, 40%);">-          "900+1100",  /* 3 */</span><br><span style="color: hsl(0, 100%, 40%);">-          "700+1300",  /* 4 */</span><br><span style="color: hsl(0, 100%, 40%);">-          "900+1300",  /* 5 */</span><br><span style="color: hsl(0, 100%, 40%);">-          "1100+1300", /* 6 */</span><br><span style="color: hsl(0, 100%, 40%);">-          "700+1500",  /* 7 */</span><br><span style="color: hsl(0, 100%, 40%);">-          "900+1500",  /* 8 */</span><br><span style="color: hsl(0, 100%, 40%);">-          "1100+1500", /* 9 */</span><br><span style="color: hsl(0, 100%, 40%);">-          "1100+1700", /* * (KP) */</span><br><span style="color: hsl(0, 100%, 40%);">-             "1500+1700", /* # (ST) */</span><br><span style="color: hsl(0, 100%, 40%);">-             "900+1700",  /* A (STP) */</span><br><span style="color: hsl(0, 100%, 40%);">-            "1300+1700", /* B (ST2P) */</span><br><span style="color: hsl(0, 100%, 40%);">-           "700+1700"   /* C (ST3P) */</span><br><span style="color: hsl(0, 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> </span><br><span style="color: hsl(0, 100%, 40%);">-    if (digit >= '0' && digit <='9') {</span><br><span style="color: hsl(0, 100%, 40%);">-                ast_playtones_start(chan, 0, mf_tones[digit-'0'], 0);</span><br><span style="color: hsl(0, 100%, 40%);">-   } else if (digit == '*') {</span><br><span style="color: hsl(0, 100%, 40%);">-              ast_playtones_start(chan, 0, mf_tones[10], 0);</span><br><span style="color: hsl(0, 100%, 40%);">-  } else if (digit == '#') {</span><br><span style="color: hsl(0, 100%, 40%);">-              ast_playtones_start(chan, 0, mf_tones[11], 0);</span><br><span style="color: hsl(0, 100%, 40%);">-  } else if (digit == 'A') {</span><br><span style="color: hsl(0, 100%, 40%);">-              ast_playtones_start(chan, 0, mf_tones[12], 0);</span><br><span style="color: hsl(0, 100%, 40%);">-  } else if (digit == 'B') {</span><br><span style="color: hsl(0, 100%, 40%);">-              ast_playtones_start(chan, 0, mf_tones[13], 0);</span><br><span style="color: hsl(0, 100%, 40%);">-  } else if (digit == 'C') {</span><br><span style="color: hsl(0, 100%, 40%);">-              ast_playtones_start(chan, 0, mf_tones[14], 0);</span><br><span style="color: hsl(0, 100%, 40%);">-  } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                /* not handled */</span><br><span style="color: hsl(0, 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%);">+  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>   }</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> </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>  return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int senddigit_mf_end(struct ast_channel *chan)</span><br><span style="color: hsl(120, 100%, 40%);">+static int read_mf_exec(struct ast_channel *chan, const char *data)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-       if (ast_channel_generator(chan)) {</span><br><span style="color: hsl(0, 100%, 40%);">-              ast_playtones_stop(chan);</span><br><span style="color: hsl(0, 100%, 40%);">-               return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-       return -1;</span><br><span style="color: hsl(0, 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> </span><br><span style="color: hsl(0, 100%, 40%);">-static int mysleep(struct ast_channel *chan, int ms, int is_external)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-     return is_external ? usleep(ms * 1000) : ast_safe_sleep(chan, ms);</span><br><span style="color: hsl(0, 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> </span><br><span style="color: hsl(0, 100%, 40%);">-static int senddigit_mf(struct ast_channel *chan, char digit, unsigned int duration,</span><br><span style="color: hsl(0, 100%, 40%);">-      unsigned int durationkp, unsigned int durationst, int is_external)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-     if (duration < DEFAULT_EMULATE_MF_DURATION) {</span><br><span style="color: hsl(0, 100%, 40%);">-                duration = DEFAULT_EMULATE_MF_DURATION;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-       if (ast_channel_tech(chan)->send_digit_begin) {</span><br><span style="color: hsl(0, 100%, 40%);">-              if (digit == '*') {</span><br><span style="color: hsl(0, 100%, 40%);">-                     duration = durationkp;</span><br><span style="color: hsl(0, 100%, 40%);">-          } else if (digit == '#' || digit == 'A' || digit == 'B' || digit == 'C') {</span><br><span style="color: hsl(0, 100%, 40%);">-                      duration = durationst;</span><br><span style="color: hsl(0, 100%, 40%);">-          }</span><br><span style="color: hsl(0, 100%, 40%);">-               senddigit_mf_begin(chan, digit);</span><br><span style="color: hsl(0, 100%, 40%);">-                mysleep(chan, duration, is_external);</span><br><span style="color: hsl(0, 100%, 40%);">-   }</span><br><span style="color: hsl(0, 100%, 40%);">-       return senddigit_mf_end(chan);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static int mf_stream(struct ast_channel *chan, const char *digits, int between, unsigned int duration,</span><br><span style="color: hsl(0, 100%, 40%);">-   unsigned int durationkp, unsigned int durationst, int is_external)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-     const char *ptr;</span><br><span style="color: hsl(0, 100%, 40%);">-        int res;</span><br><span style="color: hsl(0, 100%, 40%);">-        struct ast_silence_generator *silgen = NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    if (!between) {</span><br><span style="color: hsl(0, 100%, 40%);">-         between = 100;</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>   }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   /* Need a quiet time before sending digits. */</span><br><span style="color: hsl(0, 100%, 40%);">-  if (ast_opt_transmit_silence) {</span><br><span style="color: hsl(0, 100%, 40%);">-         silgen = ast_channel_start_silence_generator(chan);</span><br><span style="color: hsl(0, 100%, 40%);">-     }</span><br><span style="color: hsl(0, 100%, 40%);">-       res = mysleep(chan, 100, is_external);</span><br><span style="color: hsl(0, 100%, 40%);">-  if (res) {</span><br><span style="color: hsl(0, 100%, 40%);">-              goto mf_stream_cleanup;</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>  }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   for (ptr = digits; *ptr; ptr++) {</span><br><span style="color: hsl(0, 100%, 40%);">-               if (strchr("0123456789*#ABCwWfF", *ptr)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                    if (*ptr == 'f' || *ptr == 'F') {</span><br><span style="color: hsl(0, 100%, 40%);">-                               /* ignore return values if not supported by channel */</span><br><span style="color: hsl(0, 100%, 40%);">-                          ast_indicate(chan, AST_CONTROL_FLASH);</span><br><span style="color: hsl(0, 100%, 40%);">-                  } else if (*ptr == 'w' || *ptr == 'W') {</span><br><span style="color: hsl(0, 100%, 40%);">-                                /* ignore return values if not supported by channel */</span><br><span style="color: hsl(0, 100%, 40%);">-                          ast_indicate(chan, AST_CONTROL_WINK);</span><br><span style="color: hsl(0, 100%, 40%);">-                   } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                                /* Character represents valid MF */</span><br><span style="color: hsl(0, 100%, 40%);">-                             senddigit_mf(chan, *ptr, duration, durationkp, durationst, is_external);</span><br><span style="color: hsl(0, 100%, 40%);">-                        }</span><br><span style="color: hsl(0, 100%, 40%);">-                       /* pause between digits */</span><br><span style="color: hsl(0, 100%, 40%);">-                      /* The DSP code in Asterisk does not currently properly receive repeated tones</span><br><span style="color: hsl(0, 100%, 40%);">-                          if no audio is sent in the middle. Simply sending audio (even 0 Hz)</span><br><span style="color: hsl(0, 100%, 40%);">-                             works around this limitation and guarantees the correct behavior.</span><br><span style="color: hsl(0, 100%, 40%);">-                               */</span><br><span style="color: hsl(0, 100%, 40%);">-                      ast_playtones_start(chan, 0, "0", 0);</span><br><span style="color: hsl(0, 100%, 40%);">-                 res = mysleep(chan, between, is_external);</span><br><span style="color: hsl(0, 100%, 40%);">-                      senddigit_mf_end(chan);</span><br><span style="color: hsl(0, 100%, 40%);">-                 if (res) {</span><br><span style="color: hsl(0, 100%, 40%);">-                              break;</span><br><span style="color: hsl(0, 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>              } else {</span><br><span style="color: hsl(0, 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%);">+                        to = tosec * 1000.0;</span><br><span>                 }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-mf_stream_cleanup:</span><br><span style="color: hsl(0, 100%, 40%);">- if (silgen) {</span><br><span style="color: hsl(0, 100%, 40%);">-           ast_channel_stop_silence_generator(chan, silgen);</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>   }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   return res;</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> }</span><br><span> </span><br><span> static int sendmf_exec(struct ast_channel *chan, const char *vdata)</span><br><span>@@ -283,16 +413,9 @@</span><br><span>                     chan_autoservice = chan;</span><br><span>             }</span><br><span>    }</span><br><span style="color: hsl(0, 100%, 40%);">-       if (chan_autoservice && ast_autoservice_start(chan_autoservice)) {</span><br><span style="color: hsl(0, 100%, 40%);">-              ast_channel_cleanup(chan_found);</span><br><span style="color: hsl(0, 100%, 40%);">-                return -1;</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(0, 100%, 40%);">-       res = mf_stream(chan_dest, args.digits, dinterval <= 0 ? MF_BETWEEN_MS : dinterval,</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>           duration <= 0 ? MF_DURATION : duration, durationkp <= 0 ? MF_KP_DURATION : durationkp,</span><br><span>                 durationst <= 0 ? MF_ST_DURATION : durationst, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-   if (chan_autoservice && ast_autoservice_stop(chan_autoservice)) {</span><br><span style="color: hsl(0, 100%, 40%);">-               res = -1;</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span>    ast_channel_cleanup(chan_found);</span><br><span> </span><br><span>         return chan_autoservice ? 0 : res;</span><br><span>@@ -330,7 +453,8 @@</span><br><span>             return 0;</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   senddigit_mf(chan, *digit, duration_ms, duration_ms, duration_ms, 1);</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>       chan = ast_channel_unref(chan);</span><br><span> </span><br><span>  astman_send_ack(s, m, "MF successfully queued");</span><br><span>@@ -342,7 +466,8 @@</span><br><span> {</span><br><span>        int res;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    res = ast_unregister_application(sendmf_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>      res |= ast_manager_unregister("PlayMF");</span><br><span> </span><br><span>       return res;</span><br><span>@@ -352,10 +477,11 @@</span><br><span> {</span><br><span>     int res;</span><br><span> </span><br><span style="color: hsl(0, 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(readmf_name, read_mf_exec);</span><br><span>       res |= ast_register_application_xml(sendmf_name, sendmf_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> </span><br><span>   return res;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Send MF digits Application");</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_mf.txt b/doc/CHANGES-staging/app_mf_mf.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..3168f2a</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/app_mf_mf.txt</span><br><span>@@ -0,0 +1,5 @@</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 MF receiver and sender applications to support</span><br><span style="color: hsl(120, 100%, 40%);">+the R1 MF signaling protocol, including integration</span><br><span style="color: hsl(120, 100%, 40%);">+with the Dial application.</span><br><span>diff --git a/include/asterisk/app.h b/include/asterisk/app.h</span><br><span>index ab246c8..ec34f28 100644</span><br><span>--- a/include/asterisk/app.h</span><br><span>+++ b/include/asterisk/app.h</span><br><span>@@ -137,23 +137,6 @@</span><br><span>  */</span><br><span> int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/*! \brief Plays a stream and gets DTMF data from a channel</span><br><span style="color: hsl(0, 100%, 40%);">- * \param c Which channel one is interacting with</span><br><span style="color: hsl(0, 100%, 40%);">- * \param prompt File to pass to ast_streamfile (the one that you wish to play).</span><br><span style="color: hsl(0, 100%, 40%);">- *        It is also valid for this to be multiple files concatenated by "&".</span><br><span style="color: hsl(0, 100%, 40%);">- *        For example, "file1&file2&file3".</span><br><span style="color: hsl(0, 100%, 40%);">- * \param s The location where the DTMF data will be stored</span><br><span style="color: hsl(0, 100%, 40%);">- * \param maxlen Max Length of the data</span><br><span style="color: hsl(0, 100%, 40%);">- * \param timeout Timeout length waiting for data(in milliseconds).  Set to 0 for standard timeout(six seconds), or -1 for no time out.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param terminator A string of characters that may be used as terminators to end input. If NULL, "#" will be used.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- *  This function was designed for application programmers for situations where they need</span><br><span style="color: hsl(0, 100%, 40%);">- *  to play a message and then get some DTMF data in response to the message.  If a digit</span><br><span style="color: hsl(0, 100%, 40%);">- *  is pressed during playback, it will immediately break out of the message and continue</span><br><span style="color: hsl(0, 100%, 40%);">- *  execution of your code.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-int ast_app_getdata_terminator(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout, char *terminator);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> /*! \brief Full version with audiofd and controlfd.  NOTE: returns '2' on ctrlfd available, not '1' like other full functions */</span><br><span> int ast_app_getdata_full(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd);</span><br><span> </span><br><span>@@ -940,6 +923,39 @@</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 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 f5fbffd..30e83c0 100644</span><br><span>--- a/main/app.c</span><br><span>+++ b/main/app.c</span><br><span>@@ -193,26 +193,9 @@</span><br><span>  * \param s The string to read in to.  Must be at least the size of your length</span><br><span>  * \param maxlen How many digits to read (maximum)</span><br><span>  * \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for</span><br><span style="color: hsl(0, 100%, 40%);">- *      "ludicrous time" (essentially never times out)</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(120, 100%, 40%);">+ *      "ludicrous time" (essentially never times out) */</span><br><span> enum ast_getdata_result ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-        return ast_app_getdata_terminator(c, prompt, s, maxlen, timeout, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*!</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief ast_app_getdata</span><br><span style="color: hsl(0, 100%, 40%);">- * \param c The channel to read from</span><br><span style="color: hsl(0, 100%, 40%);">- * \param prompt The file to stream to the channel</span><br><span style="color: hsl(0, 100%, 40%);">- * \param s The string to read in to.  Must be at least the size of your length</span><br><span style="color: hsl(0, 100%, 40%);">- * \param maxlen How many digits to read (maximum)</span><br><span style="color: hsl(0, 100%, 40%);">- * \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for</span><br><span style="color: hsl(0, 100%, 40%);">- *      "ludicrous time" (essentially never times out)</span><br><span style="color: hsl(0, 100%, 40%);">- * \param terminator A string of characters that may be used as terminators to end input. Default if NULL is "#"</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-enum ast_getdata_result ast_app_getdata_terminator(struct ast_channel *c, const char *prompt, char *s,</span><br><span style="color: hsl(0, 100%, 40%);">-       int maxlen, int timeout, char *terminator)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span>  int res = 0, to, fto;</span><br><span>        char *front, *filename;</span><br><span> </span><br><span>@@ -249,7 +232,7 @@</span><br><span>                    fto = 50;</span><br><span>                    to = ast_channel_pbx(c) ? ast_channel_pbx(c)->dtimeoutms : 2000;</span><br><span>          }</span><br><span style="color: hsl(0, 100%, 40%);">-               res = ast_readstring(c, s, maxlen, to, fto, S_OR(terminator, "#"));</span><br><span style="color: hsl(120, 100%, 40%);">+         res = ast_readstring(c, s, maxlen, to, fto, "#");</span><br><span>          if (res == AST_GETDATA_EMPTY_END_TERMINATED) {</span><br><span>                       return res;</span><br><span>          }</span><br><span>@@ -850,6 +833,115 @@</span><br><span>    return 0;</span><br><span> }</span><br><span> </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 (*ptr == 'w') {</span><br><span style="color: hsl(120, 100%, 40%);">+                    /* 'w' -- wait half a second */</span><br><span style="color: hsl(120, 100%, 40%);">+                       res = my_sleep(chan, 500);</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 if (*ptr == 'h' || *ptr == 'H') {</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* 'h' -- 2600 Hz for half a second, but</span><br><span style="color: hsl(120, 100%, 40%);">+                              only to far end of trunk, not near end */</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_playtones_start(chan, 0, "2600", 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, 250);</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 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') {</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*#ABCwWfFhH 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>@@ -918,6 +1010,20 @@</span><br><span>     return res;</span><br><span> }</span><br><span> </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 9104ef8..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>@@ -8257,12 +8331,6 @@</span><br><span>      return ast_say_number_full(chan, num, ints, language, options, -1, -1);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-int ast_say_ordinal(struct ast_channel *chan, int num,</span><br><span style="color: hsl(0, 100%, 40%);">-  const char *ints, const char *language, const char *options)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   return ast_say_ordinal_full(chan, num, ints, language, options, -1, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> int ast_say_enumeration(struct ast_channel *chan, int num,</span><br><span>      const char *ints, const char *language, const char *options)</span><br><span> {</span><br><span>@@ -8281,12 +8349,6 @@</span><br><span>   return ast_say_digit_str_full(chan, str, ints, lang, -1, -1);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-int ast_say_money_str(struct ast_channel *chan, const char *str,</span><br><span style="color: hsl(0, 100%, 40%);">-  const char *ints, const char *lang)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    return ast_say_money_str_full(chan, str, ints, lang, -1, -1);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> int ast_say_character_str(struct ast_channel *chan, const char *str,</span><br><span>       const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity)</span><br><span> {</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/16499">change 16499</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/+/16499"/><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: I61962b359b8ec4cfd05df877ddf9f5b8f71927a4 </div>
<div style="display:none"> Gerrit-Change-Number: 16499 </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>