/*--------------------------------------------------------------------------*
                   __       __  __         $RCSfile: atdt.c,v $
                 _/ /_     / /_/ /_        $Revision: 1.3 $
         ______ /  __/____/ //  __/        $Date: 93/03/19 07:09:59 $
        / __  / / /  / __  / / /           $Author: tf $
       / __  / / /_ / /_/ / / /_           $Locker:  $
      /_/ /_/ /___//_____/ /___/           $State: Exp $

            (c) Copyright 1993 Tobias Ferber, All Rights Reserved.

 *--------------------------------------------------------------------------*/

#include "atdt.h"

static char rcs_id[]= "$Id: atdt.c,v 1.3 93/03/19 07:09:59 tf Exp $";

#define DPRI  10  /* precedence */
#define DVOL  32  /* volume */
#define DDEL   5  /* delay time */

 /*
  * +---+---+---+ +---+
  * | 1 | 2 | 3 | | A |   The lower frequency is equal for all
  * +---+---+---+ +---+   dial codes in the same line of the
  * | 4 | 5 | 6 | | B |   numeric keypad.
  * +---+---+---+ +---+
  * | 7 | 8 | 9 | | C |   The upper frequency is equal for all
  * +---+---+---+ +---+   dial codes in the same column of the
  * | * | 0 | # | | D |   numeric keypad.
  * +---+---+---+ +---+
  */

static UBYTE dialcodes[16]= { "123A456B789C*0#D" };

static UBYTE channel_map[4]= { (1<<0),  /* Audio channel 0 (left)  */
                               (1<<3),  /*               3 (left)  */
                               (1<<1),  /*               1 (right) */
                               (1<<2)   /*               2 (right) */
                             };

static BYTE waveform[8]= {    0,    /*  simple square wave form  */
                            127,    /*                           */
                            127,    /*   127    +----+           */
                              0,    /*          |    |           */
                              0,    /*     0  --+    |    +--    */
                           -127,    /*               |    |      */
                           -127,    /*  -127         +----+      */
                              0     /*                           */
                         };

/*
 *  sampling_rate = frequency * samples per waveform
 *                = frequency * sizeof(waveform)
 *
 *  bus_cycle     = 279.365 ns = 2.79365 * 10^(-7) s
 *
 *                                1
 *  sample_period = -----------------------------
 *                    sampling_rate * bus_cycle
 *
 *  Touch-tone period & cycle values are
 *           (C) Copyright 1989 by Commodore-Amiga, Inc.
 */

static UWORD periods[2][16][2]= {

  /* touch tone periods for NTSC machines */

  642,370,  /* 1 * 69.5 Hz, 120.7 Hz (sr 556, 965) */
  642,335,  /* 2 * 69.5 Hz, 133.3 Hz (sr 556,1066) */
  642,303,  /* 3 * 69.5 Hz, 147.3 Hz (sr 556,1179) */
  642,274,  /* A * 69.5 Hz, 162.9 Hz (sr 556,1303) */
  581,370,  /* 4 * 76.8 Hz, 120.7 Hz (sr 615, 965) */
  581,335,  /* 5 * 76.8 Hz, 133.3 Hz (sr 615,1066) */
  581,303,  /* 6 * 76.8 Hz, 147.3 Hz (sr 615,1179) */
  581,274,  /* B * 76.8 Hz, 162.9 Hz (sr 615,1303) */
  525,370,  /* 7 * 85.0 Hz, 120.7 Hz (sr 680, 965) */
  525,335,  /* 8 * 85.0 Hz, 133.3 Hz (sr 680,1066) */
  525,303,  /* 9 * 85.0 Hz, 147.3 Hz (sr 680,1179) */
  525,274,  /* C * 85.0 Hz, 162.9 Hz (sr 680,1303) */
  475,370,  /* * * 94.0 Hz, 120.7 Hz (sr 752, 965) */
  475,335,  /* 0 * 94.0 Hz, 133.3 Hz (sr 752,1066) */
  475,303,  /* # * 94.0 Hz, 147.3 Hz (sr 752,1179) */
  475,274,  /* D * 94.0 Hz, 162.9 Hz (sr 752,1303) */

  /* touch tone periods for PAL machines */

  636,367,  /* 1 * 70.2 Hz, 121.6 Hz (sr 562, 973) */
  636,332,  /* 2 * 70.2 Hz, 134.5 Hz (sr 562,1076) */
  636,300,  /* 3 * 70.2 Hz, 148.8 Hz (sr 562,1190) */
  636,271,  /* A * 70.2 Hz, 164.7 Hz (sr 562,1318) */
  576,367,  /* 4 * 77.5 Hz, 121.6 Hz (sr 620, 973) */
  576,332,  /* 5 * 77.5 Hz, 134.5 Hz (sr 620,1076) */
  576,300,  /* 6 * 77.5 Hz, 148.8 Hz (sr 620,1190) */
  576,271,  /* B * 77.5 Hz, 164.7 Hz (sr 620,1318) */
  520,367,  /* 7 * 85.9 Hz, 121.6 Hz (sr 687, 973) */
  520,332,  /* 8 * 85.9 Hz, 134.5 Hz (sr 687,1076) */
  520,300,  /* 9 * 85.9 Hz, 148.8 Hz (sr 687,1190) */
  520,271,  /* C * 85.9 Hz, 164.7 Hz (sr 687,1318) */
  471,367,  /* * * 94.8 Hz, 121.6 Hz (sr 758, 973) */
  471,332,  /* 0 * 94.8 Hz, 134.5 Hz (sr 758,1076) */
  471,300,  /* # * 94.8 Hz, 148.8 Hz (sr 758,1190) */
  471,271   /* D * 94.8 Hz, 164.7 Hz (sr 758,1318) */
};

