MIDI Recording Example

From Jeskola Buzz Wiki
Jump to: navigation, search

Oskari on MIDI in new buzz 2009:

If you don't enable MIDI Filtering, all midi input is sent to all machines. It won't work if you have more than one machine. MIDI Filtering + Master Keyboard Mode should be considered the default MIDI mode in the new Buzz. The option to disable them is there for backward compatibility. Developers should change their machines to accept all midi input by default if these options are enabled (pCB->GetOption).

Older Stuff by Oskari:

// 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;
			}

			
		}
	}


}