diff --git a/src/common/communication.cpp b/src/common/communication.cpp index 391eb709..4aebeeb4 100644 --- a/src/common/communication.cpp +++ b/src/common/communication.cpp @@ -101,6 +101,59 @@ void Sockets::connect() { } } +EventPayload DefaultDataConverter::read(const int /*opcode*/, + const int /*index*/, + const intptr_t /*value*/, + const void* data) const { + if (!data) { + return nullptr; + } + + // This is a simple fallback that will work in almost every case. + // Because some plugins don't zero out their string buffers when sending + // host callbacks, we will explicitely list all callbacks that expect a + // string in `DispatchDataConverter` adn `HostCallbackDataConverter`. + const char* c_string = static_cast(data); + if (c_string[0] != 0) { + return std::string(c_string); + } else { + return WantsString{}; + } +} + +std::optional DefaultDataConverter::read_value( + const int /*opcode*/, + const intptr_t /*value*/) const { + return std::nullopt; +} + +void DefaultDataConverter::write(const int /*opcode*/, + void* data, + const EventResult& response) const { + // The default behavior is to handle this as a null terminated C-style + // string + std::visit(overload{[&](const auto&) {}, + [&](const std::string& s) { + char* output = static_cast(data); + + // We use std::string for easy transport but in + // practice we're always writing null terminated + // C-style strings + std::copy(s.begin(), s.end(), output); + output[s.size()] = 0; + }}, + response.payload); +} + +void DefaultDataConverter::write_value(const int /*opcode*/, + intptr_t /*value*/, + const EventResult& /*response*/) const {} + +intptr_t DefaultDataConverter::return_value(const int /*opcode*/, + const intptr_t original) const { + return original; +} + boost::filesystem::path generate_endpoint_base(const std::string& plugin_name) { fs::path temp_directory = get_temporary_directory(); diff --git a/src/common/communication.h b/src/common/communication.h index 21e5ee47..6df4ca16 100644 --- a/src/common/communication.h +++ b/src/common/communication.h @@ -28,6 +28,8 @@ #include #include +#include "logging.h" + template using OutputAdapter = bitsery::OutputBufferAdapter; @@ -166,6 +168,61 @@ class Sockets { std::optional acceptors; }; +/** + * Encodes the base behavior for reading from and writing to the `data` argument + * 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(){}; + + /** + * Read data from the `data` void pointer into a an `EventPayload` value + * that can be serialized and conveys the meaning of the event. + */ + virtual EventPayload read(const int opcode, + const int index, + const intptr_t value, + const void* data) const; + + /** + * Read data from the `value` pointer into a an `EventPayload` value that + * can be serialized and conveys the meaning of the event. This is only used + * for the `effSetSpeakerArrangement` and `effGetSpeakerArrangement` events. + */ + virtual std::optional read_value(const int opcode, + const intptr_t value) const; + + /** + * Write the reponse back to the `data` pointer. + */ + virtual void write(const int opcode, + void* data, + const EventResult& response) const; + + /** + * Write the reponse back to the `value` pointer. This is only used during + * the `effGetSpeakerArrangement` event. + */ + virtual void write_value(const int opcode, + intptr_t value, + const EventResult& response) const; + + /** + * This function can override a callback's return value based on the opcode. + * This is used in one place to return a pointer to a `VstTime` object + * that's contantly being updated. + * + * @param opcode The opcode for the current event. + * @param original The original return value as returned by the callback + * function. + */ + virtual intptr_t return_value(const int opcode, + const intptr_t original) const; +}; + /** * Generate a unique base directory that can be used as a prefix for all Unix * domain socket endpoints used in `PluginBridge`/`Vst2Bridge`. This will @@ -261,3 +318,300 @@ inline T read_object(Socket& socket, return object; } + +/** + * 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 socket The socket to write over, should be the same socket the other + * endpoint is using to call `receive_event()`. + * @param write_mutex A mutex to ensure that only one thread can write to + * the socket at once. Needed because VST hosts and plugins can and sometimes + * will call the `dispatch()` or `audioMaster()` functions from multiple + * threads at once. + * @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 host 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 receive_event + * @relates passthrough_event + */ +template +intptr_t send_event(boost::asio::local::stream_protocol::socket& socket, + std::mutex& write_mutex, + D& data_converter, + std::optional> logging, + int opcode, + int index, + intptr_t value, + void* data, + float option) { + // Encode the right payload types for this event. Check the documentation + // for `EventPayload` for more information. These types are converted to + // C-style data structures in `passthrough_event()` so they can be passed to + // a plugin or callback function. + const EventPayload payload = + data_converter.read(opcode, index, value, data); + const std::optional value_payload = + data_converter.read_value(opcode, value); + + if (logging) { + auto [logger, is_dispatch] = *logging; + logger.log_event(is_dispatch, opcode, index, value, payload, option, + value_payload); + } + + const Event event{.opcode = opcode, + .index = index, + .value = value, + .option = option, + .payload = payload, + .value_payload = value_payload}; + + // Prevent two threads from writing over the socket at the same time and + // messages getting out of order. This is needed because we can't prevent + // the plugin or the host from calling `dispatch()` or `audioMaster()` from + // multiple threads. + EventResult response; + { + std::lock_guard lock(write_mutex); + write_object(socket, event); + response = read_object(socket); + } + + if (logging) { + auto [logger, is_dispatch] = *logging; + logger.log_event_response(is_dispatch, opcode, response.return_value, + response.payload, response.value_payload); + } + + data_converter.write(opcode, data, response); + data_converter.write_value(opcode, value, response); + + return data_converter.return_value(opcode, response.return_value); +} + +/** + * Receive an event from a socket, call a function to generate a response, and + * write the response back over the socket. This is usually used together with + * `passthrough_event()` which passes the event data through to an event + * dispatcher function. This behaviour is split into two functions to avoid + * redundant data conversions when handling MIDI data, as some plugins require + * the received data to be temporarily stored until the next event audio buffer + * gets processed. + * + * @param socket The socket to receive on and to send the response back to. + * @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. + * @param callback The function used to generate a response out of an event. + * + * @tparam F A function type in the form of `EventResponse(Event)`. + * + * @relates send_event + * @relates passthrough_event + */ +template +void receive_event(boost::asio::local::stream_protocol::socket& socket, + std::optional> logging, + F callback) { + auto event = read_object(socket); + if (logging) { + auto [logger, is_dispatch] = *logging; + logger.log_event(is_dispatch, event.opcode, event.index, event.value, + event.payload, event.option, event.value_payload); + } + + EventResult response = callback(event); + if (logging) { + auto [logger, is_dispatch] = *logging; + logger.log_event_response(is_dispatch, event.opcode, + response.return_value, response.payload, + response.value_payload); + } + + write_object(socket, response); +} + +/** + * Create a callback function that takes an `Event` object, decodes the data + * into the expected format for VST2 function calls, calls the given function + * (either `AEffect::dispatcher()` for host -> plugin events or `audioMaster()` + * for plugin -> host events), and serializes the results back into an + * `EventResult` object. I'd rather not get too Haskell-y in my C++, but this is + * the cleanest solution for this problem. + * + * This is the receiving analogue of the `*DataCovnerter` objects. + * + * @param plugin The `AEffect` instance that should be passed to the callback + * function. + * @param callback The function to call with the arguments received from the + * socket. + * + * @tparam A function with the same signature as `AEffect::dispatcher` or + * `audioMasterCallback`. + * + * @return A `EventResult(Event)` callback function that can be passed to + * `receive_event`. + * + * @relates receive_event + */ +template +auto passthrough_event(AEffect* plugin, F callback) { + return [=](Event& event) -> EventResult { + // This buffer is used to write strings and small objects to. We'll + // initialize the beginning with null values to both prevent it from + // being read as some arbitrary C-style string, and to make sure that + // `*static_cast(string_buffer.data)` will be a null pointer if + // the plugin is supposed to write a pointer there but doesn't (such as + // with `effEditGetRect`/`WantsVstRect`). + std::array string_buffer; + std::fill(string_buffer.begin(), string_buffer.begin() + sizeof(size_t), + 0); + + auto read_payload_fn = overload{ + [&](const std::nullptr_t&) -> void* { return nullptr; }, + [&](const std::string& s) -> void* { + return const_cast(s.c_str()); + }, + [&](const std::vector& buffer) -> void* { + return const_cast(buffer.data()); + }, + [&](native_size_t& window_handle) -> void* { + // This is the X11 window handle that the editor should reparent + // itself to. We have a special wrapper around the dispatch + // function that intercepts `effEditOpen` events and creates a + // Win32 window and then finally embeds the X11 window Wine + // created into this wnidow handle. Make sure to convert the + // window ID first to `size_t` in case this is the 32-bit host. + return reinterpret_cast( + static_cast(window_handle)); + }, + [&](const AEffect&) -> void* { return nullptr; }, + [&](DynamicVstEvents& events) -> void* { + return &events.as_c_events(); + }, + [&](DynamicSpeakerArrangement& speaker_arrangement) -> void* { + return &speaker_arrangement.as_c_speaker_arrangement(); + }, + [&](WantsAEffectUpdate&) -> void* { + // The host will never actually ask for an updated `AEffect` + // object since that should not be a thing. This is purely a + // meant as a workaround for plugins that initialize their + // `AEffect` object after the plugin has already finished + // initializing. + return nullptr; + }, + [&](WantsChunkBuffer&) -> void* { return string_buffer.data(); }, + [&](VstIOProperties& props) -> void* { return &props; }, + [&](VstMidiKeyName& key_name) -> void* { return &key_name; }, + [&](VstParameterProperties& props) -> void* { return &props; }, + [&](WantsVstRect&) -> void* { return string_buffer.data(); }, + [&](const WantsVstTimeInfo&) -> void* { return nullptr; }, + [&](WantsString&) -> void* { return string_buffer.data(); }}; + + // Almost all events pass data through the `data` argument. There are + // two events, `effSetParameter` and `effGetParameter` that also pass + // data through the value argument. + void* data = std::visit(read_payload_fn, event.payload); + intptr_t value = event.value; + if (event.value_payload) { + value = reinterpret_cast( + std::visit(read_payload_fn, *event.value_payload)); + } + + const intptr_t return_value = callback( + plugin, event.opcode, event.index, value, data, event.option); + + // Only write back data when needed, this depends on the event payload + // type + auto write_payload_fn = overload{ + [&](auto) -> EventResultPayload { return nullptr; }, + [&](const AEffect& updated_plugin) -> EventResultPayload { + // This is a bit of a special case! Instead of writing some + // return value, we will update values on the native VST + // plugin's `AEffect` object. This is triggered by the + // `audioMasterIOChanged` callback from the hosted VST plugin. + update_aeffect(*plugin, updated_plugin); + + return nullptr; + }, + [&](DynamicSpeakerArrangement& speaker_arrangement) + -> EventResultPayload { return speaker_arrangement; }, + [&](WantsChunkBuffer&) -> EventResultPayload { + // In this case the plugin will have written its data stored in + // an array to which a pointer is stored in `data`, with the + // return value from the event determines how much data the + // plugin has written + const uint8_t* chunk_data = *static_cast(data); + return std::vector(chunk_data, + chunk_data + return_value); + }, + [&](VstIOProperties& props) -> EventResultPayload { return props; }, + [&](VstMidiKeyName& key_name) -> EventResultPayload { + return key_name; + }, + [&](VstParameterProperties& props) -> EventResultPayload { + return props; + }, + [&](WantsAEffectUpdate&) -> EventResultPayload { return *plugin; }, + [&](WantsVstRect&) -> EventResultPayload { + // The plugin should have written a pointer to a VstRect struct + // into the data pointer. I haven't seen this fail yet, but + // since some hosts will call `effEditGetRect()` before + // `effEditOpen()` I can assume there are plugins that don't + // handle this correctly. + VstRect* editor_rect = *static_cast(data); + if (!editor_rect) { + return nullptr; + } + + return *editor_rect; + }, + [&](WantsVstTimeInfo&) -> EventResultPayload { + // Not sure why the VST API has twenty different ways of + // returning structs, but in this case the value returned from + // the callback function is actually a pointer to a + // `VstTimeInfo` struct! It can also be a null pointer if the + // host doesn't support this. + const auto time_info = + reinterpret_cast(return_value); + if (!time_info) { + return nullptr; + } else { + return *time_info; + } + }, + [&](WantsString&) -> EventResultPayload { + return std::string(static_cast(data)); + }}; + + // As mentioned about, the `effSetSpeakerArrangement` and + // `effGetSpeakerArrangement` events are the only two events that use + // the value argument as a pointer to write data to. Additionally, the + // `effGetSpeakerArrangement` expects the plugin to write its own data + // to this value. Hence why we need to encode the response here + // separately. + const EventResultPayload response_data = + std::visit(write_payload_fn, event.payload); + std::optional value_response_data = std::nullopt; + if (event.value_payload) { + value_response_data = + std::visit(write_payload_fn, *event.value_payload); + } + + EventResult response{.return_value = return_value, + .payload = response_data, + .value_payload = value_response_data}; + + return response; + }; +} diff --git a/src/common/events.h b/src/common/events.h deleted file mode 100644 index 68864b13..00000000 --- a/src/common/events.h +++ /dev/null @@ -1,408 +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 . - -#pragma once - -#include - -#include "communication.h" -#include "logging.h" - -/** - * Encodes the base behavior for reading from and writing to the `data` argument - * 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(){}; - - /** - * Read data from the `data` void pointer into a an `EventPayload` value - * that can be serialized and conveys the meaning of the event. - */ - virtual EventPayload read(const int /*opcode*/, - const int /*index*/, - const intptr_t /*value*/, - const void* data) const { - if (!data) { - return nullptr; - } - - // This is a simple fallback that will work in almost every case. - // Because some plugins don't zero out their string buffers when sending - // host callbacks, we will explicitely list all callbacks that expect a - // string in `DispatchDataConverter` adn `HostCallbackDataConverter`. - const char* c_string = static_cast(data); - if (c_string[0] != 0) { - return std::string(c_string); - } else { - return WantsString{}; - } - } - - /** - * Read data from the `value` pointer into a an `EventPayload` value that - * can be serialized and conveys the meaning of the event. This is only used - * for the `effSetSpeakerArrangement` and `effGetSpeakerArrangement` events. - */ - virtual std::optional read_value( - const int /*opcode*/, - const intptr_t /*value*/) const { - return std::nullopt; - } - - /** - * Write the reponse back to the `data` pointer. - */ - virtual void write(const int /*opcode*/, - void* data, - const EventResult& response) const { - // The default behavior is to handle this as a null terminated C-style - // string - std::visit(overload{[&](const auto&) {}, - [&](const std::string& s) { - char* output = static_cast(data); - - // We use std::string for easy transport but in - // practice we're always writing null terminated - // C-style strings - std::copy(s.begin(), s.end(), output); - output[s.size()] = 0; - }}, - response.payload); - } - - /** - * Write the reponse back to the `value` pointer. This is only used during - * the `effGetSpeakerArrangement` event. - */ - virtual void write_value(const int /*opcode*/, - intptr_t /*value*/, - const EventResult& /*response*/) const {} - - /** - * This function can override a callback's return value based on the opcode. - * This is used in one place to return a pointer to a `VstTime` object - * that's contantly being updated. - * - * @param opcode The opcode for the current event. - * @param original The original return value as returned by the callback - * function. - */ - virtual intptr_t return_value(const int /*opcode*/, - const intptr_t original) const { - return original; - } -}; - -/** - * 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 socket The socket to write over, should be the same socket the other - * endpoint is using to call `receive_event()`. - * @param write_mutex A mutex to ensure that only one thread can write to - * the socket at once. Needed because VST hosts and plugins can and sometimes - * will call the `dispatch()` or `audioMaster()` functions from multiple - * threads at once. - * @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 host 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 receive_event - * @relates passthrough_event - */ -template -intptr_t send_event(boost::asio::local::stream_protocol::socket& socket, - std::mutex& write_mutex, - D& data_converter, - std::optional> logging, - int opcode, - int index, - intptr_t value, - void* data, - float option) { - // Encode the right payload types for this event. Check the documentation - // for `EventPayload` for more information. These types are converted to - // C-style data structures in `passthrough_event()` so they can be passed to - // a plugin or callback function. - const EventPayload payload = - data_converter.read(opcode, index, value, data); - const std::optional value_payload = - data_converter.read_value(opcode, value); - - if (logging) { - auto [logger, is_dispatch] = *logging; - logger.log_event(is_dispatch, opcode, index, value, payload, option, - value_payload); - } - - const Event event{.opcode = opcode, - .index = index, - .value = value, - .option = option, - .payload = payload, - .value_payload = value_payload}; - - // Prevent two threads from writing over the socket at the same time and - // messages getting out of order. This is needed because we can't prevent - // the plugin or the host from calling `dispatch()` or `audioMaster()` from - // multiple threads. - EventResult response; - { - std::lock_guard lock(write_mutex); - write_object(socket, event); - response = read_object(socket); - } - - if (logging) { - auto [logger, is_dispatch] = *logging; - logger.log_event_response(is_dispatch, opcode, response.return_value, - response.payload, response.value_payload); - } - - data_converter.write(opcode, data, response); - data_converter.write_value(opcode, value, response); - - return data_converter.return_value(opcode, response.return_value); -} - -/** - * Receive an event from a socket, call a function to generate a response, and - * write the response back over the socket. This is usually used together with - * `passthrough_event()` which passes the event data through to an event - * dispatcher function. This behaviour is split into two functions to avoid - * redundant data conversions when handling MIDI data, as some plugins require - * the received data to be temporarily stored until the next event audio buffer - * gets processed. - * - * @param socket The socket to receive on and to send the response back to. - * @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. - * @param callback The function used to generate a response out of an event. - * - * @tparam F A function type in the form of `EventResponse(Event)`. - * - * @relates send_event - * @relates passthrough_event - */ -template -void receive_event(boost::asio::local::stream_protocol::socket& socket, - std::optional> logging, - F callback) { - auto event = read_object(socket); - if (logging) { - auto [logger, is_dispatch] = *logging; - logger.log_event(is_dispatch, event.opcode, event.index, event.value, - event.payload, event.option, event.value_payload); - } - - EventResult response = callback(event); - if (logging) { - auto [logger, is_dispatch] = *logging; - logger.log_event_response(is_dispatch, event.opcode, - response.return_value, response.payload, - response.value_payload); - } - - write_object(socket, response); -} - -/** - * Create a callback function that takes an `Event` object, decodes the data - * into the expected format for VST2 function calls, calls the given function - * (either `AEffect::dispatcher()` for host -> plugin events or `audioMaster()` - * for plugin -> host events), and serializes the results back into an - * `EventResult` object. I'd rather not get too Haskell-y in my C++, but this is - * the cleanest solution for this problem. - * - * This is the receiving analogue of the `*DataCovnerter` objects. - * - * @param plugin The `AEffect` instance that should be passed to the callback - * function. - * @param callback The function to call with the arguments received from the - * socket. - * - * @tparam A function with the same signature as `AEffect::dispatcher` or - * `audioMasterCallback`. - * - * @return A `EventResult(Event)` callback function that can be passed to - * `receive_event`. - * - * @relates receive_event - */ -template -auto passthrough_event(AEffect* plugin, F callback) { - return [=](Event& event) -> EventResult { - // This buffer is used to write strings and small objects to. We'll - // initialize the beginning with null values to both prevent it from - // being read as some arbitrary C-style string, and to make sure that - // `*static_cast(string_buffer.data)` will be a null pointer if - // the plugin is supposed to write a pointer there but doesn't (such as - // with `effEditGetRect`/`WantsVstRect`). - std::array string_buffer; - std::fill(string_buffer.begin(), string_buffer.begin() + sizeof(size_t), - 0); - - auto read_payload_fn = overload{ - [&](const std::nullptr_t&) -> void* { return nullptr; }, - [&](const std::string& s) -> void* { - return const_cast(s.c_str()); - }, - [&](const std::vector& buffer) -> void* { - return const_cast(buffer.data()); - }, - [&](native_size_t& window_handle) -> void* { - // This is the X11 window handle that the editor should reparent - // itself to. We have a special wrapper around the dispatch - // function that intercepts `effEditOpen` events and creates a - // Win32 window and then finally embeds the X11 window Wine - // created into this wnidow handle. Make sure to convert the - // window ID first to `size_t` in case this is the 32-bit host. - return reinterpret_cast( - static_cast(window_handle)); - }, - [&](const AEffect&) -> void* { return nullptr; }, - [&](DynamicVstEvents& events) -> void* { - return &events.as_c_events(); - }, - [&](DynamicSpeakerArrangement& speaker_arrangement) -> void* { - return &speaker_arrangement.as_c_speaker_arrangement(); - }, - [&](WantsAEffectUpdate&) -> void* { - // The host will never actually ask for an updated `AEffect` - // object since that should not be a thing. This is purely a - // meant as a workaround for plugins that initialize their - // `AEffect` object after the plugin has already finished - // initializing. - return nullptr; - }, - [&](WantsChunkBuffer&) -> void* { return string_buffer.data(); }, - [&](VstIOProperties& props) -> void* { return &props; }, - [&](VstMidiKeyName& key_name) -> void* { return &key_name; }, - [&](VstParameterProperties& props) -> void* { return &props; }, - [&](WantsVstRect&) -> void* { return string_buffer.data(); }, - [&](const WantsVstTimeInfo&) -> void* { return nullptr; }, - [&](WantsString&) -> void* { return string_buffer.data(); }}; - - // Almost all events pass data through the `data` argument. There are - // two events, `effSetParameter` and `effGetParameter` that also pass - // data through the value argument. - void* data = std::visit(read_payload_fn, event.payload); - intptr_t value = event.value; - if (event.value_payload) { - value = reinterpret_cast( - std::visit(read_payload_fn, *event.value_payload)); - } - - const intptr_t return_value = callback( - plugin, event.opcode, event.index, value, data, event.option); - - // Only write back data when needed, this depends on the event payload - // type - auto write_payload_fn = overload{ - [&](auto) -> EventResultPayload { return nullptr; }, - [&](const AEffect& updated_plugin) -> EventResultPayload { - // This is a bit of a special case! Instead of writing some - // return value, we will update values on the native VST - // plugin's `AEffect` object. This is triggered by the - // `audioMasterIOChanged` callback from the hosted VST plugin. - update_aeffect(*plugin, updated_plugin); - - return nullptr; - }, - [&](DynamicSpeakerArrangement& speaker_arrangement) - -> EventResultPayload { return speaker_arrangement; }, - [&](WantsChunkBuffer&) -> EventResultPayload { - // In this case the plugin will have written its data stored in - // an array to which a pointer is stored in `data`, with the - // return value from the event determines how much data the - // plugin has written - const uint8_t* chunk_data = *static_cast(data); - return std::vector(chunk_data, - chunk_data + return_value); - }, - [&](VstIOProperties& props) -> EventResultPayload { return props; }, - [&](VstMidiKeyName& key_name) -> EventResultPayload { - return key_name; - }, - [&](VstParameterProperties& props) -> EventResultPayload { - return props; - }, - [&](WantsAEffectUpdate&) -> EventResultPayload { return *plugin; }, - [&](WantsVstRect&) -> EventResultPayload { - // The plugin should have written a pointer to a VstRect struct - // into the data pointer. I haven't seen this fail yet, but - // since some hosts will call `effEditGetRect()` before - // `effEditOpen()` I can assume there are plugins that don't - // handle this correctly. - VstRect* editor_rect = *static_cast(data); - if (!editor_rect) { - return nullptr; - } - - return *editor_rect; - }, - [&](WantsVstTimeInfo&) -> EventResultPayload { - // Not sure why the VST API has twenty different ways of - // returning structs, but in this case the value returned from - // the callback function is actually a pointer to a - // `VstTimeInfo` struct! It can also be a null pointer if the - // host doesn't support this. - const auto time_info = - reinterpret_cast(return_value); - if (!time_info) { - return nullptr; - } else { - return *time_info; - } - }, - [&](WantsString&) -> EventResultPayload { - return std::string(static_cast(data)); - }}; - - // As mentioned about, the `effSetSpeakerArrangement` and - // `effGetSpeakerArrangement` events are the only two events that use - // the value argument as a pointer to write data to. Additionally, the - // `effGetSpeakerArrangement` expects the plugin to write its own data - // to this value. Hence why we need to encode the response here - // separately. - const EventResultPayload response_data = - std::visit(write_payload_fn, event.payload); - std::optional value_response_data = std::nullopt; - if (event.value_payload) { - value_response_data = - std::visit(write_payload_fn, *event.value_payload); - } - - EventResult response{.return_value = return_value, - .payload = response_data, - .value_payload = value_response_data}; - - return response; - }; -} diff --git a/src/plugin/plugin-bridge.cpp b/src/plugin/plugin-bridge.cpp index 0c0c3003..3217f75a 100644 --- a/src/plugin/plugin-bridge.cpp +++ b/src/plugin/plugin-bridge.cpp @@ -21,7 +21,6 @@ #include #include "../common/communication.h" -#include "../common/events.h" #include "../common/utils.h" #include "utils.h" diff --git a/src/wine-host/bridges/vst2.cpp b/src/wine-host/bridges/vst2.cpp index 7d5ad91f..35e59c49 100644 --- a/src/wine-host/bridges/vst2.cpp +++ b/src/wine-host/bridges/vst2.cpp @@ -21,7 +21,6 @@ #include #include "../../common/communication.h" -#include "../../common/events.h" /** * A function pointer to what should be the entry point of a VST plugin.