/*
 * 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:  runback rexxdial
 * Send numbers from within an ARexx program to the 'DialPort' port using
 * the "address 'DialPort' telephonenumber" instruction.
 * By sending the CLOSE command the program can be stopped.
 * 
 * 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 -Lm+rexxglue.o rexxdial.c
 */

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

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


extern struct MsgPort *CreatePort();

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

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) {
        printf("Bad News -- no REXX library\n");
        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){
        printf("Can't open s0 reply port\n");
        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){
        printf("Can't open Audio Device\n");
        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){
        printf("Can't open s1 reply port\n");
        CloseDevice(&s0);
        DeletePort(s0.ioa_Request.io_Message.mn_ReplyPort);
        exit(FALSE);
    }
    s1.ioa_Request.io_Unit=(struct Unit *)0x04;
 
    /*
     *    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);

}

/*
 *    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));
    }
}
