/*	Echo.c

	(c) 1987	Todor Fay

	Lattice version

	To compile:
	lc1 echo
	lc2 -v echo

	To link:
	alink with echo.with
		or
	blink with echo.with
*/

#include "exec/types.h"
#include "exec/exec.h"
#include "intuition/intuition.h"
#include "soundscape.h"

/*	This is the definition of the state structure for this module.
	It allows other programs to access these two parameters.
	This is primarily for environment loads and saves.
*/

struct EchoState {
    long length;
    long delaytime;
    long delaycount;
};

struct EchoState echostate = { sizeof(echostate) - 4,24,4 };
struct Note *notelist = 0;
unsigned short thisport;

/* 	Here's the image for the icon. */

UWORD echodata[] = {	/* 44 x 10 */ 
0,	0,	0,	
0,	0,	0,	
24,	0,	0,	
6,	0,	0,	
255,	32768,	0,	
6,	0,	0,	
24,	0,	0,	
0,	0,	0,	
0,	0,	0,	
0,	0,	0,	
0,	0,	0,	
6144,	768,	49200,	
6144,	768,	49200,	
6144,	768,	49200,	
6144,	768,	49200,	
6144,	768,	49200,	
63488,	7943,	49648,	
63488,	7943,	49648,	
0,	0,	0,	
0,	0,	0,	
};

struct Image echoimage = { 0,0,44,10,2,echodata,3,0,0 };

/*	These are the Intuition structures for the user edit window.
	These were generated with Power Windows. 
*/

UBYTE SIBuffer6[16] =
	"24";
struct StringInfo GadgetSI6 = {
	SIBuffer6,
	NULL,
	0,
	16,
	0,
	0,0,0,0,0,
	0,
	24,
	NULL
};

USHORT BorderVectors1[] = {0,0,111,0,111,9,0,9,0,0};
struct Border Border1 = {
	-2,-1,
	1,0,JAM1,
	5,
	BorderVectors1,
	NULL
};

struct IntuiText IText1 = {
	2,0,JAM2,
	-109,0,
	NULL,
	"Delay Time",
	NULL
};

struct Gadget timegadget = {
	NULL,
	141,17,
	108,8,
	GADGHCOMP,
	LONGINT+RELVERIFY+STRINGCENTER,
	STRGADGET,
	(APTR)&Border1,
	NULL,
	&IText1,
	0,
	(APTR)&GadgetSI6,
	6,
	NULL
};

UBYTE SIBuffer5[16] =
	"4";
struct StringInfo GadgetSI5 = {
	SIBuffer5,
	NULL,
	0,
	16,
	0,
	0,0,0,0,0,
	0,
	4,
	NULL
};

USHORT BorderVectors2[] = {0,0,111,0,111,9,0,9,0,0};
struct Border Border2 = {
	-2,-1,
	1,0,JAM1,
	5,
	BorderVectors2,
	NULL
};

struct IntuiText IText2 = {
	2,0,JAM2,
	-109,0,
	NULL,
	"Delay Count",
	NULL
};

struct Gadget countgadget = {
	&timegadget,
	141,32,
	108,8,
	GADGHCOMP,
	LONGINT+RELVERIFY+STRINGCENTER,
	STRGADGET,
	(APTR)&Border2,
	NULL,
	&IText2,
	0,
	(APTR)&GadgetSI5,
	5,
	NULL
};


struct NewWindow NewWindowStructure = {
	131,34,
	286,51,
	0,1,
	GADGETUP+CLOSEWINDOW+RAWKEY,
	WINDOWSIZING+WINDOWDRAG+WINDOWDEPTH+WINDOWCLOSE,
	&countgadget,
	NULL,
	"Runtime Quantization",
	NULL,
	NULL,
	5,5,
	640,200,
	WBENCHSCREEN
};


opencode(direction)

/*	Always happy to open. */

unsigned char direction;

{
    return(1);
}

closecode(direction)

/*	When closing down, throw out notelist. */

unsigned char direction;

{
    struct Note *next;
    for (;notelist;notelist = next) {
	next = (struct Note *) notelist->link.next;
	notelist->velocity = 0;     /* Force to off event. */
	Send(thisport,notelist);
    }
    return(1);
}

outcode(event)

struct Note *event;

{
    static char running = 0;
    struct Note *last, *note, *copy, *next;
    unsigned short velocity;
    switch (event->status) {
	case START :
	case CONTINUE :
	    running = 1;
	    break;
	case STOP :
	    running = 0;
	    for (;notelist;notelist = next) {
		next = (struct Note *) notelist->link.next;
		notelist->velocity = 0;
		Send(thisport,notelist);
	    }
	    break;
	case CLOCK :
	    if (!running) break;
	    last = 0;
	    for (note = notelist; note; note = next) {
		note->duration--;
		next = (struct Note *) note->link.next;
		if (!note->duration) {
		    copy = (struct Note *) AllocNode(NOTE);
		    if (copy) {
			copy->status = note->status;
			copy->value = note->value;
			velocity = (note->velocity * note->wait) /
			    echostate.delaycount;
			copy->velocity = velocity;
			Send(thisport,copy);
		    }
		    note->duration = echostate.delaytime;
		    note->wait--;
		    if (!note->wait) {
			if (last) last->link.next = note->link.next;
			else notelist = (struct Note *) note->link.next;
			FreeNode(note);
		    }
		    else last = note;
		}
		else last = note;
	    }
	    break;
    }
    if (running && echostate.delaycount &&
      (((event->status & 0xF0) == NOTEON) || 
      ((event->status & 0xF0) == NOTEOFF))) {
	event->link.next = (struct Link *) notelist;
	notelist = event;
	event->duration = echostate.delaytime;
	event->wait = echostate.delaycount;
    }
    else FreeNode(event);
}

useredit()

/*	Open a window with two string gadgets.  Wait for intuition
	messages.
	If CLOSEWINDOW, close down and return.
	If RAWKEY, send the keycode to OutConsole.  This is a SoundScape
	routine that will pass the key value to the Console Keyboard,
	so it can be played from this window.
	If GADGETUP, read the appropriate string gadget.
*/

{
    struct IntuiMessage *message;
    short code, class;
    struct Gadget *gadget;
    struct Window *window;
    GadgetSI5.LongInt = echostate.delaycount;
    GadgetSI6.LongInt = echostate.delaytime;
    window = (struct Window *) OpenWindow(&NewWindowStructure);
    for (;;) {
	while (!(message = GetMsg(window->UserPort)))
	    WaitPort(window->UserPort);
	class = message->Class;
	code = message->Code;
	gadget = (struct Gadget *) message->IAddress;
	ReplyMsg(message);
	if (class == CLOSEWINDOW) break;
	if (class == RAWKEY) OutConsole(code);
	if (class == GADGETUP) {
	    switch (gadget->GadgetID) {
		case 5 :
		    echostate.delaycount = GadgetSI5.LongInt;
		    break;
		case 6 :
		    echostate.delaytime = GadgetSI6.LongInt;
		    break;
	    }
	}
    }
    CloseWindow(window);
}

editcode(direction,command,copystate)

/*	This is the edit routine.  If the command is USEREDIT, call the
	routine useredit which puts up a window and gadgets and lets the
	user edit the two variables.  The variable notediting is
	used to avoid begin invoked multiple times.
	If GETSTATE or SAVESTATE, copy the echostate into the supplied buffer,
	copystate.
	For SETSTATE and LOADSTATE, do the reverse.
*/

char direction, command;
struct EchoState *copystate;

{
    static char notediting = 1;
    echostate.length = sizeof(echostate) - 4;
    switch (command) {
	case USEREDIT :
	    if (notediting) {
		notediting = 0;
		useredit();
		notediting = 1;
	    }
	    break;
	case GETSTATE :
	case SAVESTATE :
	    movmem(&echostate,copystate,sizeof(echostate));
	    break;
	case SETSTATE :
	case LOADSTATE :
	    movmem(copystate,&echostate,sizeof(echostate));
	    break;
    }
}
	    
long SoundScapeBase;
long IntuitionBase;

main() {
    SoundScapeBase = OpenLibrary("soundscape.library",0);
    if (SoundScapeBase) {
	notelist = 0;
	IntuitionBase = OpenLibrary("intuition.library",0);
	CloseLibrary(SoundScapeBase);
	thisport = AddMidiPort(opencode,closecode,editcode,outcode,&echoimage,
	    &echoimage,32,"echo");	
	SetTaskPri(FindTask(0),-20);
	while (MidiPort(thisport)) Delay(100);
	CloseLibrary(IntuitionBase);
    }
}
