MIDI Recording Example
From BuzzWiki
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;
}
}
}
}
