Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inst tab changes. Fixed bug. Upped polyphony. Added MIDI sustain support #52

Merged
merged 4 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 89 additions & 13 deletions src/inst.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <assert.h>
#include <stdio.h>
#define _WIN32_WINNT 0x0500 // for VK_OEM_PERIOD ?????
#define WIN32_LEAN_AND_MEAN
Expand Down Expand Up @@ -39,7 +40,14 @@ static struct window_template inst_list_template = {
};

static unsigned char valid_insts[MAX_INSTRUMENTS];
static int cnote[8];
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[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
Expand Down Expand Up @@ -68,12 +76,63 @@ 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 {
// 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.
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) {
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 < INST_MAX_POLYPHONY; ch++) {
if (sustained[ch]) {
channel_off(ch);
}
}
}

static void note_off(int note) {
for (int ch = 0; ch < 8; 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;
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));
}

Expand All @@ -82,10 +141,10 @@ static void note_on(int note, int velocity) {
if (sel < 0) return;
int inst = valid_insts[sel];

int ch;
for (ch = 0; ch < 8; ch++)
if (state.chan[ch].samp_pos < 0) break;
if (ch == 8) return;
int ch = oldest_chan - channel_order;
set_latest_channel(ch);
sustained[ch] = FALSE;

cnote[ch] = note;
struct channel_state *c = &state.chan[ch];
set_inst(&state, c, inst);
Expand All @@ -107,20 +166,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;
}
}
}
Expand Down Expand Up @@ -183,6 +250,13 @@ LRESULT CALLBACK InstrumentsWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM
switch (uMsg) {
case WM_CREATE: {
prev_chmask = chmask;

#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];

Expand Down Expand Up @@ -229,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 < 8; 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
Expand Down
2 changes: 1 addition & 1 deletion src/sound.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down