From adf33e84a86dfef57fc73a0d1195eafcd10b5eca Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Mon, 9 Mar 2020 20:13:08 +0100 Subject: [PATCH] Use a template argument for serializing behaviour This keeps it a bit more readable. --- meson.build | 2 - src/common/communication.cpp | 73 --------------------------------- src/common/communication.h | 73 +++++++++++++++++++++++++++++---- src/plugin/host-bridge.cpp | 21 +++++++++- src/wine-host/plugin-bridge.cpp | 4 +- 5 files changed, 86 insertions(+), 87 deletions(-) delete mode 100644 src/common/communication.cpp diff --git a/meson.build b/meson.build index 381a26d2..244837a0 100644 --- a/meson.build +++ b/meson.build @@ -33,7 +33,6 @@ include_dir = include_directories('src/include') shared_library( 'yabridge', [ - 'src/common/communication.cpp', 'src/common/logging.cpp', 'src/common/serialization.cpp', 'src/plugin/host-bridge.cpp', @@ -48,7 +47,6 @@ shared_library( executable( 'yabridge-host', [ - 'src/common/communication.cpp', 'src/common/logging.cpp', 'src/common/serialization.cpp', 'src/wine-host/plugin-bridge.cpp', diff --git a/src/common/communication.cpp b/src/common/communication.cpp deleted file mode 100644 index 72aebe1c..00000000 --- a/src/common/communication.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// yabridge: a Wine VST bridge -// Copyright (C) 2020 Robbert van der Helm -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include "communication.h" - -intptr_t send_event(boost::asio::local::stream_protocol::socket& socket, - bool is_dispatch, - int opcode, - int index, - intptr_t value, - void* data, - float option, - Logger* logger) { - // Encode the right payload type for this event. Check the documentation for - // `EventPayload` for more information. - EventPayload payload = nullptr; - if (data != nullptr) { - // There are some events that need specific structs that we can't simply - // serialize as a string because they might contain null bytes - if (is_dispatch && opcode == effProcessEvents) { - // Move the elements into a vector for easier serialization - payload = DynamicVstEvents(*static_cast(data)); - } else { - // TODO: More of these structs - - // Assume buffers are zeroed out, this is probably not the case - char* c_string = static_cast(data); - if (c_string[0] != 0) { - payload = std::string(c_string); - } else { - payload = NeedsBuffer{}; - } - } - } - - if (logger != nullptr) { - logger->log_event(is_dispatch, opcode, index, value, payload, option); - } - - const Event event{opcode, index, value, option, payload}; - write_object(socket, event); - - const auto response = read_object(socket); - if (logger != nullptr) { - logger->log_event_response(is_dispatch, response.return_value, - response.data); - } - if (response.data.has_value()) { - char* output = static_cast(data); - - // For correctness we will copy the entire buffer and add a terminating - // null byte ourselves. In practice `response.data` will only ever - // contain C-style strings, but this would work with any other data - // format that can contain null bytes. - std::copy(response.data->begin(), response.data->end(), output); - output[response.data->size()] = 0; - } - - return response.return_value; -} diff --git a/src/common/communication.h b/src/common/communication.h index 84a69c6c..0ba26bec 100644 --- a/src/common/communication.h +++ b/src/common/communication.h @@ -119,29 +119,86 @@ inline T read_object(Socket& socket) { return read_object(socket, object); } +/** + * Encodes the base behavior for encoding the contents of the `data` argument + * for event dispatch functions. This is sufficient for host callbacks + * (`audioMaster()`). The `dispatch()` function will require some more specific + * structs. + */ +struct DefaultDataConverter { + EventPayload operator()(int /*opcode*/, void* data) { + if (data == nullptr) { + return nullptr; + } + + // Assume buffers are zeroed out, this is probably not the case + char* c_string = static_cast(data); + if (c_string[0] != 0) { + return std::string(c_string); + } else { + return NeedsBuffer{}; + } + } +}; + /** * Serialize and send an event over a socket. This is used for both the host -> * plugin 'dispatch' events and the plugin -> host 'audioMaster' host callbacks * since they follow the same format. See one of those functions for details on * the parameters and return value of this function. * - * @param is_dispatch whether or not this is for sending `dispatch()` events or - * host callbacks. Used for the serialization of opcode specific structs in - * the `dispatch()` function. - * @param logger A logger instance. Optional since it doesn't have to be done on - * both sides. Optional references are somehow not possible in C++17 things - * like `std::reference_wrapper`, so a raw pointer it is. + * @tparam DataConverter how the `data` void pointer should be converted to a + * serializable type. For host callbacks this parameter contains either a + * string or a null pointer while `dispatch()` calls might contain opcode + * specific structs. See the documentation for `EventPayload` for more + * information. The `DefaultDataConverter` defined above handles the basic + * behavior that's sufficient for hsot callbacks. + * + * @param logging A pair containing a logger instance and whether or not this is + * for sending `dispatch()` events or host callbacks. Optional since it + * doesn't have to be done on both sides. * * @relates passthrough_event */ +template intptr_t send_event(boost::asio::local::stream_protocol::socket& socket, - bool is_dispatch, int opcode, int index, intptr_t value, void* data, float option, - Logger* logger); + std::optional> logging) { + // Encode the right payload type for this event. Check the documentation for + // `EventPayload` for more information. + EventPayload payload = DataConverter{}(opcode, data); + + if (logging.has_value()) { + auto [logger, is_dispatch] = *logging; + logger.log_event(is_dispatch, opcode, index, value, payload, option); + } + + const Event event{opcode, index, value, option, payload}; + write_object(socket, event); + + const auto response = read_object(socket); + if (logging.has_value()) { + auto [logger, is_dispatch] = *logging; + logger.log_event_response(is_dispatch, response.return_value, + response.data); + } + if (response.data.has_value()) { + char* output = static_cast(data); + + // For correctness we will copy the entire buffer and add a terminating + // null byte ourselves. In practice `response.data` will only ever + // contain C-style strings, but this would work with any other data + // format that can contain null bytes. + std::copy(response.data->begin(), response.data->end(), output); + output[response.data->size()] = 0; + } + + return response.return_value; +} /** * Receive an event from a socket and pass it through to some callback function. diff --git a/src/plugin/host-bridge.cpp b/src/plugin/host-bridge.cpp index 2ee39fe9..5a2c4376 100644 --- a/src/plugin/host-bridge.cpp +++ b/src/plugin/host-bridge.cpp @@ -138,6 +138,22 @@ HostBridge::HostBridge(audioMasterCallback host_callback) plugin = read_object(vst_host_aeffect, plugin); } +struct DispatchDataConverter : DefaultDataConverter { + EventPayload operator()(int opcode, void* data) { + // There are some events that need specific structs that we can't simply + // serialize as a string because they might contain null bytes + // TODO: More of these structs + switch (opcode) { + case effProcessEvents: + return DynamicVstEvents(*static_cast(data)); + break; + default: + return DefaultDataConverter{}(opcode, data); + break; + } + } +}; + /** * Handle an event sent by the VST host. Most of these opcodes will be passed * through to the winelib VST host. @@ -167,8 +183,9 @@ intptr_t HostBridge::dispatch(AEffect* /*plugin*/, break; } - return send_event(host_vst_dispatch, true, opcode, index, value, data, - option, &logger); + return send_event( + host_vst_dispatch, opcode, index, value, data, option, + std::pair(logger, true)); } void HostBridge::process_replacing(AEffect* /*plugin*/, diff --git a/src/wine-host/plugin-bridge.cpp b/src/wine-host/plugin-bridge.cpp index 08cc77c0..b4931359 100644 --- a/src/wine-host/plugin-bridge.cpp +++ b/src/wine-host/plugin-bridge.cpp @@ -196,8 +196,8 @@ intptr_t PluginBridge::host_callback(AEffect* /*plugin*/, intptr_t value, void* data, float option) { - return send_event(vst_host_callback, false, opcode, index, value, data, - option, nullptr); + return send_event(vst_host_callback, opcode, index, + value, data, option, std::nullopt); } intptr_t VST_CALL_CONV host_callback_proxy(AEffect* effect,