/*	(C) Copyright 1986 Jack Deckard   Helicon Software	*/
/*								*/
/*		DX7 Voice Librarian  by Jack Deckard		*/
/*	Simple program to send/recieve 32 voice data files	*/
/*	from Yamaha DX synth and load/save them on disk.	*/
/*	Special thanks to Michael Mounier, from who's ATERM     */
/*	program I learned to write C code.			*/
/*	Use Aztec +l option (32 bit int) when compiling.	*/

#include <exec/types.h>
#include <exec/exec.h>
#include <intuition/intuition.h>
#include <devices/serial.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <stdio.h>

#define VERSION 31L	/* 1.1 Library Version	*/

/* Define all global variables, pointers, structures, etc	*/
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct MsgPort *MRport;
struct MsgPort *MWport;
struct MsgPort *CreatePort();
struct IORequest *CreateExtIO();
struct IOExtSer *IORser;		/* Read serial request block	*/
struct IOExtSer *IOWser;		/* Write serial request block	*/
struct Window *window;
struct IntuiMessage *NewMessage;
struct Menu menu[2];
struct MenuItem menuitem[6];
struct IntuiText menutext[6];
struct FileHandle *filehandle;		/* Load and save voice files	*/
static char name[32];

/* Initialize NewWindow structure	*/
struct NewWindow newwindow = {
	10,		/* LeftEdge */
	20,		/* TopEdge  */
	300,		/* Width    */
	80,		/* Height   */
	0,		/* DetailPen */
	1,		/* BlockPen */
	CLOSEWINDOW | MENUPICK | VANILLAKEY | ACTIVEWINDOW
	 | INACTIVEWINDOW,	/* IDCMPFlags */
	WINDOWCLOSE | SMART_REFRESH | ACTIVATE | WINDOWDRAG | WINDOWDEPTH
	 | NOCAREREFRESH,	/* Flags */
	NULL,		/* FirstGadget */
	NULL,		/* CheckMark */
	(UBYTE *) "DX Voice Filer",	/* Title */
	NULL,		/* Screen */
	NULL,		/* BitMap */
	300,		/* MinWidth */
	80,		/* MinHeight */
	300,		/* MaxWidth */
	80,		/* MaxHeight */
	WBENCHSCREEN,	/* Type */
	};

SHORT linearray[] = {
	0,20,
	0,79,
	299,79,
	299,14
	};


