From 1fd366764d17874b8211ef63d41313995833e36d Mon Sep 17 00:00:00 2001 From: Duncan Wilkie Date: Fri, 14 Jul 2023 11:17:59 -0500 Subject: The start-ish of it all. --- controller/libs/midi.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 controller/libs/midi.c (limited to 'controller/libs') diff --git a/controller/libs/midi.c b/controller/libs/midi.c new file mode 100644 index 0000000..488b172 --- /dev/null +++ b/controller/libs/midi.c @@ -0,0 +1,347 @@ +// 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 +#include + +typedef struct { + uint8_t len; + uint8_t status; + uint8_t data1; + uint8_t data2; +} MIDIMessage; + +typedef struct { + uint8_t status; + uint8_t sub_id; + uint8_t* bytes; + uint8_t size; +} SysExMessage; + + + +// Channel voice messages. + +#IFDEF KEY_ON_VELOCITY + +MIDIMessage note_on(uint8_t channel, uint8_t note, uint8_t velocity) { + if (channel > 0x0f || note > 127 || velocity > 127) { + while(1); + } + + return {.status = 0x90 & channel, + .data1 = note, + .data2 = velocity + .len = 3}; +} + +#ELSE + +MIDIMessage note_on(uint8_t channel, uint8_t note) { + if (channel > 0x0f || note > 127) { + while(1); + } + + return {.status = 0x90 & channel, + .data1 = note, + .data2 = 0x40 + .len = 3}; +} + + +#ENDIF + +#IFDEF RELEASE_VELOCITY + +MIDIMessage note_off(uint8_t channel, uint8_t note, uint8_t velocity) { + if (channel > 0x0f || note > 127 || velocity > 127) { + while(1); + } + + return {.status = 0x80 & channel, + .data1 = note, + .data2 = velocity + .len = 3}; +} + +#ELSE + +#IFDEF ACTUAL_OFF_MESSAGE + +MIDIMessage note_off(uint8_t channel, uint8_t note) { + if (channel > 0x0f || note > 127) { + while(1); + } + + return {.status = 0x80 & channel, + .data1 = note, + .data2 = 0x40 + .len = 3}; +} + +#ELSE + +MIDIMessage note_off(uint8_t channel, uint8_t note) { + if (channel > 0x0f || note > 127) { + while(1); + } + + return {.status = 0x90 & channel, + .data1 = note, + .data2 = 0 + .len = 3}; +} + +#ENDIF +#ENDIF + +MIDIMessage control_change(uint8_t channel, uint8_t control_number, uint8_t control_value) { + if (channel > 0x0f || control_number > 119 || control_value > 127) { + while(1); + } + + return {.status = 0xb0 & channel, + .data1 = control_number, + .data2 = control_value + .len = 3}; +} + +enum Controller { + bank_select = 0, + modulation_wheel = 1, + breath_controller = 2, + // undefined + foot_controller = 4, + portamento_time = 5, + data_entry_msb = 6, + channel_volume = 7, + balance = 8, + // undefined + pan = 10, + expression = 11, + effect1 = 12, + effect2 = 13, + // undefined + gp1 = 16, + gp2 = 17, + gp3 = 18, + gp4 = 19, + damper_pedal = 64, + portamento_toggle = 65, + sostenuto = 66, + soft_pedal = 67, + legato_footswitch = 68, + hold2 = 69, + sc1_sound_variation = 70, + sc2_timbre = 71, + sc3_release_time = 72, + sc4_attack_time = 73, + sc5_brightness = 74, + sc6 = 75, + sc7 = 76, + sc8 = 77, + sc9 = 78, + sc10 = 79, + gp5 = 80, + gp6 = 81, + gp7 = 82, + gp8 = 83, + portamento_control = 84, + // undefined + effects_depth1 = 91, + effects_depth2 = 92, + effects_depth3 = 93, + effects_depth4 = 94, + effects_depth5 = 95, + data_increment = 96, + data_decrement = 97, + nonregistered_lsb = 98, + nonregistered_msb = 99, + registered_lsb = 100, + regustered_msg = 101, + // undefined 102-119 + // channel mode 120-127 +}; + +enum RegisteredParams { + pitch_bend_sensitivity = 0, + fine_tuning = 1, + coarse_tuning = 2, + tuning_program_select = 3, + tuning_bank_select = 4 +}; + + +MIDIMessage program_change(uint8_t channel, uint8_t program_number) { + if (channel > 0x0f || program_number > 119) { + while(1); + } + + return {.status = 0xc0 & channel, + .data1 = program_number, + .len = 2}; +} + +MIDIMessage aftertouch(uint8_t channel, uint8_t pressure_value) { + if (channel > 0x0f || pressure_value > 127) { + while(1); + } + + return {.status = 0xd0 & channel, + .data1 = pressure_value, + .len = 2}; +} + +MIDIMessage pitch_bend_change(uint8_t channel, uint16_t change) { + if (channel > 0x0f || pressure_value < 0x40 || pressure_value > 0x7f7f) { + while(1); + } + + return {.status = 0xe0 & channel, + .data1 = pressure_value, + .len = 2}; +} + +// Channel mode messages. + + +MIDIMessage all_sound_off(uint8_t channel) { + if (channel > 0x0f) { + while(1); + } + + return {.status = 0xb0 & channel, + .data1 = 120, + .data2 = 0}; +} + +MIDIMessage reset_all_controllers(uint8_t channel) { + if (channel > 0x0f) { + while(1); + } + + return {.status = 0xb0 & channel, + .data1 = 121, + .data2 = 0}; +} + +MIDIMessage local_control(uint8_t channel, bool status) { + if (channel > 0x0f) { + while(1); + } + + if (status) { + return {.status = 0xb0 & channel, + .data1 = 122, + .data2 = 127}; + } else { + return {.status = 0xb0 & channel, + .data1 = 122, + .data2 = 0}; + } +} + +MIDIMessage all_notes_off(uint8_t channel) { + if (channel > 0x0f) { + while(1); + } + + return {.status = 0xb0 & channel, + .data1 = 123, + .data2 = 0}; +} + +MIDIMessage omni_toggle(uint8_t channel, bool status) { + if (channel > 0x0f) { + while(1); + } + + if (status) { + return {.status = 0xb0 & channel, + .data1 = 125, + .data2 = 0}; + } else { + return {.status = 0xb0 & channel, + .data1 = 124, + .data2 = 0}; + } +} + +#define RECEIVER_CHANNEL_COUNT 0 +MIDIMessage mono_on(uint8_t channel, uint8_t channel_count) { + if (channel > 0x0f || channel_count > 127) { + while(1); + } + + return {.status = 0xb0 & channel, + .data1 = 126, + .data2 = channel_count}; +} + +MIDIMessage poly_on(uint8_t channel) { + if (channel > 0x0f) { + while(1); + } + + return {.status = 0xb0 & channel, + .data1 = 127, + .data2 = 0}; +} + + +// System common messages. +// Not implemented: +// - MTC Quarter Frame, +// - Song Position Pointer, +// - Song Select, +// - Tune Request. + +const MIDIMessage eox = {.status = 0xf7, .len = 1}; + + +// System real time messages. +// Not implemented: +// - Timing Clock, +// - Start, +// - Continue, +// - Stop, +// - Active Sensing, + +MIDIMessage rst = {.status = 0xff, .len = 1}; + + +// System exclusive messages. + +SysExMessage sysex(uint8_t sub_id, uint8_t* contents, size_t size) { + if (sub_id > 127) { + while(1); + } + + return {.status = 0xf0, + .sub_id = sub_id, + .data = contents, + .size = size}; +} + +// Only implement MIDI Tuning Standard universals +// and general information. + +SysExMessage universal_sysex(uint8_t sub_id1, uint8_t sub_id2, uint8_t* contents, size_t size, bool real_time) { + if (sub_id1 > 127 || sub_id2 > 127) { + while(1); + } + + uint8_t* new = malloc(contents, size + 2); // Might forget to free this... + new[0] = sub_id1; + new[1] = subid2; + memcpy(new + 2, contents, size); + + + if (real_time) { + return sysex(0x7e, new, size + 2); + } else { + return sysex(0x7e, new, size + 2); + } +} + +SysExMessage bulk_tuning_dump(uint8_t) -- cgit v1.2.3