mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +02:00
Pass through the plugin's AEffect struct
This commit is contained in:
@@ -49,14 +49,29 @@ fs::path find_wine_vst_host();
|
||||
fs::path generate_endpoint_name();
|
||||
bp::environment set_wineprefix();
|
||||
|
||||
intptr_t dispatch_proxy(AEffect*, int32_t, int32_t, intptr_t, void*, float);
|
||||
void process_proxy(AEffect*, float**, float**, int32_t);
|
||||
void setParameter_proxy(AEffect*, int32_t, float);
|
||||
float getParameter_proxy(AEffect*, int32_t);
|
||||
|
||||
/**
|
||||
* Fetch 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 C API.
|
||||
*/
|
||||
HostBridge& get_bridge_instance(const AEffect& plugin) {
|
||||
return *static_cast<HostBridge*>(plugin.ptr3);
|
||||
}
|
||||
|
||||
// TODO: When adding debug information, print both the path to the VST host and
|
||||
// the chosen wineprefix
|
||||
HostBridge::HostBridge(AEffect* plugin, audioMasterCallback host_callback)
|
||||
HostBridge::HostBridge(audioMasterCallback host_callback)
|
||||
: io_context(),
|
||||
socket_endpoint(generate_endpoint_name().string()),
|
||||
socket_acceptor(io_context, socket_endpoint),
|
||||
host_vst_dispatch(io_context),
|
||||
vst_host_callback(io_context),
|
||||
vst_host_aeffect(io_context),
|
||||
host_callback_function(host_callback),
|
||||
vst_host(find_wine_vst_host(),
|
||||
// The Wine VST host needs to know which plugin to load and
|
||||
@@ -68,10 +83,28 @@ HostBridge::HostBridge(AEffect* plugin, audioMasterCallback host_callback)
|
||||
// in the Wine VST host
|
||||
socket_acceptor.accept(host_vst_dispatch);
|
||||
socket_acceptor.accept(vst_host_callback);
|
||||
socket_acceptor.accept(vst_host_aeffect);
|
||||
|
||||
// TODO: REmove
|
||||
// After accepting the sockets
|
||||
removeme = std::thread([&]() { return host_callback_loop(plugin); });
|
||||
// Set up all pointers for our `AEffect` struct. We will fill this with data
|
||||
// from the VST plugin loaded in Wine at the end of this constructor.
|
||||
plugin.ptr3 = this;
|
||||
plugin.dispatcher = dispatch_proxy;
|
||||
plugin.process = process_proxy;
|
||||
plugin.setParameter = setParameter_proxy;
|
||||
plugin.getParameter = getParameter_proxy;
|
||||
// TODO: Add processReplacing
|
||||
|
||||
// TODO: Replace manual thread creation with an async_read loop
|
||||
// Start accepting host callbacks after we've set up our sockets and basic
|
||||
// `AEffect` struct.
|
||||
removeme = std::thread([&]() { return host_callback_loop(); });
|
||||
|
||||
// Read the plugin's information from the Wine process. This can only be
|
||||
// done after we started accepting host callbacks as the plugin might do
|
||||
// this during initialization.
|
||||
// XXX: If the plugin has crashed then this read should fail instead of
|
||||
// blocking indefinitely, check if this is the case
|
||||
plugin = read_object(vst_host_aeffect, plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,9 +256,9 @@ fs::path generate_endpoint_name() {
|
||||
// TODO: Replace blocking loop with async readers or threads for all of the
|
||||
// sockets. Also extract this functionality somewhere since the host event
|
||||
// callback needs to do exactly the same thing.
|
||||
void HostBridge::host_callback_loop(AEffect* plugin) {
|
||||
void HostBridge::host_callback_loop() {
|
||||
while (true) {
|
||||
passthrough_event(vst_host_callback, plugin, host_callback_function);
|
||||
passthrough_event(vst_host_callback, &plugin, host_callback_function);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,3 +285,32 @@ bp::environment set_wineprefix() {
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
// The below functions are proxy functions for the methods defined in
|
||||
// `Bridge.cpp`
|
||||
|
||||
intptr_t dispatch_proxy(AEffect* plugin,
|
||||
int32_t opcode,
|
||||
int32_t index,
|
||||
intptr_t value,
|
||||
void* data,
|
||||
float option) {
|
||||
return get_bridge_instance(*plugin).dispatch(plugin, opcode, index, value,
|
||||
data, option);
|
||||
}
|
||||
|
||||
void process_proxy(AEffect* plugin,
|
||||
float** inputs,
|
||||
float** outputs,
|
||||
int32_t sample_frames) {
|
||||
return get_bridge_instance(*plugin).process(plugin, inputs, outputs,
|
||||
sample_frames);
|
||||
}
|
||||
|
||||
void setParameter_proxy(AEffect* plugin, int32_t index, float value) {
|
||||
return get_bridge_instance(*plugin).set_parameter(plugin, index, value);
|
||||
}
|
||||
|
||||
float getParameter_proxy(AEffect* plugin, int32_t index) {
|
||||
return get_bridge_instance(*plugin).get_parameter(plugin, index);
|
||||
}
|
||||
|
||||
@@ -35,17 +35,13 @@ class HostBridge {
|
||||
* Initializes the Wine VST bridge. This sets up the sockets for event
|
||||
* handling.
|
||||
*
|
||||
* TODO: Figure out whether shared memory gives us better throughput and/or
|
||||
* lower overhead than using a Unix domain socket would.
|
||||
*
|
||||
* @param host_callback The callback function passed to the VST plugin by
|
||||
* the host.
|
||||
*
|
||||
* @throw std::runtime_error Thrown when the VST host could not be found, or
|
||||
* if it could not locate and load a VST .dll file.
|
||||
*/
|
||||
// TODO: The plugin struct should be created here, not passed in
|
||||
HostBridge(AEffect* plugin, audioMasterCallback host_callback);
|
||||
HostBridge(audioMasterCallback host_callback);
|
||||
|
||||
// The four below functions are the handlers from the VST2 API. They are
|
||||
// called through proxy functions in `plugin.cpp`.
|
||||
@@ -68,7 +64,14 @@ class HostBridge {
|
||||
float get_parameter(AEffect* plugin, int32_t index);
|
||||
|
||||
// TODO: Remove debug loop
|
||||
void host_callback_loop(AEffect* plugin);
|
||||
void host_callback_loop();
|
||||
|
||||
/**
|
||||
* This AEffect struct will be populated using the data passed by the Wine
|
||||
* VST host during initialization and then passed as a pointer to the Linux
|
||||
* native VST host from the Linux VST plugin's entry point.
|
||||
*/
|
||||
AEffect plugin;
|
||||
|
||||
private:
|
||||
boost::asio::io_context io_context;
|
||||
@@ -82,6 +85,13 @@ class HostBridge {
|
||||
boost::asio::local::stream_protocol::socket host_vst_dispatch;
|
||||
boost::asio::local::stream_protocol::socket vst_host_callback;
|
||||
|
||||
/**
|
||||
* This socket only handles updates of the `AEffect` struct instead of
|
||||
* passing through function calls. It's also used during initialization to
|
||||
* pass the Wine plugin's information to the host.
|
||||
*/
|
||||
boost::asio::local::stream_protocol::socket vst_host_aeffect;
|
||||
|
||||
/**
|
||||
* The callback function passed by the host to the VST plugin instance.
|
||||
*/
|
||||
|
||||
+6
-60
@@ -38,20 +38,6 @@ VST_EXPORT AEffect* main_plugin(audioMasterCallback audioMaster) {
|
||||
}
|
||||
}
|
||||
|
||||
intptr_t dispatch_proxy(AEffect*, int32_t, int32_t, intptr_t, void*, float);
|
||||
void process_proxy(AEffect*, float**, float**, int32_t);
|
||||
void setParameter_proxy(AEffect*, int32_t, float);
|
||||
float getParameter_proxy(AEffect*, int32_t);
|
||||
|
||||
/**
|
||||
* Fetch 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 C API.
|
||||
*/
|
||||
HostBridge& get_bridge_instance(const AEffect& plugin) {
|
||||
return *static_cast<HostBridge*>(plugin.ptr3);
|
||||
}
|
||||
|
||||
/**
|
||||
* The main VST plugin entry point. We first set up a bridge that connects to a
|
||||
* Wine process that hosts the Windows VST plugin. We then create and return a
|
||||
@@ -63,25 +49,14 @@ HostBridge& get_bridge_instance(const AEffect& plugin) {
|
||||
*/
|
||||
VST_EXPORT AEffect* VSTPluginMain(audioMasterCallback host_callback) {
|
||||
try {
|
||||
// TODO: Create the plugin instance in the bridge based on the received
|
||||
// parameters. We can then also use a smart pointer so we only
|
||||
// have to manually delete the bridge instance.
|
||||
AEffect* plugin = new AEffect();
|
||||
HostBridge* bridge = new HostBridge(plugin, host_callback);
|
||||
plugin->ptr3 = bridge;
|
||||
// This is the only place where we have to use manual memory management.
|
||||
// The bridge's destructor is called when the `effClose` opcode is
|
||||
// received.
|
||||
HostBridge* bridge = new HostBridge(host_callback);
|
||||
|
||||
plugin->dispatcher = dispatch_proxy;
|
||||
plugin->process = process_proxy;
|
||||
plugin->setParameter = setParameter_proxy;
|
||||
plugin->getParameter = getParameter_proxy;
|
||||
// // XXX: processReplacing?
|
||||
// TODO: Debug print information about the loaded plugin
|
||||
|
||||
// TODO: Add more and actual data
|
||||
plugin->magic = kEffectMagic;
|
||||
plugin->numParams = 69;
|
||||
plugin->uniqueID = 69420;
|
||||
|
||||
return plugin;
|
||||
return &bridge->plugin;
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << "Error during initialization:" << std::endl;
|
||||
std::cerr << error.what() << std::endl;
|
||||
@@ -89,32 +64,3 @@ VST_EXPORT AEffect* VSTPluginMain(audioMasterCallback host_callback) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// The below functions are proxy functions for the methods defined in
|
||||
// `Bridge.cpp`
|
||||
|
||||
intptr_t dispatch_proxy(AEffect* plugin,
|
||||
int32_t opcode,
|
||||
int32_t index,
|
||||
intptr_t value,
|
||||
void* data,
|
||||
float option) {
|
||||
return get_bridge_instance(*plugin).dispatch(plugin, opcode, index, value,
|
||||
data, option);
|
||||
}
|
||||
|
||||
void process_proxy(AEffect* plugin,
|
||||
float** inputs,
|
||||
float** outputs,
|
||||
int32_t sample_frames) {
|
||||
return get_bridge_instance(*plugin).process(plugin, inputs, outputs,
|
||||
sample_frames);
|
||||
}
|
||||
|
||||
void setParameter_proxy(AEffect* plugin, int32_t index, float value) {
|
||||
return get_bridge_instance(*plugin).set_parameter(plugin, index, value);
|
||||
}
|
||||
|
||||
float getParameter_proxy(AEffect* plugin, int32_t index) {
|
||||
return get_bridge_instance(*plugin).get_parameter(plugin, index);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,8 @@ PluginBridge::PluginBridge(std::string plugin_dll_path,
|
||||
io_context(),
|
||||
socket_endpoint(socket_endpoint_path),
|
||||
host_vst_dispatch(io_context),
|
||||
vst_host_callback(io_context) {
|
||||
vst_host_callback(io_context),
|
||||
vst_host_aeffect(io_context) {
|
||||
// Got to love these C APIs
|
||||
if (plugin_handle == nullptr) {
|
||||
throw std::runtime_error("Could not load a shared library at '" +
|
||||
@@ -85,6 +86,7 @@ PluginBridge::PluginBridge(std::string plugin_dll_path,
|
||||
// in the Linus plugin
|
||||
host_vst_dispatch.connect(socket_endpoint);
|
||||
vst_host_callback.connect(socket_endpoint);
|
||||
vst_host_aeffect.connect(socket_endpoint);
|
||||
|
||||
// Initialize after communication has been set up We'll try to do the same
|
||||
// `get_bridge_isntance` trick as in `plugin/plugin.cpp`, but since the
|
||||
@@ -97,6 +99,12 @@ PluginBridge::PluginBridge(std::string plugin_dll_path,
|
||||
"' failed to initialize.");
|
||||
}
|
||||
|
||||
// Send the plugin's information to the Linux VST plugin
|
||||
// TODO: This is now done only once at startup, do plugins update their
|
||||
// parameters? In that case we should be detecting updates and pass
|
||||
// them along accordingly.
|
||||
write_object(vst_host_aeffect, *plugin);
|
||||
|
||||
// We only needed this little hack during initialization
|
||||
current_bridge_isntance = nullptr;
|
||||
plugin->ptr1 = this;
|
||||
|
||||
@@ -75,4 +75,11 @@ class PluginBridge {
|
||||
// plugin (through the Wine VST host).
|
||||
boost::asio::local::stream_protocol::socket host_vst_dispatch;
|
||||
boost::asio::local::stream_protocol::socket vst_host_callback;
|
||||
|
||||
/**
|
||||
* This socket only handles updates of the `AEffect` struct instead of
|
||||
* passing through function calls. It's also used during initialization to
|
||||
* pass the Wine plugin's information to the host.
|
||||
*/
|
||||
boost::asio::local::stream_protocol::socket vst_host_aeffect;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user