diff --git a/src/common/communication.h b/src/common/communication.h index 47a628c3..ed94d7d0 100644 --- a/src/common/communication.h +++ b/src/common/communication.h @@ -109,25 +109,38 @@ inline T read_object(Socket& socket) { } /** - * Encodes the base behavior for encoding the contents of the `data` argument + * Encodes the base behavior for reading from and writing to 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) { + EventPayload read(const int /*opcode*/, const void* data) { if (data == nullptr) { return nullptr; } // Assume buffers are zeroed out, this is probably not the case - char* c_string = static_cast(data); + const char* c_string = static_cast(data); if (c_string[0] != 0) { return std::string(c_string); } else { return WantsString{}; } } + + void write(const int /*opcode*/, void* data, const EventResult& response) { + 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; + } + } }; /** @@ -136,21 +149,21 @@ struct DefaultDataConverter { * since they follow the same format. See one of those functions for details on * the parameters and return value of this function. * - * @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 data_converter Some struct that knows how to read data from and write + * data back to the `data` void pointer. 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 +template intptr_t send_event(boost::asio::local::stream_protocol::socket& socket, + D& data_converter, int opcode, int index, intptr_t value, @@ -159,7 +172,7 @@ intptr_t send_event(boost::asio::local::stream_protocol::socket& socket, std::optional> logging) { // Encode the right payload type for this event. Check the documentation for // `EventPayload` for more information. - EventPayload payload = DataConverter{}(opcode, data); + const EventPayload payload = data_converter.read(opcode, data); if (logging.has_value()) { auto [logger, is_dispatch] = *logging; @@ -170,21 +183,14 @@ intptr_t send_event(boost::asio::local::stream_protocol::socket& socket, 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; - } + data_converter.write(opcode, data, response); return response.return_value; } diff --git a/src/plugin/host-bridge.cpp b/src/plugin/host-bridge.cpp index 476f3631..c8eed339 100644 --- a/src/plugin/host-bridge.cpp +++ b/src/plugin/host-bridge.cpp @@ -138,16 +138,16 @@ HostBridge::HostBridge(audioMasterCallback host_callback) } struct DispatchDataConverter : DefaultDataConverter { - EventPayload operator()(int opcode, void* data) { + EventPayload read(const int opcode, const 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)); + return DynamicVstEvents(*static_cast(data)); break; default: - return DefaultDataConverter{}(opcode, data); + return DefaultDataConverter::read(opcode, data); break; } } @@ -183,9 +183,9 @@ intptr_t HostBridge::dispatch(AEffect* /*plugin*/, } // TODO: Maybe reuse buffers here when dealing with chunk data - return send_event( - host_vst_dispatch, opcode, index, value, data, option, - std::pair(logger, true)); + DispatchDataConverter converter; + return send_event(host_vst_dispatch, converter, 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 8b2d4a0b..58de40c2 100644 --- a/src/wine-host/plugin-bridge.cpp +++ b/src/wine-host/plugin-bridge.cpp @@ -195,8 +195,9 @@ intptr_t PluginBridge::host_callback(AEffect* /*plugin*/, intptr_t value, void* data, float option) { - return send_event(vst_host_callback, opcode, index, - value, data, option, std::nullopt); + DefaultDataConverter converter; + return send_event(vst_host_callback, converter, opcode, index, value, data, + option, std::nullopt); } intptr_t VST_CALL_CONV host_callback_proxy(AEffect* effect,