main()
{
VOID InitMenu(), ClearWindow();
LONG loadfile(), savefile();
LONG error;
ULONG class;
SHORT KeepGoing;
static USHORT capture, chksum;
USHORT i, code, menunum, itemnum;
static UBYTE rs_in[2];			/* read data	*/
UBYTE c[2], voicedata[4104];
voicedata[0] = 0xf0;	/* Status byte		*/
voicedata[1] = 0x43;	/* ID (Yamaha)		*/
voicedata[2] = 0x00;	/* Substatus & channel	*/
voicedata[3] = 0x09;	/* Format #		*/
voicedata[4] = 0x20;	/* MSB count		*/
voicedata[5] = 0x00;	/* LSB count		*/
voicedata[6] = 0x00;	/* 1st data byte	*/
voicedata[4101] = 0x00;	/* 4096th data byte	*/
voicedata[4102] = 0x00;	/* Check Sum		*/
voicedata[4103] = 0xf7; /* EOX			*/

/*	*	*	*	*	*	*	*	*	*/
/*  Open Intuition and Graphics libraries. If error, exit immediatly	*/
IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",31L);
if (IntuitionBase == NULL) exit(100L);

GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",VERSION);
if (GfxBase == NULL) exit(100L);

/* Initialize window struct, then call OpenWindow. Exit if error	*/
if ((window = (struct Window *)OpenWindow(&newwindow)) == NULL) exit(100L);

/*  Initialize Menu items, then submit them to Intuition		*/
InitMenu();
SetMenuStrip(window,&menu);

/*	*	*	*	*	*	*	*	*	*/
/*  Create the Read serial reply port, and request block		*/
MRport = CreatePort(SERIALNAME,0L);
if(MRport == NULL)
	{
	puts("Error during read CreatePort\n");
	ClearMenuStrip(window);
	CloseWindow(window);
	exit(100L);
	}

/* Create request block for reading from serial port. IORser is		*/
/*  pointer to that request block.					*/
IORser = (struct IOExtSer *)CreateExtIO(MRport,sizeof(struct IOExtSer));
if(IORser == NULL)
	{
	puts("Error during CreateExtIO (read)\n");
	DeletePort(MRport);
	ClearMenuStrip(window);
	CloseWindow(window);
	exit(100L);
	}

/* Set initial parms, then open the serial device			*/
IORser -> io_SerFlags = SERF_XDISABLED | SERF_SHARED;
if((error = OpenDevice(SERIALNAME,0L,IORser,0L))!=0L)
	{
	puts("Read serial device did not open\n");
	DeletePort(MRport);
	DeleteExtIO(MRport,sizeof(struct IOExtSer));
	ClearMenuStrip(window);
	CloseWindow(window);
	exit(100L);
	}

/* Now set all the other serial parms, ie MIDI baud rate, no parity,	*/
/*  8 data bits, 1 start bit, 1 stop bit, no break signal		*/
IORser -> IOSer.io_Length = 1;
IORser -> IOSer.io_Data = (APTR) &rs_in[0];
IORser -> io_SerFlags = SERF_SHARED | SERF_RAD_BOOGIE;
IORser -> io_Baud = 31250;
IORser -> io_ReadLen = 8;
IORser -> io_WriteLen = 8;
IORser -> io_StopBits = 1;
IORser -> io_RBufLen = 5120;
IORser -> IOSer.io_Command = SDCMD_SETPARAMS;
DoIO(IORser);
IORser -> IOSer.io_Command = CMD_READ;

/*	*	*	*	*	*	*	*	*	*/
/*  Create the Write serial reply port, and request block		*/
MWport = CreatePort(SERIALNAME,0L);
if(MWport == NULL)
	{
	puts("Error during CreatePort (write)\n");
	CloseDevice(IORser);
	DeletePort(MRport);
	ClearMenuStrip(window);
	CloseWindow(window);
	exit(100L);
	}

/* Create write request block (IOWser) for passing info.		*/
IOWser = (struct IOExtSer *)CreateExtIO(MWport,sizeof(struct IOExtSer));
if(IOWser == NULL)
	{
	puts("Error during CreateExtIO (write)\n");
	DeletePort(MWport);
	CloseDevice(IORser);
	DeletePort(MRport);
	ClearMenuStrip(window);
	CloseWindow(window);
	exit(100L);
	}

/* Set initial parms, then open the write serial device			*/
IOWser -> io_SerFlags = SERF_XDISABLED | SERF_SHARED;
if((error = OpenDevice(SERIALNAME,0L,IOWser,0L))!=0L)
	{
	puts("Write serial device did not open\n");
	DeletePort (MWport);
	DeleteExtIO(MWport,sizeof(struct IOExtSer));
	CloseDevice(IORser);
	DeletePort(MRport);
	ClearMenuStrip(window);
	CloseWindow(window);
	exit(100L);
	}

IOWser -> IOSer.io_Command = CMD_WRITE;
IOWser -> IOSer.io_Length = 4104;
IOWser -> IOSer.io_Data = (APTR) &voicedata[0];

/*  Display initial screen message credits	*/
Move(window->RPort,20L,20L);
Text(window->RPort,"DX Voice Filer  by Jack Deckard",31L);
Move(window->RPort,20L,28L);
Text(window->RPort,"(C) 1986 Helicon Software",25L);

/*  Wait for message at IDCMP port  */
KeepGoing = 1;
capture = 0;
SetAPen(window -> RPort,1L);
BeginIO(IORser);

while( KeepGoing )
	{
	/* wait for window message or serial port message */
	Wait((1L << IORser -> IOSer.io_Message.mn_ReplyPort -> mp_SigBit) | 
		( 1L << window -> UserPort -> mp_SigBit));
	if(CheckIO(IORser))	/* another byte sent to Amiga?	*/
		{
		WaitIO(IORser);
		c[0] = rs_in[0];
		BeginIO(IORser);
		if(c[0] > 0x7f)	/* MIDI status byte	*/
			{
			capture = 0;
			chksum = 0;
			if(c[0] == 0xf0) ++capture;
			}
		else if(capture)	/* MIDI data byte	*/
			{
			if ((capture > 5) && (capture < 4102))
				{
				voicedata[capture] = c[0];
				chksum += c[0];
				}
			else if (capture == 4102)	
				{
				ClearWindow();
				chksum = 127 & ((~chksum)+1);
				if (chksum == c[0])
					{
					Move(window->RPort,20L,20L);
					Text(window->RPort,"Gotcha",6L);
					/* save voicedata to disk	*/
					error = savefile(&voicedata[6]);
					if (error == 1L)
						{
						Move(window->RPort,20L,52L);
						Text(window->RPort,"Data Saved",10L);
						}
					else if (error == 0L)
						{
						Move(window->RPort,20L,52L);
						Text(window->RPort,"I/O ERROR  File not saved",27L);
						}
					else if (error == -1L)
						{
						Move(window->RPort,20L,52L);
						Text(window->RPort,"Save Aborted",12L);
						}
					}
				else
					{
					Move(window->RPort,20L,52L);
					Text(window->RPort,"Data Error in Voice File",24L);
					}
				}
			++capture;
			}
		}
	while(NewMessage=(struct IntuiMessage *)GetMsg(window->UserPort) )
		{
		class = NewMessage->Class;
		code = NewMessage->Code;
		ReplyMsg( NewMessage );
		switch( class )
			{
			case VANILLAKEY:
			break;
			case ACTIVEWINDOW:
			break;
			case INACTIVEWINDOW:
			break;
			case CLOSEWINDOW:  /* User is ready to quit, so
				indicate that execution should terminate with
				next iteration of the loop. */
				KeepGoing = 0;
			break;
			case MENUPICK:
				if ( code != MENUNULL )
					{
					menunum = MENUNUM( code );
					itemnum = ITEMNUM( code );
					switch(menunum)
						{
						case 0:
							switch( itemnum )
								{
								case 0:	/* DX to Amiga */
									ClearWindow();
									Move(window->RPort,20L,20L);
									Text(window->RPort,"Send DX data to Amiga  ",23L);
									Move(window->RPort,20L,36L);
									Text(window->RPort,"Put DX in SYS INFO AVAIL",24L);
									Move(window->RPort,20L,44L);
									Text(window->RPort,"mode, select MIDI TRANSMIT?",27L);
									Move(window->RPort,20L,52L);
									Text(window->RPort,"and then press YES",18L);
								break;
								case 1: /* Amiga to DX */
									ClearWindow();
									Move(window->RPort,20L,20L);
									Text(window->RPort,"Send Amiga data to DX",21L);
									/*  Load the file on disk into array voicefile  */
									error = loadfile(&voicedata[6]);
									if (error == 1L)
										{
										for (i = 6; i < 4102; ++i)
											{
											chksum += voicedata[i];
											}
										/* get 2s complement of chksum	*/
										voicedata[4102] = (UBYTE)(127 & ((~chksum)+1));
										/* send all 4104 bytes to DX	*/
										DoIO(IOWser);
										Move(window->RPort,20L,52L);
										Text(window->RPort,"Voice data to DX complete",25L);
										}
									else if (error == 0L) 
										{
										Move(window->RPort,20L,52L);
										Text(window->RPort,"I/O ERROR  File Not Found",25L);
										}
									else if (error == -1L) 
										{
										Move(window->RPort,20L,52L);
										Text(window->RPort,"Load Aborted",12L);
										}
								break;
								case 2: /* RND to DX7 */
									ClearWindow();
									Move(window->RPort,20L,20L);
									Text(window->RPort,"Send random data to DX",22L);
									/*  generate RND data into array voicefile  */
									for (i = 6; i < 4102; ++i)
										{
										voicedata[i] = RangeRand(127L);
										chksum += voicedata[i];
										}
									/* get 2s complement of chksum	*/
									voicedata[4102] = (UBYTE)(127 & ((~chksum)+1));
									/* send all 4104 bytes to DX	*/
									DoIO(IOWser);
									Move(window->RPort,20L,52L);
									Text(window->RPort,"Random data to DX complete",26L);
								break;
								}
						break;
						case 1:
							switch (itemnum)
								{
								case 0:	/* DX7	*/
									voicedata[3] = 0x09;
								break;	/* DX21	*/
								case 1:
									voicedata[3] = 0x04;
								break;
								case 2:	/* DX100	*/
									voicedata[3] = 0x04;
								break;
								}
						break;
						} /* end of switch ( menunum ) */
					} /*  end of if ( not null ) */
			break;
			} /* end of switch (class) */
		} /* end of while ( newmessage )*/
	} /* end while ( keepgoing ) */

/*	*	*	*	*	*	*	*	*	*/
/*   It must be time to quit, so we have to clean			*/
/*   up and exit.							*/

CloseDevice(IOWser);	/* Close Write serial device	*/
DeletePort(MWport);
DeleteExtIO(IOWser,sizeof(struct IOExtSer));
CloseDevice(IORser);	/* Close Read serial device	*/
DeletePort(MRport);
DeleteExtIO(IORser,sizeof(struct IOExtSer));
ClearMenuStrip(window);
CloseWindow(window);
exit(FALSE);
}			/* end of main	*/


