From b60c844b4f0e528cb4ee1b58e4a9486362b02157 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 29 Sep 2022 20:02:50 +0200 Subject: [PATCH] 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. --- src/libdtmf/dtmf_decode.c | 6 ++++ src/libdtmf/dtmf_decode.h | 1 + src/libdtmf/dtmf_encode.c | 73 +++++++++++++++++++++------------------ src/libdtmf/dtmf_encode.h | 7 ++-- src/libfm/fm.h | 4 +++ src/libfsk/fsk.h | 4 +++ src/nmt/dsp.c | 16 +++++---- src/nmt/nmt.c | 5 +-- src/test/test_dtmf.c | 2 +- 9 files changed, 74 insertions(+), 44 deletions(-) diff --git a/src/libdtmf/dtmf_decode.c b/src/libdtmf/dtmf_decode.c index 740d504..7e104bd 100644 --- a/src/libdtmf/dtmf_decode.c +++ b/src/libdtmf/dtmf_decode.c @@ -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]; diff --git a/src/libdtmf/dtmf_decode.h b/src/libdtmf/dtmf_decode.h index 7c5780f..9c162fe 100644 --- a/src/libdtmf/dtmf_decode.h +++ b/src/libdtmf/dtmf_decode.h @@ -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); diff --git a/src/libdtmf/dtmf_encode.c b/src/libdtmf/dtmf_encode.c index 53bd1b6..db1bb6e 100644 --- a/src/libdtmf/dtmf_encode.c +++ b/src/libdtmf/dtmf_encode.c @@ -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; } diff --git a/src/libdtmf/dtmf_encode.h b/src/libdtmf/dtmf_encode.h index 15ac05e..18e75ff 100644 --- a/src/libdtmf/dtmf_encode.h +++ b/src/libdtmf/dtmf_encode.h @@ -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); diff --git a/src/libfm/fm.h b/src/libfm/fm.h index b87a662..70f355b 100644 --- a/src/libfm/fm.h +++ b/src/libfm/fm.h @@ -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 */ diff --git a/src/libfsk/fsk.h b/src/libfsk/fsk.h index 3093de4..cb62170 100644 --- a/src/libfsk/fsk.h +++ b/src/libfsk/fsk.h @@ -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 */ diff --git a/src/nmt/dsp.c b/src/nmt/dsp.c index 072607c..2d6e972 100644 --- a/src/nmt/dsp.c +++ b/src/nmt/dsp.c @@ -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); diff --git a/src/nmt/nmt.c b/src/nmt/nmt.c index 287f3fb..8a49dc5 100644 --- a/src/nmt/nmt.c +++ b/src/nmt/nmt.c @@ -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; diff --git a/src/test/test_dtmf.c b/src/test/test_dtmf.c index 6640c59..f26ae3b 100644 --- a/src/test/test_dtmf.c +++ b/src/test/test_dtmf.c @@ -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);