From 99fd9f2e363c41bae7492193c86d041ef97dac2e Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 19 Aug 2023 01:05:14 -0500 Subject: [PATCH 1/4] Fixed issue with disabled channels affecting instruments tab. Increased instruments tab polyphony to 16 --- src/inst.c | 11 ++++++----- src/sound.c | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/inst.c b/src/inst.c index af107cd..9dbd09f 100644 --- a/src/inst.c +++ b/src/inst.c @@ -39,7 +39,7 @@ static struct window_template inst_list_template = { }; static unsigned char valid_insts[MAX_INSTRUMENTS]; -static int cnote[8]; +static int cnote[16]; int note_from_key(int key, BOOL shift) { if (key == VK_OEM_PERIOD) return 0x48; // continue @@ -69,7 +69,7 @@ static void draw_square(int note, HBRUSH brush) { } static void note_off(int note) { - for (int ch = 0; ch < 8; ch++) + for (int ch = 0; ch < 16; ch++) if (state.chan[ch].samp_pos >= 0 && cnote[ch] == note) { state.chan[ch].note_release = 0; state.chan[ch].next_env_state = ENV_STATE_KEY_OFF; @@ -83,9 +83,9 @@ static void note_on(int note, int velocity) { int inst = valid_insts[sel]; int ch; - for (ch = 0; ch < 8; ch++) + for (ch = 0; ch < 16; ch++) if (state.chan[ch].samp_pos < 0) break; - if (ch == 8) return; + if (ch == 16) return; cnote[ch] = note; struct channel_state *c = &state.chan[ch]; set_inst(&state, c, inst); @@ -183,6 +183,7 @@ LRESULT CALLBACK InstrumentsWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM switch (uMsg) { case WM_CREATE: { prev_chmask = chmask; + chmask = 0xFFFF; WPARAM fixed = (WPARAM)fixed_font(); char buf[40]; @@ -229,7 +230,7 @@ LRESULT CALLBACK InstrumentsWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM start_playing(); timer_speed = 0; memset(&state.chan, 0, sizeof state.chan); - for (int ch = 0; ch < 8; ch++) { + for (int ch = 0; ch < 16; ch++) { state.chan[ch].samp_pos = -1; } diff --git a/src/sound.c b/src/sound.c index 016d7c7..7678b6f 100644 --- a/src/sound.c +++ b/src/sound.c @@ -9,7 +9,7 @@ int mixrate = 44100; int bufsize = 2205; -int chmask = 255; +int chmask = 0xFF; int timer_speed = 500; HWAVEOUT hwo; static BOOL song_playing = FALSE; From 0e577837da4febb7e30f3b8060f0f3f93899c2b9 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 20 Aug 2023 19:50:52 -0500 Subject: [PATCH 2/4] Added channel playback history so the oldest note is reused when the polyphony maxes out --- src/inst.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/src/inst.c b/src/inst.c index 9dbd09f..3022467 100644 --- a/src/inst.c +++ b/src/inst.c @@ -40,6 +40,29 @@ static struct window_template inst_list_template = { static unsigned char valid_insts[MAX_INSTRUMENTS]; static int cnote[16]; +static struct history { + struct history *prev; + struct history *next; +} channel_order[16] = { + // TODO: Construct this on tab init so the polyphony can change from one location. + { &channel_order[15], &channel_order[1] }, + { &channel_order[0] , &channel_order[2] }, + { &channel_order[1], &channel_order[3] }, + { &channel_order[2], &channel_order[4] }, + { &channel_order[3], &channel_order[5] }, + { &channel_order[4], &channel_order[6] }, + { &channel_order[5], &channel_order[7] }, + { &channel_order[6], &channel_order[8] }, + { &channel_order[7], &channel_order[9] }, + { &channel_order[8], &channel_order[10] }, + { &channel_order[9], &channel_order[11] }, + { &channel_order[10], &channel_order[12] }, + { &channel_order[11], &channel_order[13] }, + { &channel_order[12], &channel_order[14] }, + { &channel_order[13], &channel_order[15] }, + { &channel_order[14], &channel_order[0] }, +}; +struct history* oldest_chan = channel_order; int note_from_key(int key, BOOL shift) { if (key == VK_OEM_PERIOD) return 0x48; // continue @@ -68,11 +91,47 @@ static void draw_square(int note, HBRUSH brush) { ReleaseDC(insttest, hdc); } +// Sets the channel as being the latest one that's been played. +static void set_latest_channel(int ch) { + if (&channel_order[ch] == oldest_chan) { + oldest_chan = oldest_chan->next; + } else { + // Remove this item from the linked list. + if (channel_order[ch].prev && channel_order[ch].next) { + channel_order[ch].prev->next = channel_order[ch].next; + channel_order[ch].next->prev = channel_order[ch].prev; + } + // Move it to the end of the linked list + channel_order[ch].next = oldest_chan ? oldest_chan : (oldest_chan = &channel_order[ch]); + channel_order[ch].prev = oldest_chan->prev ? oldest_chan->prev : (oldest_chan->prev = &channel_order[ch]); + oldest_chan->prev->next = &channel_order[ch]; + oldest_chan->prev = &channel_order[ch]; + } +} + +// Sets channel as the oldest one to have been played. +static void set_oldest_channel(int ch) { + if (&channel_order[ch] != oldest_chan) { + // Remove this item from the linked list. + if (channel_order[ch].prev && channel_order[ch].next) { + channel_order[ch].prev->next = channel_order[ch].next; + channel_order[ch].next->prev = channel_order[ch].prev; + } + // Move it to the end of the linked list + channel_order[ch].next = oldest_chan ? oldest_chan : (oldest_chan = &channel_order[ch]); + channel_order[ch].prev = oldest_chan->prev ? oldest_chan->prev : (oldest_chan->prev = &channel_order[ch]); + oldest_chan->prev->next = &channel_order[ch]; + oldest_chan->prev = &channel_order[ch]; + oldest_chan = &channel_order[ch]; + } +} + static void note_off(int note) { for (int ch = 0; ch < 16; ch++) if (state.chan[ch].samp_pos >= 0 && cnote[ch] == note) { state.chan[ch].note_release = 0; state.chan[ch].next_env_state = ENV_STATE_KEY_OFF; + set_oldest_channel(ch); } draw_square(note, GetStockObject(WHITE_BRUSH)); } @@ -82,10 +141,9 @@ static void note_on(int note, int velocity) { if (sel < 0) return; int inst = valid_insts[sel]; - int ch; - for (ch = 0; ch < 16; ch++) - if (state.chan[ch].samp_pos < 0) break; - if (ch == 16) return; + int ch = oldest_chan - channel_order; + set_latest_channel(ch); + cnote[ch] = note; struct channel_state *c = &state.chan[ch]; set_inst(&state, c, inst); From 15c5891e40e54f95f1c794b55bc8179e436c7bd4 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 21 Aug 2023 21:38:09 -0500 Subject: [PATCH 3/4] Added MIDI sustain pedal support to the instruments tab --- src/inst.c | 59 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/src/inst.c b/src/inst.c index 3022467..b332a19 100644 --- a/src/inst.c +++ b/src/inst.c @@ -40,6 +40,8 @@ static struct window_template inst_list_template = { static unsigned char valid_insts[MAX_INSTRUMENTS]; static int cnote[16]; +static char sustained[16] = { 0 }; +static char sustain = FALSE; static struct history { struct history *prev; struct history *next; @@ -111,27 +113,39 @@ static void set_latest_channel(int ch) { // Sets channel as the oldest one to have been played. static void set_oldest_channel(int ch) { - if (&channel_order[ch] != oldest_chan) { - // Remove this item from the linked list. - if (channel_order[ch].prev && channel_order[ch].next) { - channel_order[ch].prev->next = channel_order[ch].next; - channel_order[ch].next->prev = channel_order[ch].prev; + set_latest_channel(ch); + oldest_chan = &channel_order[ch]; +} + +static void channel_off(int ch) { + if (state.chan[ch].samp_pos >= 0) { + state.chan[ch].note_release = 0; + state.chan[ch].next_env_state = ENV_STATE_KEY_OFF; + set_oldest_channel(ch); + sustained[ch] = FALSE; + } +} + +static void sustain_on() { + sustain = TRUE; +} + +static void sustain_off() { + sustain = FALSE; + for (int ch = 0; ch < 16; ch++) { + if (sustained[ch]) { + channel_off(ch); } - // Move it to the end of the linked list - channel_order[ch].next = oldest_chan ? oldest_chan : (oldest_chan = &channel_order[ch]); - channel_order[ch].prev = oldest_chan->prev ? oldest_chan->prev : (oldest_chan->prev = &channel_order[ch]); - oldest_chan->prev->next = &channel_order[ch]; - oldest_chan->prev = &channel_order[ch]; - oldest_chan = &channel_order[ch]; } } static void note_off(int note) { for (int ch = 0; ch < 16; ch++) - if (state.chan[ch].samp_pos >= 0 && cnote[ch] == note) { - state.chan[ch].note_release = 0; - state.chan[ch].next_env_state = ENV_STATE_KEY_OFF; - set_oldest_channel(ch); + if (cnote[ch] == note) { + if (sustain) + sustained[ch] = TRUE; + else + channel_off(ch); } draw_square(note, GetStockObject(WHITE_BRUSH)); } @@ -143,6 +157,7 @@ static void note_on(int note, int velocity) { int ch = oldest_chan - channel_order; set_latest_channel(ch); + sustained[ch] = FALSE; cnote[ch] = note; struct channel_state *c = &state.chan[ch]; @@ -165,20 +180,28 @@ static void CALLBACK MidiInProc(HMIDIIN handle, UINT wMsg, DWORD_PTR dwInstance, param1 = (dwParam1 >> 8) & 0xFF, param2 = (dwParam1 >> 16) & 0xFF; - if ((eventType & 0x80) && eventType < 0xF0) { // If not a system exclusive MIDI message + if ((eventType & 0x80) && eventType < 0xF0) { // If not a system exclusive MIDI message switch (eventType & 0xF0) { case 0xC0: // Instrument change event SendMessage(instlist, LB_SETCURSEL, param1, 0); break; - case 0x90: // Note On event + case 0x90: // Note On event if (param2 > 0) note_on(param1 + (octave - 4)*12, param2/2); else note_off(param1 + (octave - 4)*12); break; - case 0x80: // Note Off event + case 0x80: // Note Off event note_off(param1 + (octave - 4)*12); break; + case 0xB0: // Control change + if (param1 == 64) { // Sustain pedal + if (param2 >= 64) + sustain_on(); + else + sustain_off(); + } + break; } } } From 99a283b2c192280426ab6d3324b9c5c4776ef4f3 Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 26 Aug 2023 00:13:13 -0500 Subject: [PATCH 4/4] Added #define for max polyphony. Minor code changes. --- src/inst.c | 54 +++++++++++++++++++++++---------------------------- src/structs.h | 3 ++- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/src/inst.c b/src/inst.c index b332a19..b221c6f 100644 --- a/src/inst.c +++ b/src/inst.c @@ -1,3 +1,4 @@ +#include #include #define _WIN32_WINNT 0x0500 // for VK_OEM_PERIOD ????? #define WIN32_LEAN_AND_MEAN @@ -39,32 +40,14 @@ static struct window_template inst_list_template = { }; static unsigned char valid_insts[MAX_INSTRUMENTS]; -static int cnote[16]; -static char sustained[16] = { 0 }; +static int cnote[INST_MAX_POLYPHONY]; +static char sustained[INST_MAX_POLYPHONY] = { 0 }; static char sustain = FALSE; static struct history { struct history *prev; struct history *next; -} channel_order[16] = { - // TODO: Construct this on tab init so the polyphony can change from one location. - { &channel_order[15], &channel_order[1] }, - { &channel_order[0] , &channel_order[2] }, - { &channel_order[1], &channel_order[3] }, - { &channel_order[2], &channel_order[4] }, - { &channel_order[3], &channel_order[5] }, - { &channel_order[4], &channel_order[6] }, - { &channel_order[5], &channel_order[7] }, - { &channel_order[6], &channel_order[8] }, - { &channel_order[7], &channel_order[9] }, - { &channel_order[8], &channel_order[10] }, - { &channel_order[9], &channel_order[11] }, - { &channel_order[10], &channel_order[12] }, - { &channel_order[11], &channel_order[13] }, - { &channel_order[12], &channel_order[14] }, - { &channel_order[13], &channel_order[15] }, - { &channel_order[14], &channel_order[0] }, -}; -struct history* oldest_chan = channel_order; +} channel_order[INST_MAX_POLYPHONY] = { 0 }; +static struct history* oldest_chan = channel_order; int note_from_key(int key, BOOL shift) { if (key == VK_OEM_PERIOD) return 0x48; // continue @@ -98,11 +81,13 @@ static void set_latest_channel(int ch) { if (&channel_order[ch] == oldest_chan) { oldest_chan = oldest_chan->next; } else { + // Verify channel_order items are defined. (They should also form a complete loop.) + assert(channel_order[ch].prev && channel_order[ch].next); + // Remove this item from the linked list. - if (channel_order[ch].prev && channel_order[ch].next) { - channel_order[ch].prev->next = channel_order[ch].next; - channel_order[ch].next->prev = channel_order[ch].prev; - } + channel_order[ch].prev->next = channel_order[ch].next; + channel_order[ch].next->prev = channel_order[ch].prev; + // Move it to the end of the linked list channel_order[ch].next = oldest_chan ? oldest_chan : (oldest_chan = &channel_order[ch]); channel_order[ch].prev = oldest_chan->prev ? oldest_chan->prev : (oldest_chan->prev = &channel_order[ch]); @@ -132,7 +117,7 @@ static void sustain_on() { static void sustain_off() { sustain = FALSE; - for (int ch = 0; ch < 16; ch++) { + for (int ch = 0; ch < INST_MAX_POLYPHONY; ch++) { if (sustained[ch]) { channel_off(ch); } @@ -140,13 +125,14 @@ static void sustain_off() { } static void note_off(int note) { - for (int ch = 0; ch < 16; ch++) + for (int ch = 0; ch < INST_MAX_POLYPHONY; ch++) { if (cnote[ch] == note) { if (sustain) sustained[ch] = TRUE; else channel_off(ch); } + } draw_square(note, GetStockObject(WHITE_BRUSH)); } @@ -264,7 +250,13 @@ LRESULT CALLBACK InstrumentsWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM switch (uMsg) { case WM_CREATE: { prev_chmask = chmask; - chmask = 0xFFFF; + + #if INST_MAX_POLYPHONY > 31 + #error INST_MAX_POLYPHONY must be less than 32 to prevent left-shift overflowing. + #else + chmask = (1u << INST_MAX_POLYPHONY) - 1; + #endif + WPARAM fixed = (WPARAM)fixed_font(); char buf[40]; @@ -311,8 +303,10 @@ LRESULT CALLBACK InstrumentsWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM start_playing(); timer_speed = 0; memset(&state.chan, 0, sizeof state.chan); - for (int ch = 0; ch < 16; ch++) { + for (int ch = 0; ch < INST_MAX_POLYPHONY; ch++) { state.chan[ch].samp_pos = -1; + channel_order[ch].next = &channel_order[(ch + 1) % INST_MAX_POLYPHONY]; + channel_order[ch].prev = &channel_order[(ch - 1 + INST_MAX_POLYPHONY) % INST_MAX_POLYPHONY]; } // Restore the previous instrument selection diff --git a/src/structs.h b/src/structs.h index 5e59da4..82ca716 100644 --- a/src/structs.h +++ b/src/structs.h @@ -7,6 +7,7 @@ typedef int BOOL; #define TRUE 1 typedef void *HWND; #endif +#define INST_MAX_POLYPHONY 16 // structure used for track or subroutine // "size" does not include the ending [00] byte @@ -98,7 +99,7 @@ struct song_state { short sustain_level; short sustain_rate; short gain_rate; - } chan[16]; + } chan[INST_MAX_POLYPHONY]; signed char transpose; struct slider volume; struct slider tempo;