From 46b08530ae0da14068db9cdf2ff9b4cdd9200710 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Thu, 5 Mar 2020 22:18:06 +0100 Subject: [PATCH] Pass through processReplacing Should work, almost does. --- src/common/communication.h | 26 ++++++++++------- src/plugin/host-bridge.cpp | 52 +++++++++++++++++++++------------ src/plugin/host-bridge.h | 17 ++++++++--- src/wine-host/plugin-bridge.cpp | 39 +++++++++++++++++++++++-- src/wine-host/plugin-bridge.h | 13 ++++++++- 5 files changed, 111 insertions(+), 36 deletions(-) diff --git a/src/common/communication.h b/src/common/communication.h index d93f7814..7523a091 100644 --- a/src/common/communication.h +++ b/src/common/communication.h @@ -160,23 +160,29 @@ struct ParameterResult { * A buffer of audio for the plugin to process, or the response of that * processing. The number of samples is encoded in each audio buffer's length. */ -struct AudioBuffer { +struct AudioBuffers { // When sending data we could use a vector of the right size, but when // receiving data we don't know how large this vector should be in advance // (or without sending the message length first) using buffer_type = - ArrayBuffer; + ArrayBuffer; /** - * An audio buffer for each of the plugin's audio channels. The number of - * samples is equal to `buffers[0].size()`. + * An audio buffer for each of the plugin's audio channels. */ std::vector> buffers; + /** + * The number of frames in a sample. If buffers is not empty, then + * `buffers[0].size() == sample_frames`. + */ + int sample_frames; + template void serialize(S& s) { s.container(buffers, max_audio_channels, [](S& s, auto& v) { s.container4b(v, max_buffer_size); }); + s.value4b(sample_frames); } }; @@ -296,12 +302,12 @@ inline T read_object(Socket& socket) { * * @relates passthrough_event */ -intptr_t send_event(boost::asio::local::stream_protocol::socket& socket, - int32_t opcode, - int32_t index, - intptr_t value, - void* data, - float option) { +inline intptr_t send_event(boost::asio::local::stream_protocol::socket& socket, + int32_t opcode, + int32_t index, + intptr_t value, + void* data, + float option) { auto payload = data == nullptr ? std::nullopt diff --git a/src/plugin/host-bridge.cpp b/src/plugin/host-bridge.cpp index 32e9e56d..12d5275b 100644 --- a/src/plugin/host-bridge.cpp +++ b/src/plugin/host-bridge.cpp @@ -24,8 +24,6 @@ #include #include -#include "../common/communication.h" - // TODO: I should track down the VST2 SDK for clarification on some of the // implementation details, such as the use of intptr_t isntead of void* // here. @@ -73,6 +71,7 @@ HostBridge::HostBridge(audioMasterCallback host_callback) host_vst_dispatch(io_context), vst_host_callback(io_context), host_vst_parameters(io_context), + host_vst_process_replacing(io_context), vst_host_aeffect(io_context), host_callback_function(host_callback), vst_host(find_wine_vst_host(), @@ -80,12 +79,14 @@ HostBridge::HostBridge(audioMasterCallback host_callback) // which Unix domain socket to connect to find_vst_plugin(), socket_endpoint.path(), - bp::env = set_wineprefix()) { + bp::env = set_wineprefix()), + process_buffer(std::make_unique()) { // It's very important that these sockets are connected to in the same order // in the Wine VST host socket_acceptor.accept(host_vst_dispatch); socket_acceptor.accept(vst_host_callback); socket_acceptor.accept(host_vst_parameters); + socket_acceptor.accept(host_vst_process_replacing); socket_acceptor.accept(vst_host_aeffect); // Set up all pointers for our `AEffect` struct. We will fill this with data @@ -147,24 +148,39 @@ intptr_t HostBridge::dispatch(AEffect* /*plugin*/, return send_event(host_vst_dispatch, opcode, index, value, data, option); } -void HostBridge::process(AEffect* /*plugin*/, - float** /*inputs*/, - float** /*outputs*/, - int32_t /*sample_frames*/) { - // TODO: Unimplmemented -} - void HostBridge::process_replacing(AEffect* /*plugin*/, - float** /*inputs*/, - float** /*outputs*/, - int /*sample_frames*/) { - // TODO: Unimplmemented + float** inputs, + float** outputs, + int sample_frames) { + // The inputs and outputs arrays should be `[num_inputs][sample_frames]` and + // `[num_outputs][sample_frames]` floats large respectfully. + std::vector> input_buffers( + plugin.numInputs, std::vector(sample_frames)); + for (int channel = 0; channel < plugin.numInputs; channel++) { + std::copy(inputs[channel], inputs[channel] + sample_frames + 1, + input_buffers[channel].begin()); + } + + const AudioBuffers request{input_buffers, sample_frames}; + write_object(host_vst_process_replacing, request, *process_buffer); + + // /Write the results back to the `outputs` arrays + AudioBuffers response; + response = + read_object(host_vst_process_replacing, response, *process_buffer); + + // TODO: Doesn't quite work yet, not sure which side is causing problems + assert(response.buffers.size() == static_cast(plugin.numOutputs)); + for (int channel = 0; channel < plugin.numOutputs; channel++) { + std::copy(response.buffers[channel].begin(), + response.buffers[channel].end(), outputs[channel]); + } } void HostBridge::set_parameter(AEffect* /*plugin*/, int32_t index, float value) { - Parameter request{index, value}; + const Parameter request{index, value}; write_object(host_vst_parameters, request); // This should not contain any values and just serve as an acknowledgement @@ -173,7 +189,7 @@ void HostBridge::set_parameter(AEffect* /*plugin*/, } float HostBridge::get_parameter(AEffect* /*plugin*/, int32_t index) { - Parameter request{index, std::nullopt}; + const Parameter request{index, std::nullopt}; write_object(host_vst_parameters, request); const auto response = read_object(host_vst_parameters); @@ -316,8 +332,8 @@ void process_proxy(AEffect* plugin, float** inputs, float** outputs, int32_t sample_frames) { - return get_bridge_instance(*plugin).process(plugin, inputs, outputs, - sample_frames); + return get_bridge_instance(*plugin).process_replacing( + plugin, inputs, outputs, sample_frames); } void process_replacing_proxy(AEffect* plugin, diff --git a/src/plugin/host-bridge.h b/src/plugin/host-bridge.h index e3054515..032ab134 100644 --- a/src/plugin/host-bridge.h +++ b/src/plugin/host-bridge.h @@ -23,6 +23,8 @@ #include #include +#include "../common/communication.h" + /** * This handles the communication between the Linux native VST plugin and the * Wine VST host. The functions below should be used as callback functions in an @@ -55,10 +57,10 @@ class HostBridge { intptr_t value, void* data, float option); - void process(AEffect* plugin, - float** inputs, - float** outputs, - int32_t sample_frames); + /** + * Ask the VST plugin to process audio for us. This should also be used for + * the deprecated 'process' function. + */ void process_replacing(AEffect* plugin, float** inputs, float** outputs, @@ -92,6 +94,7 @@ class HostBridge { * would cause a race condition. */ boost::asio::local::stream_protocol::socket host_vst_parameters; + boost::asio::local::stream_protocol::socket host_vst_process_replacing; /** * This socket only handles updates of the `AEffect` struct instead of @@ -113,4 +116,10 @@ class HostBridge { * The Wine process hosting the Windows VST plugin. */ boost::process::child vst_host; + + /** + * A scratch buffer for sending and receiving data during `process` and + * `processReplacing` calls. + */ + std::unique_ptr process_buffer; }; diff --git a/src/wine-host/plugin-bridge.cpp b/src/wine-host/plugin-bridge.cpp index f09e0269..13a61505 100644 --- a/src/wine-host/plugin-bridge.cpp +++ b/src/wine-host/plugin-bridge.cpp @@ -16,8 +16,6 @@ #include "plugin-bridge.h" -#include "../common/communication.h" - /** * A function pointer to what should be the entry point of a VST plugin. */ @@ -58,7 +56,9 @@ PluginBridge::PluginBridge(std::string plugin_dll_path, host_vst_dispatch(io_context), vst_host_callback(io_context), host_vst_parameters(io_context), - vst_host_aeffect(io_context) { + host_vst_process_replacing(io_context), + vst_host_aeffect(io_context), + process_buffer(std::make_unique()) { // Got to love these C APIs if (plugin_handle == nullptr) { throw std::runtime_error("Could not load a shared library at '" + @@ -88,6 +88,7 @@ PluginBridge::PluginBridge(std::string plugin_dll_path, host_vst_dispatch.connect(socket_endpoint); vst_host_callback.connect(socket_endpoint); host_vst_parameters.connect(socket_endpoint); + host_vst_process_replacing.connect(socket_endpoint); vst_host_aeffect.connect(socket_endpoint); // Initialize after communication has been set up We'll try to do the same @@ -143,10 +144,42 @@ PluginBridge::PluginBridge(std::string plugin_dll_path, } } }); + + process_replacing_handler = std::thread([&]() { + while (true) { + AudioBuffers request; + request = read_object(host_vst_process_replacing, request, + *process_buffer); + + // TODO: Check if the plugin doesn't support `processReplacing` and + // call the legacy `process` function instead + std::vector> output_buffers( + plugin->numOutputs, std::vector(request.sample_frames)); + + // The process functions expect a `float**` for their inputs and + // their outputs + std::vector inputs; + for (auto& buffer : request.buffers) { + inputs.push_back(buffer.data()); + } + std::vector outputs; + for (auto& buffer : output_buffers) { + outputs.push_back(buffer.data()); + } + + plugin->process(plugin, inputs.data(), outputs.data(), + request.sample_frames); + + AudioBuffers response{output_buffers, request.sample_frames}; + write_object(host_vst_process_replacing, response, *process_buffer); + } + }); } void PluginBridge::wait() { dispatch_handler.join(); + parameters_handler.join(); + process_replacing_handler.join(); } intptr_t PluginBridge::host_callback(AEffect* /*plugin*/, diff --git a/src/wine-host/plugin-bridge.h b/src/wine-host/plugin-bridge.h index 2f18a59f..24b61851 100644 --- a/src/wine-host/plugin-bridge.h +++ b/src/wine-host/plugin-bridge.h @@ -26,6 +26,8 @@ #include +#include "../common/communication.h" + /** * This handles the communication between the Linux native VST plugin and the * Wine VST host. The functions below should be used as callback functions in an @@ -82,6 +84,7 @@ class PluginBridge { * overlap. */ boost::asio::local::stream_protocol::socket host_vst_parameters; + boost::asio::local::stream_protocol::socket host_vst_process_replacing; /** * This socket only handles updates of the `AEffect` struct instead of @@ -98,6 +101,14 @@ class PluginBridge { * The thread that responds to `getParameter` and `setParameter` requests. */ std::thread parameters_handler; + /** + * The t thread that handles calls to `processReplacing` (and `process`). + */ + std::thread process_replacing_handler; - // TODO: Set up process and processReplacing callback handlers + /** + * A scratch buffer for sending and receiving data during `process` and + * `processReplacing` calls. + */ + std::unique_ptr process_buffer; };