/* regtones.c
   L. Stewart October 10, 1983  3:17 PM
 */

#include "Lark.h"
#include "Env.h"
#include "LarkNet.h"
#include "LarkSlave.h"
#include "RegEPROM.h"

extern int UDiv();
extern int DoubleUDiv();
extern MoveBlock();
extern int Sine();

extern STab06();
char *obuf1;
char *obuf2;
char *obuf3;
char *inbuf1;
int *bufptr;
int *silVal1;
int first;

int audioMode;

int gainTable[9];
int energies[64];
int epoint;
int runSum[2];

struct SineCB {   /* used by SineML.dsm to generate tones */
  char *dest;
  int count, freq, sinetab;
  };

struct ToneCB {
  int f1, f2, totOn, totOff, this, active, sineTable, times;
  };

static int phase1, phase2;		/* phase continuity */
static struct SineCB f1a, f1b, f2a, f2b;
static struct ToneCB tcb;
static struct ToneCB *wToneCB;

InitToneStuff()
  {
  AudioIdle();
  obuf1 = OutBuf1;
  obuf2 = OutBuf2;
  obuf3 = OutBuf3;
  inbuf1 = InBuf1;
  bufptr = BufPtr;
  silVal1 = Sil1Val;
  first = false;
  f1a.dest = obuf1;
  f1b.dest = &obuf1[160];
  f2a.dest = obuf2;
  f2b.dest = &obuf2[160];
  f1a.count = f1b.count = f2a.count = f2b.count = 160;
  wToneCB = 0;
  Zero(obuf1, 160);
  CheckWDT();
  Zero(obuf2, 160);
  Zero(obuf3, 160);
  CheckWDT();


/* initialize gainTable */
  FlushAverage();
  gainTable[0] =   920 >> 1; /*11850 >> 1  2.5 dB */
  gainTable[1] =  1560 >> 1; /*12850 >> 1  5   dB */
  gainTable[2] =  2656 >> 1; /*13850 >> 1  7.5 dB */
  gainTable[3] =  4630 >> 1; /*14850 >> 1 10   dB */
  gainTable[4] =  8292 >> 1; /*15850 >> 1 12.5 dB */
  gainTable[5] = 14320 >> 1; /*16850 >> 1 15   dB */
  gainTable[6] = 24640 >> 1; /*17850 >> 1 17.5 dB */
  gainTable[7] = 39000 >> 1; /*32767 >> 1 20   dB */
  gainTable[8] = 65535 >> 1; /*32767 >> 1 20   dB */
  };

FlushAverage()
  {
  epoint = 0;
  Zero(runSum, 2);
  Zero(energies, 64);
  };

DoEcho()
  {
  SPMode(false);
  audioMode = AMEcho;
  };

/* on and off are in milliseconds
 */

static int mf1, mf2;

PlayDigit(d)
  int d;
  {
  switch (d) {
    case TT1: { mf1 = 697; mf2 = 1209; break; };
    case TT2: { mf1 = 697; mf2 = 1336; break; };
    case TT3: { mf1 = 697; mf2 = 1477; break; };
    case TTa: { mf1 = 697; mf2 = 1633; break; };
    case TT4: { mf1 = 770; mf2 = 1209; break; };
    case TT5: { mf1 = 770; mf2 = 1336; break; };
    case TT6: { mf1 = 770; mf2 = 1477; break; };
    case TTb: { mf1 = 770; mf2 = 1633; break; };
    case TT7: { mf1 = 852; mf2 = 1209; break; };
    case TT8: { mf1 = 852; mf2 = 1336; break; };
    case TT9: { mf1 = 852; mf2 = 1477; break; };
    case TTc: { mf1 = 852; mf2 = 1633; break; };
    case TT0: { mf1 = 941; mf2 = 1336; break; };
    case TTst: { mf1 = 941; mf2 = 1209; break; };
    case TTot: { mf1 = 941; mf2 = 1477; break; };
    case TTd: { mf1 = 941; mf2 = 1633; break; };
    default: return;
    };
  PlayTone(mf1, mf2, 60, 60);
  };

