17static const double dtmf_row_freqs[4] = {697.0, 770.0, 852.0, 941.0};
18static const double dtmf_col_freqs[4] = {1209.0, 1336.0, 1477.0, 1633.0};
27static const char dtmf_keypad[4][4] = {
42 int row = -1, col = -1;
44 case '1': row = 0; col = 0;
break;
45 case '2': row = 0; col = 1;
break;
46 case '3': row = 0; col = 2;
break;
47 case 'A':
case 'a': row = 0; col = 3;
break;
48 case '4': row = 1; col = 0;
break;
49 case '5': row = 1; col = 1;
break;
50 case '6': row = 1; col = 2;
break;
51 case 'B':
case 'b': row = 1; col = 3;
break;
52 case '7': row = 2; col = 0;
break;
53 case '8': row = 2; col = 1;
break;
54 case '9': row = 2; col = 2;
break;
55 case 'C':
case 'c': row = 2; col = 3;
break;
56 case '*': row = 3; col = 0;
break;
57 case '0': row = 3; col = 1;
break;
58 case '#': row = 3; col = 2;
break;
59 case 'D':
case 'd': row = 3; col = 3;
break;
61 assert(0 &&
"invalid DTMF character");
63 *row_freq = dtmf_row_freqs[row];
64 *col_freq = dtmf_col_freqs[col];
68static double peak_near_bin(
const double *mag,
unsigned num_bins,
unsigned bin)
70 double peak = mag[bin];
71 if (bin > 0 && mag[bin - 1] > peak)
73 if (bin + 1 < num_bins && mag[bin + 1] > peak)
81 unsigned N,
double sample_rate)
85 for (
unsigned k = 1; k + 1 < num_bins; k++)
87 double mean_mag = (num_bins > 2) ? sum / (
double)(num_bins - 2) : 0.0;
88 double threshold = mean_mag * 8.0;
92 for (
int r = 0; r < 4; r++) {
93 unsigned bin = (unsigned)(dtmf_row_freqs[r] * N / sample_rate + 0.5);
94 if (bin >= num_bins) bin = num_bins - 1;
100 for (
int c = 0; c < 4; c++) {
101 unsigned bin = (unsigned)(dtmf_col_freqs[c] * N / sample_rate + 0.5);
102 if (bin >= num_bins) bin = num_bins - 1;
108 double best_row_mag = 0.0;
109 for (
int r = 0; r < 4; r++) {
110 if (row_mags[r] > threshold && row_mags[r] > best_row_mag) {
112 best_row_mag = row_mags[r];
117 double best_col_mag = 0.0;
118 for (
int c = 0; c < 4; c++) {
119 if (col_mags[c] > threshold && col_mags[c] > best_col_mag) {
121 best_col_mag = col_mags[c];
125 if (best_row < 0 || best_col < 0)
128 return dtmf_keypad[best_row][best_col];
141 assert(signal_len > 0);
142 assert(sample_rate >= 4000.0);
143 assert(max_tones > 0);
150 unsigned max_n = (unsigned)(0.035 * sample_rate);
152 while (N * 2 <= max_n) N <<= 1;
153 unsigned hop = N / 4;
154 unsigned num_bins = N / 2 + 1;
156 unsigned num_frames = (signal_len >= N)
157 ? (signal_len - N) / hop + 1
167 unsigned q24_samples = (unsigned)(0.040 * sample_rate);
168 unsigned min_on_frames = (q24_samples > N)
169 ? (q24_samples - N + hop - 1) / hop + 1 : 2;
170 if (min_on_frames < 2) min_on_frames = 2;
171 unsigned min_off_frames = min_on_frames;
174 double *window = malloc(N *
sizeof(
double));
175 double *frame = malloc(N *
sizeof(
double));
176 double *mag = malloc(num_bins *
sizeof(
double));
177 assert(window && frame && mag);
182 enum { IDLE, PENDING, ACTIVE } state = IDLE;
183 char current_digit =
'\0';
184 unsigned on_count = 0;
185 unsigned off_count = 0;
186 unsigned tone_start_frame = 0;
187 unsigned tone_end_frame = 0;
188 unsigned num_tones = 0;
190 for (
unsigned f = 0; f < num_frames && num_tones < max_tones; f++) {
191 unsigned start = f * hop;
194 for (
unsigned i = 0; i < N; i++)
195 frame[i] = signal[start + i] * window[i];
201 for (
unsigned k = 0; k < num_bins; k++) {
203 if (k > 0 && k < N / 2)
207 char digit =
detect_frame(mag, num_bins, N, sample_rate);
212 current_digit = digit;
214 tone_start_frame = f;
220 if (digit == current_digit) {
222 if (on_count >= min_on_frames) {
227 }
else if (digit !=
'\0') {
229 current_digit = digit;
231 tone_start_frame = f;
234 current_digit =
'\0';
239 if (digit == current_digit && off_count == 0) {
242 }
else if (digit == current_digit
243 && off_count >= min_off_frames) {
247 tones_out[num_tones].
digit = current_digit;
249 (double)(tone_start_frame * hop) / sample_rate;
250 tones_out[num_tones].
end_s =
251 (double)((tone_end_frame + 1) * hop) / sample_rate;
254 current_digit = digit;
256 tone_start_frame = f;
258 }
else if (digit == current_digit) {
264 if (off_count >= min_off_frames) {
266 tones_out[num_tones].
digit = current_digit;
268 (double)(tone_start_frame * hop) / sample_rate;
269 tones_out[num_tones].
end_s =
270 (double)((tone_end_frame + 1) * hop) / sample_rate;
276 current_digit = digit;
278 tone_start_frame = f;
282 current_digit =
'\0';
294 if (num_tones < max_tones) {
295 if (state == ACTIVE) {
296 tones_out[num_tones].
digit = current_digit;
298 (double)(tone_start_frame * hop) / sample_rate;
299 tones_out[num_tones].
end_s =
300 (double)((tone_end_frame + 1) * hop) / sample_rate;
302 }
else if (state == PENDING && on_count >= min_on_frames) {
303 unsigned last = tone_start_frame + on_count - 1;
304 tones_out[num_tones].
digit = current_digit;
306 (double)(tone_start_frame * hop) / sample_rate;
307 tones_out[num_tones].
end_s =
308 (double)((last + 1) * hop) / sample_rate;
321 unsigned tone_ms,
unsigned pause_ms)
325 assert(sample_rate > 0);
326 assert(tone_ms >= 40);
327 assert(pause_ms >= 40);
329 unsigned num_digits = (unsigned)strlen(digits);
330 unsigned tone_samples = (unsigned)(tone_ms * sample_rate / 1000.0);
331 unsigned pause_samples = (unsigned)(pause_ms * sample_rate / 1000.0);
336 memset(output, 0, total *
sizeof(
double));
339 double *col_tone = malloc(tone_samples *
sizeof(
double));
343 for (
unsigned d = 0; d < num_digits; d++) {
344 double row_freq, col_freq;
348 MD_sine_wave(output + offset, tone_samples, 0.5, row_freq, sample_rate);
351 MD_sine_wave(col_tone, tone_samples, 0.5, col_freq, sample_rate);
352 for (
unsigned i = 0; i < tone_samples; i++)
353 output[offset + i] += col_tone[i];
355 offset += tone_samples + pause_samples;
362 unsigned tone_ms,
unsigned pause_ms)
364 assert(sample_rate > 0);
365 if (num_digits == 0)
return 0;
367 unsigned tone_samples = (unsigned)(tone_ms * sample_rate / 1000.0);
368 unsigned pause_samples = (unsigned)(pause_ms * sample_rate / 1000.0);
369 return num_digits * tone_samples
370 + (num_digits - 1) * pause_samples;
A mini library of DSP (Digital Signal Processing) routines.
void MD_sine_wave(double *output, unsigned N, double amplitude, double freq, double sample_rate)
Generate a sine wave.
void MD_Gen_Hann_Win(double *out, unsigned n)
Generate a Hanning (Hann) window of length n.
void MD_magnitude_spectrum(const double *signal, unsigned N, double *mag_out)
Compute the magnitude spectrum of a real-valued signal.
unsigned MD_dtmf_detect(const double *signal, unsigned signal_len, double sample_rate, MD_DTMFTone *tones_out, unsigned max_tones)
Detect DTMF tones in an audio signal.
void MD_dtmf_generate(double *output, const char *digits, double sample_rate, unsigned tone_ms, unsigned pause_ms)
Generate a DTMF tone sequence.
static char detect_frame(const double *mag, unsigned num_bins, unsigned N, double sample_rate)
Detect the DTMF digit present in a single normalised magnitude frame.
static void dtmf_char_to_freqs(char ch, double *row_freq, double *col_freq)
Map a DTMF character to its row and column frequencies.
unsigned MD_dtmf_signal_length(unsigned num_digits, double sample_rate, unsigned tone_ms, unsigned pause_ms)
Calculate the number of samples needed for MD_dtmf_generate().
static double peak_near_bin(const double *mag, unsigned num_bins, unsigned bin)
Peak magnitude in bins [bin-1, bin, bin+1], clamped to [0, num_bins).
A single detected DTMF tone with timing information.
char digit
Decoded digit: '0'–'9', 'A'–'D', '*', or '#'.
double end_s
Tone offset time in seconds.
double start_s
Tone onset time in seconds.