midi: add basic polyphony

Timing is still broken/inconsistent between emulators and I don't yet
understand why.
This commit is contained in:
Zack Buhman 2023-06-27 18:32:55 +00:00
parent 778138971f
commit 9b57987717
3 changed files with 82 additions and 16 deletions

View File

@ -42,7 +42,7 @@ midi_note_to_oct_fns(const int8_t midi_note)
// maximum delay of 3258 days
using fp48_16 = fp<uint64_t, uint64_t, 16>;
constexpr uint8_t tactl = 7; // F/128
constexpr uint8_t tactl = 6; // F/128
constexpr uint8_t tima = 0xfe;
constexpr fp48_16 increment_ms{2902, 32394}; // 2902.494293212890625
@ -122,6 +122,34 @@ void auto_vector_1(void)
timer_fired = 1;
}
struct vs {
int8_t slot_ix;
int8_t count;
};
static int32_t slot_alloc;
static struct vs voice_slot[16][128];
#pragma GCC push_options
#pragma GCC optimize ("unroll-loops")
int8_t alloc_slot()
{
for (int i = 0; i < 32; i++) {
uint32_t bit = (1 << i);
if ((slot_alloc & bit) == 0) {
slot_alloc |= bit;
return i;
}
}
return -1;
}
#pragma gcc pop_options
void free_slot(int8_t i)
{
slot_alloc &= ~(1 << i);
}
void midi_step()
{
const uint32_t sine_start = reinterpret_cast<uint32_t>(&_sine_start);
@ -164,7 +192,13 @@ void midi_step()
switch (midi_event.type) {
case midi::midi_event_t::type_t::note_on:
{
scsp_slot& slot = scsp.reg.slot[0];
struct vs& v = voice_slot[midi_event.data.note_on.channel][midi_event.data.note_on.note];
if (v.slot_ix == -1) {
v.slot_ix = alloc_slot();
}
v.count++;
scsp_slot& slot = scsp.reg.slot[v.slot_ix];
scsp.ram.u32[3] = midi_event.data.note_on.note;
@ -179,16 +213,25 @@ void midi_step()
slot.LFO = 0;
slot.MIXER = MIXER__DISDL(0b110);
kyonex = 1;
if (v.count == 1)
kyonex = 1;
}
break;
case midi::midi_event_t::type_t::note_off:
{
scsp_slot& slot = scsp.reg.slot[0];
slot.LOOP = 0;
scsp.reg.slot[0].SA |= SA__KYONEX;
struct vs& v = voice_slot[midi_event.data.note_on.channel][midi_event.data.note_on.note];
if (v.slot_ix < 0) error();
kyonex = 1;
v.count -= 1;
if (v.count == 0) {
free_slot(v.slot_ix);
v.slot_ix = -1;
scsp_slot& slot = scsp.reg.slot[v.slot_ix];
slot.LOOP = 0;
scsp.reg.slot[0].SA |= SA__KYONEX;
kyonex = 1;
}
}
break;
default:
@ -227,6 +270,14 @@ void init_midi()
state.delta_time_ms = fp48_16{0};
state.midi.tempo = 500000; // default tempo
slot_alloc = 0;
for (int j = 0; j < 16; j++) {
for (int i = 0; i < 128; i++) {
voice_slot[j][i].slot_ix = -1;
voice_slot[j][i].count = 0;
}
}
}
void main()

View File

@ -103,10 +103,7 @@ int parse(uint8_t const * start)
switch (mtrk_event.event.type) {
case midi::event_t::type_t::midi:
std::cout << " midi: " << '\n';
dump_midi(mtrk_event.event.event.midi);
dump_midi(mtrk_event.event.event.midi);
break;
case midi::event_t::type_t::sysex:
std::cout << " sysex: " << '\n';
@ -120,7 +117,7 @@ int parse(uint8_t const * start)
}
assert(buf - track_start == track_length);
std::cout << "trailing/unparsed data: " << buf - track_start << '\n';
std::cout << "trailing/unparsed data: " << track_length - (buf - track_start) << '\n';
}
return 0;
}

View File

@ -383,17 +383,35 @@ def parse_track(buf):
events.append(event)
return buf, Track(events)
_slots = set()
def simulate_note(ix, ev):
if type(ev.event) is NoteOn:
print(repr(ev.event))
_slots.add((ev.event.channel, ev.event.note))
assert len(_slots) <= 32, (hex(ix))
if type(ev.event) is NoteOff:
print(repr(ev.event))
try:
_slots.remove((ev.event.channel, ev.event.note))
except:
print("ix", hex(ix))
raise
def parse_file(buf):
buf, header = parse_header(buf)
print(header)
#print(header)
assert header.ntrks > 0
tracks = []
for track_num in range(header.ntrks):
buf, track = parse_track(buf)
tracks.append(track)
print(f"track {track_num}:")
for event in track.events:
print(' ' + repr(event))
#print(f"track {track_num}:")
for i, event in enumerate(track.events):
#simulate_note(i, event)
print(event)
print("remaining data:", len(buf))
import sys