/* MusicSerial.c
   Copyright (c) 1990,1991,1992 by Thomas E. Janzen
   All Rights Reserved

   THIS SOFTWARE IS FURNISHED FREE OF CHARGE FOR STUDY AND USE AND MAY
   BE COPIED ONLY FOR PERSONAL USE OR COMPLETELY AS OFFERED WITH NO
   CHANGES FOR FREE DISTRIBUTION.  NO TITLE TO AND OWNERSHIP OF THE
   SOFTWARE IS HEREBY TRANSFERRED.  THOMAS E. JANZEN ASSUMES NO 
   RESPONSBILITY FOR THE USE OR RELIABILITY OF THIS SOFTWARE.
   
   Thomas E. Janzen
   58A School St. Apt. 2-L
   Hudson, MA 01749
   (508)562-1295
*/
/*
**  FACILITY:
**
**	AlgoRhythms music improviser on Commodore (TM) Amiga (TM)
**	compiled with SAS/C V5.10b
**
**  ABSTRACT:
**
**	MusicSerial.c manages the serial device at the MIDI bit rate.
**	All sends to MIDI occur here.
**
**  AUTHORS: Thomas E. Janzen
**
**  CREATION DATE:	26-MAR-1990
**
**  MODIFICATION HISTORY:
**    DATE	NAME	DESCRIPTION
** 7 Dec 90 T. Janzen Used SendIO rather than DoIO - didn't up performance
** 4 Nov 91 T. Janzen incorporate MIDI running status; delete SendNoteOff
**    8 DEC 91 T. Janzen conform to SAS/C 5.10b remove extern from functs
**  4 Jan 92 TEJ  last changes for 2.0
**--
*/

#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>
#ifdef CLI
#include <stdio.h>
#endif
#include "Window.h"
#include "AlgoRhythms.h"
#include "MusicSerial.h"
#include "Record.h"

#define NOTEON       (0x90)   /* MIDI Spec note on flag	*/
#define NOTEOFF      (0x80)	/* MIDI Spec note off flag	*/
#define NOTEONCMD    (0x90)	/* MIDI Note on byte 		*/
#define NOTEOFFCMD   (0x80)	/* MIDI Note off byte		*/
#define START        (0xFA)	/* MIDI Start play byte 	*/
#define STOP         (0xFC)	/* MIDI Stop play byte		*/
#define CONTINUE     (0xFB)	/* MIDI Continue play byte	*/
#define TIMINGCLOCK  (0xF8)	/* MIDI Timing clock byte	*/

int Response;

static struct IOExtSer *IORser;	/* Amiga Devices structures for serial */
static struct MsgPort *port;

extern struct IORequest *CreateExtIO();
extern void DeletePort(struct MsgPort *);

/* strings for error alerts */
static char QuitStr[]    = "Quit",
            SerErrStr[]  = "Serial Device Error",
            PrtErrStr[]  = "Port Error",
            IOErrStr[]   = "Create ExtIO Error",
            DevErrStr[]  = "Serial Device Error",
            PrmErrStr[]  = "Set Parm Error",
            WrtErrStr[]  = "Serial.device write error";
#ifndef CLI
struct IntuiText QuitTxt = {2, 1, JAM1, 5, 4, &font_choice, QuitStr, NULL};
#endif
static char	playbuffer[4];	/* MIDI note message buffer 	*/


#ifdef MEASURE
extern unsigned int NotesTotalMeasure = 0;
#endif

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)
{	
   /* set parameters of serial device */
#ifndef CLI
	struct IntuiText SerErrTxt = {2, 1, JAM1, 5, 15, &font_choice,SerErrStr,
      NULL};
#endif
	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;
#ifdef CLI
      puts (SerErrStr);
#else
      Response = AutoRequest ( w, &SerErrTxt, &QuitTxt, &QuitTxt, 
                               0L, 0L, 300L, 60L);
#endif
	}
	return error;
}

