
/*
**  jazz - a midi sequencer for Linux
**
**  Copyright (C) 1994-1996 Andreas Voss (andreas@avix.rhein-neckar.de)
**
**  Copyright (C) 1995-1996 Per Sigmond (Per.Sigmond@hia.no)
**
**  This program is free software; you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation; either version 2 of the License, or
**  (at your option) any later version.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with this program; if not, write to the Free Software
**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "wx.h"
#pragma hdrstop

#include "track.h"
#include "util.h"
#include "player.h"
#include "jazz.h"
#include "eventwin.h"

#include <stdlib.h>

#include <assert.h>

int tParam::Write(tWriteBase &io) {
	return( Msb.Write( io ) + Lsb.Write( io ) + Data.Write( io ) );
}

void tParam::SetCha( uchar cha ) {
	Msb.Channel = cha;
	Lsb.Channel = cha;
	Data.Channel = cha;
}

uchar sys_Sysex[7] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x00 };

uchar *SysExDT1( uchar aa, uchar bb, uchar cc, int datalen, uchar *data ) {

	 int length = 9 + datalen;
	 uchar *mess = new uchar[length];
	 mess[0] = 0x41;
	 mess[1] = 0x10;
	 mess[2] = 0x42;
	 mess[3] = 0x12;
	 mess[4] = aa;
   	 mess[5] = bb;
	 mess[6] = cc;
	 int i;
	 for (i = 0; i < datalen; i++)
		mess[i+7] = data[i];
	 uchar sum = 0x00;
	 for (i = 4; i < (length-2); i++)
		sum += mess[i];
   	 mess[length - 2] = 0x80 - (sum & 0x7f);
	 mess[length - 1] = 0xf7;
	 return( mess );
}

static double framesPerSecond[] = { 24.0, 25.0, 30.0, 30.0 };

tMtcTime::tMtcTime( tMtcOffset *s )
{
  type = (tMtcType) ((s->Data[0] & 0x60) >> 5);
  if (type < Mtc24) type = Mtc24;
  if (type > Mtc30Ndf) type = Mtc30Ndf;
  hour = s->Data[0] & 0x1f;
  min = s->Data[1];
  sec = s->Data[2];
  fm = s->Data[3];
}

tMtcTime::tMtcTime( long millisec, tMtcType t )
{
  type = t;
  if (type < Mtc24) type = Mtc24;
  if (type > Mtc30Ndf) type = Mtc30Ndf;
  sec = millisec / 1000;
  long msec = millisec % 1000;
  min = sec / 60;
  sec = sec % 60;
  hour = min / 60;
  min = min % 60;
  double frametime = 1000.0 / framesPerSecond[ type ];
  fm = (long) ((double) msec / frametime);
}

tMtcTime::tMtcTime( char *str, tMtcType t )
{
  type = t;
  if (type < Mtc24) type = Mtc24;
  if (type > Mtc30Ndf) type = Mtc30Ndf;
  hour = 0;
  min = 0;
  sec = 0;
  fm = 0;
  sscanf( str, "%ld:%ld:%ld.%ld", &hour, &min, &sec, &fm );
  if (fm >= framesPerSecond[ type ])
    fm = (long) framesPerSecond[ type ] - 1;
}

tMtcTime::tMtcTime( unsigned h, unsigned m, unsigned s, unsigned f, unsigned t )
{
  hour = h;
  min = m;
  sec = s;
  fm = f;
  type = (tMtcType) t;
  if (type < Mtc24) type = Mtc24;
  if (type > Mtc30Ndf) type = Mtc30Ndf;
}

void tMtcTime::ToString( char *str )
{
  sprintf( str, "%ld:%ld:%ld.%ld", hour, min, sec, fm );
}

tMtcOffset *tMtcTime::ToOffset()
{
  uchar *mess = new uchar[5];
  mess[0] = (uchar) hour | ((uchar) type << 5);
  mess[1] = (uchar) min;
  mess[2] = (uchar) sec;
  mess[3] = (uchar) fm;
  mess[4] = 0x00;
  tMtcOffset *s = new tMtcOffset(0, mess, 5);
  delete mess;
  return( s );
}

long tMtcTime::ToMillisec()
{
  long msec = (((((hour * 60L) + min) * 60L) + sec) * 1000L) +
              ((fm * 1000L) / (long) framesPerSecond[type]);
  return( msec );
}

int sysex_channel( int Channel ) {
	if (Channel < 10)
		return( Channel );
	else if (Channel == 10)
		return( 0 );
	else 	return( Channel - 1 );
}

int drumParam2Index( int par )
{
	switch (par)
	{
		case drumPitch:
			return( drumPitchIndex );
		case drumTva:
			return( drumTvaIndex );
		case drumPan:
			return( drumPanIndex );
		case drumReverb:
			return( drumReverbIndex );
		case drumChorus:
			return( drumChorusIndex );
		default:
			assert( 0 );
	}
	return 0;
}

int drumIndex2Param( int index )
{
	switch (index)
	{
		case drumPitchIndex:
			return( drumPitch );
		case drumTvaIndex:
			return( drumTva );
		case drumPanIndex:
			return( drumPan );
		case drumReverbIndex:
			return( drumReverb );
		case drumChorusIndex:
			return( drumChorus );
		default:
			assert(0);
	}
	return 0;
}

tDrumInstrumentParameter::tDrumInstrumentParameter( tNrpn *par )
: pitch( par->Lsb.Value ), next(0)
{
	for (int i = drumPitchIndex; i < numDrumParameters; i++)
	{
		param[i] = 0;
	}
	param[ drumParam2Index( par->Msb.Value ) ] = par;
}

tNrpn *tDrumInstrumentParameter::Get( int index )
{
	assert( (index >= drumPitchIndex) && (index < numDrumParameters) );
	return( param[ index ] );
}

void tDrumInstrumentParameter::Put( tNrpn *par )
{
	param[ par->Lsb.Value ] = par;
}

tDrumInstrumentParameter *tDrumInstrumentParameter::Next()
{
	return( next );
}

int tDrumInstrumentParameter::Pitch()
{
	return( pitch );
}

tDrumInstrumentParameter
*tDrumInstrumentParameterList::GetElem( int pit )
{
	tDrumInstrumentParameter *ptr = list;
	while (ptr)
	{
		if (ptr->pitch == pit)
		{
			break;
		}
		ptr = ptr->next;
	}
	return( ptr );
}

tNrpn *tDrumInstrumentParameterList::GetParam( int pit, int index )
{
	tDrumInstrumentParameter *ptr = GetElem( pit );
	if (ptr)
		 return( ptr->Get( index ) );
	else
		return( 0 );
}

void tDrumInstrumentParameterList::PutParam( tNrpn *par )
{
	tDrumInstrumentParameter *ptr = GetElem( par->Lsb.Value );
	if (!ptr)
	{
		ptr = new tDrumInstrumentParameter( par );
		ptr ->next = list;
		list = ptr;
	}
	else
	{
		ptr->param[ drumParam2Index( par->Msb.Value ) ] = par;
	}
}

void tDrumInstrumentParameterList::DelParam( int pit, int index )
{
	if (list)
	{
		tDrumInstrumentParameter *elem = GetElem( pit );
		if (elem)
		{
			if (elem->Get(index))
				delete elem->param[ index ];
			elem->param[ index ] = 0;
		}
	}
}

void tDrumInstrumentParameterList::DelElem( int pit )
{
	for (int i = drumPitchIndex; i < numDrumParameters; i++)
	{
		DelParam( pit, i );
	}

	tDrumInstrumentParameter *ptr = list;
	tDrumInstrumentParameter *prev = 0;
	while (ptr)
	{
		if (ptr->pitch == pit)
		{
			if (prev)
			{
				prev->next = ptr->next;
			}
			else
			{
				list = ptr->next;
			}
			delete ptr;
			break;
		}
		prev = ptr;
		ptr = ptr->next;
	}
}

tDrumInstrumentParameter *tDrumInstrumentParameterList::FirstElem()
{
	return( list );
}

tDrumInstrumentParameter *tDrumInstrumentParameterList::NextElem( tDrumInstrumentParameter *cur )
{
	if (cur)
	{
		tDrumInstrumentParameter *ptr = GetElem( cur->pitch );
		if (ptr)
			return( ptr->next );
		else
			return( 0 );
	}
	else
	{
		return( 0 );
	}
}

void tDrumInstrumentParameterList::Clear()
{
	tDrumInstrumentParameter *ptr = list;
	while( ptr )
	{
		list = ptr->next;
		delete ptr;
		ptr = list;
	}
}


tEventArray::tEventArray()
{
  nEvents = 0;

  MaxEvents = 0;
  Events = 0;
  Channel = 0;
  ForceChannel = 0;

  Clear();
}


void tEventArray::Clear()
{
  int i;
  for (i = 0; i < nEvents; i++)
    delete Events[i];
  nEvents = 0;

  Name   = 0;
  Copyright = 0;
  Patch  = 0;
  Volume = 0;
  Pan    = 0;
  Reverb = 0;
  Chorus = 0;
  Bank = 0;
  Speed = 0;
  Channel = 1;

  for (i = 0; i < mspModulationSysexParameters; i++)
	ModulationSettings[i] = 0;

  for (i = 0; i < bspBenderSysexParameters; i++)
	BenderSettings[i] = 0;

  for (i = 0; i < cspCAfSysexParameters; i++)
	CAfSettings[i] = 0;

  for (i = 0; i < pspPAfSysexParameters; i++)
	PAfSettings[i] = 0;

  for (i = 0; i < cspCC1SysexParameters; i++)
	CC1Settings[i] = 0;

  for (i = 0; i < cspCC2SysexParameters; i++)
	CC2Settings[i] = 0;

  CC1ControllerNr = 0;
  CC2ControllerNr = 0;

  ReverbType = 0;
  ChorusType = 0;

  for (i = 0; i < rspReverbSysexParameters; i++)
	ReverbSettings[i] = 0;

  for (i = 0; i < cspChorusSysexParameters; i++)
	ChorusSettings[i] = 0;

  PartialReserve = 0;
  MasterVol = 0;
  MasterPan = 0;
  RxChannel = 0;
  RxCAf = 0;
  RxPAf = 0;
  UseForRythm = 0;
  MtcOffset = 0;

  VibRate = 0;
  VibDepth = 0;
  VibDelay = 0;
  Cutoff = 0;
  Resonance = 0;
  EnvAttack = 0;
  EnvDecay = 0;
  EnvRelease = 0;
  BendPitchSens = 0;

  DrumParams.Clear();

  if (Events)
    delete [] Events;
  Events = 0;
  MaxEvents = 0;
}


tEventArray::~tEventArray()
{
  Clear();
}

void tEventArray::Resize()
{
  long i;
  MaxEvents += 20;
  tEvent **tmp = new tEvent * [MaxEvents];
  for (i = 0; i < nEvents; i++)
    tmp[i] = Events[i];
  for (; i < MaxEvents; i++)
    tmp[i] = 0;
  delete [] Events;
  Events = tmp;

  assert(Events);	// crash!!
}


void tEventArray::Put(tEvent *e)
{
  if (nEvents >= MaxEvents)
    Resize();
  Events[nEvents++] = e;
}


static int compare(const void *p1, const void *p2)
{
  tEvent *e1 = *(tEvent **)p1;
  tEvent *e2 = *(tEvent **)p2;
  return e1->Compare(*e2);
}


void tEventArray::Sort()
{
  qsort(Events, nEvents, sizeof(tEvent *), compare);
}



void tEventArray::Cleanup(tUndoBuffer *UndoBuffer)
{
  tEvent *e;
  tControl *c;
  tSysEx *s;
  int i;

  Sort();	// moves all killed events to the end of array

  // clear track defaults
  Name = 0;
  Copyright = 0;
  Speed = 0;
  Volume = 0;
  Pan = 0;
  Reverb = 0;
  Chorus = 0;

  for (i = 0; i < mspModulationSysexParameters; i++)
	ModulationSettings[i] = 0;

  for (i = bspBendPitchControl; i < bspBenderSysexParameters; i++)
	BenderSettings[i] = 0;

  for (i = 0; i < cspCAfSysexParameters; i++)
	CAfSettings[i] = 0;

  for (i = 0; i < pspPAfSysexParameters; i++)
	PAfSettings[i] = 0;

  for (i = 0; i < cspCC1SysexParameters; i++)
	CC1Settings[i] = 0;

  for (i = 0; i < cspCC2SysexParameters; i++)
	CC2Settings[i] = 0;

  CC1ControllerNr = 0;
  CC2ControllerNr = 0;

  ReverbType = 0;
  ChorusType = 0;

  for (i = 0; i < rspReverbSysexParameters; i++)
	ReverbSettings[i] = 0;

  for (i = 0; i < cspChorusSysexParameters; i++)
	ChorusSettings[i] = 0;

  PartialReserve = 0;
  MasterVol = 0;
  MasterPan = 0;
  RxChannel = 0;
  RxCAf = 0;
  RxPAf = 0;
  UseForRythm = 0;
  MtcOffset = 0;

  for (i = 0; i < nEvents; i++)
  {
    if ((e = Events[i])->IsKilled())
    {
      if (UndoBuffer)
      {
	for (int j = i; j < nEvents; j++)
	  UndoBuffer->Put(Events[j]);
      }
      else
      {
	for (int j = i; j < nEvents; j++)
	  delete Events[j];
      }
      nEvents = i;
      break;
    }

    // accept only events having clock == 0 as track defaults
    if (e->Clock != 0)
      continue;

    if (!Name)
      Name = e->IsTrackName();
    if (!Copyright)
      Copyright = e->IsCopyright();
    if (!Speed)
      Speed = e->IsSetTempo();
    if (!MtcOffset)
      MtcOffset = e->IsMtcOffset();
    if ((c = e->IsControl()) != 0)
    {
      switch (c->Control)
      {
        case 0x07: if (!Volume) Volume = c; break;
        case 0x0a: if (!Pan)    Pan    = c; break;
        case 0x5b: if (!Reverb) Reverb = c; break;
        case 0x5d: if (!Chorus) Chorus = c; break;
      }
    }
    if ((s = e->IsSysEx()) != 0) {
	register unsigned char syspar = s->Data[6];

	// The "1n" group of parameters:
	sys_Sysex[5] = 0x10 | sysex_channel(Channel);
	if ( !memcmp( sys_Sysex, s->Data, 6 ) ) {
		if ( !CC1ControllerNr && (syspar == 0x1f) ) {
			CC1ControllerNr = s;
		}
		else if ( !CC2ControllerNr && (syspar == 0x20) ) {
			CC2ControllerNr = s;
		}
		else if ( !RxChannel && (syspar == 0x02) ) {
			RxChannel = s;
		}
		else if ( !RxCAf && (syspar == 0x04) ) {
			RxCAf = s;
		}
		else if ( !RxPAf && (syspar == 0x07) ) {
			RxPAf = s;
		}
		else if ( !UseForRythm && (syspar == 0x15) ) {
			UseForRythm = s;
		}
	}

	// The "2n" group of parameters:
	sys_Sysex[5] = 0x20 | sysex_channel(Channel);
	if ( !memcmp( sys_Sysex, s->Data, 6 ) ) {
		if ( (syspar <= mspModLfo2Tva) && !ModulationSettings[ syspar ] ) {
			ModulationSettings[syspar] = s;
		}
		else if ( (syspar <= (bspBendLfo2Tva+0x10)) && !BenderSettings[ syspar - 0x10] ) {
			BenderSettings[syspar - 0x10] = s;
		}
		else if ( (syspar <= (cspCAfLfo2Tva+0x20)) && !CAfSettings[ syspar - 0x20] ) {
			CAfSettings[syspar - 0x20] = s;
		}
		else if ( (syspar <= (pspPAfLfo2Tva+0x30)) && !PAfSettings[ syspar - 0x30] ) {
			PAfSettings[syspar - 0x30] = s;
		}
		else if ( (syspar <= (cspCC1Lfo2Tva+0x40)) && !CC1Settings[ syspar - 0x40] ) {
			CC1Settings[syspar - 0x40] = s;
		}
		else if ( (syspar <= (cspCC2Lfo2Tva+0x50)) && !CC2Settings[ syspar - 0x50] ) {
			CC2Settings[syspar - 0x50] = s;
		}
	}

	// The "01" group of parameters:
	sys_Sysex[5] = 0x01;
	if ( !memcmp( sys_Sysex, s->Data, 6 ) ) {
		if ( (syspar == 0x30)  && !ReverbType ) {
			ReverbType = s;
		}
		else if ( (syspar == 0x38) && !ChorusType ) {
			ChorusType = s;
		}
		else if ( (syspar >= 0x31) && (syspar <= 0x36) && !ReverbSettings[ syspar - 0x31 ] ) {
			ReverbSettings[ syspar - 0x31 ] = s;
		}
		else if ( (syspar >= 0x39) && (syspar <= 0x3f) && !ChorusSettings[ syspar - 0x39 ] ) {
			ChorusSettings[ syspar - 0x39 ] = s;
		}
		else if ( (syspar == 0x10) && !PartialReserve ) {
			PartialReserve = s;
		}
	}

	// The "00" group of parameters:
	sys_Sysex[5] = 0x00;
	if ( !memcmp( sys_Sysex, s->Data, 6 ) ) {
		if ( (syspar == 0x04) && !MasterVol) {
			MasterVol = s;
		}
		else if ( (syspar == 0x06) && !MasterPan) {
			MasterPan = s;
		}
	}
    }
  }
}


void tEventArray::Length2Keyoff()
{
  int n = nEvents;
  for (int i = 0; i < n; i++)
  {
    tKeyOn *on;
    if ((on = Events[i]->IsKeyOn()) != 0 && on->Length != 0)
    {
      tEvent *of = new tKeyOff(on->Clock + on->Length, on->Channel, on->Key);
      on->Length = 0;
      Put(of);
    }
  }
  Sort();
}



void tEventArray::Keyoff2Length()
{
  int i;
  for (i = 1; i < nEvents; i++)
  {
    tKeyOff *of;
    if ((of = Events[i]->IsKeyOff()) != 0)
    {
      tEvent **e = &Events[i - 1];
      while (e >= Events)
      {
        tKeyOn *on = (*e)->IsKeyOn();
        if (on && on->Key == of->Key && on->Channel == of->Channel && on->Length == 0)
        {
          on->Length = of->Clock - on->Clock;
          if (on->Length <= 0L)
            on->Length = 1;
          of->Kill();
	  break;
	}
	-- e;
      }
    }
  }

  // kill all KeyOn's with non matching KeyOff's
  for (i = 0; i < nEvents; i++)
  {
    tKeyOn *k = Events[i]->IsKeyOn();
    if (k && k->Length <= 0)
      k->Kill();
  }
  Cleanup(0);
}

void tEventArray::Write(tWriteBase &io)
{
  tEvent *e;
  int WrittenBefore;

  Length2Keyoff();
  io.NextTrack();

  // Write copyright notice first (according to spec):
  if (Copyright) Copyright->Write(io);

  // Write MTC offset before any transmittable events (spec)
  if (MtcOffset) MtcOffset->Write(io);

  // Rpn / Nrpn:
  // All these must be written in order (three tControl's in a row)
  if (VibRate) VibRate->Write(io);
  if (VibDepth) VibDepth->Write(io);
  if (VibDelay) VibDelay->Write(io);
  if (Cutoff) Cutoff->Write(io);
  if (Resonance) Resonance->Write(io);
  if (EnvAttack) EnvAttack->Write(io);
  if (EnvDecay) EnvDecay->Write(io);
  if (EnvRelease) EnvRelease->Write(io);
  if (BendPitchSens) BendPitchSens->Write(io);

  tDrumInstrumentParameter *dpar = DrumParams.FirstElem();
  while ( dpar ) {
	int index;
	for (index = drumPitchIndex; index < numDrumParameters; index++)
	{
		if (dpar->Get(index))
			dpar->Get(index)->Write( io );
	}
	dpar = DrumParams.NextElem( dpar );
  }

  // Bank: Must be sure bank is written before program:
  if (Bank) Bank->Write(io);
  if (Patch) Patch->Write(io);

  for (int i = 0; i < nEvents; i++) {
	e = Events[i];
	WrittenBefore = 0;
	if (e->IsControl()) {
		switch (e->IsControl()->Control) {
			// Don't write these again if present as events
			// (should not happen)
			case 0x65: // Rpn Msb
			case 0x64: // Rpn Lsb
			case 0x63: // Nrpn Msb
			case 0x62: // Nrpn Lsb
			case 0x06: // Rpn/Nrpn Data
			case 0x00: // Bank
				WrittenBefore = 1;
				break;
			default:
				WrittenBefore = 0;
		}
	}
	else if (e->IsProgram()) {
		// Should not happen
		WrittenBefore = 1;
	}
	else if (e->IsCopyright() || e->IsMtcOffset()) {
		// Will probably happen
		WrittenBefore = 1;
	}
	if (!WrittenBefore) {
	    	e->Write(io);
	}
  }
  Keyoff2Length();
}



void tEventArray::Read(tReadBase &io)
{
  tEvent *e;
  Channel = 0;
  uchar Msb, Lsb, Data;
  int SpecialEvent;

  Msb = Lsb = Data = 0xff;
  int cha;

  io.NextTrack();
  while ((e = io.Read()) != 0)
  {

    SpecialEvent = 0;
    if (e->IsControl()) {
	switch (e->IsControl()->Control) {
		// Grab Rpn/Nrpn/Bank from file and save them, don't put
		// them into event-array
		case 0x63:
		case 0x65:
			Msb = e->IsControl()->Value; // Rpn/Nrpn Msb
			SpecialEvent = 1;
			break;
		case 0x62:
		case 0x64:
			Lsb = e->IsControl()->Value; // Rpn/Nrpn Lsb
			SpecialEvent = 1;
			break;
		case 0x06:
			Data = e->IsControl()->Value; // Rpn/Nrpn Data
			SpecialEvent = 1;
			cha = e->IsControl()->Channel;
			switch (Msb) {
				case 0x01: // Nrpn
					switch (Lsb) {
						case 0x08:
							VibRate = new tNrpn( cha, Msb, Lsb, Data );
							break;
						case 0x09:
							VibDepth = new tNrpn( cha, Msb, Lsb, Data );
							break;
						case 0x0a:
							VibDelay = new tNrpn( cha, Msb, Lsb, Data );
							break;
						case 0x20:
							Cutoff = new tNrpn( cha, Msb, Lsb, Data );
							break;
						case 0x21:
							Resonance = new tNrpn( cha, Msb, Lsb, Data );
							break;
						case 0x63:
							EnvAttack = new tNrpn( cha, Msb, Lsb, Data );
							break;
						case 0x64:
							EnvDecay = new tNrpn( cha, Msb, Lsb, Data );
							break;
						case 0x66:
							EnvRelease = new tNrpn( cha, Msb, Lsb, Data );
							break;
						default:
							break;
					}
					break;
				case drumPitch:
				case drumTva:
				case drumPan:
				case drumReverb:
				case drumChorus:
					DrumParams.PutParam( new tNrpn( cha, Msb, Lsb, Data ) );
					break;
				case 0x00: // Rpn
					if (Lsb == 0x00) { // Pitch Bend Sensivity
						BendPitchSens = new tRpn( cha, Msb, Lsb, Data );
					}
					break;
				default:
					break;
			}
  			Msb = Lsb = Data = 0xff;
			break;
		case 0x00:
                        if (!Bank)
                        {
			  SpecialEvent = 1;
			  Bank = e->IsControl(); // Bank
                          Bank->Clock = 0;
                        }
			break;
		default:
			SpecialEvent = 0; // Other control
			break;
	}
    }
    else if (e->IsProgram()) {
                if (!Patch)
                {
		  Patch = e->IsProgram();
                  Patch->Clock = 0;
		  SpecialEvent = 1;
                }
    }
    else if (e->IsCopyright()) {
		if (!Copyright)
		{
		  Copyright = e->IsCopyright();
		  // Just make sure clock is zero, then put into event array
		  Copyright->Clock = 0;
		}
    }
    if (!SpecialEvent) {
	    Put(e);
	    if (!Channel && e->IsChannelEvent())
	      Channel = e->IsChannelEvent()->Channel + 1;
    }
  } // while read

  if (!Channel)
    Channel = 1;

  Keyoff2Length();
}


long tEventArray::GetLastClock()
{
  if (!nEvents)
    return 0;
  return Events[nEvents-1]->Clock;
}

int tEventArray::IsEmpty()
{
  return nEvents == 0;
}

long tEventArray::GetFirstClock()
{
  if (nEvents)
    return Events[0]->Clock;
  return LastClock;
}

// ***********************************************************************
// Dialog
// ***********************************************************************

class tTrackDlg : public wxForm
{
  tEventWin *EventWin;
  tTrack *trk;
  char *TrackName;
  tNamedChoice PatchChoice;
  long PatchNr;
  long BankNr;
  int ClearTrack;

 public:
  tTrackDlg::tTrackDlg(tEventWin *w, tTrack *t);
  void EditForm(wxPanel *panel);
  virtual void OnOk();
  virtual void OnCancel();
  virtual void OnHelp();
};


tTrackDlg::tTrackDlg(tEventWin *w, tTrack *t)
  : wxForm( USED_WXFORM_BUTTONS ), PatchChoice("Patch", VoiceNames, &PatchNr)
{
  EventWin = w;
  trk = t;
}


void tTrackDlg::OnCancel()
{
  trk->DialogBox = 0;
  wxForm::OnCancel();
}

void tTrackDlg::OnHelp()
{
#ifndef VMS
        HelpInstance->LoadFile( HelpPathList.FindValidPath("jazz") );
        HelpInstance->KeywordSearch("Trackname, midi channel etc");
#endif
}


void tTrackDlg::OnOk()
{
  trk->DialogBox->GetPosition( &TrackDlgGeo[0], &TrackDlgGeo[1] );
  trk->DialogBox = 0;
  if (ClearTrack) {
	trk->Clear();
  	delete TrackName;
#ifdef wx_msw
	EventWin->Redraw();
#endif
  	wxForm::OnOk();
	return;
  }
  trk->SetName(TrackName);
  delete TrackName;
  PatchChoice.GetValue();
  BankNr = (PatchNr & 0x0000ff00) >> 8;
  PatchNr = PatchNr & 0x000000ff;
  trk->SetBank(BankNr);
  trk->SetPatch(PatchNr);
  if (trk->ForceChannel) {
	tChannelEvent *c;
	tSysEx *s;
	tEventIterator Iterator(trk);
	trk->Sort();
	tEvent *e = Iterator.Range(0, (long unsigned) trk->GetLastClock() + 1);
	while (e) {
	    	if ((c = e->IsChannelEvent()) != 0) {
		  	c = (tChannelEvent *)e->Copy();
		      	c->Channel = trk->Channel - 1;
			trk->Kill(e);
			trk->Put(c);
		}
		else if ((s = e->IsSysEx()) != 0) {
			// Check for sysex that contains channel number
			if ( 	!memcmp( sys_Sysex, s->Data, 5 ) &&
				(s->Data[5] & 0x30)) {
			    s = (tSysEx *) e->Copy();
			    s->Data[5] &= 0xf0;
			    s->Data[5] |= sysex_channel(trk->Channel);
			    trk->Kill(e);
			    trk->Put(s);
			}
		}
		e = Iterator.Next();
	} // while e
	if (trk->VibRate) trk->VibRate->SetCha( trk->Channel - 1 );
	if (trk->VibDepth) trk->VibDepth->SetCha( trk->Channel - 1 );
	if (trk->VibDelay) trk->VibDelay->SetCha( trk->Channel - 1 );
	if (trk->Cutoff) trk->Cutoff->SetCha( trk->Channel - 1 );
	if (trk->Resonance) trk->Resonance->SetCha( trk->Channel - 1 );
	if (trk->EnvAttack) trk->EnvAttack->SetCha( trk->Channel - 1 );
	if (trk->EnvDecay) trk->EnvDecay->SetCha( trk->Channel - 1 );
	if (trk->EnvRelease) trk->EnvRelease->SetCha( trk->Channel - 1 );
	if (trk->BendPitchSens) trk->BendPitchSens->SetCha( trk->Channel - 1 );
	if (trk->Bank) trk->Bank->Channel = trk->Channel - 1;
	if (trk->Patch) trk->Patch->Channel = trk->Channel - 1;
	if (!trk->DrumParams.IsEmpty())
	{
  	    tDrumInstrumentParameter *dpar = trk->DrumParams.FirstElem();
	    while ( dpar ) {
		int index;
		for (index = drumPitchIndex; index < numDrumParameters; index++)
		{
			if (dpar->Get(index))
				dpar->Get(index)->SetCha( trk->Channel - 1 );
		}
		dpar = trk->DrumParams.NextElem( dpar );
 	    }
	}
	trk->Cleanup();
  }
#ifdef wx_msw
  EventWin->Redraw();
#endif
  wxForm::OnOk();
}



void tTrackDlg::EditForm(wxPanel *panel)
{
  PatchNr   = trk->GetPatch() + (trk->GetBank() << 8);
  TrackName = copystring(trk->GetName());
  Add(wxMakeFormString("Trackname:", &TrackName, wxFORM_DEFAULT )); //, 0,0,0, 100, -1));

  Add(wxMakeFormNewLine());
  Add(PatchChoice.mkFormItem(300, 300));
  Add(wxMakeFormNewLine());
  Add(wxMakeFormShort("Channel", &trk->Channel, wxFORM_DEFAULT,
                       new wxList(wxMakeConstraintRange(1.0, 16.0), 0)));
  Add(wxMakeFormNewLine());
  Add(wxMakeFormBool("Force channel number onto all events on track", &trk->ForceChannel));
  ClearTrack = 0;
  Add(wxMakeFormNewLine());
  Add(wxMakeFormBool("Clear track (NB! erase all events, name etc...)", &ClearTrack));

  AssociatePanel(panel);
}


void tTrack::Dialog(wxFrame *parent)
{
  tTrackDlg *dlg;
  if (DialogBox)
  {
    DialogBox->Show(TRUE);
    return;
  }
  DialogBox = new wxDialogBox(parent, "Track Settings", FALSE, TrackDlgGeo[0], TrackDlgGeo[1] );
  dlg = new tTrackDlg( (tEventWin*) parent, this);
  dlg->EditForm(DialogBox);
  DialogBox->Fit();
  DialogBox->Show(TRUE);
}

// ***********************************************************************
// tUndoBuffer
// ***********************************************************************

tUndoBuffer::tUndoBuffer()
{
}

void tUndoBuffer::Clear()
{
  Killed.Clear();
  Copies.nEvents = 0;
  Copies.Clear();
}

void tUndoBuffer::Put(tEvent *e)
{
  if (e->IsKilled())
    Killed.Put(e);
  else
    Copies.Put(e);
}

void tUndoBuffer::Debug()
{
#if 0
  if (Killed.nEvents || Copies.nEvents)
  {
    int i;

    printf("tUndoBuffer.Killed:\n");
    for (i = 0; i < Killed.nEvents; i++)
    {
      tEvent *e = Killed.Events[i];
      printf("%p clk %08lx sta %02x\n", e, e->Clock, (unsigned char)e->Stat);
    }

    printf("tUndoBuffer.Copies:\n");
    for (i = 0; i < Copies.nEvents; i++)
    {
      tEvent *e = Copies.Events[i];
      printf("%p clk %08lx sta %02x\n", e, e->Clock, (unsigned char)e->Stat);
    }
    fflush(stdout);
  }
#endif
}


// ***********************************************************************
// tTrack
// ***********************************************************************


int tTrack::changed = 0;

tTrack::tTrack()
  : tEventArray()
{
  iUndo = 0;
  DialogBox = 0;
  PatchNames = 0;
  State = tsPlay;
  ForceChannel = 1;
}


Bool tTrack::IsDrumTrack()
{
  return Channel == DrumChannel;
}

void tTrack::Merge(tEventArray *t)
{
  for (int i = 0; i < t->nEvents; i++)
    Put(t->Events[i]);
  t->nEvents = 0;
}


void tTrack::MergeRange(tEventArray *other, long FromClock, long ToClock, int Replace)
{
  // Erase destin
  if (Replace)
  {
    tEventIterator Erase(this);
    tEvent *e = Erase.Range(FromClock, ToClock);
    while (e)
    {
      Kill(e);
      e = Erase.Next();
    }
  }

  // Merge Recorded Events
  tEventIterator Copy(other);
  tEvent *e = Copy.Range(FromClock, ToClock);
  while (e)
  {

    #if 0
    {
      tGetMidiBytes mb;
      e->Write(mb);
      printf("ReplaceRange: Clock %ld, Stat %02x, Bytes %d: ", e->Clock, e->Stat, mb.nBytes);
      for (int i = 0; i < mb.nBytes; i++)
	printf("%02x ", mb.Buffer[i]);
      tKeyOn *on = e->IsKeyOn();
      if (on)
	printf("KeyOn, Length = %d", on->Length);
      tKeyOff *of = e->IsKeyOff();
      if (of)
	printf("KeyOFF********");

      putchar('\n');
      fflush(stdout);
    }
    #endif

    Put(e->Copy());
    e = Copy.Next();
  }
  Cleanup();
}


void tTrack::Cleanup()
{
  tEventArray::Cleanup(&UndoBuffers[iUndo]);
}



void tTrack::Undo()
{
  int i;
  tEventArray *Killed, *Copies;
  tUndoBuffer *u;

  u = &UndoBuffers[iUndo];
  Killed = &u->Killed;
  Copies = &u->Copies;

  for (i = 0; i < Killed->nEvents; i++)
  {
    tEvent *e = Killed->Events[i];
    e->UnKill();
    tEventArray::Put(e);
  }
  Killed->nEvents = 0;

  for (i = 0; i < Copies->nEvents; i++)
  {
    tEvent *e = Copies->Events[i];
    e->Kill();
  }
  Copies->nEvents = 0;

  iUndo = (iUndo - 1 + MaxUndo) % MaxUndo;
  tEventArray::Cleanup(0);
}


void tTrack::NewUndoBuffer()
{
  iUndo = (iUndo + 1) % MaxUndo;
  UndoBuffers[iUndo].Clear();
}


void tTrack::Clear()
{
  tEventArray::Clear();
  for (int i = 0; i < MaxUndo; i++)
    UndoBuffers[i].Clear();
  State  = tsPlay;
}

// ----------------------- Copyright ------------------------------------

char *tTrack::GetCopyright()
{
  if (Copyright)
    return (char *)Copyright->Data;
  return "";
}



void tTrack::SetCopyright(char *str)
{
  if (Copyright)
    Kill(Copyright);
  if (str && strlen(str))
    {
      int len = 127;
      if (strlen(str) < len)
        len = strlen( str );
      Put(new tCopyright(0, (uchar *)str, len));
    }
  Cleanup();
}

// ----------------------- Name ------------------------------------

char *tTrack::GetName()
{
  if (Name)
    return (char *)Name->Data;
  return "";
}



void tTrack::SetName(char *str)
{
  if (Name)
    Kill(Name);
  if (strlen(str))
    Put(new tTrackName(0, (uchar *)str, strlen(str)));
  Cleanup();
}

// ------------------------  Volume ------------------------------

int tTrack::GetVolume()
{
  if (Volume)
    return Volume->Value + 1;
  return 0;
}

void tTrack::SetVolume(int Value)
{
  if (Volume)
    Kill(Volume);
  if (Value > 0)
  {
    tEvent *e = new tControl(0, Channel - 1, 0x07, Value - 1);
    Put(e);
    Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  Pan ------------------------------

int tTrack::GetPan()
{
  if (Pan)
    return Pan->Value + 1;
  return 0;
}

void tTrack::SetPan(int Value)
{
  if (Pan)
    Kill(Pan);
  if (Value > 0)
  {
    tEvent *e = new tControl(0, Channel - 1, 0x0a, Value - 1);
    Put(e);
    Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  Reverb ------------------------------

int tTrack::GetReverb()
{
  if (Reverb)
    return Reverb->Value + 1;
  return 0;
}

void tTrack::SetReverb(int Value)
{
  if (Reverb)
    Kill(Reverb);
  if (Value > 0)
  {
    tEvent *e = new tControl(0, Channel - 1, 0x5B, Value - 1);
    Put(e);
    Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  Chorus ------------------------------

int tTrack::GetChorus()
{
  if (Chorus)
    return Chorus->Value + 1;
  return 0;
}

void tTrack::SetChorus(int Value)
{
  if (Chorus)
    Kill(Chorus);
  if (Value > 0)
  {
    tEvent *e = new tControl(0, Channel - 1, 0x5D, Value - 1);
    Put(e);
    Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  Bank ------------------------------

int tTrack::GetBank()
{
  if (Bank)
    return Bank->Value;
  return 0;
}

void tTrack::SetBank(int Value)
{
  if (Bank) {
	delete Bank;
	Bank = 0;
  }
  if (Value >= 0)
  {
    Bank = new tControl(0, Channel - 1, BankControlNumber, Value);
    Midi->OutNow(Bank);
  }
}

// ------------------------  Patch ------------------------------

int tTrack::GetPatch()
{
  if (Patch)
    return Patch->Program + 1;
  return 0;
}

void tTrack::SetPatch(int PatchNr)
{
  if (Patch) {
    delete Patch;
    Patch = 0;
  }
  if (PatchNr > 0)
  {
    Patch = new tProgram(0, Channel - 1, PatchNr - 1);
    Midi->OutNow(Patch);
  }
}

// ------------------------  VibRate ------------------------------

int tTrack::GetVibRate()
{
  if (VibRate)
    return VibRate->GetVal() + 1;
  return 0;
}

void tTrack::SetVibRate(int Value)
{
  if (VibRate) {
    delete VibRate;
    VibRate = 0;
  }
  if (Value > 0)
  {
    VibRate = new tNrpn( Channel - 1, 0x01, 0x08, Value - 1 );
    Midi->OutNow( VibRate );
  }
}

// ------------------------  VibDepth ------------------------------

int tTrack::GetVibDepth()
{
  if (VibDepth)
    return VibDepth->GetVal() + 1;
  return 0;
}

void tTrack::SetVibDepth(int Value)
{
  if (VibDepth) {
    delete VibDepth;
    VibDepth = 0;
  }
  if (Value > 0)
  {
	VibDepth = new tNrpn( Channel - 1, 0x01, 0x09, Value - 1 );
	Midi->OutNow( VibDepth );
  }
}

// ------------------------  VibDelay ------------------------------

int tTrack::GetVibDelay()
{
  if (VibDelay)
    return VibDelay->GetVal() + 1;
  return 0;
}

void tTrack::SetVibDelay(int Value)
{
  if (VibDelay) {
    delete VibDelay;
    VibDelay = 0;
  }
  if (Value > 0)
  {
	VibDelay = new tNrpn( Channel - 1, 0x01, 0x0a, Value - 1 );
	Midi->OutNow( VibDelay );
  }
}

// ------------------------  Cutoff ------------------------------

int tTrack::GetCutoff()
{
  if (Cutoff)
    return Cutoff->GetVal() + 1;
  return 0;
}

void tTrack::SetCutoff(int Value)
{
  if (Cutoff) {
    delete Cutoff;
    Cutoff = 0;
  }
  if (Value > 0)
  {
	Cutoff = new tNrpn( Channel - 1, 0x01, 0x20, Value - 1 );
	Midi->OutNow( Cutoff );
  }
}

// ------------------------  Resonance ------------------------------

int tTrack::GetResonance()
{
  if (Resonance)
    return Resonance->GetVal() + 1;
  return 0;
}

void tTrack::SetResonance(int Value)
{
  if (Resonance) {
	delete Resonance;
	Resonance = 0;
  }
  if (Value > 0)
  {
	Resonance = new tNrpn( Channel - 1, 0x01, 0x21, Value - 1 );
	Midi->OutNow( Resonance );
  }
}

// ------------------------  EnvAttack ------------------------------

int tTrack::GetEnvAttack()
{
  if (EnvAttack)
    return EnvAttack->GetVal() + 1;
  return 0;
}

void tTrack::SetEnvAttack(int Value)
{
  if (EnvAttack) {
	delete EnvAttack;
	EnvAttack = 0;
  }
  if (Value > 0)
  {
	EnvAttack = new tNrpn( Channel - 1, 0x01, 0x63, Value - 1 );
	Midi->OutNow( EnvAttack );
  }
}

// ------------------------  EnvDecay ------------------------------

int tTrack::GetEnvDecay()
{
  if (EnvDecay)
    return EnvDecay->GetVal() + 1;
  return 0;
}

void tTrack::SetEnvDecay(int Value)
{
  if (EnvDecay) {
	delete EnvDecay;
	EnvDecay = 0;
  }
  if (Value > 0)
  {
	EnvDecay = new tNrpn( Channel - 1, 0x01, 0x64, Value - 1 );
	Midi->OutNow( EnvDecay );
  }
}

// ------------------------  EnvRelease ------------------------------

int tTrack::GetEnvRelease()
{
  if (EnvRelease)
    return EnvRelease->GetVal() + 1;
  return 0;
}

void tTrack::SetEnvRelease(int Value)
{
  if (EnvRelease) {
	delete EnvRelease;
	EnvRelease = 0;
  }
  if (Value > 0)
  {
	EnvRelease = new tNrpn( Channel - 1, 0x01, 0x66, Value - 1 );
	Midi->OutNow( EnvRelease );
  }
}

// ------------------------  DrumParam ------------------------------

int tTrack::GetDrumParam( int pitch, int index )
{
  if (!DrumParams.IsEmpty())
  {
    tNrpn *par = DrumParams.GetParam( pitch, index );
    if (par)
      return( par->GetVal() + 1 );
  }
  return 0;
}

void tTrack::SetDrumParam(int pitch, int index, int Value)
{
  DrumParams.DelParam( pitch, index );
  if (Value > 0)
  {
	DrumParams.PutParam( new tNrpn( Channel - 1, drumIndex2Param( index ), pitch, Value - 1 ) );
	Midi->OutNow( DrumParams.GetParam( pitch, index ) );
  }
}

// ------------------------  BendPitchSens ------------------------------

int tTrack::GetBendPitchSens()
{
  if (BendPitchSens)
    return BendPitchSens->GetVal() + 1;
  return 0;
}

void tTrack::SetBendPitchSens(int Value)
{
  if (BendPitchSens) {
	delete BendPitchSens;
	BendPitchSens = 0;
  }
  if (Value > 0)
  {
	BendPitchSens = new tRpn( Channel - 1, 0x00, 0x00, Value - 1 );
	Midi->OutNow( BendPitchSens );
  }
}

// ------------------------  Modulation Sysex ------------------------------

int tTrack::GetModulationSysex( int msp )
{
  if (ModulationSettings[ msp ])
    return ModulationSettings[ msp ]->Data[7] + 1;
  return 0;
}

void tTrack::SetModulationSysex(int msp, int Value)
{
  if (ModulationSettings[ msp ])
    Kill(ModulationSettings[ msp ]);
  if (Value > 0)
  {
    uchar val = Value-1;
    uchar *mess = SysExDT1( 0x40, 0x20 | sysex_channel(Channel), msp, 1, &val);
    tEvent *e = new tSysEx(0, mess, 10);
    delete mess;
    Put(e);
    Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  Bender Sysex ------------------------------

int tTrack::GetBenderSysex( int bsp )
{
  if (BenderSettings[ bsp ])
    return BenderSettings[ bsp ]->Data[7] + 1;
  return 0;
}

void tTrack::SetBenderSysex(int bsp, int Value)
{
  if (BenderSettings[ bsp ])
    Kill(BenderSettings[ bsp ]);
  if (Value > 0)
  {
    uchar val =  Value-1;
    uchar *mess = SysExDT1( 0x40, 0x20 | sysex_channel(Channel), bsp + 0x10, 1, &val);
    tEvent *e = new tSysEx(0, mess, 10);
    delete mess;
    Put(e);
    Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  CAf Sysex ------------------------------

int tTrack::GetCAfSysex( int csp )
{
  if (CAfSettings[ csp ])
    return CAfSettings[ csp ]->Data[7] + 1;
  return 0;
}

void tTrack::SetCAfSysex(int csp, int Value)
{
  if (CAfSettings[ csp ])
    Kill(CAfSettings[ csp ]);
  if (Value > 0)
  {
    uchar val = Value-1;
    uchar *mess = SysExDT1( 0x40, 0x20 | sysex_channel(Channel), csp + 0x20, 1, &val);
    tEvent *e = new tSysEx(0, mess, 10);
    delete mess;
    Put(e);
    Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  PAf Sysex ------------------------------

int tTrack::GetPAfSysex( int psp )
{
  if (PAfSettings[ psp ])
    return PAfSettings[ psp ]->Data[7] + 1;
  return 0;
}

void tTrack::SetPAfSysex(int psp, int Value)
{
  if (PAfSettings[ psp ])
    Kill(PAfSettings[ psp ]);
  if (Value > 0)
  {
    uchar val = Value-1;
    uchar *mess = SysExDT1( 0x40, 0x20 | sysex_channel(Channel), psp + 0x30, 1, &val);
    tEvent *e = new tSysEx(0, mess, 10);
    delete mess;
    Put(e);
    Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  CC1 Sysex ------------------------------

int tTrack::GetCC1Sysex( int csp )
{
  if (CC1Settings[ csp ])
    return CC1Settings[ csp ]->Data[7] + 1;
  return 0;
}

void tTrack::SetCC1Sysex(int csp, int Value)
{
  if (CC1Settings[ csp ])
    Kill(CC1Settings[ csp ]);
  if (Value > 0)
  {
    uchar val = Value-1;
    uchar *mess = SysExDT1( 0x40, 0x20 | sysex_channel(Channel), csp + 0x40, 1, &val );
    tEvent *e = new tSysEx(0, mess, 10);
    delete mess;
    Put(e);
    Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  CC2 Sysex ------------------------------

int tTrack::GetCC2Sysex( int csp )
{
  if (CC2Settings[ csp ])
    return CC2Settings[ csp ]->Data[7] + 1;
  return 0;
}

void tTrack::SetCC2Sysex(int csp, int Value)
{
  if (CC2Settings[ csp ])
    Kill(CC2Settings[ csp ]);
  if (Value > 0)
  {
    uchar val = Value-1;
    uchar *mess = SysExDT1( 0x40, 0x20 | sysex_channel(Channel), csp + 0x50, 1, &val );
    tEvent *e = new tSysEx(0, mess, 10);
    delete mess;
    Put(e);
    Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  CC1ControllerNr Sysex ------------------------------

int tTrack::GetCC1ControllerNr()
{
  if (CC1ControllerNr)
    return CC1ControllerNr->Data[7] + 1;
  return 0;
}

void tTrack::SetCC1ControllerNr(int Value)
{
  if (CC1ControllerNr)
    Kill(CC1ControllerNr);
  if (Value > 0)
  {
    uchar val = Value-1;
    uchar *mess = SysExDT1( 0x40, 0x10 | sysex_channel(Channel), 0x1f, 1, &val );
    tEvent *e = new tSysEx(0, mess, 10);
    delete mess;
    Put(e);
    Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  CC2ControllerNr Sysex ------------------------------

int tTrack::GetCC2ControllerNr()
{
  if (CC2ControllerNr)
    return CC2ControllerNr->Data[7] + 1;
  return 0;
}

void tTrack::SetCC2ControllerNr(int Value)
{
  if (CC2ControllerNr)
    Kill(CC2ControllerNr);
  if (Value > 0)
  {
    uchar val = Value-1;
    uchar *mess = SysExDT1( 0x40, 0x10 | sysex_channel(Channel), 0x20, 1, &val );
    tEvent *e = new tSysEx(0, mess, 10);
    delete mess;
    Put(e);
    Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  Reverb Type ------------------------------

int tTrack::GetReverbType()
{
  if (ReverbType)
    return ReverbType->Data[7] + 1;
  return 0;
}

void tTrack::SetReverbType(int Value)
{
  if (ReverbType)
    Kill(ReverbType);
  if (Value > 0)
  {
    uchar val = Value-1;
    uchar *mess = SysExDT1( 0x40, 0x01, 0x30, 1, &val );
    tEvent *e = new tSysEx(0, mess, 10);
    delete mess;
    Put(e);
    if (UseReverbMacro)
	Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  Chorus Type ------------------------------

int tTrack::GetChorusType()
{
  if (ChorusType)
    return ChorusType->Data[7] + 1;
  return 0;
}

void tTrack::SetChorusType(int Value)
{
  if (ChorusType)
    Kill(ChorusType);

  if (Value > 0)
  {
    uchar val = Value-1;
    uchar *mess = SysExDT1( 0x40, 0x01, 0x38, 1, &val );
    tEvent *e = new tSysEx(0, mess, 10);
    delete mess;
    Put(e);
    if (UseChorusMacro)
    	Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  Reverb Parameters Sysex ------------------------------

int tTrack::GetRevSysex( int rsp )
{
  if (ReverbSettings[ rsp ])
    return ReverbSettings[ rsp ]->Data[7] + 1;
  return 0;
}

void tTrack::SetRevSysex(int rsp, int Value)
{
  if (ReverbSettings[ rsp ])
    Kill(ReverbSettings[ rsp ]);

  if (Value > 0)
  {
    uchar val = Value-1;
    uchar *mess = SysExDT1( 0x40, 0x01, rsp + 0x31, 1, &val);
    tEvent *e = new tSysEx(0, mess, 10);
    delete mess;
    Put(e);
    if (!UseReverbMacro)
    	Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  Chorus Parameters Sysex ------------------------------

int tTrack::GetChoSysex( int csp )
{
  if (ChorusSettings[ csp ])
    return ChorusSettings[ csp ]->Data[7] + 1;
  return 0;
}

void tTrack::SetChoSysex(int csp, int Value)
{
  if (ChorusSettings[ csp ])
    Kill(ChorusSettings[ csp ]);

  if (Value > 0)
  {
    uchar val = Value-1;
    uchar *mess = SysExDT1( 0x40, 0x01, csp + 0x39, 1, &val );
    tEvent *e = new tSysEx(0, mess, 10);
    delete mess;
    Put(e);
    if (!UseChorusMacro)
    	Midi->OutNow(e);
  }
  Cleanup();
}


// ------------------------  Partial Reserve ------------------------------

int tTrack::GetPartRsrv( int chan )
{
  if (PartialReserve)
    return PartialReserve->Data[7 + sysex_channel(chan)] + 1;
  return 0;
}

void tTrack::SetPartRsrv(uchar *rsrv)
{
  if (PartialReserve)
    Kill(PartialReserve);

  if (rsrv) {
    uchar *mess = SysExDT1( 0x40, 0x01, 0x10, 16, rsrv );
    tEvent *e = new tSysEx(0, mess, 25);
    delete mess;
    Put(e);
    Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  Master Volume ------------------------------

int tTrack::GetMasterVol()
{
  if (MasterVol)
    return MasterVol->Data[7] + 1;
  return 0;
}

void tTrack::SetMasterVol(int Value)
{
  if (MasterVol)
    Kill(MasterVol);
  if (Value > 0)
  {
    uchar val = Value-1;
    uchar *mess = SysExDT1( 0x40, 0x00, 0x04, 1, &val );
    tEvent *e = new tSysEx(0, mess, 10);
    delete mess;
    Put(e);
    Midi->OutNow(e);
  }
  Cleanup();
}


// ------------------------  Master Pan ------------------------------

int tTrack::GetMasterPan()
{
  if (MasterPan)
    return MasterPan->Data[7] + 1;
  return 0;
}

void tTrack::SetMasterPan(int Value)
{
  if (MasterPan)
    Kill(MasterPan);
  if (Value > 0)
  {
    uchar val = Value-1;
    uchar *mess = SysExDT1( 0x40, 0x00, 0x06, 1, &val );
    tEvent *e = new tSysEx(0, mess, 10);
    delete mess;
    Put(e);
    Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  Mode Sysex ------------------------------

int tTrack::GetModeSysex( int param )
{
  switch (param) {
	case mspRxChannel:
		if (RxChannel)
			return RxChannel->Data[7] + 1;
		return 0;
	case mspRxCAf:
		if (RxCAf)
			return RxCAf->Data[7] + 1;
		return 0;
	case mspRxPAf:
		if (RxPAf)
			return RxPAf->Data[7] + 1;
		return 0;
	case mspUseForRythm:
		if (UseForRythm)
			return UseForRythm->Data[7] + 1;
		return 0;
  }
  return 0;
}

void tTrack::SetModeSysex(int param, int Value)
{
  switch (param) {
	case mspRxChannel:
		if (RxChannel)
			Kill(RxChannel);
		break;
	case mspRxCAf:
		if (RxCAf)
			Kill(RxCAf);
		break;
	case mspRxPAf:
		if (RxPAf)
			Kill(RxPAf);
		break;
	case mspUseForRythm:
		if (UseForRythm)
			Kill(UseForRythm);
		break;
  }

  if (Value > 0) {
    uchar val = Value - 1;
    uchar *mess = SysExDT1( 0x40, 0x10 | sysex_channel(Channel), param, 1, &val );
    tEvent *e = new tSysEx(0, mess, 10);
    delete mess;
    Put(e);
    Midi->OutNow(e);
  }
  Cleanup();
}

// ------------------------  Mtc offset (Time Code Offset) ------------------------------

tMtcTime* tTrack::GetMtcOffset()
{
  if (MtcOffset)
    return( new tMtcTime( MtcOffset ) );
  return( new tMtcTime( 0L, Mtc30Ndf ) );
}

void tTrack::SetMtcOffset(tMtcTime* mtc)
{
  if (MtcOffset)
    Kill(MtcOffset);
  if (mtc)
  {
    tEvent *e = mtc->ToOffset();
    Put(e);
  }
  Cleanup();
}

// ------------------------  Speed ------------------------------

int tTrack::GetSpeed()
{
  if (Speed)
    return Speed->GetBPM();
  return 100;
}


void tTrack::SetSpeed(int bpm)
{
  tEvent *e = new tSetTempo(0, bpm);
  if (Speed)
    Kill(Speed);
  Put(e);
  Midi->OutNow(e);
  Cleanup();
}


// ------------------------- State ----------------------------------


char *tTrack::GetStateChar()
{
  switch (State)
  {
    case tsPlay: return "P";
    case tsMute: return "M";
    case tsSolo: return "S";
  }
  return "?";
}

void tTrack::SetState(int NewState)
{
  State = NewState % 3;
}

void tTrack::ToggleState(int Direction)
{
  State = (State + Direction + 3) % 3;
}

// ------------------------- Channel ---------------------------

void tTrack::SetChannel(int NewChannel)
{
  Channel = NewChannel;
}


