diff --git a/Makefile b/Makefile index f4f7ff5..6ad54c2 100644 --- a/Makefile +++ b/Makefile @@ -81,7 +81,7 @@ DTASK_TARGETS := midi_tasks DTASK_GENERATED_HEADERS := $(patsubst %, .gen/%.h, $(DTASK_TARGETS)) INCLUDE += -I $(DTASK_SRC) -LIBS += -lasound +LIBS += -lasound -lz .PHONY: all all: midipush diff --git a/default.nix b/default.nix index c6d1b44..7bc2769 100644 --- a/default.nix +++ b/default.nix @@ -7,7 +7,12 @@ with pkgs; stdenv.mkDerivation rec { name = "midipush"; src = ./.; - buildInputs = [ python alsaLib fluidsynth ]; + buildInputs = [ + python + alsaLib + fluidsynth + zlib # for crc32 + ]; sf = "${soundfont-fluid}/share/soundfonts/FluidR3_GM2-2.sf2"; shellHook = '' start_sf () { diff --git a/midi_tasks.c b/midi_tasks.c index b65934f..b8f698f 100644 --- a/midi_tasks.c +++ b/midi_tasks.c @@ -165,9 +165,6 @@ typedef union { DTASK(note_record, struct { uint64_t notes[BEATS][16]; }) { int channel = *DREF_PASS(channel); - if(*DREF_PASS(disable_channel) & (1ull << channel)) { - return false; - } if(*DREF(new_button)) { if(!*DREF_PASS(deleting)) { memset(DREF(note_record), 0, sizeof(note_record_t)); @@ -208,38 +205,34 @@ DTASK(record, map_t) { if(!*DREF(deleting)) { map_clear(record); } else { - unsigned char noteon = 0x90 | channel; FORMAP(i, record) { pair_t *p = &record[i]; msg_data_t msg = { .data = p->second }; - if((msg.byte[0]) == noteon) { + if((msg.byte[0] & 0xf) == channel) { p->second = 0; } } } return true; } - if(*DREF_PASS(disable_channel) & (1ull << channel)) { - return false; - } if(*DREF(deleting)) { map_iterator it = map_iterator_begin(record, beat); pair_t *p = map_find_iter(&it); - unsigned char noteon = 0x90 | channel; while(p) { msg_data_t msg = { .data = p->second }; - if((msg.byte[0]) == noteon) { + if((msg.byte[0] & 0xf) == channel) { p->second = 0; } p = map_next(&it, p); } - } else if(*DREF(recording)) { + } else { if(state->events & DELETING) { // delete disabled, so collect garbage int n = map_filter(record, nonzero_value); printf("%d notes discarded\n", n); } - if(state->events & PUSH_MIDI_MSG_IN) { + if(*DREF(recording) && + state->events & PUSH_MIDI_MSG_IN) { const seg_t *msg_in = DREF(push_midi_msg_in); unsigned char control = msg_in->s[0] & 0xf0; if(ONEOF(control, 0x80, 0x90)) { @@ -297,7 +290,7 @@ DTASK(set_page, struct { int val, set, keep; }) { p->val = (c - 102) * 8 | (p->val & 0x07); p->set |= 0x38; } else if(top) { - p->val = p->val & 0x38 | (c - 20); + p->val = (p->val & 0x38) | (c - 20); p->set |= 0x07; } } else if (p->set) { diff --git a/midipush.c b/midipush.c index dad5a52..34e82c9 100644 --- a/midipush.c +++ b/midipush.c @@ -22,6 +22,7 @@ #include #include #include +#include // crc32 #include "dtask.h" #include "types.h" @@ -40,6 +41,8 @@ #include "midi_tasks.h" #include "midipush.h" +#define STATE_FILE "midipush.state" + const int initial = PRINT_MIDI_MSG | LIGHT_BAR | PLAYBACK | SHOW_PROGRAM | PASSTHROUGH | SHOW_DISABLE_CHANNEL | TRANSPOSE; static snd_rawmidi_t *rawmidi_in = NULL, *rawmidi_out = NULL, *synth_out = NULL; @@ -289,6 +292,54 @@ int get_pfds(snd_rawmidi_t *m, struct pollfd *pfds, int pfds_n) { return n; } +static +bool load_state(const char *name, midi_tasks_state_t *state) { + int fd = open(name, O_RDONLY); + if(fd >= 0) { + char *task_specific = (char *)state + sizeof(dtask_state_t); + size_t task_specific_size = sizeof(*state) - sizeof(dtask_state_t); + read(fd, task_specific, task_specific_size); + read(fd, record, static_sizeof(record)); // *** + uLong crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, task_specific, task_specific_size); + crc = crc32(crc, record, static_sizeof(record)); + state->record = record; + uLong crc_read; + read(fd, &crc_read, sizeof(crc_read)); + close(fd); + if(crc == crc_read) { + printf("state loaded from: %s\n", name); + return true; + } else { + printf("bad crc: %s\n", name); + memset(task_specific, 0, task_specific_size); + memset(record, 0, static_sizeof(record)); + } + } else { + printf("failed to load: %s\n", name); + } + return false; +} + +static +void save_state(const char *name, midi_tasks_state_t *state) { + int fd = open(name, O_WRONLY | O_CREAT, 0644); + if(fd >= 0) { + char *task_specific = (char *)state + sizeof(dtask_state_t); + size_t task_specific_size = sizeof(*state) - sizeof(dtask_state_t); + uLong crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, task_specific, task_specific_size); + crc = crc32(crc, record, static_sizeof(record)); + write(fd, task_specific, task_specific_size); + write(fd, record, static_sizeof(record)); // *** + write(fd, &crc, sizeof(crc)); + close(fd); + printf("state saved to: %s\n", name); + } else { + printf("failed to save: %s\n", name); + } +} + STATIC_ALLOC(record, pair_t, 1 << 15); int main(int argc, char *argv[]) { static_alloc_init(); @@ -331,16 +382,22 @@ int main(int argc, char *argv[]) { n = snd_rawmidi_open(NULL, &synth_out, portname, SND_RAWMIDI_SYNC | SND_RAWMIDI_NONBLOCK); assert_throw(n >= 0, "Problem opening MIDI port: %s", snd_strerror(n)); + if(!load_state(STATE_FILE, &midi_state)) { + midi_state.record = init_map(record, record_size); + } + pfds_in_n = get_pfds(rawmidi_in, pfds_in, LENGTH(pfds_in)); dtask_enable((dtask_state_t *)&midi_state, initial); dtask_select((dtask_state_t *)&midi_state); - midi_state.record = init_map(record, record_size); while(read_midi_msgs(rawmidi_in, midi_input_rb, &midi_state)) { poll(pfds_in, pfds_in_n, 10); gettimeofday(&midi_state.time_of_day, NULL); dtask_run((dtask_state_t *)&midi_state, TIME_OF_DAY); } + dtask_disable((dtask_state_t *)&midi_state, initial); + save_state(STATE_FILE, &midi_state); + snd_rawmidi_close(rawmidi_in); snd_rawmidi_close(rawmidi_out); return 0;