VOID InitMenu()    /*  Set up Menu	*/
{
/* Initialize Menu Header Voice Data Transfer */
menu[0].NextMenu = &menu[1];
menu[0].LeftEdge = 5;
menu[0].TopEdge = 0;
menu[0].Width = 165;
menu[0].Height = 10;
menu[0].Flags = MENUENABLED | MIDRAWN;
menu[0].MenuName = "Voice Data Transfer";
menu[0].FirstItem = &menuitem[0];

/* Initialize menu item  "DX to Amiga"  */
menuitem[0].NextItem = &menuitem[1];
menuitem[0].LeftEdge = 0;
menuitem[0].TopEdge = 0;
menuitem[0].Width = 156;
menuitem[0].Height = 11;
menuitem[0].Flags = ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP;
menuitem[0].MutualExclude = 0xffff;
menuitem[0].ItemFill = (APTR)&menutext[0];
menuitem[0].SelectFill = NULL;
menuitem[0].Command = 'c';
menuitem[0].SubItem = NULL;
menuitem[0].NextSelect = NULL;

menutext[0].FrontPen = 0;
menutext[0].BackPen = 1;
menutext[0].DrawMode = JAM2;
menutext[0].LeftEdge = 0;
menutext[0].TopEdge = 1;
menutext[0].ITextFont = NULL;
menutext[0].IText = (UBYTE *) "DX to Amiga";
menutext[0].NextText = NULL;

/* Second Menu Item  "Amiga to DX"  */
menuitem[1].NextItem = &menuitem[2];
menuitem[1].LeftEdge = 0;
menuitem[1].TopEdge = 11;
menuitem[1].Width = 156;
menuitem[1].Height = 11;
menuitem[1].Flags = ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP;
menuitem[1].MutualExclude = 0xffff;
menuitem[1].ItemFill = (APTR) &menutext[1];
menuitem[1].SelectFill = NULL;
menuitem[1].Command = 's';
menuitem[1].SubItem = NULL;
menuitem[1].NextSelect = NULL;

menutext[1].FrontPen = 0;
menutext[1].BackPen = 1;
menutext[1].DrawMode = JAM2;
menutext[1].LeftEdge = 0;
menutext[1].TopEdge = 1;
menutext[1].ITextFont = NULL;
menutext[1].IText = (UBYTE *) "Amiga to DX";
menutext[1].NextText = NULL;

/* Third Menu Item  "RND to DX"  */
menuitem[2].NextItem = NULL;
menuitem[2].LeftEdge = 0;
menuitem[2].TopEdge = 22;
menuitem[2].Width = 156;
menuitem[2].Height = 11;
menuitem[2].Flags = ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP;
menuitem[2].MutualExclude = 0xffff;
menuitem[2].ItemFill = (APTR) &menutext[2];
menuitem[2].SelectFill = NULL;
menuitem[2].Command = 'r';
menuitem[2].SubItem = NULL;
menuitem[2].NextSelect = NULL;

menutext[2].FrontPen = 0;
menutext[2].BackPen = 1;
menutext[2].DrawMode = JAM2;
menutext[2].LeftEdge = 0;
menutext[2].TopEdge = 1;
menutext[2].ITextFont = NULL;
menutext[2].IText = (UBYTE *) "RND to DX";
menutext[2].NextText = NULL;

/* Initialize Menu Header DX Synth */
menu[1].NextMenu = NULL;
menu[1].LeftEdge = 175;
menu[1].TopEdge = 0;
menu[1].Width = 140;
menu[1].Height = 10;
menu[1].Flags = MENUENABLED | MIDRAWN;
menu[1].MenuName = "DX Synthesizer";
menu[1].FirstItem = &menuitem[3];

/* Initialize menu item  "DX7"  */
menuitem[3].NextItem = &menuitem[4];
menuitem[3].LeftEdge = 0;
menuitem[3].TopEdge = 0;
menuitem[3].Width = 132;
menuitem[3].Height = 11;
menuitem[3].Flags = CHECKIT | CHECKED | ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP;
menuitem[3].MutualExclude = 0xfffe;
menuitem[3].ItemFill = (APTR)&menutext[3];
menuitem[3].SelectFill = NULL;
menuitem[3].Command = '7';
menuitem[3].SubItem = NULL;
menuitem[3].NextSelect = NULL;

menutext[3].FrontPen = 0;
menutext[3].BackPen = 1;
menutext[3].DrawMode = JAM2;
menutext[3].LeftEdge = CHECKWIDTH;
menutext[3].TopEdge = 1;
menutext[3].ITextFont = NULL;
menutext[3].IText = (UBYTE *) "DX7";
menutext[3].NextText = NULL;

/* Second Menu Item  "DX21"  */
menuitem[4].NextItem = &menuitem[5];
menuitem[4].LeftEdge = 0;
menuitem[4].TopEdge = 11;
menuitem[4].Width = 132;
menuitem[4].Height = 11;
menuitem[4].Flags = CHECKIT | ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP;
menuitem[4].MutualExclude = 0xfffd;
menuitem[4].ItemFill = (APTR) &menutext[4];
menuitem[4].SelectFill = NULL;
menuitem[4].Command = '2';
menuitem[4].SubItem = NULL;
menuitem[4].NextSelect = NULL;

menutext[4].FrontPen = 0;
menutext[4].BackPen = 1;
menutext[4].DrawMode = JAM2;
menutext[4].LeftEdge = CHECKWIDTH;
menutext[4].TopEdge = 1;
menutext[4].ITextFont = NULL;
menutext[4].IText = (UBYTE *) "DX21";
menutext[4].NextText = NULL;

/* Third Menu Item  "DX100"  */
menuitem[5].NextItem = NULL;
menuitem[5].LeftEdge = 0;
menuitem[5].TopEdge = 22;
menuitem[5].Width = 132;
menuitem[5].Height = 11;
menuitem[5].Flags = CHECKIT | ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP;
menuitem[5].MutualExclude = 0xfffb;
menuitem[5].ItemFill = (APTR) &menutext[5];
menuitem[5].SelectFill = NULL;
menuitem[5].Command = '1';
menuitem[5].SubItem = NULL;
menuitem[5].NextSelect = NULL;

menutext[5].FrontPen = 0;
menutext[5].BackPen = 1;
menutext[5].DrawMode = JAM2;
menutext[5].LeftEdge = CHECKWIDTH;
menutext[5].TopEdge = 1;
menutext[5].ITextFont = NULL;
menutext[5].IText = (UBYTE *) "DX100";
menutext[5].NextText = NULL;
}


