/* musicserial.c */
/* Copyright 1990 Thomas E. Janzen All Rights Reserved */
/*
**  FACILITY:
**
**	AlgoRhythms music improviser on Commodore (TM) Amiga (TM)
**	compiled with Lattice (TM) C 5.05
**
**  ABSTRACT:
**
**	Algorhythms improvises music over the MIDI serial port.
**
**  AUTHORS: Thomas E. Janzen
**
**  CREATION DATE:	26-MAR-1990
**
**  MODIFICATION HISTORY:
**    DATE	NAME	DESCRIPTION
**--
*/

#include	"exec/types.h"
#include	"exec/nodes.h"
#include	"exec/lists.h"
#include	"exec/ports.h"
#include	"exec/libraries.h"
#include	"exec/devices.h"
#include	"devices/serial.h"
#include	"exec/io.h"
#include	"intuition/intuition.h"
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/exec.h>
#include <proto/mathffp.h>
#include <proto/intuition.h>

#define NOTEON (0x90)
#define NOTEOFF (0x80)
#define MIDIADDRESS (0x00)
#define NOTEONCMD (0x90)
#define NOTEOFFCMD (0x80)
#define STARTFUNCT (0)
#define STOPFUNCT (1)
#define CLOCKFUNCT (2)
#define CONTFUNCT (3)
#define START 0xFA
#define STOP 0xFC
#define CONTINUE 0xFB
#define TIMINGCLOCK 0xF8

extern struct Window *w;
extern int fubar;

int midi_addr=0;
extern int quit;
int Response;

#include "AlgoRhythms.h"

extern struct GfxBase *GfxBase;
extern struct IntuitionBase *IntuitionBase;
extern struct DOSBase *DOSBase;
extern struct MathBase *MathBase;

struct IOExtSer *IORser;
struct MsgPort *port;

extern struct IORequest *CreateExtIO();

/* Devices prototypes */

extern void DeletePort(struct MsgPort *);

const char QuitStr[]="Quit";
const char SerErrStr[]="Serial Device Error";
const char PrtErrStr[]="Port Error";
const char IOErrStr[]="Create ExtIO Error";
const char DevErrStr[]="Serial Device Error";
const char PrmErrStr[]="Set Parm Error";
const char WrtErrStr[]="Serial.device write error";

struct IntuiText QuitTxt={2,1,JAM1,5,4,NULL,QuitStr,NULL};

void SendFunction(int Function);

char	playbuffer[4];	/* MIDI note message buffer 	*/

int SetParams(struct IOExtSer *io,unsigned long rbuf_len,
	unsigned char rlen,unsigned char wlen,unsigned long brk,
	unsigned long baud,unsigned char sf,
	unsigned long ta0,unsigned long ta1)
{
	struct IntuiText SerErrTxt={2,1,JAM1,5,15,NULL,SerErrStr,NULL};
	int error;
	io->io_ReadLen	=rlen;
	io->io_BrkTime	=brk; /*length of break timej (irrelevant)*/
	io->io_Baud	=baud;
	io->io_WriteLen	=wlen;
	io->io_StopBits	=0x01;
	io->io_RBufLen	=rbuf_len;
	io->io_SerFlags	=(1<<SERB_RAD_BOOGIE);
	io->IOSer.io_Command=SDCMD_SETPARAMS;
	io->io_TermArray.TermArray0=ta0;
	io->io_TermArray.TermArray1=ta1;
	if((error=DoIO(io)) !=0) {
		quit=TRUE;
		fubar=TRUE;
		Response=AutoRequest(w,&SerErrTxt,
			&QuitTxt,&QuitTxt,0L,0L,300L,60L);
	}
	return(error);
}

