From 58d749862f94bfebf84f9246a0bc79f41455f388 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Thu, 6 Feb 2020 19:01:30 +0100 Subject: [PATCH] Delegate the plugin functionality to a class This way we can hopefully contain all the manual memory management and raw pointers somewhat. --- meson.build | 3 +- src/plugin/bridge.cpp | 50 ++++++++++++++++++++++ src/plugin/bridge.h | 31 ++++++++++++++ src/plugin/plugin.cpp | 94 +++++++++++++++++++++++++++++++++++++++++ src/plugin/yabridge.cpp | 89 -------------------------------------- 5 files changed, 177 insertions(+), 90 deletions(-) create mode 100644 src/plugin/bridge.cpp create mode 100644 src/plugin/bridge.h create mode 100644 src/plugin/plugin.cpp delete mode 100644 src/plugin/yabridge.cpp diff --git a/meson.build b/meson.build index fc3c56fa..59114994 100644 --- a/meson.build +++ b/meson.build @@ -25,7 +25,8 @@ include_dir = include_directories('src/include') common_src = [] linux_plugin_src = [ - 'src/plugin/yabridge.cpp', + 'src/plugin/bridge.cpp', + 'src/plugin/plugin.cpp', ] wine_host_src = [ diff --git a/src/plugin/bridge.cpp b/src/plugin/bridge.cpp new file mode 100644 index 00000000..ba50cf82 --- /dev/null +++ b/src/plugin/bridge.cpp @@ -0,0 +1,50 @@ +#include "bridge.h" + +#include + +// 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. + +/** + * Handle an event sent by the VST host. Most of these opcodes will be passed + * through to the winelib VST host. + */ +intptr_t Bridge::dispatch(AEffect* /*plugin*/, + int32_t opcode, + int32_t /*parameter*/, + intptr_t /*value*/, + void* result, + float /*option*/) { + switch (opcode) { + case effGetEffectName: + const std::string plugin_name("Hello, world!"); + std::copy(plugin_name.begin(), plugin_name.end(), + static_cast(result)); + + // return 1; // TODO: Why? + break; + } + + // TODO: Unimplmemented + return 0; +} + +void Bridge::process(AEffect* /*plugin*/, + float** /*inputs*/, + float** /*outputs*/, + int32_t /*sample_frames*/) { + // TODO: Unimplmemented +} + +void Bridge::set_parameter(AEffect* /*plugin*/, + int32_t /*index*/, + float /*value*/) { + // TODO: Unimplmemented +} + +float Bridge::get_parameter(AEffect* /*plugin*/, int32_t /*index*/ +) { + // TODO: Unimplmemented + return 0.0f; +} diff --git a/src/plugin/bridge.h b/src/plugin/bridge.h new file mode 100644 index 00000000..f8d2545e --- /dev/null +++ b/src/plugin/bridge.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +/** + * 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 + * `AEffect` object. + */ +class Bridge { + public: + // The below four functions are the handlers from the VST2 API. They are + // called through proxy functions in `plugin.cpp`. + + /** + * Handle an event sent by the VST host. Most of these opcodes will be + * passed through to the winelib VST host through a Unix domain socket. + */ + intptr_t dispatch(AEffect* plugin, + int32_t opcode, + int32_t parameter, + intptr_t value, + void* result, + float option); + void process(AEffect* plugin, + float** inputs, + float** outputs, + int32_t sample_frames); + void set_parameter(AEffect* plugin, int32_t index, float value); + float get_parameter(AEffect* plugin, int32_t index); +}; diff --git a/src/plugin/plugin.cpp b/src/plugin/plugin.cpp new file mode 100644 index 00000000..2613b980 --- /dev/null +++ b/src/plugin/plugin.cpp @@ -0,0 +1,94 @@ +#include + +#include +#include + +#include "bridge.h" + +#define VST_EXPORT __attribute__((visibility("default"))) + +// The main entry point for VST plugins should be called `VSTPluginMain``. The +// other two exist for legacy reasons since some old hosts might still use +// them.` +extern "C" { +extern VST_EXPORT AEffect* VSTPluginMain(audioMasterCallback); + +VST_EXPORT AEffect* MAIN(audioMasterCallback audioMaster) { + return VSTPluginMain(audioMaster); +} + +VST_EXPORT AEffect* main_plugin(audioMasterCallback audioMaster) { + return VSTPluginMain(audioMaster); +} +} + +intptr_t dispatch(AEffect*, int32_t, int32_t, intptr_t, void*, float); +void process(AEffect*, float**, float**, int32_t); +void setParameter(AEffect*, int32_t, float); +float getParameter(AEffect*, int32_t); + +/** + * Retrieve the bridge instance stored in an unused pointer from a VST plugin. + * This is sadly needed as a workaround to avoid using globals since we need + * free function pointers to interface with the VST API. + */ +Bridge* get_bridge_instance(const AEffect& plugin) { + return static_cast(plugin.ptr3); +} + +/** + * The main VST plugin entry point. This finds the Windows VST plugin that + * should be run, executes it in our VST host inside Wine, and sets up + * communication between the two processes. + * + * This is a bit of a mess since we're interacting with an external C API. To + * keep this somewhat contained this is the only place where we're doing manual + * memory management. + */ +VST_EXPORT AEffect* VSTPluginMain(audioMasterCallback /*audioMaster*/) { + // TODO: Since we are returning raw pointers, how does cleanup work? + AEffect* plugin = new AEffect(); + plugin->ptr3 = new Bridge(); + + plugin->dispatcher = dispatch; + plugin->process = process; + plugin->setParameter = setParameter; + plugin->getParameter = getParameter; + // // XXX: processReplacing? + + // TODO: Add more and actual data + plugin->magic = kEffectMagic; + plugin->numParams = 69; + plugin->uniqueID = 69420; + + return plugin; +} + +// The below functions are proxy functions for the methods defined in +// `Bridge.cpp` + +intptr_t dispatch(AEffect* plugin, + int32_t opcode, + int32_t parameter, + intptr_t value, + void* result, + float option) { + return get_bridge_instance(*plugin)->dispatch(plugin, opcode, parameter, + value, result, option); +} + +void process(AEffect* plugin, + float** inputs, + float** outputs, + int32_t sample_frames) { + return get_bridge_instance(*plugin)->process(plugin, inputs, outputs, + sample_frames); +} + +void setParameter(AEffect* plugin, int32_t index, float value) { + return get_bridge_instance(*plugin)->set_parameter(plugin, index, value); +} + +float getParameter(AEffect* plugin, int32_t index) { + return get_bridge_instance(*plugin)->get_parameter(plugin, index); +} diff --git a/src/plugin/yabridge.cpp b/src/plugin/yabridge.cpp deleted file mode 100644 index 32fdc9e0..00000000 --- a/src/plugin/yabridge.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include - -#include - -#define VST_EXPORT __attribute__((visibility("default"))) - -// The main entry point for VST plugins should be called `VSTPluginMain``. The -// other two exist for legacy reasons since some old hosts might still use -// them.` -extern "C" { -extern VST_EXPORT AEffect* VSTPluginMain(audioMasterCallback); - -VST_EXPORT AEffect* MAIN(audioMasterCallback audioMaster) { - return VSTPluginMain(audioMaster); -} - -VST_EXPORT AEffect* main_plugin(audioMasterCallback audioMaster) { - return VSTPluginMain(audioMaster); -} -} - -intptr_t dispatch(AEffect*, int32_t, int32_t, intptr_t, void*, float); -void process(AEffect*, float**, float**, int32_t); -void setParameter(AEffect*, int32_t, float); -float getParameter(AEffect*, int32_t); - -/** - * The main VST plugin entry point. This finds the Windows VST plugin that - * should be run, executes it in our VST host inside Wine, and sets up - * communication between the two processes. - */ -VST_EXPORT AEffect* VSTPluginMain(audioMasterCallback /*audioMaster*/) { - // TODO: Do something useful ehre - std::cout << "Hello, world!" << std::endl; - - AEffect* effect = new AEffect(); - effect->magic = kEffectMagic; - effect->dispatcher = dispatch; - effect->process = process; - // XXX: processReplacing? - effect->setParameter = setParameter; - effect->getParameter = getParameter; - effect->numParams = 69; - effect->uniqueID = 69420; - - return effect; -} - -// 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. - -/** - * Handle an event sent by the VST host. - * - * TODO: Look up what the return value here is actually doing. - */ -intptr_t dispatch(AEffect* /*effect*/, - int32_t opcode, - int32_t /*parameter*/, - intptr_t /*value*/, - void* result, - float /*option*/) { - switch (opcode) { - case effGetEffectName: - const std::string plugin_name("Hello, world!"); - std::copy(plugin_name.begin(), plugin_name.end(), - static_cast(result)); - - return 1; // TODO: Why? - break; - } - - // TODO: Unimplmemented - return 0; -} - -void process(AEffect*, float**, float**, int32_t) { - // TODO: Unimplmemented -} - -void setParameter(AEffect*, int32_t, float) { - // TODO: Unimplmemented -} - -float getParameter(AEffect*, int32_t) { - // TODO: Unimplmemented - return 0.0f; -}