VOID ClearWindow()	/* Clear the window,	*/
{
Move(window->RPort,10L,20L);
ClearScreen(window->RPort);
Move(window->RPort,1L,79L);
Draw(window->RPort,1L,20L);
Move(window->RPort,0L,20L);
PolyDraw(window->RPort,4L,&linearray[0]);
Move(window->RPort,298L,14L);
Draw(window->RPort,298L,79L);
}


/*  Load a file on disk using buffered I/O, 4096 bytes long.      */
/*  Open the disk file, read the array, and close the file  */

LONG loadfile(buffer)
UBYTE *buffer;   /*  pointer to byte array voice file	*/
{
VOID filename();

filename();
if (name[0] == 0) return(-1L);	/* aborted save	*/
filehandle = Open(name,MODE_OLDFILE);
if (filehandle == 0L) return(0L);
if(Read(filehandle,buffer,4096L) == -1L)
	{
	Close(filehandle); /* error	*/
	return(0L);
	}
Close(filehandle);
return(1L);
}


/*  Create a file on disk using buffered I/O, 4096 bytes long		*/
/*  Open the disk file, write the array to it, and close the file.	*/

LONG savefile(buffer)
UBYTE *buffer;   /*  pointer to voice file  */
{
VOID filename();

filename();
if (name[0] == 0) return(-1L);	/* aborted save	*/
filehandle = Open(name,MODE_NEWFILE);
if (filehandle == 0L) return(0L);
if(Write(filehandle,buffer,4096L) == -1L)
	{
	Close(filehandle);	/* error	*/
	return(0L);
	}
Close(filehandle);
return(1L);
}

