/*		M	I	D	I	R	E	C

			      Little MIDI Recorder
		Written, arranged, produced by Carl Ch. v. Loesch.

	V0.1	February'89,	V0.2	August'89,	V0.3	July'90
	V1.0	August'90.
#define	DEBUG
*/
#include "Lynx.h"
extern	struct	Menu		Menu1;
extern	struct	NewWindow	NewWindowStructure1;
EXPORT	char	ret[80];

#include <MIDI/MIDI.h>
EXPORT	struct	MidiBase	*MidiBase;
EXPORT	struct	MSource		*source=0;
EXPORT	struct	MDest		*dest=0;
EXPORT	struct	MRoute		*droute=0, *sroute=0, *troute;
static	struct	MRouteInfo	routeinfo = { MMF_CHAN + MMF_SYSRT, 0xffff };

EXPORT	char	prgname[] =	"Recorder",	*name  = prgname;
EXPORT	char	midiin[] =	"MidiIn",	*input = midiin;
EXPORT	char	midiout[] =	"MidiOut",	*output= midiout;

#include <libraries/ARPbase.h>
EXPORT	struct	ArpBase		*ArpBase;
EXPORT	struct	IntuitionBase	*IntuitionBase;
EXPORT	char	file[FCHARS+DSIZE+1]="", dir[DSIZE+1]="Sequences",
		loadname[FCHARS+1]="Hello", savename[FCHARS+1]="Hello";
EXPORT	struct	FileRequester	*freq;
EXPORT	struct	Window		*win;

#define	TAPELEN		15000
EXPORT	struct Tape {
	union Event event;
	short clock;
} *tape;

#define	RECORDERPRIO	3L
EXPORT	char	banner[] =
 "*| Little MIDI Recorder V1.0 by Carlo \"Lynx\" von Loesch (c)90 |*";
EXPORT	char	*title = banner;
EXPORT	char	nomem[] = "Out of RAM";
EXPORT	ULONG	tc = 0, tl = 0, ck = 0, nextck, lastck;
EXPORT	FLAG	on = YES, thru = NO, play = NO, rec = NO;
EXPORT	long	midisig, videosig, sigs;

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

	ifnot (ArpBase = OpenLibrary("arp.library",ArpVersion))
		Ciao("No ARP.Library");
	IntuitionBase =	ArpBase -> IntuiBase;
	ifnot (MidiBase = ArpOpenLibrary (MIDINAME,MIDIVERSION))
		Ciao ("No MIDI.Library");
	ifnot (tape = ArpAlloc (TAPELEN * sizeof(struct Tape)))
		Ciao (nomem);

	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];
	}
	ifnot (source = CreateMSource (name, NULL))	Ciao (nomem);
	ifnot (sroute = MRouteSource (source, output, NULL))
		Ciao ("No route out");
	ifnot (dest = CreateMDest (name, NULL))		Ciao (nomem);
	ifnot (droute = MRouteDest (input, dest, &routeinfo))
		Ciao ("No route in");
	midisig = 1L<<dest->DestPort->SIGH;

	win = MyOpenWindow(&NewWindowStructure1); Say (banner);
	SetMenuStrip (win, &Menu1);
	sigs = videosig | midisig;

	ifnot (freq = ArpAllocFreq()) Ciao (nomem); 
	freq->fr_Dir = dir; freq->fr_Window = win;
	freq -> fr_FuncFlags = FRF_NewWindFunc;

#ifdef RECORDERPRIO
	SetTaskPri(FindTask((char *) 0L), RECORDERPRIO);
#endif
	while (on) {
		t = Wait (sigs);
		if (t == midisig) while (e = GetMidiMsg (dest)) {
			HandleMIDI (e->l); FreeMidiMsg (e);
		} else MyCheckTui();
	}
	Ciao (NULL);
}

HandleMIDI (e) union Event e; {
	if (e.p[0] > 0xf0) select (e.p[0]) {
when MS_CLOCK:		if (play) PlayIt(); break;
when MS_START:		Rewind();
when MS_CONTINUE:	play=YES; LBUG ("PLAY at clk", ck); break;
when MS_STOP:		play=NO; LBUG ("STOP at clk", ck); break;
otherwise		XBUG ("Weird Event", e.l);
	}
	else if (rec && play && tl < TAPELEN) {
#ifdef TOUCHSTART
		if (!tl) ck=1;
	/* start counting from first input, ignoring sync */
#endif
		tape[tl].clock = ck - lastck;
		tape[tl++].event = e;
		lastck = ck;
	}
}

PlayIt() {
	if (!rec && (ck == nextck)) {
		PutMidiMsg (source, &tape[tc++].event);
		while (tape[tc].clock == 0)
			PutMidiMsg (source, &tape[tc++].event);
		if (tc < tl) nextck = ck + tape[tc].clock;
		else nextck = 0;
	}
	ck++;
}

Thru() {
	if (thru = !thru)
	  if (troute = MRoutePublic (input, output, routeinfo))
		SPrintf (ret,
"Route between %s and %s has been established.", input, output);
	  else { thru = NO; SPrintf (ret,
"No route between %s and %s could be established.", input, output);
	  }
	else {
		DeleteMRoute (troute);
		SPrintf (ret, "Ok. No more route between %s and %s.",
			input, output);
	}
	Say (ret);
}

Record() {
	rec=!rec;
	FBUG ("RECORD", rec);
	Rewind();
}
Rewind() {
	tc=0; ck=1; nextck=(ULONG) tape[0].clock; lastck=1;
	if (rec) tl=0;
	LBUG ("REWIND with tl", tl);
}
SaveIt() {
	if (tl) Save (file, &tape[0],(long) tl*sizeof(struct Tape));
}
LoadIt() {
	play=NO; Rewind();
	tl = Load (file, &tape[0],(long) TAPELEN*sizeof(struct Tape))
		/ sizeof(struct Tape);
}
Help() {
	SPrintf (ret, "Clock: %ld, Counter: %ld, SeqLength: %ld.",
		ck, tc, tl); Say (ret);
}

MyCheckTui() {
	register struct	IntuiMessage	*tuimsg;
	register ULONG	class, obj;
	register USHORT	code;

	while (tuimsg = GetMsg (win->UserPort)) {
		class =	tuimsg->Class;
		code =	tuimsg->Code;
		obj=	tuimsg->IAddress;	ReplyMsg (tuimsg);
		switch (class) {
case MENUPICK:		DoMenu(code);		break;
case GADGETUP:		HandleEvent (obj);	break;
case CLOSEWINDOW:	Ciao (NULL);
		}
	}
}

DoMenu (code) USHORT code; {
	NBUG (" MenuCode", code);
	select (MENUNUM(code)) {
when 0:		select (ITEMNUM(code)) {
	when 0:		LoadAs(); break;
	when 1:		LoadIt(); break;
	when 2:		SaveAs(); break;
	when 3:		SaveIt(); break;
	when 4:		Ciao(NULL);
		} break;
when 1:		select (ITEMNUM(code)) {
	when 0:		Help(); break;
	when 1:		Thru();
		}
	}
}

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

Ciao (str)  char *str; {
	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) {
		if (str)	Puts(str);
		CloseLibrary	(ArpBase);
	}
	exit (str ? 4404L : 0L);
}