void Open_MIDI_Port (void)
{
#ifndef CLI
	struct IntuiText PrtErrTxt = {2, 1, JAM1, 5, 15, 
                                 &font_choice, PrtErrStr, NULL},
	                 IOErrTxt  = {2, 1, JAM1, 5, 15, 
                                 &font_choice, IOErrStr,  NULL},
	                 DevErrTxt = {2, 1, JAM1, 5, 15, 
                                 &font_choice, DevErrStr, NULL},
	                 PrmErrTxt = {2, 1, JAM1, 5, 15, 
                                 &font_choice, PrmErrStr, NULL};
#endif
	int error;
	unsigned long     rbl = 512,	/*read buffer length*/
	                  brk = 750000, 
		                  /* length of break in usec, usu. 750000 */
	                  baud = 31250,	/* MIDI baud rate 31.25k bits/sec*/
	                  t0  = 0x51040303,	/*termination characters*/
	                  t1  = 0x03030303;
	unsigned char     rwl = 0x08, /*bits per read char */
	                  wwl = 0x08, /*bits per write 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;
#ifdef CLI
      puts (PrtErrStr);
#else
      Response = AutoRequest (w, &PrtErrTxt, &QuitTxt, &QuitTxt, 
                              0L, 0L, 300L, 60L);
#endif
	}
	IORser = 
	(struct IOExtSer *)CreateExtIO(port, sizeof(struct IOExtSer));
	if (IORser == NULL) 
   {
		quit = TRUE;
		fubar = TRUE;
#ifdef CLI
      puts (IOErrStr);
#else
      Response = AutoRequest (w, &IOErrTxt, 
			&QuitTxt, &QuitTxt, 0L, 0L, 300L, 60L);
#endif
	}
	open:
	if ((error = OpenDevice(SERIALNAME, 0, IORser, 0)) != 0) 
   {
		quit = TRUE;
		fubar = TRUE;
#ifdef CLI
      puts (DevErrStr);
#else
      Response = AutoRequest (w, &DevErrTxt, 
			&QuitTxt, &QuitTxt, 0L, 0L, 300L, 60L);
#endif
	}

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

int WriteSer (struct IOExtSer *io, char *data, int length)
{
	int	error;
#ifndef CLI
	struct IntuiText WrtErrTxt = {2, 1, JAM1, 5, 15, &font_choice, 
                                 WrtErrStr, NULL};
#endif
	io->IOSer.io_Data = (APTR)data;
	io->IOSer.io_Length = length;
	io->IOSer.io_Command = CMD_WRITE;

	if((error = WaitIO(io)) != 0)
   {
		quit = TRUE;	/* flag to get out of program */
		fubar = TRUE;	/* flag to dump out of program with error*/
#ifdef CLI
      puts (WrtErrStr);
#else
      Response = AutoRequest (w, &WrtErrTxt, 
			&QuitTxt, &QuitTxt, 0L, 0L, 300L, 60L);
#endif
	}
   SendIO (io);
	return error;
}

void PlayNoteOn (const NOTEEVENT *PlayEvent) 
{ 
   static unsigned char Running_Status = 0X00;
   
   if (PlayEvent->Channel < 0)
   {
      Running_Status = 0;
      return;
   }
	playbuffer[0] = (unsigned char)(PlayEvent->Channel | NOTEONCMD);
	playbuffer[2] = (unsigned char)(PlayEvent->Dynamic);

   if (playbuffer[2])
   {
    	playbuffer[1] = (unsigned char)(scale[PlayEvent->Pitch]);
#ifdef MEASURE
NotesTotalMeasure++;
#endif
   }
   else
   {
      playbuffer[1] = (unsigned char)(PlayEvent->CurPitch);
   }

   if (playbuffer[0] == Running_Status)
   {
	   WriteSer (IORser, &playbuffer[1], 2);	/* send it out MIDI */
   }
   else
   {
	   WriteSer (IORser, playbuffer, 3);	/* send it out MIDI */
      Running_Status = playbuffer[0];
   }
   return;
}

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;
      default:
         break;
	}
	WriteSer (IORser, playbuffer, 1);	/* send it out MIDI */
   return;
}

void StopAllNotes (NOTEEVENT NotestoStop[])
{
	int i;

	for (i = 0; i < 16; i++)
	{
      if (NotestoStop[i].Playing)
      {
         NotestoStop[i].Dynamic = 0;
		   PlayNoteOn (&NotestoStop[i]);
         if (Recording)
         {
            Record_Note_Event (&NotestoStop[i]);
         }
         NotestoStop[i].Playing = 0;
         Delay (1);
		   if (fubar == TRUE) 
         {
            break;
         }
      }
	}
	SendFunction (STOPFUNCT);
   return;
}

void StopMIDI (void)
{	
   /* close up shop */
	if (port) DeletePort (port);
	if (IORser) CloseDevice (IORser);
   return;
}

