/*	Transpose.c

	© 1989 Blue Ribbon Bakery
*/
#include "bars.h"
#include <libraries/dos.h>
#include <exec/memory.h>
#include <intuition/intuition.h>

#define ID_TRAN	0x5452414E

/*	Because the Transpose tool can send to another pipeline,
	it has two images.
*/

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

  0x0,   0x0, 
  0x0,   0x0, 
  0x3ff,   0xc000, 
  0xf3c,   0xf000, 
  0xf38,   0x7000, 
  0x3f30,   0x3c00, 
  0x3f24,   0x9c00, 
  0xc3c,   0xf000, 
  0xc3c,   0xf000, 
  0x3ff,   0xc000, 
  0x3c,   0x0, 
  0x0,   0x0,
 /*------ plane # 1: --------*/

  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0, 
  0x3,   0x0, 
  0xc007,   0x8300, 
  0xf00f,   0xcf00, 
  0xf01b,   0x6f00, 
  0xc003,   0x300, 
  0x3,   0x0, 
  0x0,   0x0, 
  0x3c,   0x0, 
  0xff,   0x0,
 /*------ plane # 2: --------*/

  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0, 
  0x3,   0x0, 
  0x7,   0x8000, 
  0xf,   0xc000, 
  0x1b,   0x6000, 
  0x3,   0x0, 
  0x3,   0x0, 
  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0};
 struct  Image transposedownimage =
 {
 0,0,
 24 , 12 , 3 ,
 &transposedown[0],
 0x1f,0x00,
 NULL,
 };
 
static UWORD transposeup[] = { 
/*------ plane # 0: --------*/

  0x0,   0x0, 
  0x3c,   0x0, 
  0x3ff,   0xc000, 
  0xf3c,   0xf000, 
  0xf38,   0x7000, 
  0x3f30,   0x3c00, 
  0x3f24,   0x9c00, 
  0xc3c,   0xf000, 
  0xc3c,   0xf000, 
  0x3ff,   0xc000, 
  0x0,   0x0, 
  0x0,   0x0,
 /*------ plane # 1: --------*/

  0xff,   0x0, 
  0x3c,   0x0, 
  0x0,   0x0, 
  0x3,   0x0, 
  0xc007,   0x8300, 
  0xf00f,   0xcf00, 
  0xf01b,   0x6f00, 
  0xc003,   0x300, 
  0x3,   0x0, 
  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0,
 /*------ plane # 2: --------*/

  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0, 
  0x3,   0x0, 
  0x7,   0x8000, 
  0xf,   0xc000, 
  0x1b,   0x6000, 
  0x3,   0x0, 
  0x3,   0x0, 
  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0};
 struct  Image transposeupimage =
 {
 0,0,
 24 , 12 , 3 ,
 &transposeup[0],
 0x1f,0x00,
 NULL,
 };
 

struct TransposeTool {
    struct Tool tool;
    short transpose;
    char octave;
    char interval;
};


static struct IntuiText transposeIText1 = {
	7,0,JAM2,
	-104,1,
	NULL,
	"Scale Degree:",
	NULL
};

static struct Gadget transposeGadget2 = {
	NULL,
	124,18,
	198,10,
	GADGHBOX+GADGHIMAGE,
	RELVERIFY+GADGIMMEDIATE,
	BOOLGADGET,
	NULL,
	NULL,
	&transposeIText1,
	NULL,
	NULL,
	2,
	NULL
};

static struct IntuiText transposeIText2 = {
	7,0,JAM2,
	-72,1,
	NULL,
	"+ Octave:",
	NULL
};

static struct Gadget transposeGadget1 = {
	&transposeGadget2,
	124,34,
	198,10,
	GADGHBOX+GADGHIMAGE,
	RELVERIFY+GADGIMMEDIATE,
	BOOLGADGET,
	NULL,
	NULL,
	&transposeIText2,
	NULL,
	NULL,
	1,
	NULL
};

