From ed60865743a5d65240c9da353edb4dc20cf7009f Mon Sep 17 00:00:00 2001 From: Duncan Wilkie Date: Tue, 18 Jul 2023 11:55:03 -0500 Subject: Rename, add startup file. --- controller/libs/base_midi.c | 835 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 835 insertions(+) create mode 100644 controller/libs/base_midi.c (limited to 'controller/libs/base_midi.c') diff --git a/controller/libs/base_midi.c b/controller/libs/base_midi.c new file mode 100644 index 0000000..e146ec0 --- /dev/null +++ b/controller/libs/base_midi.c @@ -0,0 +1,835 @@ + // This file is written to be read side-along with the MIDI 1.0 spec. +// Note the tables at its very end, with /absolutely no direct allusion in the text/. + +#include "midi.h" +#include +#include + +#ifdef RUNNING_STATUS +uint8_t last_status = 0x00; +#endif + +// Helper functions +#define MAX_DATA_BYTE 0b01111111 +static inline void MIDI_bare_send(uint8_t status) { +#ifdef DEBUG + if (status <= MAX_DATA_BYTE) { + while(true); // Illegal status. + } +#endif + + pfns.uart_write(status); + +} + +static inline void MIDI_single_send(uint8_t status, uint8_t byte) { +#ifdef DEBUG + if (status <= MAX_DATA_BYTE || byte > MAX_DATA_BYTE) { + while(true); // Illegal status or data byte. + } +#endif + +#ifdef RUNNING_STATUS + if (status != last_status) { + pfns.uart_write(status); + last_status = status; + } +#else + pfns.uart_write(status); +#endif + pfns.uart_write(byte); +} + +static inline void MIDI_send(uint8_t status, uint8_t byte1, uint8_t byte2) { +#ifdef DEBUG + if (status <= MAX_DATA_BYTE || byte1 > MAX_DATA_BYTE || byte2 > MAX_DATA_BYTE) { + while(true); // Illegal status or data byte. + } +#endif + +#ifdef RUNNING_STATUS + if (status != last_status) { + pfns.uart_write(status); + last_status = status; + } +#else + pfns.uart_write(status); +#endif + pfns.uart_write(byte1); + pfns.uart_write(byte2); +} + + +#define MAX_NON_UNIVERSAL_ID (MAX_DATA_BYTE - 2) +#define SYSEX_STATUS 0xf0 +#define EOX_STATUS 0xf7 +void sysex(uint16_t manufacturer_id, uint8_t* contents, size_t contents_len) { +#ifdef DEBUG + if (manufacturer_id == 0 || (manufacturer_id >> 8) > MAX_DATA_BYTE || (manufacturer_id & 0x00ff) > MAX_DATA_BYTE) { + while(true); // Invalid ID. + } + + // Pretty inefficient to iterate contents twice, but prevents writing partial SysEx messages to the bus. + for (size_t i = 0; i < contents_len; i++) { + if (contents[i] > MAX_DATA_BYTE) { + while(true); + } + } +#endif + + if ((manufacturer_id >> 8) != 0) { + pfns.uart_write(SYSEX_STATUS); + pfns.uart_write(0x00); + pfns.uart_write(manufacturer_id >> 8); + pfns.uart_write(manufacturer_id & 0x00ff); + } else { + if (manufacturer_id > MAX_NON_UNIVERSAL_ID) { + while(true); // Used universal sysex ID. + } + + pfns.uart_write(manufacturer_id); + } + + for (size_t i = 0; i < contents_len; i++) { + pfns.uart_write(contents[i]); + } + + pfns.uart_write(EOX_STATUS); +} + +#define UNIVERSAL_NONREALTIME_SUBID 0x7e +#define UNIVERSAL_REALTIME_SUBID 0x7f +void universal_nonrealtime(uint8_t device_id, uint16_t sub_id, uint8_t *contents, size_t contents_len) { +#ifdef DEBUG + if ((sub_id >> 8) > MAX_DATA_BYTE || (sub_id & 0x00ff) > MAX_DATA_BYTE) { + while(true); + } + + for (size_t i = 0; i < contents_len; i++) { + if (contents[i] > MAX_DATA_BYTE) { + while(true); + } + } +#endif + + pfns.uart_write(SYSEX_STATUS); + pfns.uart_write(UNIVERSAL_NONREALTIME_SUBID); + pfns.uart_write(device_id); + pfns.uart_write(sub_id >> 8); + pfns.uart_write(sub_id & 0x00ff); + + for (size_t i = 0; i < contents_len; i++) { + pfns.uart_write(contents[i]); + } + + pfns.uart_write(EOX_STATUS); +} + +void universal_realtime(uint8_t device_id, uint16_t sub_id, uint8_t *contents, size_t contents_len) { +#ifdef DEBUG + if ((sub_id >> 8) > MAX_DATA_BYTE || (sub_id & 0x00ff) > MAX_DATA_BYTE) { + while(true); + } + + for (size_t i = 0; i < contents_len; i++) { + if (contents[i] > MAX_DATA_BYTE) { + while(true); + } + } +#endif + + pfns.uart_write(SYSEX_STATUS); + pfns.uart_write(UNIVERSAL_REALTIME_SUBID); + pfns.uart_write(device_id); + pfns.uart_write(sub_id >> 8); + pfns.uart_write(sub_id & 0x00ff); + + for (size_t i = 0; i < contents_len; i++) { + pfns.uart_write(contents[i]); + } + + pfns.uart_write(EOX_STATUS); +} +// Channel voice messages. + +#define MAX_CHANNEL 0x0f +#define NOTE_OFF_MASK 0x80 +#define NOTE_ON_MASK 0x90 +#define POLY_KEY_PRESSURE_MASK 0xa0 +#define CONTROL_CHANGE_MASK 0xb0 +#define PROGRAM_CHANGE_MASK 0xc0 +#define AFTERTOUCH_MASK 0xd0 +#define PITCH_BEND_MASK 0xe0 + +#define MIDDLE_VELOCITY 0x40 + +#ifdef KEY_ON_VELOCITY +void note_on(uint8_t channel, uint8_t note, uint8_t velocity) { +#ifdef DEBUG + if (channel > MAX_CHANNEL || note > MAX_DATA_BYTE || velocity > MAX_DATA_BYTE) { + while(true); + } +#endif + + MIDI_send(NOTE_ON_MASK | channel, note, velocity); + +} + +#else + +void note_on(uint8_t channel, uint8_t note) { +#ifdef DEBUG + if (channel > MAX_CHANNEL || note > MAX_DATA_BYTE) { + while(true); + } +#endif + + MIDI_send(NOTE_ON_MASK | channel, note, MIDDLE_VELOCITY); + +} + + +#endif + +#ifdef RELEASE_VELOCITY + +void note_off(uint8_t channel, uint8_t note, uint8_t velocity) { +#ifdef DEBUG + if (channel > MAX_CHANNEL || note > MAX_DATA_BYTE || velocity > MAX_DATA_BYTE) { + while(true); + } +#endif + + MIDI_send(NOTE_OFF_MASK | channel, note, velocity); + +} + +#else + +void note_off(uint8_t channel, uint8_t note) { +#ifdef DEBUG + if (channel > MAX_CHANNEL || note > MAX_DATA_BYTE) { + while(true); + } +#endif + +#ifdef ACTUAL_OFF_MESSAGE + MIDI_send(NOTE_OFF_MASK | channel, note, MIDDLE_VELOCITY); +#else + MIDI_send(NOTE_ON_MASK | channel, note, 0); +#endif + +} + +#endif + +#define MAX_CONTROLLER 119 +void control_change(uint8_t channel, uint8_t controller_number, uint8_t control_value) { +#ifdef DEBUG + if (channel > MAX_CHANNEL || controller_number > MAX_CONTROLLER || control_value > MAX_DATA_BYTE) { + while(true); + } +#endif + + MIDI_send(CONTROL_CHANGE_MASK | channel, controller_number, control_value); + +} + +void program_change(uint8_t channel, uint8_t program_number) { +#ifdef DEBUG + if (channel > MAX_CHANNEL || program_number > MAX_DATA_BYTE) { + while(true); + } +#endif + + MIDI_single_send(PROGRAM_CHANGE_MASK | channel, program_number); + +} + +void aftertouch(uint8_t channel, uint8_t pressure_value) { +#ifdef DEBUG + if (channel > MAX_CHANNEL || pressure_value > MAX_DATA_BYTE) { + while(true); + } +#endif + + MIDI_single_send(AFTERTOUCH_MASK | channel, pressure_value); + +} + +void pitch_bend_change(uint8_t channel, uint16_t pressure_value) { +#ifdef DEBUG + if (channel > MAX_CHANNEL || pressure_value > ((MAX_DATA_BYTE << 8) | MAX_DATA_BYTE)) { + while(true); + } +#endif + + MIDI_single_send(PITCH_BEND_MASK | channel, pressure_value); + +} + + + +// Channel mode messages. + +#define SOUND_OFF_MODE (MAX_CONTROLLER + 1) +#define RESET_ALL_MODE (MAX_CONTROLLER + 2) +#define LOCAL_CONTROL_MODE (MAX_CONTROLLER + 3) +#define NOTES_OFF_MODE (MAX_CONTROLLER + 4) +#define OMNI_OFF_MODE (MAX_CONTROLLER + 5) +#define OMNI_ON_MODE (MAX_CONTROLLER + 6) +#define MONO_ON_MODE (MAX_CONTROLLER + 7) +#define POLY_ON_MODE (MAX_CONTROLLER + 8) + +void all_sound_off(uint8_t channel) { +#ifdef DEBUG + if (channel > MAX_CHANNEL) { + while(true); + } +#endif + + MIDI_send(CONTROL_CHANGE_MASK | channel, SOUND_OFF_MODE, 0); + +} + +void reset_all_controllers(uint8_t channel) { +#ifdef DEBUG + if (channel > MAX_CHANNEL) { + while(true); + } +#endif + + MIDI_send(CONTROL_CHANGE_MASK | channel, RESET_ALL_MODE, 0); + +} + +void local_control_on(uint8_t channel) { +#ifdef DEBUG + if (channel > MAX_CHANNEL) { + while(true); + } +#endif + + MIDI_send(CONTROL_CHANGE_MASK | channel, LOCAL_CONTROL_MODE, MAX_DATA_BYTE); + +} + +void local_control_off(uint8_t channel) { +#ifdef DEBUG + if (channel > MAX_CHANNEL) { + while(true); + } +#endif + + MIDI_send(CONTROL_CHANGE_MASK | channel, LOCAL_CONTROL_MODE, 0); + +} + +void all_notes_off(uint8_t channel) { +#ifdef DEBUG + if (channel > MAX_CHANNEL) { + while(true); + } +#endif + + MIDI_send(CONTROL_CHANGE_MASK | channel, NOTES_OFF_MODE, 0); + +} + +void omni_on(uint8_t channel) { +#ifdef DEBUG + if (channel > MAX_CHANNEL) { + while(true); + } +#endif + + MIDI_send(CONTROL_CHANGE_MASK | channel, OMNI_ON_MODE, 0); + +} + +void omni_off(uint8_t channel) { +#ifdef DEBUG + if (channel > MAX_CHANNEL) { + while(true); + } +#endif + + MIDI_send(CONTROL_CHANGE_MASK | channel, OMNI_OFF_MODE, 0); + +} + +#define RECEIVER_CHANNEL_COUNT 0 +void mono_on(uint8_t channel, uint8_t channel_count) { +#ifdef DEBUG + if (channel > MAX_CHANNEL || channel_count > MAX_DATA_BYTE) { + while(true); + } +#endif + + MIDI_send(CONTROL_CHANGE_MASK | channel, MONO_ON_MODE, channel_count); + +} + +void poly_on(uint8_t channel) { +#ifdef DEBUG + if (channel > MAX_CHANNEL) { + while(true); + } +#endif + + MIDI_send(CONTROL_CHANGE_MASK | channel, POLY_ON_MODE, 0); + +} + + +// System common messages. +// Not implemented: +// - MTC Quarter Frame +// EOX is automatically sent in the functions that send exclusives. + +#define MTC_QUARTER_FRAME_STATUS 0xf1 +#define SONG_POSITION_POINTER_STATUS 0xf2 +// Undefined. +#define SONG_SELECT_STATUS 0xf3 +#define TUNE_REQUEST_STATUS 0xf6 + +void song_position_pointer(uint16_t position) { +#ifdef DEBUG + if ((position & 0x0ff) > MAX_DATA_BYTE || (position >> 8) > MAX_DATA_BYTE) { + while(true); + } +#endif + + MIDI_send(SONG_POSITION_POINTER_STATUS, position & 0x00ff, position >> 8); + +} + +void song_select(uint8_t song) { +#ifdef DEBUG + if (song > MAX_DATA_BYTE) { + while(true); + } +#endif + + MIDI_single_send(SONG_SELECT_STATUS, song); + +} + +void tune_request() { + MIDI_bare_send(TUNE_REQUEST_STATUS); +} + + + +// System real time messages. +// Not implemented: +// - Timing Clock, +// - Start, +// - Continue, +// - Stop, +// - Active Sensing. + +#define TIMING_CLOCK_STATUS 0xf8 +// Undefined. +#define START_STATUS 0xfa +#define CONTINUE_STATUS 0xfb +#define STOP_STATUS 0xfc +// Undefined. +#define ACTIVE_SENSING_STATUS 0xfe +#define SYSTEM_RESET_STATUS 0xff +void timing_clock() { + MIDI_bare_send(TIMING_CLOCK_STATUS); +} + +void srt_start() { + MIDI_bare_send(START_STATUS); +} + +void srt_continue() { + MIDI_bare_send(CONTINUE_STATUS); +} + +void srt_stop() { + MIDI_bare_send(STOP_STATUS); +} + +void active_sensing() { + MIDI_bare_send(ACTIVE_SENSING_STATUS); +} + +void MIDI_reset() { + MIDI_bare_send(SYSTEM_RESET_STATUS); +} + + +// Universal system exclusive messages. + + +// Only implement MIDI Tuning Standard universals and general information. + + +#define TUNING_STANDARD_SUBID 0x08 +#define BULK_DUMP_REQUEST_SUBID 0x00 +void bulk_tuning_dump_request(uint8_t device_id, uint8_t program) { +#ifdef DEBUG + if (program > MAX_DATA_BYTE || device_id > MAX_DATA_BYTE) { + while(true); + } +#endif + + uint8_t contents[] = {program}; + + universal_nonrealtime(device_id, (TUNING_STANDARD_SUBID << 8) | BULK_DUMP_REQUEST_SUBID, contents, 1); + +} + + +#define BULK_DUMP_REPLY_SUBID 0x01 +void bulk_tuning_dump(uint8_t device_id, uint8_t program, char* name, uint8_t* tuning_data) { +#ifdef DEBUG + if (device_id > MAX_DATA_BYTE || program > MAX_DATA_BYTE) { + while(true); + } +#endif + + uint8_t *contents = malloc(1 + TUNING_NAME_LENGTH + TUNING_LENGTH + 1); + contents[0] = program; + memcpy(contents + 1, name, TUNING_NAME_LENGTH); + memcpy(contents + 1 + TUNING_NAME_LENGTH, tuning_data, TUNING_LENGTH); + + // I hate this freaking "standard"...what TF do I checksum??? + uint8_t checksum = UNIVERSAL_NONREALTIME_SUBID ^ device_id ^ TUNING_STANDARD_SUBID ^ BULK_DUMP_REPLY_SUBID ^ program; + for (int i = 0; i < TUNING_LENGTH; i++) { + checksum ^= tuning_data[i]; + } + + contents[1 + TUNING_NAME_LENGTH + DEVICE_KEY_COUNT] = checksum; + + universal_nonrealtime(device_id, (TUNING_STANDARD_SUBID << 8) | BULK_DUMP_REPLY_SUBID, contents, + 1 + TUNING_NAME_LENGTH + TUNING_LENGTH + 1); + + free(contents); + +} + +#define NOTE_CHANGE_SUBID 0x02 +void single_note_tuning_change(uint8_t device_id, uint8_t program, uint8_t *note_tuning_data, uint8_t keys_changed) { +#ifdef DEBUG + if (device_id > MAX_DATA_BYTE || program > MAX_DATA_BYTE || keys_changed > DEVICE_KEY_COUNT) { + while(true); + } +#endif + + uint8_t *contents = malloc(2 + NOTE_TUNING_BYTES_PER_KEY * keys_changed); + contents[0] = program; + contents[1] = keys_changed; + memcpy(contents + 2, note_tuning_data, NOTE_TUNING_BYTES_PER_KEY * keys_changed); + + universal_realtime(device_id, (TUNING_STANDARD_SUBID << 8) | NOTE_CHANGE_SUBID, contents, + 2 + NOTE_TUNING_BYTES_PER_KEY * keys_changed); + + free(contents); + +} + +// TODO: tuning banks. + + +// Parsing. +// Stream parsing. +typedef struct { + uint8_t status; + bool first_byte; + uint8_t byte0; + bool send_eox; + uint8_t *stack; + size_t top; + size_t size; +} ParserMemory; + +#define WAITING_FOR_STATUS 0x00 +ParserMemory memory = {.status = WAITING_FOR_STATUS, + .first_byte = false, + .byte0 = 0x00, + .send_eox = false, + .stack = NULL, // TODO: initialize from main. + .top = 0, + .size = 128}; + +static inline void push_fn(uint8_t val) { + if (memory.top == memory.size - 1) { + memory.stack = realloc(memory.stack, memory.size + 128); + } + + memory.stack[memory.top + 1] = val; + ++memory.top; +} + +#define push() do { \ + push_fn(byte); \ + } while(0) + +static inline uint8_t pop() { + if (memory.size - memory.top > 128) { + memory.stack = realloc(memory.stack, memory.size - 128); + } + + --memory.top; + return memory.stack[memory.top + 1]; +} + +static inline void reset() { + memory.stack = realloc(memory.stack, 128); + memory.size = 128; + memory.top = 0; + memory.status = WAITING_FOR_STATUS; +} + +#define second_byte() do { \ + memory.first_byte = false; \ + memory.byte0 = byte; \ + } while(0) + + +#define channel (memory.status & 0x0f) + + +// Memory should be large enough to handle the largest message that's expected to be processed automatically here. +void parse_midi_stream(uint8_t byte) { + switch (byte) { + // Status byte. + case NOTE_OFF_MASK ... (PITCH_BEND_MASK + MAX_CHANNEL): + memory.status = byte; + break; + + case MTC_QUARTER_FRAME_STATUS ... SONG_SELECT_STATUS: + memory.status = byte; + break; + case TUNE_REQUEST_STATUS: + pfns.tune_request_handler(); + memory.status = WAITING_FOR_STATUS; + break; + + case TIMING_CLOCK_STATUS: + pfns.timing_clock_handler(); + break; + case START_STATUS: + pfns.start_handler(); + break; + case CONTINUE_STATUS: + pfns.continue_handler(); + break; + case STOP_STATUS: + pfns.stop_handler(); + break; + case ACTIVE_SENSING_STATUS: + pfns.active_sensing_handler(); + break; + case SYSTEM_RESET_STATUS: + pfns.system_reset_handler(); + break; + case SYSEX_STATUS: + memory.status = byte; + break; + case EOX_STATUS: + if (memory.send_eox) { + pfns.end_of_sysex_handler(); + memory.send_eox = false; + } + reset(); + break; + + // Data byte. + case 0x00 ... MAX_DATA_BYTE: + switch (memory.status) { + case WAITING_FOR_STATUS: + break; + + case NOTE_OFF_MASK ... (NOTE_OFF_MASK + MAX_CHANNEL): + if (memory.first_byte) + second_byte(); + else { + pfns.note_off_handler(channel, memory.byte0, byte); + memory.first_byte = true; + } + break; + case NOTE_ON_MASK ... (NOTE_ON_MASK + MAX_CHANNEL): + if (memory.first_byte) + second_byte(); + else { + pfns.note_on_handler(channel, memory.byte0, byte); + memory.first_byte = true; + } + break; + case POLY_KEY_PRESSURE_MASK ... (POLY_KEY_PRESSURE_MASK + MAX_CHANNEL): + if (memory.first_byte) + second_byte(); + else { + pfns.poly_key_handler(channel, memory.byte0, byte); + memory.first_byte = true; + } + break; + case CONTROL_CHANGE_MASK ... (CONTROL_CHANGE_MASK + MAX_CHANNEL): + if (memory.first_byte) + second_byte(); + else { + switch (memory.byte0) { + case 0x00 ... MAX_CONTROLLER: + pfns.control_change_handler(channel, memory.byte0, byte); + break; + case SOUND_OFF_MODE: + pfns.all_sound_off_handler(channel); + break; + case RESET_ALL_MODE: + pfns.reset_all_controllers_handler(channel); + break; + case LOCAL_CONTROL_MODE: + pfns.local_control_handler(channel, byte); + break; + case NOTES_OFF_MODE: + pfns.all_notes_off_handler(channel); + break; + case OMNI_OFF_MODE: + pfns.omni_off_handler(channel); + break; + case OMNI_ON_MODE: + pfns.omni_on_handler(channel); + break; + case MONO_ON_MODE: + pfns.mono_on_handler(channel, byte); + break; + case POLY_ON_MODE: + pfns.poly_on_handler(channel); + break; + default: + while(true); // Should be unreachable. + } + memory.first_byte = true; + } + break; + case PROGRAM_CHANGE_MASK ... (PROGRAM_CHANGE_MASK + MAX_CHANNEL): + pfns.program_change_handler(channel, byte); + break; + case AFTERTOUCH_MASK ... (AFTERTOUCH_MASK + MAX_CHANNEL): + pfns.aftertouch_handler(channel, byte); + break; + case PITCH_BEND_MASK ... (PITCH_BEND_MASK + MAX_CHANNEL): + pfns.pitch_bend_change_handler(channel, byte); + break; + + + case MTC_QUARTER_FRAME_STATUS: + if (memory.first_byte) + second_byte(); + else { + pfns.mtc_quarter_frame_handler(memory.byte0, byte); + memory.first_byte = true; + } + break; + case SONG_POSITION_POINTER_STATUS: + if (memory.first_byte) + second_byte(); + else { + pfns.song_position_pointer_handler(memory.byte0, byte); + memory.first_byte = true; + } + break; + case SONG_SELECT_STATUS: + pfns.song_select_handler(byte); + break; + + case SYSEX_STATUS: + if (memory.first_byte) { + reset(); // In case last sysex got interrupted by a status. + if (byte <= MAX_NON_UNIVERSAL_ID) + pfns.sysex_collector(byte); + second_byte(); + } else + switch (memory.byte0) { + case UNIVERSAL_NONREALTIME_SUBID: + push(); + if (memory.top > 2) { + uint8_t device_id = memory.stack[0]; + uint8_t subid1 = memory.stack[1]; + uint8_t subid2 = memory.stack[2]; + switch (subid1) { + case TUNING_STANDARD_SUBID: + switch (subid2) { + case BULK_DUMP_REQUEST_SUBID: + pfns.bulk_tuning_dump_request_handler(device_id, byte); + reset(); + break; + case BULK_DUMP_REPLY_SUBID: + if (memory.top == 4 + TUNING_NAME_LENGTH + TUNING_LENGTH - 1) { + uint8_t tuning_program = memory.stack[3]; + uint8_t checksum = UNIVERSAL_NONREALTIME_SUBID ^ device_id ^ TUNING_STANDARD_SUBID \ + ^ BULK_DUMP_REPLY_SUBID ^ tuning_program; + for (int i = 0; i < TUNING_LENGTH; i++) { + checksum ^= memory.stack[4 + TUNING_NAME_LENGTH - 1 + i]; + } + + if (memory.stack[memory.top] == checksum) { + pfns.bulk_tuning_dump_handler(memory.stack[0], memory.stack[3], (char *)(memory.stack + 4), + memory.stack + 4 + TUNING_NAME_LENGTH); + } + + reset(); + } + default: + while(true); // Unrecognized tuning standard subid2. + } + break; + default: + if (!pfns.unimplemented_universal_sysex_collector(byte)) { + reset(); + } else { + push(); + } + break; + } + } + break; + + case UNIVERSAL_REALTIME_SUBID: + push(); + if (memory.top > 2) { + uint8_t device_id = memory.stack[0]; + uint8_t subid1 = memory.stack[1]; + uint8_t subid2 = memory.stack[2]; + switch (subid1) { + case TUNING_STANDARD_SUBID: + switch (subid2) { + case NOTE_CHANGE_SUBID: + uint8_t program_number = memory.stack[3]; + uint8_t change_count = memory.stack[4]; + if (memory.top >= 4 && memory.top == 4 + (size_t)change_count + 1 - 1) { + pfns.single_note_tuning_change_handler(device_id, program_number, change_count, memory.stack + 5); + reset(); + } + break; + default: + while(true); // Unrecognized tuning standard message. + } + break; + default: + break; + } + } + break; + default: + if (!pfns.sysex_collector(byte)) { + reset(); + } else { + push(); + } + break; + } + default: + while(true); // Unhandled status got set, somehow. + } + break; + + default: + while(true); // Unrecognized status byte. + } +} -- cgit v1.2.3