Kjetil's Information Center: A Blog About My Projects

DTMF Sound Generator

This is a simple program to generate DTMF sounds using SDL as used by telephones. Simply pass the "phone number" as the command line argument.

The code:

#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_audio.h>
#include <math.h> /* sin() */

#define AUDIO_SAMPLE_RATE 44100

typedef struct audio_data_s {
  uint32_t sample_no;
  double freq_1;
  double freq_2;
  bool stop;
  bool stopped;
} audio_data_t;

static void dtmf(char c, double *freq_h, double *freq_v)
{
  switch (c) {
  default:
  case '1':
  case '4':
  case '7':
  case '*':
    *freq_h = 1209;
    break;
  case '2':
  case '5':
  case '8':
  case '0':
    *freq_h = 1336;
    break;
  case '3':
  case '6':
  case '9':
  case '#':
    *freq_h = 1477;
    break;
  case 'A':
  case 'B':
  case 'C':
  case 'D':
    *freq_h = 1633;
    break;
  }

  switch (c) {
  default:
  case '1':
  case '2':
  case '3':
  case 'A':
    *freq_v = 697;
    break;
  case '4':
  case '5':
  case '6':
  case 'B':
    *freq_v = 770;
    break;
  case '7':
  case '8':
  case '9':
  case 'C':
    *freq_v = 852;
    break;
  case '*':
  case '0':
  case '#':
  case 'D':
    *freq_v = 941;
    break;
  }
}

static void audio_callback(void *userdata, Uint8 *stream, int len)
{
  int i;
  audio_data_t *audio_data;
  double sample;
  double time;
  
  audio_data = (audio_data_t *)userdata;

  if (audio_data->stop == false) {
    audio_data->stopped = false;
  }

  for (i = 0; i < len; i++) {
    time = audio_data->sample_no / (double)AUDIO_SAMPLE_RATE;
    sample = sin(2.0 * M_PI * audio_data->freq_1 * time);
    sample += sin(2.0 * M_PI * audio_data->freq_2 * time);
    sample /= 2;
    if (audio_data->stopped) {
      stream[i] = 128;
    } else {
      stream[i] = (Uint8)(127 + (sample * 127));
      if (audio_data->stop && stream[i] >= 126 && stream[i] <= 130) {
        audio_data->stopped = true;
      }
    }
    audio_data->sample_no++;
  }
}

int main(int argc, char *argv[])
{
  SDL_AudioSpec desired, obtained;
  audio_data_t audio_data;
  char *number;

  if (argc != 2) {
    fprintf(stderr, "Usage: %s <number>\n", argv[0]);
    return 0;
  }

  if (SDL_Init(SDL_INIT_AUDIO) != 0) {
    fprintf(stderr, "SDL_Init() failed: %s\n", SDL_GetError());
    return 1;
  }
  atexit(SDL_Quit);

  desired.freq     = AUDIO_SAMPLE_RATE;
  desired.format   = AUDIO_U8;
  desired.channels = 1;
  desired.samples  = 1024;
  desired.userdata = &audio_data;
  desired.callback = audio_callback;

  if (SDL_OpenAudio(&desired, &obtained) != 0) {
    fprintf(stderr, "SDL_OpenAudio() failed: %s\n", SDL_GetError());
    return 1;
  }

  if (obtained.format != AUDIO_U8) {
    fprintf(stderr, "Did not get unsigned 8-bit audio format!\n");
    SDL_CloseAudio();
    return 1;
  }

  for (number = &argv[1][0]; *number != '\0'; number++) {
    audio_data.sample_no = 0;
    audio_data.stop = false;
    dtmf(*number, &audio_data.freq_1, &audio_data.freq_2);
    SDL_PauseAudio(0);
    SDL_Delay(200);
    audio_data.stop = true;
    SDL_Delay(20);
    SDL_PauseAudio(1);
    SDL_Delay(30);
  }

  SDL_CloseAudio();

  return 0;
}

          


Topic: Scripts and Code, by Kjetil @ 11/03-2022, Article Link