/*
 * This program can dial a telephone number by producing the telephone tones
 * You can use it by holding the telephone receiver next to the right speaker.
 *
 * The program uses the frequencies specified by the CCITT V.19
 * recommendation
 *
 * Usage:  rexxdial
 * Send numbers from within an ARexx program to the 'DialPort' port using
 * the "address 'DialPort' telephonenumber" instruction.
 * Sending the 'CLOSE' command will stop the program.
 * 
 * This program can be used by eg: VLT and SuperBase Proffesional
 *
 * Author : Arthur van Rooijen                   15-April-1990
 *
 * Note: This program is only tested in the Netherlands at the moment
 *
 * Compile: lc rexxdial.c
 * Link   : blink FROM LIB:cback.o+"rexxdial.o"+LIB:rexxglue.o
 *                TO "rexxdial"
 *                LIB LIB:lcm.lib
 *                LIB:lc.lib LIB:amiga.lib
 *                BATCH
 */

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <hardware/custom.h>
#include <hardware/dmabits.h>
#include <libraries/dos.h>
#include <proto/dos.h>
#include <devices/audio.h>

#include <rexx/storage.h>
#include <rexx/rxslib.h>

#define BANNER     "\nTelephone Dialer Version 1.0 by Arthur van Rooijen\n"
#define NOREXX     "Can not open REXX library"
#define CNTCREPORT "Can not open Message Port"
#define CNTOPAUDIO "Can not open Audio Device"

extern struct MsgPort *CreatePort();

void sound(UWORD per1, UWORD per2);
void dial(UBYTE digit);
UWORD duration(UWORD frek);
void fill_wave(BYTE *data);

long _stack = 4000;
char *_procname = "Dialer";
long _priority = 0;
long _BackGroundIO = 1;
extern BPTR _Backstdout;

BYTE chip wave1[16];

/*
 *	Frequencies specified by CCITT V.19
 *
 *				B1=1209 Hz	B2=1336 Hz	B3=1477 Hz	B4=1633 Hz
 *	A1=697 Hz       1           2           3           A
 *  A2=770 Hz		4			5			6			B
 *  A3=852 Hz		7			8			9			C
 *  A4=941 Hz		*			0			#			D
 *
 *  The periods are calculated by the next formula:
 *		period = 3579545 / ( 16 * frequency)
 */
struct { UBYTE key;
		 UWORD per1,
		 	   per2;
}table[]={{'0',238,167},
          {'1',321,185},
          {'2',321,167},
          {'3',321,151},
          {'4',291,185},
          {'5',291,167},
          {'6',291,151},
          {'7',263,185},
          {'8',263,167},
          {'9',263,151},
          {'*',238,185},
          {'#',238,151},
          {'A',321,137},
          {'B',291,137},
          {'C',263,137},
          {'D',238,137}};

struct IOAudio s0,s1;
struct RexxLib *RexxSysBase;


