From 76a02622caf8f16979d8bc67890764768f7d06c0 Mon Sep 17 00:00:00 2001 From: Paul Harrison Date: Sun, 7 Jun 2026 17:49:16 -0700 Subject: DTMF Encoder --- Makefile | 9 ++++ dtmfencode.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wav.h | 18 ++++++++ 3 files changed, 171 insertions(+) create mode 100644 Makefile create mode 100644 dtmfencode.c create mode 100644 wav.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..316eca8 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +.PHONY: test + +all: dtmfencode test + +dtmfencode: dtmfencode.c + cc $^ -o $@ + +test: dtmfencode + ./dtmfencode test.wav "1234567890*#ABCD" 40000 100 diff --git a/dtmfencode.c b/dtmfencode.c new file mode 100644 index 0000000..be02660 --- /dev/null +++ b/dtmfencode.c @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include +#include +#include "wav.h" + +void dtmf_freqs(char c, int *a, int *b) { + switch (c) { + case '1': + case '2': + case '3': + case 'A': + *a = 697; + break; + case '4': + case '5': + case '6': + case 'B': + *a = 770; + break; + case '7': + case '8': + case '9': + case 'C': + *a = 852; + break; + case '*': + case '0': + case '#': + case 'D': + *a = 941; + break; + default: + *a = 0; + } + + switch (c) { + case '1': + case '4': + case '7': + case '*': + *b = 1209; + break; + case '2': + case '5': + case '8': + case '0': + *b = 1336; + break; + case '3': + case '6': + case '9': + case '#': + *b = 1477; + break; + case 'A': + case 'B': + case 'C': + case 'D': + *b = 1633; + break; + default: + *b = 0; + } +} + +int16_t dtmf_gen(char c, int t, int rate) { + int a, b; + dtmf_freqs(c, &a, &b); + double v = 0.5 * sin(2.0 * M_PI * (double)a * ((double)t / (double)rate)); + v += 0.5 * sin(2.0 * M_PI * (double)b * ((double)t / (double)rate)); + return v * 32767.0; +} + +int main(int argc, char *argv[]) { + /* usage */ + if (argc != 5) { + fprintf(stderr, "usage: %s \n", argv[0]); + return 2; + } + + /* read arguments */ + char *outfilename = argv[1]; + char *dtmf = argv[2]; + int width, spacing; + sscanf(argv[3], "%d", &width); + sscanf(argv[4], "%d", &spacing); + + /* open output file */ + FILE *fout = fopen(outfilename, "w"); + if (fout == NULL) { + fprintf(stderr, "could not open '%s' for writing.\n", outfilename); + return 1; + } + + /* create header */ + struct wav_header wav_header; + memcpy(wav_header.riff_magic, "RIFF", 4); + memcpy(wav_header.wave_magic, "WAVE", 4); + memcpy(wav_header.format_magic, "fmt ", 4); + memcpy(wav_header.data_magic, "data", 4); + wav_header.format_size = 16; + wav_header.format_type = 1; + wav_header.channels = 1; + wav_header.sample_rate = 44100; + wav_header.bits_per_sample = 16; + wav_header.rate1 = (wav_header.sample_rate * wav_header.bits_per_sample * wav_header.channels) / 8; + wav_header.rate2 = (wav_header.bits_per_sample * wav_header.channels) / 8; + + /* go past header */ + fseek(fout, sizeof(wav_header), SEEK_SET); + + /* generate sound */ + int num_digits = strlen(dtmf); + uint32_t sample_count = wav_header.sample_rate * num_digits * (width + spacing); + + for (int digit_index = 0; digit_index < num_digits; digit_index++) { + char digit = dtmf[digit_index]; + + /* emit dtmf tone */ + for (int t = 0; t < (float)wav_header.sample_rate * (float)(width) / 1000.0f; t++) { + int16_t sample = dtmf_gen(digit, t, wav_header.sample_rate); + fwrite(&sample, sizeof(int16_t), 1, fout); + } + + /* emit spacing */ + fseek(fout, (float)wav_header.sample_rate * (float)(spacing) / 1000.0f, SEEK_CUR); + } + + /* update header */ + wav_header.size = ftell(fout) - 8; + wav_header.data_size = ftell(fout) - sizeof(wav_header); + + /* write header */ + fseek(fout, 0, SEEK_SET); + fwrite(&wav_header, sizeof(wav_header), 1, fout); + + /* close file */ + fclose(fout); + + return 0; +} diff --git a/wav.h b/wav.h new file mode 100644 index 0000000..d5e26cb --- /dev/null +++ b/wav.h @@ -0,0 +1,18 @@ +#pragma once +#include + +struct wav_header { + char riff_magic[4]; + uint32_t size; + char wave_magic[4]; + char format_magic[4]; + uint32_t format_size; + uint16_t format_type; + uint16_t channels; + uint32_t sample_rate; + uint32_t rate1; + uint16_t rate2; + uint16_t bits_per_sample; + char data_magic[4]; + uint32_t data_size; +}; -- cgit v1.2.3