/* function to get file name	*/
VOID filename()
{
ULONG	class;
USHORT	code,
	keepgoing = 1,
	i = 0;
LONG	x = 100;

Move(window->RPort,20L,36L);
Text(window->RPort,"File name? ",11L);

while(keepgoing)
	{
	Wait( 1L << window -> UserPort -> mp_SigBit);
	while(NewMessage=(struct IntuiMessage *)GetMsg(window->UserPort))
		{
		class = NewMessage->Class;
		code = NewMessage->Code;
		ReplyMsg(NewMessage);
		if(class = VANILLAKEY)
			{
			if(code)
				{
				if(code == 13)	/* return	*/
					{
					name[i]=0;
					keepgoing = 0;
					}
				if(code == 27)	/* ESC	*/
					{
					name[0]=0;
					keepgoing = 0;
					}
				else if((code == 8) && (i > 0)) /* backspace	*/
					{
					i--;
					name[i] = 0;
					Move(window->RPort,x,36L);
					Text(window->RPort," ",1L);
					Move(window->RPort,x,36L);
					x=x-8L;
					}
				else if(code >= 32)
					{
					name[i]=code;
					Text(window->RPort,&name[i],1L);
					x=x+8L;
					i++;
					}
				}
			}
		} /* end of new message loop	*/
	} /* end of while(keepgoing)	*/
} /* end of function */

/* end of file */