Asterisk - The Open Source Telephony Project GIT-master-f36a736
app_sf.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2021, Naveen Albert
5 *
6 * Naveen Albert <asterisk@phreaknet.org>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*! \file
20 *
21 * \brief SF sender and receiver applications
22 *
23 * \author Naveen Albert <asterisk@phreaknet.org>
24 *
25 * \ingroup applications
26 */
27
28/*** MODULEINFO
29 <support_level>extended</support_level>
30 ***/
31
32#include "asterisk.h"
33
34#include "asterisk/file.h"
35#include "asterisk/pbx.h"
36#include "asterisk/channel.h"
37#include "asterisk/dsp.h"
38#include "asterisk/app.h"
39#include "asterisk/module.h"
42
43/*** DOCUMENTATION
44 <application name="ReceiveSF" language="en_US">
45 <since>
46 <version>16.24.0</version>
47 <version>18.10.0</version>
48 <version>19.2.0</version>
49 </since>
50 <synopsis>
51 Detects SF digits on a channel and saves them to a variable.
52 </synopsis>
53 <syntax>
54 <parameter name="variable" required="true">
55 <para>The input digits will be stored in the given
56 <replaceable>variable</replaceable> name.</para>
57 </parameter>
58 <parameter name="digits" required="false">
59 <para>Maximum number of digits to read. Default is unlimited.</para>
60 </parameter>
61 <parameter name="timeout">
62 <para>The number of seconds to wait for all digits, if greater
63 than <literal>0</literal>. Can be floating point. Default
64 is no timeout.</para>
65 </parameter>
66 <parameter name="frequency">
67 <para>The frequency for which to detect pulsed digits.
68 Default is 2600 Hz.</para>
69 </parameter>
70 <parameter name="options">
71 <optionlist>
72 <option name="d">
73 <para>Delay audio by a frame to try to extra quelch.</para>
74 </option>
75 <option name="e">
76 <para>Allow receiving extra pulses 11 through 16.</para>
77 </option>
78 <option name="m">
79 <para>Mute conference.</para>
80 </option>
81 <option name="q">
82 <para>Quelch SF from in-band.</para>
83 </option>
84 <option name="r">
85 <para>"Radio" mode (relaxed SF).</para>
86 </option>
87 </optionlist>
88 </parameter>
89 </syntax>
90 <description>
91 <para>Reads SF digits from the user in to the given
92 <replaceable>variable</replaceable>.</para>
93 <para>This application does not automatically answer the channel and
94 should be preceded with <literal>Answer</literal> or
95 <literal>Progress</literal> as needed.</para>
96 <variablelist>
97 <variable name="RECEIVESFSTATUS">
98 <para>This is the status of the read operation.</para>
99 <value name="START" />
100 <value name="ERROR" />
101 <value name="HANGUP" />
102 <value name="MAXDIGITS" />
103 <value name="TIMEOUT" />
104 </variable>
105 </variablelist>
106 </description>
107 <see-also>
108 <ref type="application">ReceiveMF</ref>
109 <ref type="application">SendMF</ref>
110 <ref type="application">SendSF</ref>
111 <ref type="application">Read</ref>
112 </see-also>
113 </application>
114 <application name="SendSF" language="en_US">
115 <since>
116 <version>16.24.0</version>
117 <version>18.10.0</version>
118 <version>19.2.0</version>
119 </since>
120 <synopsis>
121 Sends arbitrary SF digits on the current or specified channel.
122 </synopsis>
123 <syntax>
124 <parameter name="digits" required="true">
125 <para>List of digits 0-9 to send; w for a half-second pause,
126 also f or F for a flash-hook if the channel supports flash-hook,
127 h or H for 250 ms of 2600 Hz, and W for a wink if the channel
128 supports wink.</para>
129 </parameter>
130 <parameter name="frequency" required="false">
131 <para>Frequency to use. (defaults to 2600 Hz).</para>
132 </parameter>
133 <parameter name="channel" required="false">
134 <para>Channel where digits will be played</para>
135 </parameter>
136 </syntax>
137 <description>
138 <para>It will send all digits or terminate if it encounters an error.</para>
139 </description>
140 <see-also>
141 <ref type="application">SendDTMF</ref>
142 <ref type="application">SendMF</ref>
143 <ref type="application">ReceiveMF</ref>
144 <ref type="application">ReceiveSF</ref>
145 </see-also>
146 </application>
147 ***/
148
150 OPT_DELAY = (1 << 0),
151 OPT_MUTE = (1 << 1),
152 OPT_QUELCH = (1 << 2),
153 OPT_RELAXED = (1 << 3),
154 OPT_EXTRAPULSES = (1 << 4),
155};
156
163});
164
165static const char *readsf_name = "ReceiveSF";
166static const char sendsf_name[] = "SendSF";
167
168/*!
169 * \brief Detects SF digits on channel using DSP
170 *
171 * \param chan channel on which to read digits
172 * \param buf Buffer in which to store digits
173 * \param buflen Size of buffer
174 * \param timeout ms to wait for all digits before giving up
175 * \param maxdigits Maximum number of digits
176 * \param freq Frequency to use
177 * \param features DSP features
178 * \param extrapulses Whether to recognize extra pulses
179 *
180 * \retval 0 if successful
181 * \retval -1 if unsuccessful (including hangup).
182 */
183static int read_sf_digits(struct ast_channel *chan, char *buf, int buflen, int timeout, int maxdigits, int freq, int features, int extrapulses) {
184 /* Bell System Technical Journal 39 (Nov. 1960) */
185 #define SF_MIN_OFF 25
186 #define SF_ON 67
187 #define SF_BETWEEN 600
188 #define SF_MIN_DETECT 50
189
190 struct ast_dsp *dsp = NULL;
191 struct ast_frame *frame = NULL;
192 struct timeval start, pulsetimer, digittimer;
193 int remaining_time = timeout;
194 char *str = buf;
195 int hits = 0, digits_read = 0;
196 unsigned short int sf_on = 0;
197 int res = 0;
198
199 if (!(dsp = ast_dsp_new())) {
200 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
201 pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "ERROR");
202 return -1;
203 }
205 /* tolerance is 46 to 76% make break at 8 to 12 pps */
206 ast_dsp_set_freqmode(dsp, freq, SF_MIN_DETECT, 16, 0);
207
208 start = ast_tvnow();
209 *str = 0; /* start with empty output buffer */
210
211 while (timeout == 0 || remaining_time > 0) {
212 if (timeout > 0) {
213 remaining_time = ast_remaining_ms(start, timeout);
214 if (remaining_time <= 0) {
215 pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "TIMEOUT");
216 break;
217 }
218 }
219 if (digits_read >= (buflen - 1)) { /* we don't have room to store any more digits (very unlikely to happen for a legitimate reason) */
220 /* This result will probably not be usable, so status should not be START */
221 pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "MAXDIGITS");
222 break;
223 }
224 if (ast_waitfor(chan, 1000) > 0) {
225 frame = ast_read(chan);
226 if (!frame) {
227 ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", ast_channel_name(chan));
228 pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "HANGUP");
229 break;
230 } else if (frame->frametype == AST_FRAME_VOICE) {
231 frame = ast_dsp_process(chan, dsp, frame);
232 if (frame->frametype == AST_FRAME_DTMF) {
233 char result = frame->subclass.integer;
234 if (result == 'q') {
235 sf_on = 1;
236 pulsetimer = ast_tvnow(); /* reset the pulse timer */
237 /* now, we need at least a 33ms pause to register the pulse */
238 }
239 } else {
240 if (sf_on) {
241 int timeleft = ast_remaining_ms(pulsetimer, SF_MIN_OFF);
242 if (timeleft <= 0) {
243 sf_on = 0;
244 /* The pulse needs to end no more than 30ms after we detected it */
245 if (timeleft > -30) {
246 hits++;
247 digittimer = ast_tvnow(); /* reset the digit timer */
248 ast_debug(5, "Detected SF pulse (pulse #%d)\n", hits);
249 ast_dsp_free(dsp);
250 if (!(dsp = ast_dsp_new())) {
251 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
252 pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "ERROR");
253 ast_frfree(frame);
254 return -1;
255 }
257 ast_dsp_set_freqmode(dsp, freq, SF_MIN_DETECT, 16, 0);
258 } else {
259 ast_debug(5, "SF noise, ignoring, time elapsed was %d ms\n", timeleft);
260 }
261 }
262 } else if (hits > 0 && ast_remaining_ms(digittimer, SF_BETWEEN) <= 0) {
263 /* has the digit finished? */
264 ast_debug(2, "Received SF digit: %d\n", hits);
265 digits_read++;
266 if (hits > 10) {
267 if (extrapulses) {
268 /* dahdi-base.c translates 11 to * and 12 to # */
269 if (hits == 11) {
270 hits = '*';
271 } else if (hits == 12) {
272 hits = '#';
273 } else if (hits == 13) {
274 hits = 'D';
275 } else if (hits == 14) {
276 hits = 'C';
277 } else if (hits == 15) {
278 hits = 'B';
279 } else if (hits == 16) {
280 hits = 'A';
281 } else {
282 ast_debug(3, "Got %d SF pulses, is someone playing with the phone?\n", hits);
283 hits = 'A';
284 }
285 *str++ = hits;
286 } else {
287 ast_debug(2, "Got more than 10 pulses, truncating to 10\n");
288 hits = 0; /* 10 dial pulses = digit 0 */
289 *str++ = hits + '0';
290 }
291 } else {
292 if (hits == 10) {
293 hits = 0; /* 10 dial pulses = digit 0 */
294 }
295 *str++ = hits + '0';
296 }
297 *str = 0;
298 hits = 0;
299 if (maxdigits > 0 && digits_read >= maxdigits) {
300 pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "START");
301 ast_frfree(frame);
302 break;
303 }
304 }
305 }
306 }
307 ast_frfree(frame);
308 } else {
309 pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "HANGUP");
310 res = -1;
311 }
312 }
313 if (dsp) {
314 ast_dsp_free(dsp);
315 }
316 ast_debug(3, "channel '%s' - event loop stopped { timeout: %d, remaining_time: %d }\n", ast_channel_name(chan), timeout, remaining_time);
317 return res;
318}
319
320static int read_sf_exec(struct ast_channel *chan, const char *data)
321{
322#define BUFFER_SIZE 256
323 char tmp[BUFFER_SIZE] = "";
324 double tosec;
325 struct ast_flags flags = {0};
326 char *argcopy = NULL;
327 int res, features = 0, digits = 0, to = 0, freq = 2600;
328
329 AST_DECLARE_APP_ARGS(arglist,
330 AST_APP_ARG(variable);
331 AST_APP_ARG(digits);
332 AST_APP_ARG(timeout);
333 AST_APP_ARG(freq);
335 );
336
337 if (ast_strlen_zero(data)) {
338 ast_log(LOG_WARNING, "ReceiveSF requires an argument (variable)\n");
339 return -1;
340 }
341
342 argcopy = ast_strdupa(data);
343
344 AST_STANDARD_APP_ARGS(arglist, argcopy);
345
346 if (!ast_strlen_zero(arglist.options)) {
348 }
349
350 if (!ast_strlen_zero(arglist.timeout)) {
351 tosec = atof(arglist.timeout);
352 if (tosec <= 0) {
353 to = 0;
354 } else {
355 to = tosec * 1000.0;
356 }
357 }
358
359 if (!ast_strlen_zero(arglist.digits) && (ast_str_to_int(arglist.digits, &digits) || digits <= 0)) {
360 ast_log(LOG_WARNING, "Invalid number of digits: %s\n", arglist.digits);
361 return -1;
362 }
363
364 if (!ast_strlen_zero(arglist.freq) && (ast_str_to_int(arglist.freq, &freq) || freq <= 0)) {
365 ast_log(LOG_WARNING, "Invalid freq: %s\n", arglist.freq);
366 return -1;
367 }
368
369 if (ast_strlen_zero(arglist.variable)) {
370 ast_log(LOG_WARNING, "Invalid! Usage: ReceiveSF(variable[,timeout][,option])\n");
371 return -1;
372 }
373
375 features |= DSP_DIGITMODE_MUTEMAX;
376 }
377
379 features |= DSP_DIGITMODE_MUTECONF;
380 }
381
383 features |= DSP_DIGITMODE_NOQUELCH;
384 }
385
387 features |= DSP_DIGITMODE_RELAXDTMF;
388 }
389
390 res = read_sf_digits(chan, tmp, BUFFER_SIZE, to, digits, freq, features, ast_test_flag(&flags, OPT_EXTRAPULSES));
391 pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
392 if (!ast_strlen_zero(tmp)) {
393 ast_verb(3, "SF digits received: '%s'\n", tmp);
394 } else if (!res) { /* if channel hung up, don't print anything out */
395 ast_verb(3, "No SF digits received.\n");
396 }
397 return res;
398}
399
400static int sendsf_exec(struct ast_channel *chan, const char *vdata)
401{
402 int res;
403 char *data;
404 int frequency = 2600;
405 struct ast_channel *chan_found = NULL;
406 struct ast_channel *chan_dest = chan;
407 struct ast_channel *chan_autoservice = NULL;
409 AST_APP_ARG(digits);
410 AST_APP_ARG(frequency);
411 AST_APP_ARG(channel);
412 );
413
414 if (ast_strlen_zero(vdata)) {
415 ast_log(LOG_WARNING, "SendSF requires an argument\n");
416 return 0;
417 }
418
419 data = ast_strdupa(vdata);
421
422 if (ast_strlen_zero(args.digits)) {
423 ast_log(LOG_WARNING, "The digits argument is required (0-9,wf)\n");
424 return 0;
425 }
426 if (!ast_strlen_zero(args.frequency) && (ast_str_to_int(args.frequency, &frequency) || frequency < 1)) {
427 ast_log(LOG_WARNING, "Invalid duration: %s\n", args.frequency);
428 return -1;
429 }
430 if (!ast_strlen_zero(args.channel)) {
431 chan_found = ast_channel_get_by_name(args.channel);
432 if (!chan_found) {
433 ast_log(LOG_WARNING, "No such channel: %s\n", args.channel);
434 return 0;
435 }
436 chan_dest = chan_found;
437 if (chan_found != chan) {
438 chan_autoservice = chan;
439 }
440 }
441 res = ast_sf_stream(chan_dest, chan_autoservice, NULL, args.digits, frequency, 0);
442 ast_channel_cleanup(chan_found);
443
444 return chan_autoservice ? 0 : res;
445}
446
447static int unload_module(void)
448{
449 int res;
450
453
454 return res;
455}
456
457static int load_module(void)
458{
459 int res;
460
463
464 return res;
465}
466
467AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "SF Sender and Receiver Applications");
const char * str
Definition: app_jack.c:147
read_option_flags
Definition: app_mf.c:190
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "SF Sender and Receiver Applications")
#define SF_MIN_OFF
static const char sendsf_name[]
Definition: app_sf.c:166
static const char * readsf_name
Definition: app_sf.c:165
#define SF_BETWEEN
#define BUFFER_SIZE
static int read_sf_exec(struct ast_channel *chan, const char *data)
Definition: app_sf.c:320
static int sendsf_exec(struct ast_channel *chan, const char *vdata)
Definition: app_sf.c:400
#define SF_MIN_DETECT
static int load_module(void)
Definition: app_sf.c:457
static int unload_module(void)
Definition: app_sf.c:447
@ OPT_EXTRAPULSES
Definition: app_sf.c:154
@ OPT_MUTE
Definition: app_sf.c:151
@ OPT_RELAXED
Definition: app_sf.c:153
@ OPT_QUELCH
Definition: app_sf.c:152
@ OPT_DELAY
Definition: app_sf.c:150
static int read_sf_digits(struct ast_channel *chan, char *buf, int buflen, int timeout, int maxdigits, int freq, int features, int extrapulses)
Detects SF digits on channel using DSP.
Definition: app_sf.c:183
static const struct ast_app_option read_app_options[128]
Definition: app_sf.c:163
Asterisk main include file. File version handling, generic pbx functions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
static int tmp()
Definition: bt_open.c:389
static PGresult * result
Definition: cel_pgsql.c:84
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3181
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4276
#define ast_channel_cleanup(c)
Cleanup a channel reference.
Definition: channel.h:3015
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1473
Conversion utility functions.
int ast_str_to_int(const char *str, int *res)
Convert the given string to a signed integer.
Definition: conversions.c:44
Convenient Signal Processing routines.
void ast_dsp_free(struct ast_dsp *dsp)
Definition: dsp.c:1783
#define DSP_DIGITMODE_NOQUELCH
Definition: dsp.h:34
#define DSP_DIGITMODE_MUTEMAX
Definition: dsp.h:36
struct ast_frame * ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp, struct ast_frame *inf)
Return AST_FRAME_NULL frames when there is silence, AST_FRAME_BUSY on busies, and call progress,...
Definition: dsp.c:1499
#define DSP_DIGITMODE_MUTECONF
Definition: dsp.h:35
#define DSP_FEATURE_FREQ_DETECT
Definition: dsp.h:45
int ast_dsp_set_freqmode(struct ast_dsp *dsp, int freq, int dur, int db, int squelch)
Set arbitrary frequency detection mode.
Definition: dsp.c:1872
void ast_dsp_set_features(struct ast_dsp *dsp, int features)
Select feature set.
Definition: dsp.c:1768
struct ast_dsp * ast_dsp_new(void)
Allocates a new dsp, assumes 8khz for internal sample rate.
Definition: dsp.c:1758
#define DSP_DIGITMODE_RELAXDTMF
Definition: dsp.h:37
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
Generic File Format Support. Should be included by clients of the file handling routines....
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
int ast_sf_stream(struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *chan2, const char *digits, int frequency, int is_external)
Send a string of SF digits to a channel.
Definition: main/app.c:1097
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3066
#define AST_FRAME_DTMF
#define ast_frfree(fr)
@ AST_FRAME_VOICE
#define ast_debug(level,...)
Log a DEBUG message.
#define ast_verb(level,...)
#define LOG_WARNING
Tone Indication Support.
Asterisk module definitions.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
Core PBX routines and definitions.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
#define NULL
Definition: resample.c:96
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Main Channel structure associated with a channel.
const char * data
Definition: dsp.c:407
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass
enum ast_frame_type frametype
const char * args
static struct test_options options
int ast_remaining_ms(struct timeval start, int max_ms)
Calculate remaining milliseconds given a starting timestamp and upper bound.
Definition: utils.c:2281
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
#define ast_test_flag(p, flag)
Definition: utils.h:63