// 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)