From 341cb163786bb34a3a57ed9163475225282cccfd Mon Sep 17 00:00:00 2001 From: Naveen Albert Date: Wed, 1 Oct 2025 10:40:48 -0400 Subject: [PATCH] app_sf: Add post-digit timer option to ReceiveSF. Add a sorely needed option to set a timeout between digits, rather than for receiving the entire number. This is needed if the number of digits being sent is unknown by the receiver in advance. Previously, we had to wait for the entire timer to expire. Resolves: #1493 UserNote: The 't' option for ReceiveSF now allows for a timer since the last digit received, in addition to the number-wide timeout. --- apps/app_sf.c | 81 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 69 insertions(+), 12 deletions(-) diff --git a/apps/app_sf.c b/apps/app_sf.c index cd8f79a6bc..0fb902f435 100644 --- a/apps/app_sf.c +++ b/apps/app_sf.c @@ -78,6 +78,20 @@ + @@ -146,18 +160,35 @@ ***/ +/* Default post-digit timer */ +#define DEFAULT_POST_DIGIT_TIMER 0.8 + +/* Bell System Technical Journal 39 (Nov. 1960) */ +#define SF_MIN_OFF 25 +#define SF_ON 67 +#define SF_BETWEEN 600 +#define SF_MIN_DETECT 50 + enum read_option_flags { OPT_DELAY = (1 << 0), OPT_MUTE = (1 << 1), OPT_QUELCH = (1 << 2), OPT_RELAXED = (1 << 3), OPT_EXTRAPULSES = (1 << 4), + OPT_DIGIT_TIMEOUT = (1 << 5), +}; + +enum { + OPT_ARG_DIGIT_TIMEOUT, + /* note: this entry _MUST_ be the last one in the enum */ + OPT_ARG_ARRAY_SIZE, }; AST_APP_OPTIONS(read_app_options, { AST_APP_OPTION('d', OPT_DELAY), AST_APP_OPTION('e', OPT_EXTRAPULSES), AST_APP_OPTION('m', OPT_MUTE), + AST_APP_OPTION_ARG('t', OPT_DIGIT_TIMEOUT, OPT_ARG_DIGIT_TIMEOUT), AST_APP_OPTION('q', OPT_QUELCH), AST_APP_OPTION('r', OPT_RELAXED), }); @@ -172,6 +203,7 @@ static const char sendsf_name[] = "SendSF"; * \param buf Buffer in which to store digits * \param buflen Size of buffer * \param timeout ms to wait for all digits before giving up + * \param digit_timeout ms to wait for the next digit before giving up * \param maxdigits Maximum number of digits * \param freq Frequency to use * \param features DSP features @@ -180,13 +212,8 @@ static const char sendsf_name[] = "SendSF"; * \retval 0 if successful * \retval -1 if unsuccessful (including hangup). */ -static int read_sf_digits(struct ast_channel *chan, char *buf, int buflen, int timeout, int maxdigits, int freq, int features, int extrapulses) { - /* Bell System Technical Journal 39 (Nov. 1960) */ - #define SF_MIN_OFF 25 - #define SF_ON 67 - #define SF_BETWEEN 600 - #define SF_MIN_DETECT 50 - +static int read_sf_digits(struct ast_channel *chan, char *buf, int buflen, int timeout, int digit_timeout, int maxdigits, int freq, int features, int extrapulses) +{ struct ast_dsp *dsp = NULL; struct ast_frame *frame = NULL; struct timeval start, pulsetimer, digittimer; @@ -212,6 +239,23 @@ static int read_sf_digits(struct ast_channel *chan, char *buf, int buflen, int t if (timeout > 0) { remaining_time = ast_remaining_ms(start, timeout); if (remaining_time <= 0) { + ast_debug(1, "SF all-digit timer expired\n"); + pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "TIMEOUT"); + break; + } + } + /* If we haven't received a digit yet, don't apply the post-digit timer just yet, + * since the sender may not have started sending any digits. + * + * Note that we use the digit timer, which is reset for each SF pulse, + * as opposed to simply an entire digit being received. + * This is done because we only want to expire the timer if there has been no activity + * since the last digit. If we're in the middle of receiving a digit (e.g. 0) + * we may not have a full digit yet but that should not cause an expiration. */ + if (digits_read > 0 && digit_timeout > 0) { + int remaining_time_for_next_digit = ast_remaining_ms(digittimer, digit_timeout); + if (remaining_time_for_next_digit <= 0) { + ast_debug(1, "SF post-digit timer expired (>= %d ms since last SF pulse)\n", digit_timeout); pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "TIMEOUT"); break; } @@ -261,7 +305,7 @@ static int read_sf_digits(struct ast_channel *chan, char *buf, int buflen, int t } } else if (hits > 0 && ast_remaining_ms(digittimer, SF_BETWEEN) <= 0) { /* has the digit finished? */ - ast_debug(2, "Received SF digit: %d\n", hits); + ast_debug(2, "Received SF digit: %d\n", hits == 10 ? 0 : hits); /* Edge case for 10, since this is the digit '0' */ digits_read++; if (hits > 10) { if (extrapulses) { @@ -321,10 +365,11 @@ static int read_sf_exec(struct ast_channel *chan, const char *data) { #define BUFFER_SIZE 256 char tmp[BUFFER_SIZE] = ""; - double tosec; + double tosec, digitsec; struct ast_flags flags = {0}; + char *opt_args[OPT_ARG_ARRAY_SIZE]; char *argcopy = NULL; - int res, features = 0, digits = 0, to = 0, freq = 2600; + int res, features = 0, digits = 0, to = 0, digit_timeout = 0, freq = 2600; AST_DECLARE_APP_ARGS(arglist, AST_APP_ARG(variable); @@ -344,7 +389,7 @@ static int read_sf_exec(struct ast_channel *chan, const char *data) AST_STANDARD_APP_ARGS(arglist, argcopy); if (!ast_strlen_zero(arglist.options)) { - ast_app_parse_options(read_app_options, &flags, NULL, arglist.options); + ast_app_parse_options(read_app_options, &flags, opt_args, arglist.options); } if (!ast_strlen_zero(arglist.timeout)) { @@ -355,6 +400,18 @@ static int read_sf_exec(struct ast_channel *chan, const char *data) to = tosec * 1000.0; } } + if (ast_test_flag(&flags, OPT_DIGIT_TIMEOUT)) { + digitsec = (!ast_strlen_zero(opt_args[OPT_ARG_DIGIT_TIMEOUT]) ? atof(opt_args[OPT_ARG_DIGIT_TIMEOUT]) : DEFAULT_POST_DIGIT_TIMER); + if (digitsec <= 0) { + digit_timeout = 0; + } else { + digit_timeout = digitsec * 1000.0; + if (digit_timeout < SF_BETWEEN) { + ast_log(LOG_WARNING, "SF post-digit timer (%d) cannot be less than the SF inter-digit timeout (%d ms)\n", digit_timeout, SF_BETWEEN); + digit_timeout = SF_BETWEEN; /* If we have a shorter timeout, it won't work at all */ + } + } + } if (!ast_strlen_zero(arglist.digits) && (ast_str_to_int(arglist.digits, &digits) || digits <= 0)) { ast_log(LOG_WARNING, "Invalid number of digits: %s\n", arglist.digits); @@ -387,7 +444,7 @@ static int read_sf_exec(struct ast_channel *chan, const char *data) features |= DSP_DIGITMODE_RELAXDTMF; } - res = read_sf_digits(chan, tmp, BUFFER_SIZE, to, digits, freq, features, ast_test_flag(&flags, OPT_EXTRAPULSES)); + res = read_sf_digits(chan, tmp, BUFFER_SIZE, to, digit_timeout, digits, freq, features, ast_test_flag(&flags, OPT_EXTRAPULSES)); pbx_builtin_setvar_helper(chan, arglist.variable, tmp); if (!ast_strlen_zero(tmp)) { ast_verb(3, "SF digits received: '%s'\n", tmp);