/*			      Virtual MIDI Keyboard
		Written, arranged, produced by Carl Ch. v. Loesch.
				(c)  1989, 1990.
*/
#include "Lynx.h"

#include <libraries/ARPbase.h>
struct	ArpBase		*ArpBase;
struct	IntuitionBase	*IntuitionBase;
struct	GfxBase		*GfxBase;
struct	Window		*win;
struct	RastPort	*rp;

#include <MIDI/MIDI.h>
struct	MidiBase	*MidiBase;
struct	MSource		*source=0;
struct	MDest		*dest=0;
struct	MRoute		*droute=0, *sroute=0, *troute;
struct	MRouteInfo	routeinfo = { MMF_NOTEOFF | MMF_NOTEON, 0xffff };

static UBYTE keytable[128] = {
	0,61,63,0,66,68,70,0,73,75,0,78,80,82,0,0,	/*  0..15 */
	62,64,65,67,69,71,72,74,76,77,79,81,0,0,0,0,	/* 16..31 */
	42,44,46,0,49,51,0,54,56,58,0,0,0,0,0,0,	/* 32..47 */
	41,43,45,47,48,50,52,53,55,57,59,0,0,0,0,0,	/* 48..63 */
	0,0,60,0,83,0,0,0,0,0,0,0,0,0,0,0,		/* 64..79 (tab,cr) */
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 80..95 */
	0,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 96..111 (rsh) */
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 112..127 */
	};

#define	JOKES	6
static	char jokes[JOKES][70] = {
"                    INCORRECT PASSWORD. TRY AGAIN.                    ",
"           The password to go eat at the mensa is 'MAHLZEIT'          ",
"          How many cans can a can can if a can can can cans?          ",
" The Lynx says:    The best is yet to come... to come... to come...   ",
"    The Lord of Sequence. The ultimate Amiga MIDI sequencer program.  ",
"Greet'ns to SNEAK, NOAK, KIR, MADGUIT, KBR, YETI, ANT, WIZ, CRACKAKAN.",
};

struct NewWindow nw = {
	0, 11, 640, 40,
	1, 2, CLOSEWINDOW+RAWKEY,
	WINDOWCLOSE+WINDOWDEPTH+WINDOWDRAG+SMART_REFRESH,
	NULL, NULL, NULL, NULL, NULL,
	0, 0, 0, 0,
	WBENCHSCREEN
};

char	keyboard[] =	"Keyboard",	*name  = keyboard;
char	midiin[] =	"MidiIn",	*input = midiin;
char	midiout[] =	"MidiOut",	*output= midiout;
char	nomem[] = "Gimme RAM!";
char	banner[] =
 "*| Virtual MIDI Keyboard V1.0 by Carlo \"Lynx\" von Loesch (c)89 |*";
char	header[] = "*| Virtual MIDI Keyboard ", *title = header;
FLAG	logon = TRUE, thru = FALSE;
EXPORT	long	videosig, sigs;

main(argc, argv) long argc; char *argv[]; {
	register struct	IntuiMessage	*tuimsg;
	register union Event	*e;
	register long	t;
	register short	s;

	ifnot (ArpBase = OpenLibrary("arp.library",ArpVersion))
		Ciao("No ARP.Library");
	IntuitionBase =	ArpBase -> IntuiBase;
	GfxBase =	ArpBase -> GfxBase;
	ifnot (MidiBase = ArpOpenLibrary (MIDINAME,MIDIVERSION))
		Ciao ("No MIDI.Library");

	for (t=1; t<argc; t++) {
		if (argv[t][0]=='-') select (argv[t][1]) {
when 'i':		input = argv[++t]; break;
when 'o':		output = argv[++t]; break;
when 't':		Thru(); break;
otherwise		Ciao("Bad args");
		}
		else title = name = argv[t];
	}
	win = MyOpenWindow (&nw);
	SetWindowTitles (win, title, banner);
	rp = win->RPort;
	for (t=0; t<10; t++) DrawOctave (rp, t);

	ifnot	(source = CreateMSource (name, NULL))	Ciao (nomem);
	ifnot	(dest = CreateMDest (name, NULL))	Ciao (nomem);
	sroute = MRouteSource (source, output, NULL);
	droute = MRouteDest (input, dest, &routeinfo);

	sigs = videosig | (1L << dest->DestPort->SIGH);
	while (logon) {
		if (videosig != Wait (sigs))
		    while (e = GetMidiMsg (dest)) {
			Show (e->l);
			FreeMidiMsg (e);
		}
		else while (tuimsg = GetMsg (win->UserPort)) {
			t=tuimsg->Class; s=tuimsg->Code; ReplyMsg (tuimsg);
			select (t) {
when RAWKEY:			HitaKey(s); break;
when CLOSEWINDOW:		logon = FALSE;
			}
		}
	}
	Ciao ("Please close the window, it's cold outside.  *grin*  :-)");
}

