-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
bump cmidi2 and add AAPXS MIDI2 sysex8 parser and generator (WIP).
context: #169 The SysEx8 parser and generator implementation is still standalone. They need to be integrated to `AAPXS[Client|Service]Instance`. It makes use of new cmidi2 functions for SysEx8 parser, so bump cmidi2 too.
- Loading branch information
1 parent
8471699
commit ebc7664
Showing
7 changed files
with
191 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
182 changes: 182 additions & 0 deletions
182
androidaudioplugin/src/main/cpp/core/aap_midi2_helper.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
#ifndef AAP_CORE_HOSTING_UTILITY_H | ||
#define AAP_CORE_HOSTING_UTILITY_H | ||
|
||
#pragma clang diagnostic push | ||
#pragma clang diagnostic ignored "-Wunused-variable" | ||
#pragma clang diagnostic ignored "-Wunused-function" | ||
#pragma clang diagnostic ignored "-Wunused-but-set-variable" | ||
#include <cmidi2.h> | ||
#pragma clang diagnostic pop | ||
|
||
|
||
namespace aap { | ||
static inline void merge_event_inputs(void *mergeTmp, int32_t mergeBufSize, void* eventInputs, int32_t eventInputsSize, aap_buffer_t *buffer, PluginInstance* instance) { | ||
if (eventInputsSize == 0) | ||
return; | ||
for (int i = 0; i < instance->getNumPorts(); i++) { | ||
auto port = instance->getPort(i); | ||
if (port->getContentType() == AAP_CONTENT_TYPE_MIDI2 && port->getPortDirection() == AAP_PORT_DIRECTION_INPUT) { | ||
auto mbh = (AAPMidiBufferHeader*) buffer->get_buffer(*buffer, i); | ||
size_t newSize = cmidi2_ump_merge_sequences((cmidi2_ump*) mergeTmp, mergeBufSize, | ||
(cmidi2_ump*) eventInputs, (size_t) eventInputsSize, | ||
(cmidi2_ump*) mbh + 1, (size_t) mbh->length); | ||
mbh->length = newSize; | ||
memcpy(mbh + 1, mergeTmp, newSize); | ||
return; | ||
} | ||
} | ||
} | ||
|
||
static uint32_t aapMidi2ExtensionHelperGetUInt32(uint8_t* dst) { | ||
if (cmidi2_util_is_platform_little_endian()) | ||
return dst[0] + (dst[1] << 8) + (dst[2] << 16) + (dst[1] << 24); | ||
else | ||
return *((uint32_t*) dst); | ||
} | ||
|
||
static void aapMidi2ExtensionHelperPutUInt32(uint8_t* dst, uint32_t value) { | ||
if (cmidi2_util_is_platform_little_endian()) { | ||
dst[0] = value & 0xFF; | ||
dst[1] = (value >> 8) & 0xFF; | ||
dst[2] = (value >> 16) & 0xFF; | ||
dst[3] = value >> 24; | ||
} | ||
else | ||
*((uint32_t*) dst) = value; | ||
} | ||
|
||
static void* aapMidi2ExtensionInvokeHelperSysEx8Forge(uint64_t data1, uint64_t data2, size_t index, void* context) { | ||
auto forge = (cmidi2_ump_forge*) context; | ||
if (cmidi2_ump_forge_add_packet_128(forge, data1, data2)) | ||
return nullptr; | ||
else | ||
return (void*) true; | ||
} | ||
|
||
/** | ||
* Turns AAPXS extension invocation buffer into MIDI2 UMP. | ||
* | ||
* It requires an additional `uint8_t` buffer (`conversionHelperBuffer`) to | ||
* process the actual conversion without extra memory allocation for RT safety. | ||
* | ||
* @param dst the destination buffer | ||
* @param dstSizeInInt the size of `dst` | ||
* @param conversionHelperBuffer the conversion buffer for temporary storage | ||
* @param conversionHelperBufferSize the size of `conversionHelperBuffer` | ||
* @param group "group" field as in MIDI UMP | ||
* @param uri the extension URI (must be null terminated) | ||
* @param data the extension invocation data | ||
* @param dataSize the size of `data` | ||
* @return `true` if success, `false` for failure (so far insufficient memory only). | ||
*/ | ||
[[maybe_unused]] // FIXME: test! | ||
static bool aapMidi2GenerateAAPXSSysEx8(uint32_t* dst, | ||
size_t dstSizeInInt, | ||
uint8_t* conversionHelperBuffer, | ||
size_t conversionHelperBufferSize, | ||
uint8_t group, | ||
const char* uri, | ||
const uint8_t* data, | ||
size_t dataSize) { | ||
size_t required = 16 + strlen(uri) + 4 + dataSize * sizeof(uint32_t); | ||
if (dstSizeInInt < required || conversionHelperBufferSize < required) | ||
return false; | ||
|
||
uint8_t* sysex = conversionHelperBuffer; | ||
uint8_t* ptr = sysex; | ||
uint8_t sysexStart[] { | ||
0x7Eu, 0x7Fu, // universal sysex | ||
0, 1, // code | ||
0, // extension flags | ||
0, 0, 0, 0 // reserved | ||
}; | ||
memcpy(ptr, sysexStart, sizeof(sysexStart)); | ||
ptr += sizeof(sysexStart); | ||
|
||
// uri_size and uri | ||
size_t strSize = strlen(uri); | ||
aapMidi2ExtensionHelperPutUInt32(ptr, strSize); | ||
ptr += sizeof(uint32_t); | ||
memcpy(ptr, uri, strSize); | ||
ptr += strSize; | ||
|
||
// dataSize and data | ||
aapMidi2ExtensionHelperPutUInt32(ptr, dataSize); | ||
ptr += sizeof(uint32_t); | ||
memcpy(ptr, data, dataSize); | ||
ptr += dataSize; | ||
|
||
// write sysex data to dst. | ||
cmidi2_ump_forge forge; | ||
cmidi2_ump_forge_init(&forge, dst, dstSizeInInt); | ||
cmidi2_ump_sysex8_process(group, sysex, ptr - sysex, 0, | ||
aapMidi2ExtensionInvokeHelperSysEx8Forge, &forge); | ||
|
||
return true; | ||
} | ||
|
||
struct aap_midi2_aapxs_parse_context { | ||
uint8_t group; | ||
char uri[AAP_MAX_EXTENSION_URI_SIZE]; // must be null-terminated | ||
uint8_t *data; // buffer must be allocated by the parsing host | ||
uint32_t dataSize; // parsed result | ||
uint8_t* conversionHelperBuffer; | ||
size_t conversionHelperBufferSize; | ||
}; | ||
|
||
cmidi2_ump_binary_read_state* sysex8_binary_reader_helper_select_stream(uint8_t targetStreamId, void* context) { | ||
return (cmidi2_ump_binary_read_state*) context; | ||
} | ||
|
||
// Reads AAPXS SysEx8 UMP into `aap_midi2_aapxs_parse_context`. | ||
// Returns true if it is successfully parsed and it turned out to be AAPXS SysEx8. | ||
// In any other case, return false. | ||
[[maybe_unused]] // FIXME: test! | ||
static bool aapMidi2ParseAAPXSysEx8(aap_midi2_aapxs_parse_context* context, uint8_t* umpData, size_t umpSize) { | ||
cmidi2_ump* ump = (cmidi2_ump*) umpData; | ||
if (cmidi2_ump_get_message_type(ump) != CMIDI2_MESSAGE_TYPE_SYSEX8_MDS) | ||
return false; | ||
if (cmidi2_ump_get_status_code(ump) != CMIDI2_SYSEX_START) | ||
return false; | ||
|
||
// Retrieve simple binary data array. | ||
context->group = cmidi2_ump_get_group(ump); | ||
cmidi2_ump_binary_read_state state; | ||
cmidi2_ump_binary_read_state_init(&state, nullptr, context->conversionHelperBuffer, context->conversionHelperBufferSize, false); | ||
if (cmidi2_ump_get_sysex8_data(sysex8_binary_reader_helper_select_stream, &state, nullptr, ump, umpSize / 4) == 0) | ||
return false; | ||
if (state.resultCode != CMIDI2_BINARY_READER_RESULT_COMPLETE) | ||
return false; | ||
|
||
// Now state.data (== context->conversionHelperBuffer) contains the entire SysEx8 buffer. | ||
// It's time to parse the buffer according to the AAPXS SysEx8: | ||
// > `[5g sz si 7E] [7F co-de ext-flag] [re-se-rv-ed] [uri-size] [..uri..] [value-size] [..value..]` | ||
|
||
// Check if it is long enough to contain the expected data... | ||
if (state.dataSize < 20) | ||
return false; | ||
|
||
uint8_t* data = state.data; | ||
// Check if this sysex8 is Universal SysEx, and contains code field for AAPXS | ||
if (data[3] != 0x7E || data[4] != 0x7F || data[5] != 0 || data[6] != 1) | ||
return false; | ||
|
||
// filling in results... | ||
context->group = data[0]; | ||
|
||
size_t uriSize = aapMidi2ExtensionHelperGetUInt32(data + 12); | ||
if (state.dataSize < 20 + uriSize) | ||
return false; | ||
memcpy(context->uri, data + 16, uriSize); | ||
|
||
size_t dataSize = aapMidi2ExtensionHelperGetUInt32(data + 16 + uriSize); | ||
if (state.dataSize < 20 + uriSize + dataSize) | ||
return false; | ||
memcpy(context->data, data + 16 + uriSize, dataSize); | ||
context->dataSize = dataSize; | ||
|
||
return true; | ||
} | ||
} | ||
|
||
#endif //AAP_CORE_HOSTING_UTILITY_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters