Skip to content

Commit

Permalink
RemotePluginInstance can now provide its own way to handle host exten…
Browse files Browse the repository at this point in the history
…sion.

`AAPXSDefinition.process_incoming_host_aapxs_request` was never used because
there was nothing that could provide valid host extensions in the host
(RemotePluginInstance). Now there is a function delegate `getHostExtension`,
it is technically possible for host app developers to provide their own way
to handle extensions. For example, Parameter update notification handler
could be implemented by anyone.
  • Loading branch information
atsushieno committed Nov 13, 2023
1 parent a02a029 commit b268cc6
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 20 deletions.
11 changes: 7 additions & 4 deletions androidaudioplugin/src/main/cpp/core/aapxs/parameters-aapxs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,16 @@ void aap::xs::AAPXSDefinition_Parameters::aapxs_parameters_process_incoming_host
struct AAPXSDefinition *feature, AAPXSRecipientInstance *aapxsInstance,
AndroidAudioPluginHost *host, AAPXSRequestContext *request) {
auto ext = (aap_parameters_host_extension_t*) host->get_extension(host, AAP_PARAMETERS_EXTENSION_URI);
if (!ext)
return; // FIXME: should there be any global error handling?
switch (request->opcode) {
case OPCODE_NOTIFY_PARAMETERS_CHANGED:
ext->notify_parameters_changed(ext, host);
case OPCODE_NOTIFY_PARAMETERS_CHANGED: {
if (ext)
ext->notify_parameters_changed(ext, host);
//else {
// FIXME: log warning?
//}
aapxsInstance->send_aapxs_reply(aapxsInstance, request);
break;
}
default:
// FIXME: log warning?
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ aap::LocalPluginInstance::LocalPluginInstance(
aapxs_out_merge_buffer = calloc(1, event_midi2_buffer_size);

aapxs_midi2_processor.setExtensionCallback([&](aap_midi2_aapxs_parse_context* context) {
// FIXME: this should be implemented in LocalPluginInstance.
auto aapxsInstance = getAAPXSDispatcher().getPluginAAPXSByUri(context->uri);
// We need to copy extension data buffer before calling it.
memcpy(aapxsInstance->serialization->data, (int32_t*) context->data, context->dataSize);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,7 @@ aap::RemotePluginInstance::RemotePluginInstance(PluginClient* client,
shared_memory_store = new ClientPluginSharedMemoryStore();

aapxs_session.setReplyHandler([&](aap_midi2_aapxs_parse_context* context) {
auto aapxs = feature_registry->items()->getByUri(context->uri);
if (aapxs) {
auto aapxsInstance = getAAPXSDispatcher().getPluginAAPXSByUri(context->uri);
AAPXSRequestContext request{nullptr, nullptr, aapxsInstance->serialization, context->urid, context->uri, context->request_id, context->opcode};
if (aapxs->process_incoming_plugin_aapxs_reply)
aapxs->process_incoming_plugin_aapxs_reply(aapxs, aapxsInstance, plugin, &request);
else
aap::a_log_f(AAP_LOG_LEVEL_WARN, LOG_TAG, "AAPXS %s does not have a reply handler (opcode: %d)", context->uri, context->opcode);
}
else
aap::a_log_f(AAP_LOG_LEVEL_WARN, LOG_TAG, "AAPXS for %s is not registered (opcode: %d)", context->uri, context->opcode);
handleAAPXSReply(context);
});
}

Expand Down Expand Up @@ -165,12 +155,14 @@ void aap::RemotePluginInstance::process(int32_t frameCount, int32_t timeoutInNan

void *
aap::RemotePluginInstance::internalGetHostExtension(AndroidAudioPluginHost *host, const char *uri) {
auto instance = (RemotePluginInstance *) host->context;
if (strcmp(uri, AAP_PLUGIN_INFO_EXTENSION_URI) == 0) {
auto instance = (RemotePluginInstance *) host->context;
instance->host_plugin_info.get = get_plugin_info;
return &instance->host_plugin_info;
}
// FIXME: implement more host extensions
// look for user-implemented host extensions
if (instance->getHostExtension)
return instance->getHostExtension(host, uri);
return nullptr;
}

Expand Down Expand Up @@ -282,3 +274,28 @@ aap::xs::AAPXSDefinitionClientRegistry *aap::RemotePluginInstance::getAAPXSRegis
void aap::RemotePluginInstance::setupAAPXS() {
standards = std::make_unique<xs::ClientStandardExtensions>();
}

void aap::RemotePluginInstance::handleAAPXSReply(aap_midi2_aapxs_parse_context *context) {
auto aapxs = feature_registry->items()->getByUri(context->uri);
if (aapxs) {
if (context->opcode >= 0) {
// plugin AAPXS reply
auto aapxsInstance = getAAPXSDispatcher().getPluginAAPXSByUri(context->uri);
AAPXSRequestContext request{nullptr, nullptr, aapxsInstance->serialization, context->urid, context->uri, context->request_id, context->opcode};
if (aapxs->process_incoming_plugin_aapxs_reply)
aapxs->process_incoming_plugin_aapxs_reply(aapxs, aapxsInstance, plugin, &request);
else
aap::a_log_f(AAP_LOG_LEVEL_WARN, LOG_TAG, "AAPXS %s does not have a reply handler (opcode: %d)", context->uri, context->opcode);
} else {
// host AAPXS request
auto aapxsInstance = getAAPXSDispatcher().getHostAAPXSByUri(context->uri);
AAPXSRequestContext request{nullptr, nullptr, aapxsInstance->serialization, context->urid, context->uri, context->request_id, context->opcode};
if (aapxs->process_incoming_host_aapxs_request)
aapxs->process_incoming_host_aapxs_request(aapxs, aapxsInstance, &plugin_host_facade, &request);
else
aap::a_log_f(AAP_LOG_LEVEL_WARN, LOG_TAG, "AAPXS %s does not have a reply handler (opcode: %d)", context->uri, context->opcode);
}
}
else
aap::a_log_f(AAP_LOG_LEVEL_WARN, LOG_TAG, "AAPXS for %s is not registered (opcode: %d)", context->uri, context->opcode);
}
9 changes: 7 additions & 2 deletions docs/design/EXTENSIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ This will make extension implementation independent of specific AAP versions to

Typical plugin extension APIs are usually synchronous i.e. their functions block throughout the execution, but AAP extension foundation is designed to be "async ready", especially when the plugin enters ACTIVE = realtime mode.

It is because AAP, unlike those desktop plugin APIs, needs interaction between the host and plugin and keeping a thread per extension function call costs potentially a lot of synchronization constructs e.g. 100 locks per 100 `getPreset()` calls or potentially more (100 locks in the host + 100 locks in the plugin + 100 locks in AAPXS).

They can be designed and implemented in synchronous manner (we actually have synchronous API as some transitive solutions), but then there is no assured realtime safety.


Expand Down Expand Up @@ -110,13 +112,13 @@ There should be two kinds of accesses to AAPXS from `RemotePluginInstance`: AAPX

### vNext Hosting entrypoint to AAPXS Runtime API

Since AAP sends extension controllers either via Binder IPC (non-RT) or AAPXS SysEx8 (RT), unknown extensions can be still *supported*. For untyped extension accesses, `RemotePluginInstance` provides `sendExtensionRequest()` (`sendExtensionMessage()` in the existing implementation).
Since AAP sends extension controllers either via Binder IPC (non-RT) or AAPXS SysEx8 (RT), unknown extensions can be still *supported*. For untyped extension accesses, `RemotePluginInstance` provides `sendExtensionRequest()`.

`sendExtensionRequest()` takes `AAPXSRequestContext` which contains *already serialized* requests as a binary chunk in its member `AAPXSSerializationContext`. Then it dispatches the request to the appropriate handler: Binder IPC at INACTIVE (non-RT) state, or AAPXS SysEx8 at ACTIVE RT state.

Serialization is handled by each AAPXS. For such a hosting implementation that does not directly support the extension (or a plugin implementation that does not directly suppot the host extension), there is untyped AAPXS API that is implemented without strongly-typed extension API. The host can still convey and even invoke its dynamically assigned extension invocation handler in `AAPXSDefinition`.

At AAPXS Runtime level, there are utility functions in `aap_midi2_helper.h` for AAPXS parsing and generation in C, and `AAPXSMidi2Processor` and `AAPXSMidi2ClientSession` as the internal helpers in C++. To support asynchronous invocation under control, a host can assign an async callback `aapxs_completion_callback` to `AAPXSRequestContext`, which is then invoked when `AAPXSMidi2ClientSession` receives a corresponding reply to the request.
At AAPXS Runtime level, there are utility functions in `aap_midi2_helper.h` for AAPXS parsing and generation in C, and `AAPXSMidi2RecipientSession` and `AAPXSMidi2InitiatorSession` as the internal helpers in C++ in `aapxs-hosting-runtime.h`. To support asynchronous invocation under control, a host can assign an async callback `aapxs_completion_callback` to `AAPXSRequestContext`, which is then invoked when `AAPXSMidi2InitiatorSession` receives a corresponding reply to the request.


## vNext: what AAPXS developer writes
Expand All @@ -134,6 +136,8 @@ Each AAPXS developer provides the following stuff:
- Strongly typed plugin extension client (optionally) to help plugin client development.
- Strongly typed host extension client (optionally) to help plugin service development.

`samples/aapxssample` is an example plugin that provides its own AAPXS (no dedicated host client app there yet though).

### vNext: Untyped Extension Handler implementation

We need AAPXS implementation by extension API developer at:
Expand All @@ -144,3 +148,4 @@ We need AAPXS implementation by extension API developer at:
- weakly-typed plugin service AAPXS reply sender: Once the plugin extension function does its job, then the result should be serialized by `process_incoming_plugin_aapxs_request()` or whatever it asynchronously invokes at its completion. It should then call `send_aapxs_reply()` function (in `AAPXSServiceInstance`) which is assigned by the host. It takes `requestId` for correlation.
- weakly-typed plugin client AAPXS reply handler: a caller would need to deserialize the AAPXS binary and continue the client extension call, but it is totally optional.
- If it was an asynchronous call, then the `aapxs_completion_callback` should be invoked.

2 changes: 1 addition & 1 deletion external/cmidi2
Submodule cmidi2 updated 3 files
+3 −1 README.md
+621 −42 cmidi2.h
+108 −15 cmidi2_test.c
5 changes: 5 additions & 0 deletions include/aap/core/host/plugin-instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,11 @@ namespace aap {
// AAPXS v2 registry
xs::AAPXSDefinitionClientRegistry* getAAPXSRegistry();
xs::StandardExtensions &getStandardExtensions() override { return *standards; }

void handleAAPXSReply(aap_midi2_aapxs_parse_context *context);

// Host developers can override this function to return their own extensions.
std::function<void*(AndroidAudioPluginHost *host, const char *uri)> getHostExtension;
};
}

Expand Down

0 comments on commit b268cc6

Please sign in to comment.