From 6380915e6e8d98e2c933ea6eb8df02fb1b123b97 Mon Sep 17 00:00:00 2001 From: Duncan Wilkie Date: Tue, 18 Jul 2023 17:00:44 -0500 Subject: Somewhat finalized base MIDI; got the minimal MIDI-CI done. --- controller/libs/base_midi.c | 6 +- controller/libs/midi_ci.c | 174 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 controller/libs/midi_ci.c (limited to 'controller/libs') diff --git a/controller/libs/base_midi.c b/controller/libs/base_midi.c index e146ec0..6efd2f3 100644 --- a/controller/libs/base_midi.c +++ b/controller/libs/base_midi.c @@ -1,10 +1,10 @@ // 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 "base_midi.h" #include #include - +// TODO: startup function to initialize ParseMemory buffer. #ifdef RUNNING_STATUS uint8_t last_status = 0x00; #endif @@ -83,9 +83,11 @@ void sysex(uint16_t manufacturer_id, uint8_t* contents, size_t contents_len) { pfns.uart_write(manufacturer_id >> 8); pfns.uart_write(manufacturer_id & 0x00ff); } else { +#ifdef DEBUG if (manufacturer_id > MAX_NON_UNIVERSAL_ID) { while(true); // Used universal sysex ID. } +#endif pfns.uart_write(manufacturer_id); } diff --git a/controller/libs/midi_ci.c b/controller/libs/midi_ci.c new file mode 100644 index 0000000..7013d7a --- /dev/null +++ b/controller/libs/midi_ci.c @@ -0,0 +1,174 @@ +// 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); + +} -- cgit v1.2.3