/* Telphone Tones generator   A.J. Fisher   August 1995
   C++ vsn   AJF   Dec. 1999 */

#include <stdio.h>
#include <math.h>
#include <new.h>
#include <string.h>
#include <libcgi.h>

#define global
#define unless(x)    if(!(x))
#define until(x)     while(!(x))

#define MAXSTR	     512
#define SAMPLERATE   8000
#define SINEBITS     11
#define SINELEN	     (1 << SINEBITS)
#define TWOPI	     (2.0 * M_PI)
#define TWO32	     4294967296.0	/* 2^32 */

typedef unsigned char uchar;
typedef unsigned int uint;

union word
  { word(char *sx) { s = sx; }
    word(int nx)   { n = nx; }
    char *s; int n;
  };

static short expandtab[128];	/* entries 0 .. 32767 */
static char compresstab[32768]; /* entries 0 .. 127   */
static double sinetab[SINELEN];

static int header[8] =
  { 0x2e736e64,			/* ".snd" magic number */
    32,				/* length of hdr */
    0,				/* *nominal* length of body, in samples */
    1, SAMPLERATE, 1, 0, 0,	/* don't ask me what this means! */
  };

extern "C" int atoi(char*);

static void newhandler(), initaudio(), makeexpandtab(), makecompresstab(), makesinetab();
static void sendheader(), writeword(uint);
static void senddtmf(char*), sendtone(int), sendwarble(double, double), sendfreqs(double, double, int, int);
static void usage(), fail(char*, word = 0);

inline int ifix(double x) { return (x >= 0.0) ? (int) (x+0.5) : (int) (x-0.5); }


global void main()
  { set_new_handler(newhandler);
    logweb("teletones", "");
    getentries();
    initaudio();
    if (isset("dial"))
      { char *s = getval("dial");
	senddtmf(s);
      }
    else if (isset("tone"))
      { char *s = getval("tone");
	sendtone(atoi(s));
      }
    else usage();
    exit(0);
  }

static void newhandler()
  { fail("No room!");
  }

static void initaudio()
  { makeexpandtab();
    makecompresstab();
    makesinetab();
    sendheader();
  }

static void makeexpandtab()
  { /* make mu-law expanding table */
    for (int n = 0; n < 128; n++)
      { double x = (pow(256.0, (double) n / 127.0) - 1.0) / 255.0;	/* result in range 0 .. 1 */
	expandtab[n] = ifix(x * 32767.0);				/* in range 0 .. 32767	  */
      }
  }

static void makecompresstab()
  { /* make mu-law compressing table */
    int i = 0, j = 0;
    while (i < 32768)
      { compresstab[i] = j;
	if (j < 127 && (expandtab[j+1] - i) < (i - expandtab[j])) j++;
	i++;
      }
  }

static void makesinetab()
  { for (int k = 0; k < SINELEN; k++)
      { double th = TWOPI * (double) k / (double) SINELEN;
	sinetab[k] = sin(th);
      }
  }

static void sendheader()
  { printf("Content-Type: audio/basic\n\n");
    for (int i = 0; i < 8; i++) writeword(header[i]);
  }

static void writeword(uint n)
  {
    #ifdef linux
	for (int i = 0; i < 4; i++) { putchar(n >> 24); n <<= 8; }
    #else
	for (int i = 0; i < 4; i++) { putchar(n & 0xff); n >>= 8; }
    #endif
  }

static double rowtab[4] = {  697.0,  770.0,  852.0,  941.0 };
static double coltab[4] = { 1209.0, 1336.0, 1477.0, 1633.0 };

static void senddtmf(char *s)
  { int k = 0;
    until (s[k] == '\0')
      { char c = s[k++];
	char *dstr = "123A456B789C*0#D";
	char *p = strchr(dstr, c);
	unless (p == NULL)
	  { uchar n = p - dstr;
	    sendfreqs(rowtab[n >> 2], coltab[n & 3], 100, 1);  /* tones for 100 ms   */
	    sendfreqs(0.0, 0.0, 100, 1);		       /* silence for 100 ms */
	  }
      }
  }

static void sendtone(int n)
  { switch (n)
      { default:
	    usage();

	case 0:		/* BT */
	  { for (int i = 0; i < 14; i++)
	      { sendfreqs(400.0, 400.0, 375, 1);
		sendfreqs(0.0, 0.0, 375, 1);
	      }
	    break;
	  }

	case 1:		/* EET */
	  { for (int i = 0; i < 7; i++)
	      { sendfreqs(400.0, 400.0, 400, 0);    /* quieter */
		sendfreqs(0.0, 0.0, 350, 0);
		sendfreqs(400.0, 400.0, 225, 1);
		sendfreqs(0.0, 0.0, 525, 1);
	      }
	    break;
	  }

	case 2:		/* RT */
	  { for (int i = 0; i < 4; i++)
	      { sendfreqs(400.0, 450.0, 400, 1);
		sendfreqs(0.0, 0.0, 200, 1);
		sendfreqs(400.0, 450.0, 400, 1);
		sendfreqs(0.0, 0.0, 2000, 1);
	      }
	    break;
	  }

	case 3:		/* NU */
	  { sendfreqs(400.0, 400.0, 10000, 1);
	    break;
	  }

	case 4:		/* PT */
	  { for (int i = 0; i < 40; i++)
	      { sendfreqs(400.0, 400.0, 125, 1);
		sendfreqs(0.0, 0.0, 125, 1);
	      }
	    break;
	  }

	case 5:		/* DT */
	  { sendfreqs(350.0, 450.0, 10000, 1);
	    break;
	  }

	case 6:		/* Warble 1 */
	  { for (int i = 0; i < 4; i++)
	      { sendwarble(1500.0, 1700.0);
		sendfreqs(0.0, 0.0, 200, 1);
		sendwarble(1500.0, 1700.0);
		sendfreqs(0.0, 0.0, 2000, 1);
	      }
	    break;
	  }

	case 7:		/* Warble 2 */
	  { for (int i = 0; i < 4; i++)
	      { sendwarble(900.0, 1100.0);
		sendfreqs(0.0, 0.0, 200, 1);
		sendwarble(900.0, 1100.0);
		sendfreqs(0.0, 0.0, 2000, 1);
	      }
	    break;
	  }
      }
  }

static void sendwarble(double f1, double f2)
  { /* 400ms of warble */
    for (int i = 0; i < 3; i++)
      { sendfreqs(f1, f1, 62, 1);
	sendfreqs(f2, f2, 63, 1);
      }
    sendfreqs(f1, f1, 25, 1);
  }

static double amptab[2] = { 8191.75, 16383.5 };

inline int phinc(double f)
  { /* given frequency f, return corresponding phase increment */
    return ifix(TWO32 * f / (double) SAMPLERATE);
  }

inline double sine(uint ptr)
  { /* given 32-bit pointer ptr, return corresponding element from sine table */
    return sinetab[ptr >> (32-SINEBITS)];
  }

static void sendfreqs(double f1, double f2, int ms, int ak)
  { double amp = amptab[ak];
    int phinc1 = phinc(f1), phinc2 = phinc(f2);
    int ns = ms * (SAMPLERATE/1000);
    uint ptr1 = 0, ptr2 = 0;
    for (int n = 0; n < ns; n++)
      { double val = amp * (sine(ptr1) + sine(ptr2));
	int ival = ifix(val);
	if (ival < -32768 || ival > 32767) fail("Bug! out of range: %08x", ival);
	putchar((ival >= 0) ? 255 - compresstab[ival] : 127 - compresstab[~ival]);
	ptr1 += phinc1; ptr2 += phinc2;
      }
  }

static void usage()
  { fail("Usage error!");
  }

static void fail(char *msg, word p1)
  { discard_output();	/* in case we've sent audio/basic */
    printf("Content-type: text/html\n\n");
    printf("<HTML>\n\n");
    printf("<h2> Error! </h2>\n");
    printf(msg, p1); putchar('\n');
    exit(0);
  }