void _main()
{
	UBYTE sunit=0x06;
	int i;
	char number[32];

    struct MsgPort MyPort;
    struct RexxMsg *rmptr;
    LONG           test;


    RexxSysBase = (struct RexxLib *)OpenLibrary("rexxsyslib.library",0L);
    if (RexxSysBase == NULL) {
    	Write(_Backstdout,NOREXX,strlen(NOREXX));
        Close(_Backstdout);
        exit(0L);
    }

	/*
	 *	Open the audio device and create 2 channels for the right
     *  speaker
     */
    if((s0.ioa_Request.io_Message.mn_ReplyPort=CreatePort("s0",0L))==NULL){
    	Write(_Backstdout,CNTCREPORT,strlen(CNTCREPORT));
        Close(_Backstdout);
    	exit(0L);
    }
    
    s0.ioa_Request.io_Message.mn_Node.ln_Pri=10;
    s0.ioa_Data=&sunit;
    s0.ioa_Length=(ULONG)sizeof(sunit);
     
    if((OpenDevice(AUDIONAME,0L,&s0,0L))!=NULL){
    	Write(_Backstdout,CNTOPAUDIO,strlen(CNTOPAUDIO));
        Close(_Backstdout);
        DeletePort(s0.ioa_Request.io_Message.mn_ReplyPort);
    	exit(0L);
    }

    s0.ioa_Request.io_Command=CMD_WRITE;
    s0.ioa_Request.io_Flags=ADIOF_PERVOL;
    s0.ioa_Request.io_Unit=(struct Unit *)0x02;
    s0.ioa_Data=(UBYTE *)wave1;
    s0.ioa_Length=sizeof(wave1);
    s0.ioa_Volume=64;

    s1=s0;
    if((s1.ioa_Request.io_Message.mn_ReplyPort=CreatePort("s1",0L))==NULL){
    	Write(_Backstdout,CNTCREPORT,strlen(CNTCREPORT));
        Close(_Backstdout);
	    CloseDevice(&s0);
	    DeletePort(s0.ioa_Request.io_Message.mn_ReplyPort);
    	exit(0L);
    }
    s1.ioa_Request.io_Unit=(struct Unit *)0x04;
 
	/*
	 *	Write BANNER and close output filehandler to allow window to
     *  close
     */
   	Write(_Backstdout,BANNER,strlen(BANNER));
    Close(_Backstdout);

	/*
	 *	Create a SIN wave of 16 samples in chip memory
     */
	fill_wave(wave1);

    /*
     *	Create a message port for the rexx interface and make it public
     */
    InitPort(&MyPort,"DialPort");
    AddPort(&MyPort);


    for (;;) {
		/*
		 *	Wait for telephone numbers
		 */
		Wait(1<<MyPort.mp_SigBit);
        rmptr = (struct RexxMsg *) GetMsg(&MyPort);

        /* 
         *	See whether it's the close command
         */
		strcpy(number,rmptr->rm_Args[0]);
		test = strcmp(number,"CLOSE");

		/*
		 * Send a return code
		 */
		rmptr->rm_Result1 = 0;
      	rmptr->rm_Result2 = 0;
      	ReplyMsg(rmptr);

        /*
         *	Stop if close command was send
         */
        if (test == 0) break;

		/*
		 *	Dial number
		 */
		for(i=0;i<strlen(number);i++){
			dial(number[i]);
    	}

	}

	/*
	 *	Clean up everything
     */
    CloseDevice(&s0);
    DeletePort(s0.ioa_Request.io_Message.mn_ReplyPort);
    DeletePort(s1.ioa_Request.io_Message.mn_ReplyPort);

    RemPort(&MyPort);
    FreePort(&MyPort);
	exit(0L);
}

/*
 *	Produce the right frequencies for a key
 *	create a pause after the tone and for an unknown key
 *  The keys are : 0 1 2 3 4 5 6 7 8 9 * # A B C D 
 */
void dial(UBYTE key)
{
	int i=0,
	    found=0;

	while(!found){
		if (table[i].key==key){
			found=1;
            sound(table[i].per1,table[i].per2);
            for (i=0;i<7000;i++);
        }
        if(++i==16){
            found=1;
            for (i=0;i<10000;i++);
        }
    }
}


/*
 *	Produce two tones off the same length on the right channel
 */
void sound(UWORD per1,
           UWORD per2)
{

    s0.ioa_Period=per1;
    s0.ioa_Cycles=duration(per1);

    s1.ioa_Period=per2;
    s1.ioa_Cycles=duration(per2);

	BeginIO(&s0);
	BeginIO(&s1);
	WaitIO(&s0);
	WaitIO(&s1);

}

/*
 *	Calculate the cycles to produce a tone with frequency freq and a
 *  length of 0.3 seconds
 */
UWORD duration(UWORD freq)
{
	return (UWORD)((0.3/16.0/(float)freq)*1e6);
}

/*
 *	Create a SIN wave of 16 samples
 */
void fill_wave(BYTE *data)
{
	double x;

	for (x=0;x<=PI*2;x+=.4){
		*(data++)=(BYTE)floor(127*sin(x));
	}
}
