diff --git a/src/common/communication.h b/src/common/communication.h index 5a417c3e..9bc3243a 100644 --- a/src/common/communication.h +++ b/src/common/communication.h @@ -110,15 +110,27 @@ inline T read_object(Socket& socket) { /** * 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 + * for event dispatch functions. This provides base functionality for these + * kinds of events. The `dispatch()` function will require some more specific * structs. */ class DefaultDataConverter { public: - virtual ~DefaultDataConverter() {}; + virtual ~DefaultDataConverter(){}; - virtual EventPayload read(const int /*opcode*/, const intptr_t /*value*/, const void* data) { + /** + * Read data from the `data` void pointer into a an `EventPayload` value + * that can be serialized and conveys the meaning of the event. + * + * If this returns a nullopt, then the event won't be performed at all. Some + * plugins perform `audioMasterUpdateDisplay` host callbacks and apparently + * some hosts just outright crash when they receive these functions, so they + * have to be filtered out. Please let me know if there's some way to detect + * whether the host supports these callbacks before sending them! + */ + virtual std::optional read(const int /*opcode*/, + const intptr_t /*value*/, + const void* data) { if (data == nullptr) { return nullptr; } @@ -132,7 +144,9 @@ class DefaultDataConverter { } } - virtual void write(const int /*opcode*/, void* data, const EventResult& response) { + virtual void write(const int /*opcode*/, + void* data, + const EventResult& response) { if (response.data.has_value()) { char* output = static_cast(data); @@ -174,21 +188,28 @@ intptr_t send_event(boost::asio::local::stream_protocol::socket& socket, float option, std::optional> logging) { // Encode the right payload type for this event. Check the documentation for - // `EventPayload` for more information. - const EventPayload payload = data_converter.read(opcode, value, data); - - if (logging.has_value()) { - auto [logger, is_dispatch] = *logging; - logger.log_event(is_dispatch, opcode, index, value, payload, option); + // `EventPayload` for more information. We have to skip some opcodes because + // some VST hsots will outright crash if they receive them, please let me + // know if there's a better way to do this. + const std::optional payload = + data_converter.read(opcode, value, data); + if (!payload.has_value()) { + return 1; } - const Event event{opcode, index, value, option, payload}; + if (logging.has_value()) { + auto [logger, is_dispatch] = logging.value(); + logger.log_event(is_dispatch, opcode, index, value, payload.value(), + option); + } + + const Event event{opcode, index, value, option, payload.value()}; write_object(socket, event); const auto response = read_object(socket); if (logging.has_value()) { - auto [logger, is_dispatch] = *logging; + auto [logger, is_dispatch] = logging.value(); logger.log_event_response(is_dispatch, response.return_value, response.data); } @@ -222,7 +243,7 @@ void passthrough_event(boost::asio::local::stream_protocol::socket& socket, std::optional> logging) { auto event = read_object(socket); if (logging.has_value()) { - auto [logger, is_dispatch] = *logging; + auto [logger, is_dispatch] = logging.value(); logger.log_event(is_dispatch, event.opcode, event.index, event.value, event.payload, event.option); } @@ -264,7 +285,7 @@ void passthrough_event(boost::asio::local::stream_protocol::socket& socket, event.payload); if (logging.has_value()) { - auto [logger, is_dispatch] = *logging; + auto [logger, is_dispatch] = logging.value(); logger.log_event_response(is_dispatch, return_value, response_data); } diff --git a/src/plugin/host-bridge.cpp b/src/plugin/host-bridge.cpp index d6ed65b5..64c0fb42 100644 --- a/src/plugin/host-bridge.cpp +++ b/src/plugin/host-bridge.cpp @@ -142,7 +142,7 @@ class DispatchDataConverter : DefaultDataConverter { DispatchDataConverter(std::vector& chunk_data) : chunk(chunk_data) {} - EventPayload read(const int opcode, const intptr_t value, const void* data) { + std::optional read(const int opcode, const intptr_t value, 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 diff --git a/src/wine-host/plugin-bridge.cpp b/src/wine-host/plugin-bridge.cpp index 58de40c2..9b1a2b25 100644 --- a/src/wine-host/plugin-bridge.cpp +++ b/src/wine-host/plugin-bridge.cpp @@ -189,13 +189,40 @@ void PluginBridge::wait() { process_replacing_handler.join(); } +class HostCallbackDataConverter : DefaultDataConverter { + public: + std::optional read(const int opcode, + const intptr_t value, + const void* data) { + switch (opcode) { + // Some hsots will outright crash if they receive this opcode, not + // sure why they don't just ignore it. Please let me know if there's + // a better way to handle this instead of just ignoring the event! + // + // TODO: Filtering these two events fixes crashes, but should this + // be needed? `audioMasterWantMidi` is deprecated though. + case audioMasterWantMidi: + case audioMasterUpdateDisplay: + return std::nullopt; + break; + default: + return DefaultDataConverter::read(opcode, value, data); + break; + } + } + + void write(const int opcode, void* data, const EventResult& response) { + return DefaultDataConverter::write(opcode, data, response); + } +}; + intptr_t PluginBridge::host_callback(AEffect* /*plugin*/, int opcode, int index, intptr_t value, void* data, float option) { - DefaultDataConverter converter; + HostCallbackDataConverter converter; return send_event(vst_host_callback, converter, opcode, index, value, data, option, std::nullopt); }