#define transposeGadgetList1 transposeGadget1

static struct NewWindow transposeNewWindowStructure1 = {
	271,35,
	344,53,
	6,1,
	MOUSEBUTTONS+MOUSEMOVE+GADGETDOWN+GADGETUP+CLOSEWINDOW,
	WINDOWDRAG+WINDOWDEPTH+WINDOWCLOSE+REPORTMOUSE+ACTIVATE+NOCAREREFRESH,
	&transposeGadget1,
	NULL,
	"Transpose",
	NULL,
	NULL,
	5,5,
	-1,-1,
	CUSTOMSCREEN
};

 
extern struct Functions *functions;

extern printf();

/*	Transpose uses the two system routines twelvetoscale() and
	scaletotwelve() to translate from 12 tones per octave into
	the user selected scale and back.

	Result = twelvetoscale(clip,time,value,offset)

	Clip:  The clip to use.  If 0, it always defaults to the master
		song parameters.
	Time:  Time of the event, in case there are keys changes in the
		performance.
	Value:  Note value, in MIDI numbers.
	Offset:  Pointer to a byte that will be handed an offset, should
		this be an accidental.  For example, C# would set the
		offset to 1 to indicate the result must be sharped.
	Result:  The resulting interval in the user selected scale.

	Result = scaletotwelve(clip,time,value);

	Clip:  The clip to use.
	Time:  Time of the event.
	Value:  Note interval in the scale.
	Result:  Resulting interval, in twelve tones per octave.

	If you wish to use the offset created by twelvetoscale(), 
	add it to the final result.

	Notice that Transpose behaves differently if there is a
	seperate PipeLine connected to this Tool.  It makes a copy
	of the event, transposes the copy and sends it over.
	Otherwise, it just transposes the event itself and passes
	it on down the Pipe.
*/
	
static struct Event *processeventcode(event)

struct NoteEvent *event;

{
    struct TransposeTool *tool = (struct TransposeTool *) event->tool;
    short value;
    char offset;
    struct NoteEvent *copy;
    event->tool = event->tool->next;
    if (!(event->type & EVENT_VOICE)) return((struct Event *)event);
    if ((event->status == MIDI_NOTEON) || (event->status == MIDI_NOTEOFF) ||
	(event->status == MIDI_PTOUCH)) {
	value = (*functions->twelvetoscale)
	    (tool->tool.clip,event->time,event->value,&offset);
	value += tool->transpose;
	value = (*functions->scaletotwelve)(tool->tool.clip,event->time,value);
	value += offset;
	while (value < 0) value += 12;
	while (value > 127) value -= 12;
	if (tool->tool.branch) {
	    copy = (struct NoteEvent *) (*functions->allocevent)();
	    if (copy) {
		copy->value = value;
		copy->velocity = event->velocity;
		copy->duration = event->duration;
		copy->type = event->type;
		copy->status = event->status;
		event->next = copy;
		copy->time = event->time;
		copy->tool = tool->tool.branch;
	    }
	}
	else event->value = value;
    }
    return((struct Event *)event);
}

/*	The sliding proportional gadgets are actaully defined simply
	as Boolean gadgets.  When a gadget is clicked, DragSlider() is
	called, and one of the parameters passed to it is a routine
	that displays the number in the center of the knob.
	DragSlider is called with 5 parameters:

	result = DragSlider(window,gadget,width,routine,flag);

	Window:  This window.
	Gadget:  Pointer to the sliding gadget.
	Width:  Width, in pixels of the knob.
	Routine:  Routine to display the data in the knob.
	Flag:  If set, this drags up and down, instead of left to right.

	The final result (which is returned by the routine you
	provide) is returned by DragSlider.

	Your draw routine receives four parameters:

	routine(RastPort,X,Y,Position)

	RastPort:  RastPort to draw in.
	X,Y:  X and Y coordinates to draw to.
	Position:  Number between 0 and 65535, representing the
		position of the slider.
*/