static int FreqToFrac(freq)
  int freq;
  {
  int v[2];
  v[1] = UDiv(freq,2);
  v[0] = (freq & 1) ? 0x8000: 0;
  /* 0 is 0 and 4000 is 08000H */
  /* multiplier is freq * 32768/4000 */
  return(DoubleUDiv(v, 4000));
  };

AudioIdle()
  {
  audioMode = AMOff;
  };

/*  f1 and f2 are in Hz
    on and off are times in milliseconds,  either can be 0
 */

int PlayTone(f1, f2, on, off)
  int f1, f2, on, off;
  {
  tcb.f1 = FreqToFrac(f1);
  tcb.f2 = FreqToFrac(f2);
  tcb.this = tcb.totOn = on/20;
  tcb.totOff = off/20;
  if ((on == 0) || (off == 0)) tcb.times = 1;
  else tcb.times = 2;
  tcb.active = true;
  tcb.sineTable = (int) &STab06;
  wToneCB = &tcb;
  audioMode = AMTone;
  SPMode(false);
  f1a.freq = f1b.freq = tcb.f1;
  f2a.freq = f2b.freq = tcb.f2;
  f1a.sinetab = f1b.sinetab = tcb.sineTable;
  f2a.sinetab = f2b.sinetab = tcb.sineTable;
  while (wToneCB) {
    TryEcho();
    CheckWDT();
    };
  };
  
TryEcho()
  {
  if (!first && *bufptr >= 160) {
    if (audioMode != AMTone) Limit();
    switch (audioMode) {
      case AMEcho: { CopyLow(); break; };
      case AMTone: { ToneLow(); break; };
      default:
      };
    first = true;
    };
  if (first && *bufptr < 160) {
    if (audioMode != AMTone) Limit();
    switch (audioMode) {
      case AMEcho: { CopyHigh(); break; };
      case AMTone: { ToneHigh(); break; };
      default:
      };
    first = false;
    };
  };

static CopyLow()
  {
  MoveBlock(obuf1, inbuf1, 80);
  };

static CopyHigh()
  {
  MoveBlock(&obuf1[160], &inbuf1[160], 80);
  };

static ToneMode()
  {
  if (wToneCB == 0) return;
  if (wToneCB->times == 0) {
    wToneCB = 0;
    AudioIdle();
    return;
    };
  if (wToneCB->this == 1) wToneCB->times -= 1;
  if (wToneCB->this == 0) {
    if (wToneCB->active) {
      if (wToneCB->totOff > 0) wToneCB->active = false;
      };
    else {
      if (wToneCB->totOn > 0) wToneCB->active = true;
      };
    if (wToneCB->active) wToneCB->this = wToneCB->totOn;
    else wToneCB->this = wToneCB->totOff;
    };
  else wToneCB->this -= 1;
  };

static ToneLow()
  {
  ToneMode();
  if (wToneCB) {
    if (wToneCB->active) {
      phase1 = Sine(phase1, &f1a);
      phase2 = Sine(phase2, &f2a);
      };
    };
  };

static ToneHigh()
  {
  ToneMode();
  if (wToneCB) {
    if (wToneCB->active) {
      phase1 = Sine(phase1, &f1b);
      phase2 = Sine(phase2, &f2b);
      };
    };
  };

Limit()
  {
  int i;
  int energy;
  energy = *silVal1 >> 1;
  DoubleDecrement(runSum, energies[epoint]);
  DoubleIncrement(runSum, energy);
  energies[epoint] = energy;
  epoint += 1;
  epoint &= 63;
  energy = DoubleUDiv(runSum, 64);
  for (i = 0; i < 8; i += 1) {
    if (energy < gainTable[i]) {
      SetIngain(3, i);
      return;
      };
    };
  SetIngain(3, 7);
  };