18static const double dtmf_row_freqs[4] = {697.0, 770.0, 852.0, 941.0};
19static const double dtmf_col_freqs[4] = {1209.0, 1336.0, 1477.0, 1633.0};
28static const char dtmf_keypad[4][4] = {
43 int row = -1, col = -1;
45 case '1': row = 0; col = 0;
break;
46 case '2': row = 0; col = 1;
break;
47 case '3': row = 0; col = 2;
break;
48 case 'A':
case 'a': row = 0; col = 3;
break;
49 case '4': row = 1; col = 0;
break;
50 case '5': row = 1; col = 1;
break;
51 case '6': row = 1; col = 2;
break;
52 case 'B':
case 'b': row = 1; col = 3;
break;
53 case '7': row = 2; col = 0;
break;
54 case '8': row = 2; col = 1;
break;
55 case '9': row = 2; col = 2;
break;
56 case 'C':
case 'c': row = 2; col = 3;
break;
57 case '*': row = 3; col = 0;
break;
58 case '0': row = 3; col = 1;
break;
59 case '#': row = 3; col = 2;
break;
60 case 'D':
case 'd': row = 3; col = 3;
break;
67 *row_freq = dtmf_row_freqs[row];
68 *col_freq = dtmf_col_freqs[col];
72static double peak_near_bin(
const double *mag,
unsigned num_bins,
unsigned bin)
74 double peak = mag[bin];
75 if (bin > 0 && mag[bin - 1] > peak)
77 if (bin + 1 < num_bins && mag[bin + 1] > peak)
85 unsigned N,
double sample_rate)
89 for (
unsigned k = 1; k + 1 < num_bins; k++)
91 double mean_mag = (num_bins > 2) ? sum / (
double)(num_bins - 2) : 0.0;
92 double threshold = mean_mag * 8.0;
96 for (
int r = 0; r < 4; r++) {
97 unsigned bin = (unsigned)(dtmf_row_freqs[r] * N / sample_rate + 0.5);
98 if (bin >= num_bins) bin = num_bins - 1;
104 for (
int c = 0; c < 4; c++) {
105 unsigned bin = (unsigned)(dtmf_col_freqs[c] * N / sample_rate + 0.5);
106 if (bin >= num_bins) bin = num_bins - 1;
112 double best_row_mag = 0.0;
113 for (
int r = 0; r < 4; r++) {
114 if (row_mags[r] > threshold && row_mags[r] > best_row_mag) {
116 best_row_mag = row_mags[r];
121 double best_col_mag = 0.0;
122 for (
int c = 0; c < 4; c++) {
123 if (col_mags[c] > threshold && col_mags[c] > best_col_mag) {
125 best_col_mag = col_mags[c];
129 if (best_row < 0 || best_col < 0)
132 return dtmf_keypad[best_row][best_col];
154 unsigned max_n = (unsigned)(0.035 * sample_rate);
156 while (N * 2 <= max_n) N <<= 1;
157 unsigned hop = N / 4;
158 unsigned num_bins = N / 2 + 1;
160 unsigned num_frames = (signal_len >= N)
161 ? (signal_len - N) / hop + 1
171 unsigned q24_samples = (unsigned)(0.040 * sample_rate);
172 unsigned min_on_frames = (q24_samples > N)
173 ? (q24_samples - N + hop - 1) / hop + 1 : 2;
174 if (min_on_frames < 2) min_on_frames = 2;
175 unsigned min_off_frames = min_on_frames;
178 double *window = malloc(N *
sizeof(
double));
179 double *frame = malloc(N *
sizeof(
double));
180 double *mag = malloc(num_bins *
sizeof(
double));
188 enum { IDLE, PENDING, ACTIVE } state = IDLE;
189 char current_digit =
'\0';
190 unsigned on_count = 0;
191 unsigned off_count = 0;
192 unsigned tone_start_frame = 0;
193 unsigned tone_end_frame = 0;
194 unsigned num_tones = 0;
196 for (
unsigned f = 0; f < num_frames && num_tones < max_tones; f++) {
197 unsigned start = f * hop;
200 for (
unsigned i = 0; i < N; i++)
201 frame[i] = signal[start + i] * window[i];
207 for (
unsigned k = 0; k < num_bins; k++) {
209 if (k > 0 && k < N / 2)
213 char digit =
detect_frame(mag, num_bins, N, sample_rate);
218 current_digit = digit;
220 tone_start_frame = f;
226 if (digit == current_digit) {
228 if (on_count >= min_on_frames) {
233 }
else if (digit !=
'\0') {
235 current_digit = digit;
237 tone_start_frame = f;
240 current_digit =
'\0';
245 if (digit == current_digit && off_count == 0) {
248 }
else if (digit == current_digit
249 && off_count >= min_off_frames) {
253 tones_out[num_tones].
digit = current_digit;
255 (double)(tone_start_frame * hop) / sample_rate;
256 tones_out[num_tones].
end_s =
257 (double)((tone_end_frame + 1) * hop) / sample_rate;
260 current_digit = digit;
262 tone_start_frame = f;
264 }
else if (digit == current_digit) {
270 if (off_count >= min_off_frames) {
272 tones_out[num_tones].
digit = current_digit;
274 (double)(tone_start_frame * hop) / sample_rate;
275 tones_out[num_tones].
end_s =
276 (double)((tone_end_frame + 1) * hop) / sample_rate;
282 current_digit = digit;
284 tone_start_frame = f;
288 current_digit =
'\0';
300 if (num_tones < max_tones) {
301 if (state == ACTIVE) {
302 tones_out[num_tones].
digit = current_digit;
304 (double)(tone_start_frame * hop) / sample_rate;
305 tones_out[num_tones].
end_s =
306 (double)((tone_end_frame + 1) * hop) / sample_rate;
308 }
else if (state == PENDING && on_count >= min_on_frames) {
309 unsigned last = tone_start_frame + on_count - 1;
310 tones_out[num_tones].
digit = current_digit;
312 (double)(tone_start_frame * hop) / sample_rate;
313 tones_out[num_tones].
end_s =
314 (double)((last + 1) * hop) / sample_rate;
327 unsigned tone_ms,
unsigned pause_ms)
335 unsigned num_digits = (unsigned)strlen(digits);
336 unsigned tone_samples = (unsigned)(tone_ms * sample_rate / 1000.0);
337 unsigned pause_samples = (unsigned)(pause_ms * sample_rate / 1000.0);
342 memset(output, 0, total *
sizeof(
double));
345 double *col_tone = malloc(tone_samples *
sizeof(
double));
349 for (
unsigned d = 0; d < num_digits; d++) {
350 double row_freq, col_freq;
354 MD_sine_wave(output + offset, tone_samples, 0.5, row_freq, sample_rate);
357 MD_sine_wave(col_tone, tone_samples, 0.5, col_freq, sample_rate);
358 for (
unsigned i = 0; i < tone_samples; i++)
359 output[offset + i] += col_tone[i];
361 offset += tone_samples + pause_samples;
368 unsigned tone_ms,
unsigned pause_ms)
371 if (num_digits == 0)
return 0;
373 unsigned tone_samples = (unsigned)(tone_ms * sample_rate / 1000.0);
374 unsigned pause_samples = (unsigned)(pause_ms * sample_rate / 1000.0);
375 return num_digits * tone_samples
376 + (num_digits - 1) * pause_samples;
A mini library of DSP (Digital Signal Processing) routines.
@ MD_ERR_INVALID_SIZE
A size or count argument is invalid (e.g.
@ MD_ERR_INVALID_RANGE
A range or bound is invalid (e.g.
@ MD_ERR_ALLOC_FAILED
A memory allocation failed.
@ MD_ERR_NULL_POINTER
A required pointer argument is NULL.
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).
void md_report_error(MD_ErrorCode code, const char *func_name, const char *message)
Report a precondition violation to the active error handler.
Internal header for cross-file dependencies within the minidsp module.
#define MD_CHECK(cond, code, msg, retval)
Check a precondition in a function that returns a value.
#define MD_CHECK_VOID(cond, code, msg)
Check a precondition in a void function.
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.