/* ------------------------------------------------------------
 *  c  =  65.4 Hz   130.9 Hz   261.7 Hz   523.4 Hz   1046.8 Hz
 *  c# =  69.3 Hz   138.6 Hz   277.3 Hz   554.5 Hz   1109.0 Hz
 *  d  =  73.4 Hz   146.9 Hz   293.7 Hz   587.5 Hz   1175.0 Hz
 *  d# =  77.8 Hz   155.6 Hz   311.2 Hz   622.4 Hz   1244.9 Hz
 *  e  =  82.4 Hz   164.9 Hz   329.7 Hz   659.4 Hz   1318.9 Hz
 *  f  =  87.3 Hz   174.7 Hz   349.3 Hz   698.7 Hz   1397.3 Hz
 *  f# =  92.5 Hz   185.0 Hz   370.1 Hz   740.2 Hz   1480.4 Hz
 *  g  =  98.0 Hz   196.1 Hz   392.1 Hz   784.2 Hz   1568.4 Hz
 *  g# = 103.9 Hz   207.7 Hz   415.4 Hz   830.8 Hz   1661.7 Hz
 *  a  = 110.0 Hz   220.1 Hz   440.1 Hz   880.3 Hz   1760.5 Hz
 *  a# = 116.6 Hz   233.1 Hz   466.3 Hz   932.6 Hz   1865.2 Hz
 *  h  = 123.5 Hz   247.0 Hz   494.0 Hz   988.0 Hz   1976.1 Hz
 * ------------------------------------------------------------
 */

static UWORD cycles[16][2]= {

  /* length of each tone in audio cycles */

   96,166,  /* 1 */
  106,204,  /* 2 */
  118,250,  /* 3 */
  130,304,  /* A */
   96,150,  /* 4 */
  106,184,  /* 5 */
  118,226,  /* 6 */
  130,276,  /* B */
   96,136,  /* 7 */
  106,166,  /* 8 */
  118,204,  /* 9 */
  130,250,  /* C */
   96,124,  /* * */
  106,150,  /* 0 */
  118,186,  /* # */
  130,226   /* D */
};

int tvmode(void)
/* result:  0=NTSC, 1=PAL */
{ struct GfxBase *g= (struct GfxBase *)OpenLibrary("graphics.library",0L);
  int mode= (g && (g->DisplayFlags & PAL)==PAL) ? 1:0;
  if(g) CloseLibrary((struct Library *)g);
  return(mode);
}

