summaryrefslogtreecommitdiff
path: root/controller/libs/midi.c
diff options
context:
space:
mode:
Diffstat (limited to 'controller/libs/midi.c')
-rw-r--r--controller/libs/midi.c347
1 files changed, 347 insertions, 0 deletions
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 <stdint.h>
+#include <stdbool.h>
+
+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)