// An implementation of the MIDI Capability Inquiry v1.2 Specification, over a MIDI 1.0 transport, // with only Property Exchange support. // Intended to be read alongside the corresponding specs; specifically in the order: // - MIDI CI Specification, minimum requirements + Property Exchange chapter, // - Common Rules for MIDI CI Property Exchange. #include "base_midi.h" uint32_t our_muid = rand(); // TODO: think. #define MIDI_CI_SUBID 0x0d #define PROFILE_CONFIGURATION_MASK 0x20 #define PROPERTY_EXCHANGE_MASK 0x30 #define PROCESS_INQUIRY_MASK 0x40 #define MANAGEMENT_MASK 0x70 #define MIDI_CI_VERSION 0x02 #define TO_FUNCTION_BLOCK_ID 0x7f static void ci(uint8_t source_target, uint8_t message_type, uint32_t destination_miud, uint8_t *contents, uint8_t contents_len) { uint8_t *new = malloc(1 + 4 + 4 + contents_len); new[0] = MIDI_CI_VERSION; new[1] = our_muid & 0x00'00'00'ff; new[2] = (our_muid >> 8) & 0x00'00'00'ff; new[3] = (our_muid >> 16) & 0x00'00'00'ff; new[4] = (our_muid >> 24) & 0x00'00'00'ff; new[8] = target_muid & 0x00'00'00'ff; new[7] = (target_muid >> 8) & 0x00'00'00'ff; new[6] = (target_muid >> 16) & 0x00'00'00'ff; new[5] = (target_muid >> 24) & 0x00'00'00'ff; memcpy(new + 9, contents, contents_len); universal_nonrealtime(source_target, (MIDI_CI_SUBID << 8) | message_type, contents, 1 + 4 + 4 + contents_len); free(new); } #define DISCOVERY_SUBID (MANAGEMENT_MASK | 0x00) #define CI_CATEGORY_SUPPORTED 0b00011100 #define MAX_SYSEX_SIZE 512 #define BROADCAST_MUID 0x7f'7f'7f'7f #define SINGLE_OUTPUT_PATH 0x00 void discovery() { uint8_t *new = malloc(3 + 2 + 2 + 4 + 1 + 4 + 1); #if (DEVICE_MANUFACTURER <= MAX_DATA_BYTE) new[0] = DEVICE_MANUFACTURER; new[1] = 0; new[2] = 0; #else new[0] = 0; new[1] = DEVICE_MANUFACTURER >> 8; new[2] = DEVICE_MANUFACTURER & 0x00ff; #endif new[3] = DEVICE_FAMILY & 0x00ff; new[4] = DEVICE_FAMILY >> 8; new[5] = DEVICE_MODEL & 0x00ff; new[6] = DEVICE_MODEL >> 8; new[7] = SOFTWARE_REVISION & 0x00'00'00'ff; new[8] = (SOFTWARE_REVISION >> 8) & 0x00'00'00'ff; new[9] = (SOFTWARE_REVISION >> 16) & 0x00'00'00'ff; new[10] = (SOFTWARE_REVISION >> 24) & 0x00'00'00'ff; new[11] = CI_CATEGORY_SUPPORTED; new[12] = MAX_SYSEX_SIZE & 0x00'00'00'ff; new[13] = (MAX_SYSEX_SIZE >> 8) & 0x00'00'00'ff; new[14] = (MAX_SYSEX_SIZE >> 16) & 0x00'00'00'ff; new[15] = (MAX_SYSEX_SIZE >> 24) & 0x00'00'00'ff; new[16] = SINGLE_OUTPUT_PATH; ci(TO_FUNCTION_BLOCK_ID, DISCOVERY_SUBID, BROADCAST_MUID, new, 17); free(new); } #define REPLY_TO_DISCOVERY_SUBID 0x71 #define NO_FUNCTION_BLOCK 0x7f void discovery_reply(uint8_t initiator_muid, uint8_t initiator_output_path_id) { uint8_t *new = malloc(3 + 2 + 2 + 4 + 1 + 4 + 1 + 1); #if (DEVICE_MANUFACTURER <= MAX_DATA_BYTE) new[0] = DEVICE_MANUFACTURER; new[1] = 0; new[2] = 0; #else new[0] = 0; new[1] = DEVICE_MANUFACTURER >> 8; new[2] = DEVICE_MANUFACTURER & 0x00ff; #endif new[3] = DEVICE_FAMILY & 0x00ff; new[4] = DEVICE_FAMILY >> 8; new[5] = DEVICE_MODEL & 0x00ff; new[6] = DEVICE_MODEL >> 8; new[7] = SOFTWARE_REVISION & 0x00'00'00'ff; new[8] = (SOFTWARE_REVISION >> 8) & 0x00'00'00'ff; new[9] = (SOFTWARE_REVISION >> 16) & 0x00'00'00'ff; new[10] = (SOFTWARE_REVISION >> 24) & 0x00'00'00'ff; new[11] = CI_CATEGORY_SUPPORTED; new[12] = MAX_SYSEX_SIZE & 0x00'00'00'ff; new[13] = (MAX_SYSEX_SIZE >> 8) & 0x00'00'00'ff; new[14] = (MAX_SYSEX_SIZE >> 16) & 0x00'00'00'ff; new[15] = (MAX_SYSEX_SIZE >> 24) & 0x00'00'00'ff; new[16] = initiator_output_path_id; new[17] = NO_FUNCTION_BLOCK; ci(TO_FUNCTION_BLOCK_ID, REPLY_TO_DISCOVERY_SUBID, initiator_muid, new, 18); free(new); } // TODO: Decide if Inquiry: Endpoint and Reply to Endpoint are necessary for standard conformace if we're over MIDI 1.0. // TODO: should we set a new MUID inside this function? #define INVALIDATE_MUID_SUBID 0x7e void invalidate_muid() { uint8_t *new = malloc(4); new[0] = our_muid & 0x00'00'00'ff; new[1] = (our_muid >> 8) & 0x00'00'00'ff; new[2] = (our_muid >> 16) & 0x00'00'00'ff; new[3] = (our_muid >> 24) & 0x00'00'00'ff; ci(TO_FUNCTION_BLOCK_ID, BROADCAST_MUID, new, 4); free(new); } // TODO: consider structifying? #define ACK_SUBID 0x7d void ci_ack(uint8_t acking_device_id, uint8_t acking_muid, uint8_t original_classification, uint8_t status_code, uint8_t status_data, uint8_t details1, uint8_t details2, uint8_t details3, uint8_t details4, uint8_t details5, uint16_t length, uint8_t *text) { uint8_t *new = malloc(1 + 1 + 1 + 5 + 2 + length); new[0] = original_classification; new[1] = status_code; new[2] = status_data; new[3] = details1; new[4] = details2; new[5] = details3; new[6] = details4; new[7] = details5; new[8] = length & 0x00ff; new[9] = length >> 8; memcpy(new + 10, text, length); ci(acking_device_id, ACK_SUBID, acking_muid, new, 1 + 1 + 1 + 5 + 2 + length); free(new); } #define NAK_SUBID 0x7f void ci_nack(uint8_t naking_device_id, uint8_t naking_muid, uint8_t original_classification, uint8_t status_code, uint8_t status_data, uint8_t details1, uint8_t details2, uint8_t details3, uint8_t details4, uint8_t details5, uint16_t length, uint8_t *text) { uint8_t *new = malloc(1 + 1 + 1 + 5 + 2 + length); new[0] = original_classification; new[1] = status_code; new[2] = status_data; new[3] = details1; new[4] = details2; new[5] = details3; new[6] = details4; new[7] = details5; new[8] = length & 0x00ff; new[9] = length >> 8; memcpy(new + 10, text, length); ci(naking_device_id, NAK_SUBID, naking_muid, new, 1 + 1 + 1 + 5 + 2 + length); free(new); }