struct IOAudio *alloc_dtone(void)
{ struct IOAudio *ioa= (struct IOAudio *)NULL;
  struct MsgPort *replyport= (struct MsgPort *)CreatePort(NULL,DPRI);
  if(replyport != (struct MsgPort *)NULL)
  { ioa= (struct IOAudio *)CreateExtIO(replyport,sizeof(struct IOAudio));
    if(ioa != (struct IOAudio *)NULL)
    { long err;
      ioa->ioa_Data   = channel_map;
      ioa->ioa_Length = sizeof(channel_map);
      err= OpenDevice(AUDIONAME,0,(struct IOAudio *)ioa,0L);
      if(err==0)
      { UBYTE *w= (UBYTE *)AllocMem(sizeof(waveform),MEMF_CHIP);
        if(w != (UBYTE *)NULL)
        { CopyMem((APTR)waveform,(APTR)w,sizeof(waveform));
          ioa->ioa_Data   = (UBYTE *)w;
          ioa->ioa_Length = sizeof(waveform);
          /* purge all pending I/O: */
          ioa->ioa_Volume= 0;
          ioa->ioa_Cycles= 1; /* DON'T use 0 here! */
          ioa->ioa_Request.io_Command= CMD_WRITE;
          DoIO(ioa);
        }
        else
        { CloseDevice((struct IORequest *)ioa);
          err= 1;
        }
      }
      if(err!=0)
      { DeleteExtIO((struct IOStdReq *)ioa,sizeof(struct IOAudio));
        ioa= (struct IOAudio *)NULL;
      }
    }
    if(ioa == (struct IOAudio *)NULL)
      DeletePort(replyport);
  }
  return(ioa);
}

void free_dtone(struct IOAudio *ioa)
{ if(ioa != (struct IOAudio *)NULL)
  { struct MsgPort *mp= ioa->ioa_Request.io_Message.mn_ReplyPort;
    UBYTE *data  = ioa->ioa_Data;
    ULONG length = ioa->ioa_Length;
    CloseDevice((struct IORequest *)ioa);
    if(data != (UBYTE *)NULL && length > 0)
      FreeMem(data,length);
    DeleteExtIO((struct IOStdReq *)ioa, sizeof(struct IOAudio));
    if(mp != (struct MsgPort *)NULL)
      DeletePort(mp);
  }
}

void play_dtone(struct IOAudio *ioa0, struct IOAudio *ioa1, int code)
{
  int mode= tvmode(); /* 0=NTSC, 1=PAL */

#ifdef BEGINIO

  ioa0->ioa_Request.io_Command = CMD_WRITE;
  ioa0->ioa_Request.io_Flags   = ADIOF_PERVOL;
  ioa0->ioa_Cycles             = cycles[code][0];
  ioa0->ioa_Period             = periods[mode][code][0];
  ioa0->ioa_Volume             = DVOL;

  ioa1->ioa_Request.io_Command = CMD_WRITE;
  ioa1->ioa_Request.io_Flags   = ADIOF_PERVOL;
  ioa1->ioa_Cycles             = cycles[code][1];
  ioa1->ioa_Period             = periods[mode][code][1];
  ioa1->ioa_Volume             = DVOL;

  BeginIO(ioa0);  /* BeginIO() missing in amiga13.lib ?! */
  BeginIO(ioa1);

#else

  /* Using SendIO() instead of BeginIO() leads to the following problem:
   * SendIO() will set the io_Flags field of the io_Request to
   * zero _before_ the request is sent.  This is why we have to
   * adjust volume, cycles and period before the CMD_WRITE command.
   * This does not always work !
   */

  ioa0->ioa_Cycles             = cycles[code][0];
  ioa0->ioa_Period             = periods[mode][code][0];
  ioa0->ioa_Volume             = DVOL;
  ioa0->ioa_Request.io_Command = ADCMD_PERVOL;
  SendIO(ioa0);

  ioa1->ioa_Cycles             = cycles[code][1];
  ioa1->ioa_Period             = periods[mode][code][1];
  ioa1->ioa_Volume             = DVOL;
  ioa1->ioa_Request.io_Command = ADCMD_PERVOL;
  SendIO(ioa1);

  /**/

  ioa0->ioa_Request.io_Command = CMD_WRITE;
  SendIO(ioa0);

  ioa1->ioa_Request.io_Command = CMD_WRITE;
  SendIO(ioa1);

#endif

  WaitIO(ioa0);
  WaitIO(ioa1);
}

BOOL atdt(STRPTR numstr)
{
  BOOL ok= FALSE;
  int i=0;
  struct IOAudio *a0= alloc_dtone(),
                 *a1= alloc_dtone();

  if(a0 && a1)
  { ok= TRUE;
    while(numstr && *numstr)
    { for(i=0;i<16;i++)
      { if(toupper(*numstr)==dialcodes[i])
        { play_dtone(a0,a1,i);
          Delay(DDEL);
        }
      }
      ++numstr;
    }
  }
  if(a0) free_dtone(a0);
  if(a1) free_dtone(a1);
  return(ok);
}
