DTMF: Now allows to give duration and pause for digit

Also the dtmf encoder will return less samples, if the digit(+pause)
ends, so that the caller call set the next digit to play seamlessly.

A reset function allows to clear the decoder states, to prevent glitches
when re-attaching to an interrupted stream.
This commit is contained in:
Andreas Eversberg 2022-09-29 20:02:50 +02:00
parent a756ba8fd9
commit b60c844b4f
9 changed files with 74 additions and 44 deletions

View File

@ -98,6 +98,12 @@ void dtmf_decode_exit(dtmf_dec_t *dtmf)
fm_demod_exit(&dtmf->demod_high);
}
void dtmf_decode_reset(dtmf_dec_t *dtmf)
{
dtmf->detected = 0;
dtmf->count = 0;
}
void dtmf_decode_filter(dtmf_dec_t *dtmf, sample_t *samples, int length, sample_t *frequency_low, sample_t *frequency_high, sample_t *amplitude_low, sample_t *amplitude_high)
{
sample_t I_low[length], Q_low[length];

View File

@ -30,6 +30,7 @@ typedef struct dtmf_dec {
int dtmf_decode_init(dtmf_dec_t *dtmf, void *priv, void (*recv_digit)(void *priv, char digit, dtmf_meas_t *meas), int samplerate, double max_amplitude, double min_amplitude);
void dtmf_decode_exit(dtmf_dec_t *dtmf);
void dtmf_decode_reset(dtmf_dec_t *dtmf);
void dtmf_decode(dtmf_dec_t *dtmf, sample_t *samples, int length);
void dtmf_decode_filter(dtmf_dec_t *dtmf, sample_t *samples, int length, sample_t *frequency_low, sample_t *frequency_high, sample_t *amplitude_low, sample_t *amplitude_high);

View File

@ -27,10 +27,6 @@
#define PEAK_DTMF_LOW 0.2818 /* -11 dBm, relative to 0 dBm level */
#define PEAK_DTMF_HIGH 0.3548 /* -9 dBm, relative to 0 dBm level */
#define DTMF_DURATION 0.100 /* duration in seconds */
static sample_t dsp_sine_dtmf_low[65536];
static sample_t dsp_sine_dtmf_high[65536];
void dtmf_encode_init(dtmf_enc_t *dtmf, int samplerate, double dBm_level)
{
@ -38,17 +34,15 @@ void dtmf_encode_init(dtmf_enc_t *dtmf, int samplerate, double dBm_level)
memset(dtmf, 0, sizeof(*dtmf));
dtmf->samplerate = samplerate;
dtmf->max = (int)((double)samplerate * DTMF_DURATION + 0.5);
// FIXME: do this globally and not per instance */
for (i = 0; i < 65536; i++) {
dsp_sine_dtmf_low[i] = sin((double)i / 65536.0 * 2.0 * PI) * PEAK_DTMF_LOW * dBm_level;
dsp_sine_dtmf_high[i] = sin((double)i / 65536.0 * 2.0 * PI) * PEAK_DTMF_HIGH * dBm_level;
dtmf->sine_low[i] = sin((double)i / 65536.0 * 2.0 * PI) * PEAK_DTMF_LOW * dBm_level;
dtmf->sine_high[i] = sin((double)i / 65536.0 * 2.0 * PI) * PEAK_DTMF_HIGH * dBm_level;
}
}
/* set dtmf tone */
void dtmf_encode_set_tone(dtmf_enc_t *dtmf, char tone)
int dtmf_encode_set_tone(dtmf_enc_t *dtmf, char tone, double on_duration, double off_duration)
{
double f1, f2;
@ -71,53 +65,66 @@ void dtmf_encode_set_tone(dtmf_enc_t *dtmf, char tone)
case'd':case 'D': f1 = 941.0; f2 = 1633.0; break;
default:
dtmf->tone = 0;
return;
return -1;
}
dtmf->tone = tone;
dtmf->pos = 0;
dtmf->on = (int)((double)dtmf->samplerate * on_duration);
dtmf->off = dtmf->on + (int)((double)dtmf->samplerate * off_duration);
dtmf->phaseshift65536[0] = 65536.0 / ((double)dtmf->samplerate / f1);
dtmf->phaseshift65536[1] = 65536.0 / ((double)dtmf->samplerate / f2);
dtmf->phase65536[0] = 0.0;
dtmf->phase65536[1] = 0.0;
return 0;
}
/* Generate audio stream from DTMF tone. Keep phase for next call of function. */
void dtmf_encode(dtmf_enc_t *dtmf, sample_t *samples, int length)
/* Generate audio stream from DTMF tone.
* Keep phase for next call of function.
* Stop, if tone has finished and return only the samples that were used.
*/
int dtmf_encode(dtmf_enc_t *dtmf, sample_t *samples, int length)
{
double *phaseshift, *phase;
int i, pos, max;
sample_t *sine_low, *sine_high;
int count = 0;
int i;
/* use silence, if no tone */
if (!dtmf->tone) {
memset(samples, 0, length * sizeof(*samples));
return;
}
/* if no tone */
if (!dtmf->tone)
return 0;
sine_low = dtmf->sine_low;
sine_high = dtmf->sine_high;
phaseshift = dtmf->phaseshift65536;
phase = dtmf->phase65536;
pos = dtmf->pos;
max = dtmf->max;
for (i = 0; i < length; i++) {
*samples++ = dsp_sine_dtmf_low[(uint16_t)phase[0]]
+ dsp_sine_dtmf_high[(uint16_t)phase[1]];
*samples++ = sine_low[(uint16_t)phase[0]]
+ sine_high[(uint16_t)phase[1]];
phase[0] += phaseshift[0];
if (phase[0] >= 65536)
phase[0] -= 65536;
if (phase[0] >= 65536.0)
phase[0] -= 65536.0;
phase[1] += phaseshift[1];
if (phase[1] >= 65536)
phase[1] -= 65536;
if (phase[1] >= 65536.0)
phase[1] -= 65536.0;
dtmf->pos++;
/* tone ends */
if (++pos == max) {
if (dtmf->pos == dtmf->on) {
phaseshift[0] = 0.0;
phaseshift[1] = 0.0;
phase[0] = 0.0;
phase[1] = 0.0;
}
/* pause ends */
if (dtmf->pos == dtmf->off) {
dtmf->tone = 0;
break;
}
}
length -= i;
count += i;
dtmf->pos = pos;
/* if tone ends, fill rest with silence */
if (length)
memset(samples, 0, length * sizeof(*samples));
return count;
}

View File

@ -2,13 +2,16 @@
typedef struct dtmf_enc {
int samplerate; /* samplerate */
char tone; /* current tone to be played */
int on, off; /* samples to turn on and afterwards off */
int pos; /* sample counter for tone */
int max; /* max number of samples for tone duration */
double phaseshift65536[2]; /* how much the phase of sine wave changes per sample */
double phase65536[2]; /* current phase */
sample_t sine_low[65536]; /* sine tables at individual levels */
sample_t sine_high[65536];
} dtmf_enc_t;
void dtmf_encode_init(dtmf_enc_t *dtmf, int samplerate, double dBm_level);
void dtmf_encode_set_tone(dtmf_enc_t *dtmf, char tone);
void dtmf_encode(dtmf_enc_t *dtmf, sample_t *samples, int length);
int dtmf_encode_set_tone(dtmf_enc_t *dtmf, char tone, double on_duration, double off_duration);
int dtmf_encode(dtmf_enc_t *dtmf, sample_t *samples, int length);

View File

@ -1,3 +1,6 @@
#ifndef _LIB_FM_H
#define _LIB_FM_H
#include "../libfilter/iir_filter.h"
int fm_init(int fast_math);
@ -38,3 +41,4 @@ void fm_demod_exit(fm_demod_t *demod);
void fm_demodulate_complex(fm_demod_t *demod, sample_t *frequency, int length, float *baseband, sample_t *I, sample_t *Q);
void fm_demodulate_real(fm_demod_t *demod, sample_t *frequency, int length, sample_t *baseband, sample_t *I, sample_t *Q);
#endif /* _LIB_FM_H */

View File

@ -1,3 +1,6 @@
#ifndef _LIB_FSK_H
#define _LIB_FSK_H
#include "../libfm/fm.h"
typedef struct fsk_mod {
@ -41,3 +44,4 @@ int fsk_demod_init(fsk_demod_t *fsk, void *inst, void (*receive_bit)(void *inst,
void fsk_demod_cleanup(fsk_demod_t *fsk);
void fsk_demod_receive(fsk_demod_t *fsk, sample_t *sample, int length);
#endif /* _LIB_FSK_H */

View File

@ -356,16 +356,20 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double __at
if ((nmt->dsp_mode == DSP_MODE_AUDIO || nmt->dsp_mode == DSP_MODE_DTMF)
&& nmt->trans && nmt->trans->callref) {
int count;
int len, count;
count = samplerate_downsample(&nmt->sender.srstate, samples, length);
len = samplerate_downsample(&nmt->sender.srstate, samples, length);
if (nmt->compandor)
expand_audio(&nmt->cstate, samples, count);
if (nmt->dsp_mode == DSP_MODE_DTMF)
dtmf_encode(&nmt->dtmf, samples, count);
expand_audio(&nmt->cstate, samples, len);
if (nmt->dsp_mode == DSP_MODE_DTMF) {
/* encode and fill with silence after finish */
count = dtmf_encode(&nmt->dtmf, samples, len);
if (count < len)
memset(samples + count, 0, sizeof(*samples) * (len - count));
}
spl = nmt->sender.rxbuf;
pos = nmt->sender.rxbuf_pos;
for (i = 0; i < count; i++) {
for (i = 0; i < len; i++) {
spl[pos++] = samples[i];
if (pos == 160) {
call_up_audio(nmt->trans->callref, spl, 160);

View File

@ -58,6 +58,7 @@ static int sms_ref = 0;
#define RINGING_TO 60.0 /* how long may the phone ring */
#define SUPERVISORY_TO1 3.0 /* 3 sec to detect after setup */
#define SUPERVISORY_TO2 20.0 /* 20 sec lost until abort */
#define DTMF_DURATION 0.1 /* 100ms */
/* Counters */
#define PAGE_TRIES 3 /* How many time do we try to page the phone */
@ -1519,7 +1520,7 @@ static void rx_active(nmt_t *nmt, frame_t *frame)
break;
}
digit = nmt_value2digit(frame->digit);
dtmf_encode_set_tone(&nmt->dtmf, digit);
dtmf_encode_set_tone(&nmt->dtmf, digit, DTMF_DURATION, 0.0);
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received (odd) digit %c.\n", digit);
nmt->mft_num++;
break;
@ -1542,7 +1543,7 @@ static void rx_active(nmt_t *nmt, frame_t *frame)
break;
}
digit = nmt_value2digit(frame->digit);
dtmf_encode_set_tone(&nmt->dtmf, digit);
dtmf_encode_set_tone(&nmt->dtmf, digit, DTMF_DURATION, 0.0);
PDEBUG_CHAN(DNMT, DEBUG_INFO, "Received (even) digit %c.\n", digit);
nmt->mft_num++;
break;

View File

@ -96,7 +96,7 @@ int main(void)
for (i = 0; i < 16; i++) {
printf("Testing digit '%c' encoding and decoding:\n", test_digits[i]);
memset(samples, 0, sizeof(samples[0]) * SAMPLERATE);
dtmf_encode_set_tone(&dtmf_enc, test_digits[i]);
dtmf_encode_set_tone(&dtmf_enc, test_digits[i], 1.0, 0.0);
dtmf_encode(&dtmf_enc, samples + SAMPLERATE / 10, SAMPLERATE / 20);
got_digit = 0;
dtmf_decode(&dtmf_dec, samples, SAMPLERATE);