From fd1e00fa758a991bc37eb457044132aa6f9b85ae Mon Sep 17 00:00:00 2001 From: Atsushi Eno Date: Thu, 28 Sep 2023 15:33:22 +0900 Subject: [PATCH] [WIP] async realtime AAPXS work. It implements basic workflow for asynchronous realtime AAPXS support. context: https://github.com/atsushieno/aap-core/issues/169 https://github.com/atsushieno/aap-core/issues/176 --- .../src/main/cpp/CMakeLists.txt | 1 + .../core/hosting/AAPXSMidi2ClientSession.cpp | 58 ++++++++++++++ .../cpp/core/hosting/AAPXSMidi2Processor.cpp | 35 ++++++++- .../src/main/cpp/core/hosting/PluginHost.cpp | 8 +- .../cpp/core/hosting/PluginInstance.Local.cpp | 53 ++++++++++++- .../core/hosting/PluginInstance.Remote.cpp | 77 +++++++++++++++++-- .../main/cpp/core/hosting/PluginInstance.cpp | 75 ++++++------------ include/aap/core/AAPXSMidi2ClientSession.h | 38 +++++++++ include/aap/core/AAPXSMidi2Processor.h | 12 ++- include/aap/core/aap_midi2_helper.h | 2 + include/aap/core/host/plugin-host.h | 2 +- include/aap/core/host/plugin-instance.h | 41 +++++----- 12 files changed, 311 insertions(+), 91 deletions(-) create mode 100644 androidaudioplugin/src/main/cpp/core/hosting/AAPXSMidi2ClientSession.cpp create mode 100644 include/aap/core/AAPXSMidi2ClientSession.h diff --git a/androidaudioplugin/src/main/cpp/CMakeLists.txt b/androidaudioplugin/src/main/cpp/CMakeLists.txt index 2c46030c..bfa0d8a9 100644 --- a/androidaudioplugin/src/main/cpp/CMakeLists.txt +++ b/androidaudioplugin/src/main/cpp/CMakeLists.txt @@ -4,6 +4,7 @@ project(androidaudioplugin LANGUAGES CXX) # List of sources. Android build has some additional sources. set (androidaudioplugin_SOURCES + "core/hosting/AAPXSMidi2ClientSession.cpp" "core/hosting/AAPXSMidi2Processor.cpp" "core/hosting/PluginInformation.cpp" "core/hosting/PluginInstance.cpp" diff --git a/androidaudioplugin/src/main/cpp/core/hosting/AAPXSMidi2ClientSession.cpp b/androidaudioplugin/src/main/cpp/core/hosting/AAPXSMidi2ClientSession.cpp new file mode 100644 index 00000000..12eeb679 --- /dev/null +++ b/androidaudioplugin/src/main/cpp/core/hosting/AAPXSMidi2ClientSession.cpp @@ -0,0 +1,58 @@ + + +#include "aap/core/AAPXSMidi2ClientSession.h" +#include +#include "aap/core/aap_midi2_helper.h" +#include "aap/ext/midi.h" + +aap::AAPXSMidi2ClientSession::AAPXSMidi2ClientSession(int32_t midiBufferSize) + : midi_buffer_size(midiBufferSize) { + aapxs_rt_midi_buffer = (uint8_t*) calloc(1, midi_buffer_size); + aapxs_rt_conversion_helper_buffer = (uint8_t*) calloc(1, midi_buffer_size); + aap_midi2_aapxs_parse_context_prepare(&aapxs_parse_context, + aapxs_rt_midi_buffer, + aapxs_rt_conversion_helper_buffer, + AAP_MIDI2_AAPXS_DATA_MAX_SIZE); +} + +aap::AAPXSMidi2ClientSession::~AAPXSMidi2ClientSession() { + if (aapxs_rt_midi_buffer) + free(aapxs_rt_midi_buffer); + if (aapxs_rt_conversion_helper_buffer) + free(aapxs_rt_conversion_helper_buffer); +} + +void aap::AAPXSMidi2ClientSession::addSession( + void (*addMidi2Event)(AAPXSMidi2ClientSession * session, void *userData, int32_t messageSize), + void* addMidi2EventUserData, + int32_t group, + int32_t requestId, + AAPXSClientInstance *aapxsInstance, + int32_t messageSize, + int32_t opcode) { + size_t size = aap_midi2_generate_aapxs_sysex8((uint32_t*) aapxs_rt_midi_buffer, + midi_buffer_size / sizeof(int32_t), + (uint8_t*) aapxs_rt_conversion_helper_buffer, + midi_buffer_size, + group, + requestId, + aapxsInstance->uri, + opcode, + (uint8_t*) aapxsInstance->data, + messageSize); + addMidi2Event(this, addMidi2EventUserData, size); +} + +void aap::AAPXSMidi2ClientSession::processReply(void* buffer) { + auto mbh = (AAPMidiBufferHeader *) buffer; + void* data = mbh + 1; + CMIDI2_UMP_SEQUENCE_FOREACH(data, mbh->length, iter) { + auto umpSize = mbh->length - ((uint8_t*) iter - (uint8_t*) data); + if (aap_midi2_parse_aapxs_sysex8(&aapxs_parse_context, iter, umpSize)) + handle_reply(&aapxs_parse_context); + + // FIXME: should we remove those AAPXS SysEx8 from the UMP buffer? + // It is going to be extraneous to the host. + } + +} \ No newline at end of file diff --git a/androidaudioplugin/src/main/cpp/core/hosting/AAPXSMidi2Processor.cpp b/androidaudioplugin/src/main/cpp/core/hosting/AAPXSMidi2Processor.cpp index baf9dc77..3da53b30 100644 --- a/androidaudioplugin/src/main/cpp/core/hosting/AAPXSMidi2Processor.cpp +++ b/androidaudioplugin/src/main/cpp/core/hosting/AAPXSMidi2Processor.cpp @@ -23,11 +23,40 @@ void aap::AAPXSMidi2Processor::process(void* buffer) { void* data = mbh + 1; CMIDI2_UMP_SEQUENCE_FOREACH(data, mbh->length, iter) { auto umpSize = mbh->length - ((uint8_t*) iter - (uint8_t*) data); - if (aap_midi2_parse_aapxs_sysex8(&aapxs_parse_context, iter, umpSize)) + if (aap_midi2_parse_aapxs_sysex8(&aapxs_parse_context, iter, umpSize)) { call_extension(&aapxs_parse_context); - // FIXME: should we remove those extension bytes from the UMP buffer? - // It is going to be extraneous to the plugin. + auto ump = (cmidi2_ump*) iter; + ump += 4; + while (cmidi2_ump_get_message_type(ump) == CMIDI2_MESSAGE_TYPE_SYSEX8_MDS && + cmidi2_ump_get_status_code(ump) < CMIDI2_SYSEX_END) + ump += 4; + iter = (uint8_t*) ump; + } + + // FIXME: should we remove those AAPXS SysEx8 bytes from the UMP buffer? + // It is going to be extraneous to the rest of the plugin processing. } } + +void aap::AAPXSMidi2Processor::addReply( + void (*addMidi2Event)(AAPXSMidi2Processor * processor, void *userData, int32_t messageSize), + void* addMidi2EventUserData, + int32_t group, + int32_t requestId, + AAPXSServiceInstance *aapxsInstance, + int32_t messageSize, + int32_t opcode) { + size_t size = aap_midi2_generate_aapxs_sysex8((uint32_t*) midi2_aapxs_data_buffer, + AAP_MIDI2_AAPXS_DATA_MAX_SIZE / sizeof(int32_t), + (uint8_t*) midi2_aapxs_conversion_helper_buffer, + AAP_MIDI2_AAPXS_DATA_MAX_SIZE, + group, + requestId, + aapxsInstance->uri, + opcode, + (uint8_t*) aapxsInstance->data, + messageSize); + addMidi2Event(this, addMidi2EventUserData, size); +} diff --git a/androidaudioplugin/src/main/cpp/core/hosting/PluginHost.cpp b/androidaudioplugin/src/main/cpp/core/hosting/PluginHost.cpp index 84948f58..56c7f7fb 100644 --- a/androidaudioplugin/src/main/cpp/core/hosting/PluginHost.cpp +++ b/androidaudioplugin/src/main/cpp/core/hosting/PluginHost.cpp @@ -49,9 +49,11 @@ void initializeStandardAAPXSRegistry() { } -aap::PluginHost::PluginHost(PluginListSnapshot* contextPluginList, AAPXSRegistry* aapxsRegistry) - : plugin_list(contextPluginList) -{ +aap::PluginHost::PluginHost(PluginListSnapshot* contextPluginList, + AAPXSRegistry* aapxsRegistry, + int32_t eventMidi2InputBufferSize) + : plugin_list(contextPluginList), + event_midi2_input_buffer_size(eventMidi2InputBufferSize) { assert(contextPluginList); if (standard_aapxs_registry == nullptr) diff --git a/androidaudioplugin/src/main/cpp/core/hosting/PluginInstance.Local.cpp b/androidaudioplugin/src/main/cpp/core/hosting/PluginInstance.Local.cpp index f4c09047..0560f532 100644 --- a/androidaudioplugin/src/main/cpp/core/hosting/PluginInstance.Local.cpp +++ b/androidaudioplugin/src/main/cpp/core/hosting/PluginInstance.Local.cpp @@ -3,6 +3,13 @@ #define LOG_TAG "AAP.Local.Instance" + +void aapxsProcessorAddEventUmpOutput(aap::AAPXSMidi2Processor* processor, void* context, int32_t messageSize) { + auto instance = (aap::LocalPluginInstance *) context; + instance->addEventUmpOutput(processor->midi2_aapxs_data_buffer, messageSize); +} + + aap::LocalPluginInstance::LocalPluginInstance(PluginHost *host, AAPXSRegistry *aapxsRegistry, int32_t instanceId, @@ -23,6 +30,17 @@ aap::LocalPluginInstance::LocalPluginInstance(PluginHost *host, // We need to copy extension data buffer before calling it. memcpy(aapxsInstance->data, (int32_t*) context->data, context->dataSize); controlExtension(context->uri, context->opcode); + + // FIXME: this should be called only at the *end* of controlExtension() + // which should be asynchronously handled. + aapxs_midi2_processor.addReply(aapxsProcessorAddEventUmpOutput, + this, + context->group, + context->request_id, + aapxsInstance, + context->dataSize, + context->opcode + ); }); } @@ -149,7 +167,20 @@ void aap::LocalPluginInstance::requestProcessToHost() { ((PluginService*) host)->requestProcessToHost(instance_id); } -void aap::LocalPluginInstance::processAAPXSSysEx8Input() { +const char* local_trace_name = "AAP::LocalPluginInstance_process"; +void aap::LocalPluginInstance::process(int32_t frameCount, int32_t timeoutInNanoseconds) { + process_requested_to_host = false; + + struct timespec timeSpecBegin{}, timeSpecEnd{}; +#if ANDROID + if (ATrace_isEnabled()) { + ATrace_beginSection(local_trace_name); + clock_gettime(CLOCK_REALTIME, &timeSpecBegin); + } +#endif + + // retrieve AAPXS SysEx8 requests and start extension calls, if any. + // (might be synchronously done) for (auto i = 0, n = getNumPorts(); i < n; i++) { auto port = getPort(i); if (port->getContentType() != AAP_CONTENT_TYPE_MIDI2 || @@ -159,4 +190,24 @@ void aap::LocalPluginInstance::processAAPXSSysEx8Input() { void* data = aapBuffer->get_buffer(*aapBuffer, i); aapxs_midi2_processor.process(data); } + + plugin->process(plugin, getAudioPluginBuffer(), frameCount, timeoutInNanoseconds); + + // before sending back to host, merge AAPXS SysEx8 UMPs from async extension calls + // into the plugin's MIDI output buffer. + if (std::unique_lock tryLock(ump_sequence_merger_mutex, std::try_to_lock); tryLock.owns_lock()) { + merge_ump_sequences(AAP_PORT_DIRECTION_OUTPUT, event_midi2_merge_buffer, event_midi2_buffer_size, + event_midi2_buffer, event_midi2_buffer_offset, + getAudioPluginBuffer(), this); + event_midi2_buffer_offset = 0; + } + +#if ANDROID + if (ATrace_isEnabled()) { + clock_gettime(CLOCK_REALTIME, &timeSpecEnd); + ATrace_setCounter(local_trace_name, + (timeSpecEnd.tv_sec - timeSpecBegin.tv_sec) * 1000000000 + timeSpecEnd.tv_nsec - timeSpecBegin.tv_nsec); + ATrace_endSection(); + } +#endif } diff --git a/androidaudioplugin/src/main/cpp/core/hosting/PluginInstance.Remote.cpp b/androidaudioplugin/src/main/cpp/core/hosting/PluginInstance.Remote.cpp index 446588f5..f57e92c7 100644 --- a/androidaudioplugin/src/main/cpp/core/hosting/PluginInstance.Remote.cpp +++ b/androidaudioplugin/src/main/cpp/core/hosting/PluginInstance.Remote.cpp @@ -14,8 +14,14 @@ aap::RemotePluginInstance::RemotePluginInstance(PluginClient* client, client(client), aapxs_registry(aapxsRegistry), standards(this), - aapxs_manager(std::make_unique(this)) { + aapxs_manager(std::make_unique(this)), + aapxs_session(eventMidi2InputBufferSize) { shared_memory_store = new ClientPluginSharedMemoryStore(); + + aapxs_session.setReplyHandler([&](aap_midi2_aapxs_parse_context* context) { + auto aapxsInstance = aapxs_manager->getInstanceFor(context->uri); + aapxsInstance->endAsyncCall(context); + }); } void aap::RemotePluginInstance::configurePorts() { @@ -48,15 +54,29 @@ AndroidAudioPluginHost* aap::RemotePluginInstance::getHostFacadeForCompleteInsta return &plugin_host_facade; } +void aapxsSessionAddEventUmpInput(aap::AAPXSMidi2ClientSession* client, void* context, int32_t messageSize) { + auto instance = (aap::RemotePluginInstance *) context; + instance->addEventUmpInput(client->aapxs_rt_midi_buffer, messageSize); +} + void aap::RemotePluginInstance::sendExtensionMessage(const char *uri, int32_t messageSize, int32_t opcode) { auto aapxsInstance = aapxs_manager->getInstanceFor(uri); - // Here we have to get a native plugin instance and send extension message. - // It is kind af annoying because we used to implement Binder-specific part only within the - // plugin API (binder-client-as-plugin.cpp)... - // So far, instead of rewriting a lot of code to do so, we let AAPClientContext - // assign its implementation details that handle Binder messaging as a std::function. - send_extension_message_impl(aapxsInstance->uri, getInstanceId(), messageSize, opcode); + // If it is at ACTIVE state it has to switch to AAPXS SysEx8 MIDI messaging mode, + // otherwise it goes to the Binder route. + if (instantiation_state == PLUGIN_INSTANTIATION_STATE_ACTIVE) { + // aapxsInstance already contains binary data here, so we retrieve data from there. + int32_t group = 0; // will we have to give special semantics on it? + int32_t requestId = aapxsSysEx8RequestSerial(); + aapxs_session.addSession(aapxsSessionAddEventUmpInput, this, group, requestId, aapxsInstance, messageSize, opcode); + } else { + // Here we have to get a native plugin instance and send extension message. + // It is kind af annoying because we used to implement Binder-specific part only within the + // plugin API (binder-client-as-plugin.cpp)... + // So far, instead of rewriting a lot of code to do so, we let AAPClientContext + // assign its implementation details that handle Binder messaging as a std::function. + send_extension_message_impl(aapxsInstance->uri, getInstanceId(), messageSize, opcode); + } } void aap::RemotePluginInstance::prepare(int frameCount) { @@ -120,6 +140,48 @@ uint32_t aap::RemotePluginInstance::aapxsSysEx8RequestSerial() { return aapxs_request_id_serial++; } +void aap::RemotePluginInstance::process(int32_t frameCount, int32_t timeoutInNanoseconds) { + const char* remote_trace_name = "AAP::RemotePluginInstance_process"; + struct timespec timeSpecBegin{}, timeSpecEnd{}; +#if ANDROID + if (ATrace_isEnabled()) { + ATrace_beginSection(remote_trace_name); + clock_gettime(CLOCK_REALTIME, &timeSpecBegin); + } +#endif + + // merge input from UI with the host's MIDI inputs + if (std::unique_lock tryLock(ump_sequence_merger_mutex, std::try_to_lock); tryLock.owns_lock()) { + merge_ump_sequences(AAP_PORT_DIRECTION_INPUT, event_midi2_merge_buffer, event_midi2_buffer_size, + event_midi2_buffer, event_midi2_buffer_offset, + getAudioPluginBuffer(), this); + event_midi2_buffer_offset = 0; + } + + // now we can pass the input to the plugin. + plugin->process(plugin, getAudioPluginBuffer(), frameCount, timeoutInNanoseconds); + + // retrieve AAPXS SysEx8 replies if any. + for (auto i = 0, n = getNumPorts(); i < n; i++) { + auto port = getPort(i); + if (port->getContentType() != AAP_CONTENT_TYPE_MIDI2 || + port->getPortDirection() != AAP_PORT_DIRECTION_OUTPUT) + continue; + auto aapBuffer = getAudioPluginBuffer(); + void* data = aapBuffer->get_buffer(*aapBuffer, i); + aapxs_session.processReply(data); + } + +#if ANDROID + if (ATrace_isEnabled()) { + clock_gettime(CLOCK_REALTIME, &timeSpecEnd); + ATrace_setCounter(remote_trace_name, + (timeSpecEnd.tv_sec - timeSpecBegin.tv_sec) * 1000000000 + timeSpecEnd.tv_nsec - timeSpecBegin.tv_nsec); + ATrace_endSection(); + } +#endif +} + //---- aap::RemotePluginInstance::RemotePluginNativeUIController::RemotePluginNativeUIController(RemotePluginInstance* owner) { @@ -156,4 +218,3 @@ void aap::RemoteAAPXSManager::staticSendExtensionMessage(AAPXSClientInstance* cl auto thisObj = (RemotePluginInstance*) clientInstance->host_context; thisObj->sendExtensionMessage(clientInstance->uri, messageSize, opcode); } - diff --git a/androidaudioplugin/src/main/cpp/core/hosting/PluginInstance.cpp b/androidaudioplugin/src/main/cpp/core/hosting/PluginInstance.cpp index dcf6fa84..6959f003 100644 --- a/androidaudioplugin/src/main/cpp/core/hosting/PluginInstance.cpp +++ b/androidaudioplugin/src/main/cpp/core/hosting/PluginInstance.cpp @@ -12,12 +12,12 @@ aap::PluginInstance::PluginInstance(const PluginInformation* pluginInformation, instantiation_state(PLUGIN_INSTANTIATION_STATE_INITIAL), plugin(nullptr), pluginInfo(pluginInformation), - event_midi2_input_buffer_size(eventMidi2InputBufferSize) { + event_midi2_buffer_size(eventMidi2InputBufferSize) { assert(pluginInformation); assert(loadedPluginFactory); - assert(event_midi2_input_buffer_size > 0); - event_midi2_input_buffer = calloc(1, event_midi2_input_buffer_size); - event_midi2_input_buffer_merged = calloc(1, event_midi2_input_buffer_size); + assert(event_midi2_buffer_size > 0); + event_midi2_buffer = calloc(1, event_midi2_buffer_size); + event_midi2_merge_buffer = calloc(1, event_midi2_buffer_size); } aap::PluginInstance::~PluginInstance() { @@ -26,10 +26,10 @@ aap::PluginInstance::~PluginInstance() { plugin_factory->release(plugin_factory, plugin); plugin = nullptr; delete shared_memory_store; - if (event_midi2_input_buffer) - free(event_midi2_input_buffer); - if (event_midi2_input_buffer_merged) - free(event_midi2_input_buffer_merged); + if (event_midi2_buffer) + free(event_midi2_buffer); + if (event_midi2_merge_buffer) + free(event_midi2_merge_buffer); } aap_buffer_t* aap::PluginInstance::getAudioPluginBuffer() { @@ -151,59 +151,34 @@ void aap::PluginInstance::deactivate() { void aap::PluginInstance::addEventUmpInput(void *input, int32_t size) { - const std::lock_guard lock{event_input_buffer_mutex}; - if (event_midi2_input_buffer_offset + size > event_midi2_input_buffer_size) + const std::lock_guard lock{ump_sequence_merger_mutex}; + if (event_midi2_buffer_offset + size > event_midi2_buffer_size) return; - memcpy((uint8_t *) event_midi2_input_buffer + event_midi2_input_buffer_offset, + memcpy((uint8_t *) event_midi2_buffer + event_midi2_buffer_offset, input, size); - event_midi2_input_buffer_offset += size; + event_midi2_buffer_offset += size; } -const char* remote_trace_name = "AAP::RemotePluginInstance_process"; -const char* local_trace_name = "AAP::LocalPluginInstance_process"; - -void aap::PluginInstance::process(int32_t frameCount, int32_t timeoutInNanoseconds) { - struct timespec timeSpecBegin{}, timeSpecEnd{}; -#if ANDROID - if (ATrace_isEnabled()) { - ATrace_beginSection(this->pluginInfo->isOutProcess() ? remote_trace_name : local_trace_name); - clock_gettime(CLOCK_REALTIME, &timeSpecBegin); - } -#endif - - // merge input from UI with the host's MIDI inputs - if (std::unique_lock tryLock(event_input_buffer_mutex, std::try_to_lock); tryLock.owns_lock()) { - merge_event_inputs(event_midi2_input_buffer_merged, event_midi2_input_buffer_size, - event_midi2_input_buffer, event_midi2_input_buffer_offset, - getAudioPluginBuffer(), this); - event_midi2_input_buffer_offset = 0; - } - - // retrieve AAPXS SysEx8 requests if any. - processAAPXSSysEx8Input(); - - // now we can pass the input to the plugin. - plugin->process(plugin, getAudioPluginBuffer(), frameCount, timeoutInNanoseconds); - -#if ANDROID - if (ATrace_isEnabled()) { - clock_gettime(CLOCK_REALTIME, &timeSpecEnd); - ATrace_setCounter(this->pluginInfo->isOutProcess() ? remote_trace_name : local_trace_name, - (timeSpecEnd.tv_sec - timeSpecBegin.tv_sec) * 1000000000 + timeSpecEnd.tv_nsec - timeSpecBegin.tv_nsec); - ATrace_endSection(); - } -#endif +void aap::PluginInstance::addEventUmpOutput(void *input, int32_t size) { + // unlike client side, we are not multithreaded during the audio processing, + // but multiple async extension calls may race, so lock here too. + const std::lock_guard lock{ump_sequence_merger_mutex}; + if (event_midi2_buffer_offset + size > event_midi2_buffer_size) + return; + memcpy((uint8_t *) event_midi2_buffer + event_midi2_buffer_offset, + input, size); + event_midi2_buffer_offset += size; } -void aap::PluginInstance::merge_event_inputs(void *mergeTmp, int32_t mergeBufSize, void* eventInputs, int32_t eventInputsSize, aap_buffer_t *buffer, PluginInstance* instance) { - if (eventInputsSize == 0) +void aap::PluginInstance::merge_ump_sequences(aap_port_direction portDirection, void *mergeTmp, int32_t mergeBufSize, void* sequence, int32_t sequenceSize, aap_buffer_t *buffer, PluginInstance* instance) { + if (sequenceSize == 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) { + if (port->getContentType() == AAP_CONTENT_TYPE_MIDI2 && port->getPortDirection() == portDirection) { 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*) sequence, (size_t) sequenceSize, (cmidi2_ump*) mbh + 1, (size_t) mbh->length); mbh->length = newSize; memcpy(mbh + 1, mergeTmp, newSize); diff --git a/include/aap/core/AAPXSMidi2ClientSession.h b/include/aap/core/AAPXSMidi2ClientSession.h new file mode 100644 index 00000000..050790a1 --- /dev/null +++ b/include/aap/core/AAPXSMidi2ClientSession.h @@ -0,0 +1,38 @@ +#ifndef AAP_CORE_AAPXSMIDI2CLIENTSESSION_H +#define AAP_CORE_AAPXSMIDI2CLIENTSESSION_H + +#include +#include +#include "aap/aapxs.h" +#include "aap_midi2_helper.h" + +namespace aap { + class AAPXSMidi2ClientSession { + int32_t midi_buffer_size; + aap_midi2_aapxs_parse_context aapxs_parse_context{}; + std::function handle_reply; + + public: + AAPXSMidi2ClientSession(int32_t midiBufferSize); + ~AAPXSMidi2ClientSession(); + + uint8_t *aapxs_rt_midi_buffer{nullptr}; + uint8_t *aapxs_rt_conversion_helper_buffer{nullptr}; + + void setReplyHandler(std::function handleReply) { + handle_reply = handleReply; + } + + void addSession(void (*addMidi2Event)(AAPXSMidi2ClientSession *, void *, int32_t), + void* addMidi2EventUserData, + int32_t group, + int32_t requestId, + AAPXSClientInstance *aapxsInstance, + int32_t messageSize, + int32_t opcode); + + void processReply(void* buffer); + }; +} + +#endif //AAP_CORE_AAPXSMIDI2CLIENTSESSION_H diff --git a/include/aap/core/AAPXSMidi2Processor.h b/include/aap/core/AAPXSMidi2Processor.h index 352f650f..e43696ab 100644 --- a/include/aap/core/AAPXSMidi2Processor.h +++ b/include/aap/core/AAPXSMidi2Processor.h @@ -4,11 +4,10 @@ #include #include "../android-audio-plugin.h" #include "aap_midi2_helper.h" +#include "aap/aapxs.h" namespace aap { class AAPXSMidi2Processor { - uint8_t* midi2_aapxs_data_buffer{nullptr}; - uint8_t* midi2_aapxs_conversion_helper_buffer{nullptr}; aap_midi2_aapxs_parse_context aapxs_parse_context{}; std::function call_extension; @@ -16,11 +15,20 @@ namespace aap { AAPXSMidi2Processor(); virtual ~AAPXSMidi2Processor(); + uint8_t* midi2_aapxs_data_buffer{nullptr}; + uint8_t* midi2_aapxs_conversion_helper_buffer{nullptr}; + void setExtensionCallback(std::function caller) { call_extension = caller; } void process(void* buffer); + + void + addReply(void (*addMidi2Event)(AAPXSMidi2Processor *, void *, int32_t), + void *addMidi2EventUserData, + int32_t group, int32_t requestId, AAPXSServiceInstance *aapxsInstance, int32_t messageSize, + int32_t opcode); }; } diff --git a/include/aap/core/aap_midi2_helper.h b/include/aap/core/aap_midi2_helper.h index 772f21f4..ba5d931d 100644 --- a/include/aap/core/aap_midi2_helper.h +++ b/include/aap/core/aap_midi2_helper.h @@ -33,6 +33,8 @@ uint32_t aap_midi2_aapxs_get_request_id(uint8_t* ump); * @param conversionHelperBuffer the conversion buffer for temporary storage * @param conversionHelperBufferSize the size of `conversionHelperBuffer` * @param group "group" field as in MIDI UMP + * @param requestId The requestId that should be unique within the instance, + * correlated to the reply (for asynchronous UMP messaging). * @param uri the extension URI (must be null terminated) * @param opcode the AAPXS opcode for the context operation * @param data the extension invocation data diff --git a/include/aap/core/host/plugin-host.h b/include/aap/core/host/plugin-host.h index faee9efe..fcb377e8 100644 --- a/include/aap/core/host/plugin-host.h +++ b/include/aap/core/host/plugin-host.h @@ -55,7 +55,7 @@ namespace aap { int32_t event_midi2_input_buffer_size{4096}; public: - PluginHost(PluginListSnapshot* contextPluginList, AAPXSRegistry* aapxsRegistry = nullptr); + PluginHost(PluginListSnapshot* contextPluginList, AAPXSRegistry* aapxsRegistry = nullptr, int32_t eventMidi2InputBufferSize = 4096); virtual ~PluginHost() {} diff --git a/include/aap/core/host/plugin-instance.h b/include/aap/core/host/plugin-instance.h index 3a92dbc5..4b7903dd 100644 --- a/include/aap/core/host/plugin-instance.h +++ b/include/aap/core/host/plugin-instance.h @@ -8,6 +8,7 @@ #include "plugin-host.h" #include "../aap_midi2_helper.h" #include "aap/core/AAPXSMidi2Processor.h" +#include "aap/core/AAPXSMidi2ClientSession.h" #if ANDROID #include @@ -29,11 +30,10 @@ namespace aap { AndroidAudioPluginFactory *plugin_factory; - NanoSleepLock event_input_buffer_mutex{}; - - void merge_event_inputs(void *mergeTmp, int32_t mergeBufSize, void* eventInputs, int32_t eventInputsSize, aap_buffer_t *buffer, PluginInstance* instance); - protected: + NanoSleepLock ump_sequence_merger_mutex{}; + void merge_ump_sequences(aap_port_direction portDirection, void *mergeTmp, int32_t mergeBufSize, void* sequence, int32_t sequenceSize, aap_buffer_t *buffer, PluginInstance* instance); + int instance_id{-1}; PluginInstantiationState instantiation_state{PLUGIN_INSTANTIATION_STATE_INITIAL}; bool are_ports_configured{false}; @@ -42,10 +42,12 @@ namespace aap { const PluginInformation *pluginInfo; std::unique_ptr > configured_ports{nullptr}; std::unique_ptr > cached_parameters{nullptr}; - void* event_midi2_input_buffer{nullptr}; - void* event_midi2_input_buffer_merged{nullptr}; - int32_t event_midi2_input_buffer_size{0}; - int32_t event_midi2_input_buffer_offset{0}; + // for client, it collects event inputs and AAPXS SysEx8 UMPs + // for service, it collects AAPXS SysEx8 UMPs (can be put multiple async results) + void* event_midi2_buffer{nullptr}; + void* event_midi2_merge_buffer{nullptr}; + int32_t event_midi2_buffer_size{0}; + int32_t event_midi2_buffer_offset{0}; PluginInstance(const PluginInformation *pluginInformation, AndroidAudioPluginFactory *loadedPluginFactory, @@ -58,14 +60,6 @@ namespace aap { void setupPortConfigDefaults(); void setupPortsViaMetadata(); - // This function should be invoked every time this instance is about to call the plugin's `process()`. - // When this instance's `process()` is invoked, it parses the input passed from the client - // (via MIDI2 buffer) and if it found any AAPXS SysEx8 calls, then it dispatches the - // extension request to the actual `plugin`. - // - // Do nothing at client. - virtual void processAAPXSSysEx8Input() {}; - public: virtual ~PluginInstance(); @@ -117,7 +111,7 @@ namespace aap { void deactivate(); - virtual void process(int32_t frameCount, int32_t timeoutInNanoseconds); + virtual void process(int32_t frameCount, int32_t timeoutInNanoseconds) = 0; virtual StandardExtensions &getStandardExtensions() = 0; @@ -128,6 +122,7 @@ namespace aap { // Event controller (GUI) support void addEventUmpInput(void* input, int32_t size); + void addEventUmpOutput(void* input, int32_t size); }; /** @@ -166,7 +161,6 @@ namespace aap { protected: AndroidAudioPluginHost *getHostFacadeForCompleteInstantiation() override; - void processAAPXSSysEx8Input() override; public: LocalPluginInstance(PluginHost *host, AAPXSRegistry *aapxsRegistry, int32_t instanceId, @@ -192,7 +186,8 @@ namespace aap { StandardExtensions &getStandardExtensions() override { return standards; } - // It is invoked by AudioPluginInterfaceImpl, and supposed to dispatch request to extension service + // It is invoked by AudioPluginInterfaceImpl and AAPXSMidi2Processor callback, + // and supposed to dispatch request to extension service void controlExtension(const std::string &uri, int32_t opcode) { auto aapxsInstance = getInstanceFor(uri.c_str()); auto feature = aapxs_registry->getByUri(uri.c_str()); @@ -207,10 +202,7 @@ namespace aap { instantiation_state = PLUGIN_INSTANTIATION_STATE_INACTIVE; } - void process(int32_t frameCount, int32_t timeoutInNanoseconds) override { - process_requested_to_host = false; - PluginInstance::process(frameCount, timeoutInNanoseconds); - } + void process(int32_t frameCount, int32_t timeoutInNanoseconds) override; void requestProcessToHost(); }; @@ -247,6 +239,7 @@ namespace aap { AndroidAudioPluginHost plugin_host_facade{}; RemotePluginInstanceStandardExtensionsImpl standards; std::unique_ptr aapxs_manager; + AAPXSMidi2ClientSession aapxs_session; std::unique_ptr native_ui_controller{}; friend class RemoteAAPXSManager; @@ -291,6 +284,8 @@ namespace aap { void prepare(int frameCount) override; + void process(int32_t frameCount, int32_t timeoutInNanoseconds) override; + RemotePluginNativeUIController* getNativeUIController() { return native_ui_controller.get(); } void* getRemoteWebView();