/*	CompanyB.c

	Accompany B Source

	© 1989 Blue Ribbon Bakery
*/

#include "bars.h"
#include <libraries/dos.h>
#include <exec/memory.h>
#include <string.h>
#include <intuition/intuition.h>


/*	Unique ToolID for Accompany B */

#define ID_ACYB	0x41435942

/* 	Tool Icon. */

static UWORD chord[] = { 
/*------ plane # 0: --------*/

  0x0,   0x0, 
  0xffff,   0xf000, 
  0xffff,   0xfc00, 
  0x2000,   0xe000, 
  0xffff,   0xfc00, 
  0x780,   0x0, 
  0xffff,   0xfc00, 
  0xffff,   0xfc00, 
  0x2018,   0x0, 
  0xffff,   0xfc00, 
  0xffff,   0xf000, 
  0x0,   0x0,
 /*------ plane # 1: --------*/

  0x0,   0x0, 
  0x2798,   0xe000, 
  0x2798,   0xe000, 
  0xf867,   0xfc00, 
  0x2798,   0xe300, 
  0xdfe7,   0x1f00, 
  0x2798,   0xe300, 
  0x2798,   0xe300, 
  0xf87f,   0x1c00, 
  0x2798,   0xe000, 
  0x2798,   0xe000, 
  0x0,   0x0,
 /*------ plane # 2: --------*/

  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0, 
  0xdfff,   0x1c00, 
  0x0,   0x0, 
  0xf87f,   0xfc00, 
  0x0,   0x0, 
  0x0,   0x0, 
  0xdfe7,   0xfc00, 
  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0};
static  struct  Image chordimage =
 {
 0,0,
 24 , 12 , 3 ,
 &chord[0],
 0x1f,0x00,
 NULL,
 };
 
/*	Accompany B Tool definition.  All we need, in addition to the
	normal Tool information, is the octave the chords are played on. */

struct ChordTool {
    struct Tool tool;
    short octave;
};

static struct ToolMaster master;

extern struct Functions *functions;

extern printf();

static void createchord(source,pattern,root,time,tool)

struct NoteEvent *source;
long pattern;
short root;
long time;
struct ChordTool *tool;

{
    unsigned short i;
    long bit;
    struct NoteEvent *new;
    for (i=0;i<24;i++) {
	bit = 1 << i;
	if (bit & pattern) {
	    new = (struct NoteEvent *) (*functions->allocevent)();
	    if (new) {
		new->type = EVENT_VOICE;
		new->status = MIDI_NOTEON;
		new->time = time;
		new->value = i + root;
		new->velocity = source->velocity;
		new->tool = tool->tool.next;
		(*functions->qevent)(new);
	    }
	    new = (struct NoteEvent *) (*functions->allocevent)();
	    if (new) {
		new->type = EVENT_VOICE;
		new->status = MIDI_NOTEOFF;
		new->time = time + source->duration;
		new->value = i + root;
		new->velocity = 0;
		new->tool = tool->tool.next;
		(*functions->qevent)(new);
	    }
	}
    }
}

static void qrhythm(tool,start,end)

struct ChordTool *tool;
long start,end;

{
    struct NoteEvent *rhythm;
    struct ChordEvent *chord;
    long nextstart;
    if (!tool->tool.touched) {
	tool->octave = 60;
	tool->tool.touched = TOUCH_INIT;
    }
    for (;start < end;) {
	rhythm = (struct NoteEvent *) (*functions->nextrhythmbeat)
	    (tool->tool.clip,start,&start);
	if (rhythm) {
	    if (start >= end) break;
	    chord = (struct ChordEvent *) 
		(*functions->timetochord)(tool->tool.clip,start);
	    if (chord) {
		createchord(rhythm,chord->chord->pattern,
		    chord->root+tool->octave,start,tool);
	    }
	}
	else break;
	start++;
    }
}

/*	Accompany B is an input Tool, so it creates MIDI events, rather than
	processing them.  You can provide a special routine that processes 
	transport commands.  Whenever the user starts, stops, or punches in
	and out of record, this routine is called.  In addition, this routine
	is called on every beat.  On each beat, Accompany B queues the next
	set of chords to play.
*/
	  
static void transporttrack(track,command,time,end)

struct Track *track;
long command, time, end;

{
    switch (command) {
	case TC_START :
	    qrhythm(track->toollist,time,end);
	    break;	    
	case TC_PLAY :
	    qrhythm(track->toollist,time,end);
	    break;
	case TC_STOP :
	case TC_POSITION :
	    break;
	case TC_TICK :
	    qrhythm(track->toollist,time,end);
	    break;
    }
}

/*	This is the actual routine that Bars&Pipes Transport mechanism
	calls.  Transportcode() then scans for all tracks with Accompany B
	as input and calls transporttrack() to create the notes for each
	Track.  Notice that if Multiin (multiple inputs) is enabled, 
	more than one Accompany B can generate notes.  Otherwise, only the
	selected Accompany B generates notes.  This is a good example for
	how you can make an Input Tool that handles either one or multiple
	inputs.
*/

static void transportcode(command,time,end)

register long command, time, end;

