// allocated track:
// 1. same LastNote if note not in pattern
// 2. first free with no note in pattern
// 3. first free
// 4. track with oldest high note (C6+)
// 5. track with oldest mid note (C3+)
// 6. track with oldest note

int mi::AllocateTrack(CSequence *pseq, int note)
{
	
	for (int c = 0; c < numTracks; c++)
	{
		if (Tracks[c].LastNote == note)
		{
			if (pseq != NULL)
			{
				byte *pdata = (byte *)pCB->GetPlayingRow(pseq, 2, c);
				if (*pdata == NOTE_NO)
					return c;
				
			}
			
		}
	}

	int best = -1;


	for (c = 0; c < numTracks; c++)
	{
		if (Tracks[c].Voices[Tracks[c].FreeVoice^1].State == CVoice::inactive)
		{
			if (pseq != NULL)
			{
				byte *pdata = (byte *)pCB->GetPlayingRow(pseq, 2, c);
				if (*pdata == NOTE_NO)
					return c;
				
			}

			if (best == -1)
				best = c;
		}
	}

	if (best != -1)
		return best;

	// if we got here it means all voices are active
	int oldt = -1;
	
	// check high
	for (c = 0; c < numTracks; c++)
	{
		if (Tracks[c].LastNote >= ((6 << 4) + 1))		// >= C6
		{
			int at = Tracks[c].Voices[Tracks[c].FreeVoice^1].ActiveTime;
			if (at > oldt)
			{
				oldt = at;
				best = c;
			}
		}
		
	}

	if (best != -1)
		return best;

	// check mid
	for (c = 0; c < numTracks; c++)
	{
		if (Tracks[c].LastNote >= ((3 << 4) + 1))		// >= C3
		{
			int at = Tracks[c].Voices[Tracks[c].FreeVoice^1].ActiveTime;
			if (at > oldt)
			{
				oldt = at;
				best = c;
			}
		}
		
	}
	
	if (best != -1)
		return best;

	// check low
	for (c = 0; c < numTracks; c++)
	{
		int at = Tracks[c].Voices[Tracks[c].FreeVoice^1].ActiveTime;
		if (at > oldt)
		{
			oldt = at;
			best = c;
		}
	}
		

	assert(best != -1);

	return best;

}

void miex::MidiControlChange(int const ctrl, int const channel, int const value)
{
	// TODO: check channel

	if (ctrl != 64)	// 64 = sustain pedal
		return;


	if (value < 64)
	{
		CSequence *pseq;

		int stateflags = pmi->pCB->GetStateFlags();
		
		if (stateflags & SF_PLAYING && stateflags & SF_RECORDING)
			pseq = pmi->pCB->GetPlayingSequence(pmi->ThisMachine);
		else 
			pseq = NULL;

		int notedelay = pmi->pMasterInfo->PosInTick * 24 / pmi->pMasterInfo->SamplesPerTick;
		
		pmi->SustainPedal = false;
		for (int c = 0; c < pmi->numTracks; c++)
		{
			if (pmi->Tracks[c].Note != NOTE_NO && pmi->Tracks[c].Sustained)
			{
				pmi->Tracks[c].Sustained = false;
				pmi->MidiNoteOff(c, pseq, notedelay);
			}
		}
	}
	else
	{
		pmi->SustainPedal = true;
	}



}


void mi::MidiNoteOff(int c, CSequence *pseq, int notedelay)
{
	Tracks[c].NoteOff();

	if (pseq != NULL)
	{
		byte *pdata = (byte *)pCB->GetPlayingRow(pseq, 2, c);

		if (notedelay > 0)
		{
			if (pdata[0] != NOTE_NO)
			{
				if (pdata[2] == 0x0d)
				{
					int ondelay = pdata[3] >> 1;
					pdata[2] = 0x0b;
					pdata[3] = (ondelay << 4) | (notedelay >> 1);
				}
				else
				{
					// ondelay = 0
					pdata[2] = 0x0b;
					pdata[3] = (notedelay >> 1);
				}
			}						
			else
			{
				pdata[0] = NOTE_OFF;
				pdata[2] = 0x0d;
				pdata[3] = (byte)notedelay;
			}
		}
		else
		{
			pdata[0] = NOTE_OFF;
		}


	}

}


void mi::MidiNote(int const channel, int const value, int const velocity)
{
	// TODO: check channel

	if (value / 12 > 9)
         return;	

	byte n = (((value / 12)-1) << 4) | ((value % 12) + 1);

	CSequence *pseq;

	int stateflags = pCB->GetStateFlags();
	
	if (stateflags & SF_PLAYING && stateflags & SF_RECORDING)
		pseq = pCB->GetPlayingSequence(ThisMachine);
	else 
		pseq = NULL;


	int notedelay = pMasterInfo->PosInTick * 24 / pMasterInfo->SamplesPerTick;

	if (velocity > 0)
	{
		int c = AllocateTrack(pseq, n);

		Tracks[c].Note = n;
		Tracks[c].Velocity = velocity;
		Tracks[c].NoteOn();

		if (pseq != NULL)
		{
			byte *pdata = (byte *)pCB->GetPlayingRow(pseq, 2, c);
			pdata[0] = n;
			pdata[1] = velocity;
			
			if (notedelay > 0)
			{
				pdata[2] = 0x0d;
				pdata[3] = (byte)notedelay;
			}
		}
	

	}
	else
	{
		for (int c = 0; c < numTracks; c++)
		{
			if (Tracks[c].Note == n)
			{
				if (SustainPedal)
					Tracks[c].Sustained = true;
				else
					MidiNoteOff(c, pseq, notedelay);

				break;
			}

			
		}
	}


}