static transposeroutine(rp,x,y,position)

struct RastPort *rp;
unsigned short x,y,position;

{
    char text[20];
    long result;
    Move(rp,x+4,y + 8);
    SetAPen(rp,0);
    SetDrMd(rp,JAM1);
    result = position - 32768;
    if (result <= 0) {
	result = (result - 3277) / 6554;
	sprintf(text," %ld",result);
    }
    else {
	result = (result + 3277) / 6554;
	sprintf(text," +%ld",result);
    }
    Text(rp,text,strlen(text));
    return((long)result);
}

static intervalroutine(rp,x,y,position)

struct RastPort *rp;
unsigned short x,y,position;

{
    static char *intervals[13] = 
{ "<7th","<6th","<5th","<4th","<3rd","<2nd"," U",
">2nd",">3rd",">4th",">5th",">6th",">7th"};
    long result;
    SetAPen(rp,0);
    SetDrMd(rp,JAM1);
    result = position + 2731;
    result = result / 5462;
    if (result == 6) Move(rp,x+8,y + 8);
    else Move(rp,x+4,y + 8);
    Text(rp,intervals[result],strlen(intervals[result]));
    return((long)result - 6);
}

void edittoolcode(tool)

struct TransposeTool *tool;

{
    struct IntuiMessage *message;
    struct Window *window;
    long class, code;
    struct PropInfo *propinfo;
    char refresh = 1;
    struct Gadget *gadget;
    struct NewWindow *newwindow;
    transposeNewWindowStructure1.Screen = functions->screen;
    if (tool->tool.touched & TOUCH_EDIT) {
	transposeNewWindowStructure1.LeftEdge = tool->tool.left;
	transposeNewWindowStructure1.TopEdge = tool->tool.top;
    }
    newwindow = (struct NewWindow *) 
	(*functions->DupeNewWindow)(&transposeNewWindowStructure1);
    if (!newwindow) return;
    window = (struct Window *) (*functions->FlashyOpenWindow)(newwindow);
    if (!window) return;
    tool->tool.window = window;
    propinfo = (struct PropInfo *) (*functions->GetPropInfo)(window,1);
    for (;;) {
	if (refresh) {
	    tool->transpose = (tool->octave * 7) + tool->interval;
	    code = tool->octave * 6553;
	    code += 32768;
	    (*functions->DrawSlider)(window,
		(*functions->GetGadget)(window,1),code,40,transposeroutine,0);
	    code = tool->interval + 6;
	    code *= 5460;
	    (*functions->DrawSlider)(window,
		(*functions->GetGadget)(window,2),code,40,intervalroutine,0);
	}
	refresh = 0;
	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) {
	    if (gadget->GadgetID == 1) {
		tool->octave = (*functions->DragSlider)
		    (window,gadget,40,transposeroutine,0);
	    }
	    else tool->interval = (*functions->DragSlider)
		(window,gadget,40,intervalroutine,0);
	    refresh = 1;
	}
    }
    tool->tool.window = 0;
    tool->tool.left = window->LeftEdge;
    tool->tool.top = window->TopEdge;
    tool->tool.touched = TOUCH_EDIT | TOUCH_INIT;
    (*functions->FlashyCloseWindow)(window);
    (*functions->DeleteNewWindow)(newwindow);
}

static struct ToolMaster master;

struct ToolMaster *inittoolmaster()

{
    memset((char *)&master,0,sizeof(struct ToolMaster));
    master.toolid = ID_TRAN;
    master.image = &transposedownimage;
    master.upimage = &transposeupimage;
    master.toolsize = sizeof(struct TransposeTool);
    strcpy(master.name,"Transpose");
    master.edittool = edittoolcode;
    master.processevent = processeventcode;
    master.tooltype = TOOL_NORMAL | TOOL_BRANCHOUT;
    return(&master);
}