HitaKey	(raw)
short	raw; {
	static union Event e;
	static char	chan;
	static FLAG amiga = FALSE;
#ifdef	CTRL
	static FLAG ctrl = FALSE;
#endif
	register UBYTE z;

	z = (UBYTE) (raw & 0x7f);
	if (z == 103) {
		if (raw & 0x80) amiga=FALSE;
		else amiga=TRUE;
		return;
	}
#ifdef	CTRL
	if (z == 99) {
		if (raw & 0x80) ctrl=FALSE;
		else ctrl=TRUE;
		return;
	}
	if (ctrl) select (raw) {
when 51:/* C */	logon = FALSE;
	}
	else
#endif
	if (amiga) select (raw) {
when 0: /* ` */	Thru();
	}
	else if (e.p[1] = keytable[z]) {
		e.p[0] = MS_NOTEON + chan;
		if (raw & 0x80)	e.p[2] = 0;
		else		e.p[2] = 64;
/* I think 64 is the standard velocity value for non-dynamic keyboards */
		PutMidiMsg (source, &e);
		Show (e);
	}
	else select (raw) {
when 89:/*F10*/	Thru();	break;
when 43:/* # */	Jokerman();	break;
when 81:/*F2*/	chan+=2; if (chan>16) chan=1;
when 80:/*F1*/	if (--chan<0) chan=15;
		title[0] = chan+49; Say (title);
	}
}

Thru() {
	register UBYTE *ret;

	if (thru = !thru)
	    if (troute = MRoutePublic (input, output, routeinfo))
		ret = "Route has been established.";
	    else { thru = NO; ret = "No route could be established."; }
	else { DeleteMRoute (troute); ret = "Route deleted."; }
	Say (ret);
}

#define COLORS	4
#define DEPTH	2
#define	TOP	11
#define	BOT	37
#define	MID	26
#define	LEFMARG	5

Show (e)
 union Event e; {
	register long z, o;
	static char offs[12] =
		{ 1, 6, 11, 16, 22, 27, 32, 37, 42, 47, 52, 58 };
	static char white[12] =
		{ 1, 0, 10, 0, 19, 28, 0, 37, 0, 46, 0, 55 };

	z = e.p[2];			/* velocity */
	if (!z || e.p[0] == MS_NOTEOFF)	SetAPen (rp, 0L);
	else SetAPen (rp, z / (128/(COLORS-1)) + 1 );
	if (z == 7) Jokerman();

	z = e.p[1] % 12;	o = 63 * (e.p[1]-z)/12 + LEFMARG;
	if (white[z]) RectFill (rp, o+white[z], MID+1, o+white[z]+7, BOT-1);
	z = o + offs[z] + 1;
	RectFill (rp, z, TOP+1, z+3, MID-1);
}

DrawOctave	(raz, x)
   int	raz, x; {
	x = 63*x + LEFMARG;
	Move (raz, x, TOP);	/* box around it */
	Draw (raz, x+63, TOP);	Draw (raz, x+63, BOT);
	Draw (raz, x, BOT);	Draw (raz, x, TOP);

	x+=9;	Move (raz, x, BOT);	DrawKey (raz, x, 3);
	x+=9;	Move (raz, x, BOT);	DrawKey (raz, x, 2);
	x+=9;	Move (raz, x, BOT);	Draw	(raz, x, TOP);
	x+=9;	Move (raz, x, BOT);	DrawKey (raz, x, 4);
	x+=9;	Move (raz, x, BOT);	DrawKey (raz, x, 3);
	x+=9;	Move (raz, x, BOT);	DrawKey (raz, x, 2);
}

DrawKey		(raz, x, i)
   int	raz, x, i; {
	Draw (raz, x, MID);
	Draw (raz, x-i, MID);
	Draw (raz, x-i, TOP);
	Move (raz, x, MID);
	Draw (raz, x-i+5, MID);
	Draw (raz, x-i+5, TOP);
}

Jokerman() {
	static UBYTE	joker = 0;
	SetWindowTitles (win, jokes[joker++], banner);
	if (joker==JOKES) joker = 0;
}

Say (str) char *str; { if (win) SetWindowTitles (win, -1L, str); }

Ciao	(str)
  char	*str; {
	if (str) { Say (str); Delay (25L); }
	if (win)	CloseWindowSafely (win);
	if (droute)	DeleteMRoute	(droute);
	if (sroute)	DeleteMRoute	(sroute);
	if (thru)	DeleteMRoute	(troute);
	if (dest)	DeleteMDest	(dest);
	if (source)	DeleteMSource	(source);
	if (ArpBase)	CloseLibrary	(ArpBase);
	exit (NULL);
}