{
    struct Tool *tool;
    struct Track *track;
    if (functions->multiin) {
	track = (struct Track *) functions->tracklist;	
	for (;track;track = track->next) {
	    tool = (struct Tool *) track->toollist;
	    if (tool && (tool->toolid == ID_ACYB)) {
		transporttrack(track,command,time,end);
	    }
	}
    }
    else {
	track = master.intrack;
	if (track) {
	    tool = track->toollist;
	    if (tool && (tool->toolid == ID_ACYB)) {
		transporttrack(track,command,time,end);
	    }
	}
    }
}

/*	For this Tool, processeventcode just passes the note on.
*/

static struct Event *processeventcode(event)

struct Event *event;

{
    event->tool = event->tool->next;
    return(event);
}

static SHORT chordBorderVectors1[] = {
	0,0,
	102,0,
	102,14,
	0,14,
	0,0
};
static struct Border chordBorder1 = {
	-1,-1,
	4,0,JAM1,
	5,
	chordBorderVectors1,
	NULL
};

static struct IntuiText chordIText1 = {
	7,0,JAM2,
	6,3,
	NULL,
	"Octave:",
	NULL
};

static struct Gadget chordGadget1 = {
	NULL,
	36,21,
	101,13,
	GADGHBOX+GADGHIMAGE,
	RELVERIFY+GADGIMMEDIATE,
	BOOLGADGET,
	(APTR)&chordBorder1,
	NULL,
	&chordIText1,
	NULL,
	NULL,
	1,
	NULL
};

#define chordGadgetList1 chordGadget1

static struct NewWindow chordNewWindowStructure1 = {
	101,49,
	176,46,
	6,1,
	GADGETDOWN+GADGETUP+CLOSEWINDOW,
	WINDOWDRAG+WINDOWDEPTH+WINDOWCLOSE+ACTIVATE+NOCAREREFRESH,
	&chordGadget1,
	NULL,
	"Accompany B",
	NULL,
	NULL,
	5,5,
	-1,-1,
	CUSTOMSCREEN
};

/*	The user interface code.  This opens a window and lets the user
	select the octave.  This must be reentrant, in other words 
	multiple windows can be opened at once.  To facilitate this,
	Inovatools routines that create and handle a copy of the
	window structure are used (see the Inovatools documentation.)
*/

void edittoolcode(tool)

struct ChordTool *tool;

{
    struct IntuiMessage *message;
    struct Window *window;
    long class, code;
    short octave;
    char string[10];
    static struct IntuiText itext = { 7,0,JAM2,0,0,NULL,0,0 };
    struct Gadget *gadget;
    struct NewWindow *newwindow;
    chordNewWindowStructure1.Screen = functions->screen;
    if (tool->tool.touched & TOUCH_EDIT) {
	chordNewWindowStructure1.LeftEdge = tool->tool.left;
	chordNewWindowStructure1.TopEdge = tool->tool.top;
	chordNewWindowStructure1.Width = tool->tool.width;
	chordNewWindowStructure1.Height = tool->tool.height;
    }
    if (!tool->tool.touched) tool->octave = 60;
    newwindow = (struct NewWindow *) 
	(*functions->DupeNewWindow)(&chordNewWindowStructure1);
    if (!newwindow) return;
    window = (struct Window *) (*functions->FlashyOpenWindow)(newwindow);
    if (!window) return;
    tool->tool.window = window;
    for (;;) {
	(*functions->Itoa)(tool->octave / 12,string,10);
	itext.IText = string;
	PrintIText(window->RPort,&itext,108,24);
	message = (struct IntuiMessage *) (*functions->GetIntuiMessage)(window);
	class = message->Class;
	code = message->Code;
	gadget = (struct Gadget *) message->IAddress;
	ReplyMsg((struct Message *)message);
	if (class == CLOSEWINDOW) break;
	else if (class == GADGETDOWN) {
	    class = gadget->GadgetID;
	    switch (class) {
		case 1 :
		    octave = (*functions->popupoctave)(window,tool->octave / 12);
		    tool->octave = octave * 12;
		    break;
	    }
	}
    }
    tool->tool.window = 0;
    tool->tool.left = window->LeftEdge;
    tool->tool.top = window->TopEdge;
    tool->tool.width = window->Width;
    tool->tool.height = window->Height;
    tool->tool.touched = TOUCH_INIT | TOUCH_EDIT;
    (*functions->FlashyCloseWindow)(window);
    (*functions->DeleteNewWindow)(newwindow);
}

/*	Remove the transport handler. */

static void removetool()

{
    (*functions->removetransport)(transportcode);
}

/*	Initialization code.  In addition to setting the neccessary
	ToolMaster parameters, the transport handler is installed.  
*/

struct ToolMaster *inittoolmaster()

{
    memset((char *)&master,0,sizeof(struct ToolMaster));
    master.toolid = ID_ACYB;
    master.image = &chordimage;
    strcpy(master.name,"Accompany B");
    master.edittool = edittoolcode;
    master.processevent = processeventcode;
    master.tooltype = TOOL_INPUT;
    master.removetool = removetool;
    master.toolsize = sizeof(struct ChordTool);
    (*functions->installtransport)(transportcode);
    return(&master);
}