void Open_MIDI_Port(void)
{
	struct IntuiText PrtErrTxt={2,1,JAM1,5,15,NULL,PrtErrStr,NULL};
	struct IntuiText IOErrTxt={2,1,JAM1,5,15,NULL,IOErrStr,NULL};
	struct IntuiText DevErrTxt={2,1,JAM1,5,15,NULL,DevErrStr,NULL};
	struct IntuiText PrmErrTxt={2,1,JAM1,5,15,NULL,PrmErrStr,NULL};

	int error;
	unsigned long rbl = 512;	/*read buffer length*/
	unsigned long brk = 750000; 
		/* length of break in usec, usu. 750000 */
	unsigned long baud= 31250;	/*baud rate 31.25k*/
	unsigned long t0  = 0x51040303;	/*termination characters*/
	unsigned long t1  = 0x03030303;
	unsigned char rwl = 0x08; /*bits per read char */
	unsigned char wwl = 0x08;/*bits per write char */
	unsigned char sf  = 0x00; /*serial flags cf D-124 in libs devices*/
	/* Try to get to the serial device. */
	/* page 396 of Libraries and Devices was the example*/

	port=CreatePort(SERIALNAME,0);
	if(port==NULL){
		quit=TRUE;
		fubar=TRUE;
		Response=AutoRequest(w,&PrtErrTxt,
			&QuitTxt,&QuitTxt,0L,0L,300L,60L);
	}
	IORser= 
	(struct IOExtSer *)CreateExtIO(port,sizeof(struct IOExtSer));
	if(IORser == NULL) {
		quit=TRUE;
		fubar=TRUE;
		Response=AutoRequest(w,&IOErrTxt,
			&QuitTxt,&QuitTxt,0L,0L,300L,60L);
	}
	open:
	if ((error = OpenDevice(SERIALNAME,0,IORser,0))!=0) {
		quit=TRUE;
		fubar=TRUE;
		Response=AutoRequest(w,&DevErrTxt,
			&QuitTxt,&QuitTxt,0L,0L,300L,60L);
	}

	if((error=SetParams(IORser,rbl,rwl,wwl,brk,baud,sf,t0,t1))!=0){
		fubar=TRUE;
		quit=TRUE;
		Response=AutoRequest(w,&PrmErrTxt,
			&QuitTxt,&QuitTxt,0L,0L,300L,60L);
		}
	/* The serial device is open, so go ahead and play music.*/
}

int WriteSer(struct IOExtSer *io,char *data,int length)
{
	int	error;
	struct IntuiText WrtErrTxt={2,1,JAM1,5,15,NULL,WrtErrStr,NULL};

	io->IOSer.io_Data=(APTR)data;
	io->IOSer.io_Length=length;
	io->IOSer.io_Command=CMD_WRITE;

	if((error=DoIO(io))!=0){
		quit=TRUE;
		fubar=TRUE;
		Response=AutoRequest(w,&WrtErrTxt,
			&QuitTxt,&QuitTxt,0L,0L,300L,60L);
	}
	return(error);
}

void PlayNoteOn(const struct NoteEvent *PlayEvent) { 
	extern int scale[];
	/* Play a note; give note, dynamic*/
	playbuffer[0]=(unsigned char)(PlayEvent->Channel | NOTEONCMD);
	playbuffer[1]=(unsigned char)(scale[PlayEvent->Pitch]);
	playbuffer[2]=(unsigned char)(PlayEvent->Dynamic);
	WriteSer(IORser,playbuffer,3);	/* send it out MIDI */
}

void PlayNoteOff(const struct NoteEvent *StopEvent) {	
	/* MIDI note off routine ; input is number of note */ 
	extern int	scale[];
	playbuffer[0]=(unsigned char)(StopEvent->Channel | NOTEOFFCMD);
	playbuffer[1]=(unsigned char)(StopEvent->CurPitch); 
		/* note number */
	playbuffer[2]=0;		/* zero dynamic */
	WriteSer(IORser,playbuffer,3); /*send it out MIDI */
}

void SendFunction(int Function) { 
	playbuffer[1]=0;
	switch (Function) {
		case STARTFUNCT:
		playbuffer[0]=(unsigned char)(START);
		break;
		case STOPFUNCT:
		playbuffer[0]=(unsigned char)(STOP);
		break;
		case CLOCKFUNCT:
		playbuffer[0]=(unsigned char)(TIMINGCLOCK);
		break;
		case CONTFUNCT:
		playbuffer[0]=(unsigned char)(CONTINUE);
		break;
	}
	WriteSer(IORser,playbuffer,1);	/* send it out MIDI */
}

void StopAllNotes(const struct NoteEvent NotestoStop[]) {
	int i;
	for(i=0;i<16;i++) {
		PlayNoteOff(&NotestoStop[i]);
		if(fubar == TRUE) break;
	}
	SendFunction(STOPFUNCT);
}

void StopMIDI(void){
	if (port) DeletePort(port);
	if (IORser) CloseDevice(IORser);
}

