mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +02:00
Add dedicated IAudioProcessor/IComponent sockets
This way every relevant object instance will get its own thread for handling these calls. The alternative would be creating a full fat Vst3MessageHandler pair for all object instances, but that would be a huge waste.
This commit is contained in:
@@ -491,12 +491,14 @@ class AdHocSocketHandler {
|
||||
if (acceptor) {
|
||||
acceptor->accept(socket);
|
||||
|
||||
// As mentioned in `acceptor's` docstring, this acceptor will be
|
||||
// recreated in `receive_multi()` on another context, and
|
||||
// potentially on the other side of the connection in the case
|
||||
// where we're handling `vst_host_callback` VST2 events
|
||||
acceptor.reset();
|
||||
boost::filesystem::remove(endpoint.path());
|
||||
if constexpr (ad_hoc_sockets) {
|
||||
// As mentioned in `acceptor's` docstring, this acceptor will be
|
||||
// recreated in `receive_multi()` on another context, and
|
||||
// potentially on the other side of the connection in the case
|
||||
// where we're handling `vst_host_callback` VST2 events
|
||||
acceptor.reset();
|
||||
boost::filesystem::remove(endpoint.path());
|
||||
}
|
||||
} else {
|
||||
socket.connect(endpoint);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#include <variant>
|
||||
|
||||
#include "../logging/vst3.h"
|
||||
@@ -221,6 +222,16 @@ class Vst3MessageHandler : public AdHocSocketHandler<Thread, ad_hoc_sockets> {
|
||||
* sockets, and the call to `connect()` will then accept any incoming
|
||||
* connections.
|
||||
*
|
||||
* We'll have a host -> plugin connection for sending control messages (which is
|
||||
* just a made up term to more easily differentiate between the two directions),
|
||||
* and a plugin -> host connection to allow the plugin to make callbacks. Both
|
||||
* of these connections are capable of spawning additional sockets and threads
|
||||
* as needed.
|
||||
*
|
||||
* For audio processing (or anything that implement `IAudioProcessor` or
|
||||
* `IComonent`) we'll use dedicated sockets per instance, since we don't want to
|
||||
* do anything that could increase latency there.
|
||||
*
|
||||
* @tparam Thread The thread implementation to use. On the Linux side this
|
||||
* should be `std::jthread` and on the Wine side this should be `Win32Thread`.
|
||||
*/
|
||||
@@ -250,7 +261,8 @@ class Vst3Sockets : public Sockets {
|
||||
listen),
|
||||
vst_host_callback(io_context,
|
||||
(base_dir / "vst_host_callback.sock").string(),
|
||||
listen) {}
|
||||
listen),
|
||||
io_context(io_context) {}
|
||||
|
||||
~Vst3Sockets() { close(); }
|
||||
|
||||
@@ -264,17 +276,113 @@ class Vst3Sockets : public Sockets {
|
||||
// that may still be active
|
||||
host_vst_control.close();
|
||||
vst_host_callback.close();
|
||||
|
||||
// This map should be empty at this point, but who knows
|
||||
std::lock_guard lock(audio_processor_sockets_mutex);
|
||||
for (auto& [instance_id, socket] : audio_processor_sockets) {
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Since audio processing may be done completely in parallel we might
|
||||
// want to have a dedicated socket per processor/controller pair. For
|
||||
// this we would need to figure out how to associate a plugin instance
|
||||
// with a socket.
|
||||
/**
|
||||
* Connect to the dedicated `IAudioProcessor` and `IConnect` handling socket
|
||||
* for a plugin object instance. This should be called on the plugin side
|
||||
* after instantiating such an object.
|
||||
*
|
||||
* @param instance_id The object instance identifier of the socket.
|
||||
*/
|
||||
void add_audio_processor_and_connect(size_t instance_id) {
|
||||
std::lock_guard lock(audio_processor_sockets_mutex);
|
||||
audio_processor_sockets.try_emplace(
|
||||
instance_id, io_context,
|
||||
(base_dir / ("host_vst_audio_processor_" +
|
||||
std::to_string(instance_id) + ".sock"))
|
||||
.string(),
|
||||
false);
|
||||
audio_processor_sockets.at(instance_id).connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and listen on a dedicated `IAudioProcessor` and `IConnect`
|
||||
* handling socket for a plugin object instance. The calling thread will
|
||||
* block until the socket has been closed. This should be called from the
|
||||
* Wine plugin host side after instantiating such an object.
|
||||
*
|
||||
* @param instance_id The object instance identifier of the socket.
|
||||
* @param socket_listening_latch A promise we'll set a value for once the
|
||||
* socket is being listened on so we can wait for it. Otherwise it can be
|
||||
* that the native plugin already tries to connect to the socket before
|
||||
* Wine plugin host is even listening on it.
|
||||
* @param cb An overloaded function that can take every type `T` in the
|
||||
* `AudioProcessorRequest` variant and then returns `T::Response`.
|
||||
*/
|
||||
template <typename F>
|
||||
void add_audio_processor_and_listen(
|
||||
size_t instance_id,
|
||||
std::promise<void>& socket_listening_latch,
|
||||
F cb) {
|
||||
{
|
||||
std::lock_guard lock(audio_processor_sockets_mutex);
|
||||
audio_processor_sockets.try_emplace(
|
||||
instance_id, io_context,
|
||||
(base_dir / ("host_vst_audio_processor_" +
|
||||
std::to_string(instance_id) + ".sock"))
|
||||
.string(),
|
||||
true);
|
||||
}
|
||||
|
||||
socket_listening_latch.set_value();
|
||||
audio_processor_sockets.at(instance_id).connect();
|
||||
audio_processor_sockets.at(instance_id)
|
||||
.receive_messages(std::nullopt, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* If `instance_id` is in `audio_processor_sockets`, then close its socket
|
||||
* and remove it from the map. This is called from the destructor of
|
||||
* `Vst3PluginProxyImpl` on the plugin side and when handling
|
||||
* `Vst3PluginProxy::Destruct` on the Wine plugin host side.
|
||||
*
|
||||
* @param instance_id The object instance identifier of the socket.
|
||||
*
|
||||
* @return Whether the socket was closed and removed. Returns false if it
|
||||
* wasn't in the map.
|
||||
*/
|
||||
bool remove_audio_processor(size_t instance_id) {
|
||||
std::lock_guard lock(audio_processor_sockets_mutex);
|
||||
if (audio_processor_sockets.contains(instance_id)) {
|
||||
audio_processor_sockets.at(instance_id).close();
|
||||
audio_processor_sockets.erase(instance_id);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message from the native plugin to the Wine plugin host to handle
|
||||
* an `IAudioProcessor` or `IComponent` call. Since those functions are
|
||||
* called from a hot loop we want every instance to have a dedicated socket
|
||||
* and thread for handling those.
|
||||
*
|
||||
* @tparam T Some object in the `AudioProcessorRequest` variant.
|
||||
*/
|
||||
template <typename T>
|
||||
typename T::Response send_audio_processor_message(
|
||||
const T& object,
|
||||
std::optional<std::pair<Vst3Logger&, bool>> logging) {
|
||||
// TODO: These calls should reuse buffers
|
||||
return audio_processor_sockets.at(object.instance_id)
|
||||
.send_message(object, logging);
|
||||
}
|
||||
|
||||
/**
|
||||
* For sending messages from the host to the plugin. After we have a better
|
||||
* idea of what our communication model looks like we'll probably want to
|
||||
* provide an abstraction similar to `EventHandler`.
|
||||
* provide an abstraction similar to `EventHandler`. For optimization
|
||||
* reasons calls to `IAudioProcessor` or `IComponent` are handled using the
|
||||
* dedicated sockets in `audio_processor_sockets`.
|
||||
*
|
||||
* This will be listened on by the Wine plugin host when it calls
|
||||
* `receive_multi()`.
|
||||
@@ -287,4 +395,22 @@ class Vst3Sockets : public Sockets {
|
||||
* want to provide an abstraction similar to `EventHandler`.
|
||||
*/
|
||||
Vst3MessageHandler<Thread, CallbackRequest, true> vst_host_callback;
|
||||
|
||||
private:
|
||||
boost::asio::io_context& io_context;
|
||||
|
||||
/**
|
||||
* Every `IAudioProcessor` or `IComponent` instance (which likely implements
|
||||
* both of those) will get a dedicated socket. These functions are always
|
||||
* called in a hot loop, so there should not be any waiting or additional
|
||||
* thread or socket creation happening there.
|
||||
*
|
||||
* THe last `false` template arguments means that we'll disable all ad-hoc
|
||||
* socket and thread spawning behaviour. Otherwise every plugin instance
|
||||
* would have one dedicated thread for handling function calls to these
|
||||
* interfaces, and then another dedicated thread just idling around.
|
||||
*/
|
||||
std::map<size_t, Vst3MessageHandler<Thread, AudioProcessorRequest, false>>
|
||||
audio_processor_sockets;
|
||||
std::mutex audio_processor_sockets_mutex;
|
||||
};
|
||||
|
||||
+273
-273
@@ -90,210 +90,6 @@ bool Vst3Logger::log_request(bool is_host_vst,
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(
|
||||
bool is_host_vst,
|
||||
const YaAudioProcessor::SetBusArrangements& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IAudioProcessor::setBusArrangements(inputs = "
|
||||
"[SpeakerArrangement; "
|
||||
<< request.inputs.size() << "], numIns = " << request.num_ins
|
||||
<< ", outputs = [SpeakerArrangement; " << request.outputs.size()
|
||||
<< "], numOuts = " << request.num_outs << ")";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(
|
||||
bool is_host_vst,
|
||||
const YaAudioProcessor::GetBusArrangement& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IAudioProcessor::getBusArrangement(dir = " << request.dir
|
||||
<< ", index = " << request.index << ", &arr)";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(
|
||||
bool is_host_vst,
|
||||
const YaAudioProcessor::CanProcessSampleSize& request) {
|
||||
return log_request_base(
|
||||
is_host_vst, Logger::Verbosity::all_events, [&](auto& message) {
|
||||
message
|
||||
<< request.instance_id
|
||||
<< ": IAudioProcessor::canProcessSampleSize(symbolicSampleSize "
|
||||
"= "
|
||||
<< request.symbolic_sample_size << ")";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(
|
||||
bool is_host_vst,
|
||||
const YaAudioProcessor::GetLatencySamples& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IAudioProcessor::getLatencySamples()";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::SetupProcessing& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IAudioProcessor::setupProcessing(setup = "
|
||||
"<SetupProcessing with mode = "
|
||||
<< request.setup.processMode << ", symbolic_sample_size = "
|
||||
<< request.setup.symbolicSampleSize
|
||||
<< ", max_buffer_size = " << request.setup.maxSamplesPerBlock
|
||||
<< " and sample_rate = " << request.setup.sampleRate << ">)";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::SetProcessing& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IAudioProcessor::setProcessing(state = "
|
||||
<< (request.state ? "true" : "false") << ")";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::Process& request) {
|
||||
return log_request_base(
|
||||
is_host_vst, Logger::Verbosity::all_events, [&](auto& message) {
|
||||
// This is incredibly verbose, but if you're really a plugin that
|
||||
// handles processing in a weird way you're going to need all of
|
||||
// this
|
||||
|
||||
std::ostringstream num_input_channels;
|
||||
num_input_channels << "[";
|
||||
for (bool is_first = true;
|
||||
const auto& buffers : request.data.inputs) {
|
||||
num_input_channels << (is_first ? "" : ", ")
|
||||
<< buffers.num_channels();
|
||||
is_first = false;
|
||||
}
|
||||
num_input_channels << "]";
|
||||
|
||||
std::ostringstream num_output_channels;
|
||||
num_output_channels << "[";
|
||||
for (bool is_first = true;
|
||||
const auto& num_channels : request.data.outputs_num_channels) {
|
||||
num_output_channels << (is_first ? "" : ", ") << num_channels;
|
||||
is_first = false;
|
||||
}
|
||||
num_output_channels << "]";
|
||||
|
||||
message << request.instance_id
|
||||
<< ": IAudioProcessor::process(data = <ProcessData with "
|
||||
"input_channels = "
|
||||
<< num_input_channels.str()
|
||||
<< ", output_channels = " << num_output_channels.str()
|
||||
<< ", num_samples = " << request.data.process_mode
|
||||
<< ", input_parameter_changes = <IParameterChanges* for "
|
||||
<< request.data.input_parameter_changes.num_parameters()
|
||||
<< " parameters>, output_parameter_changes = "
|
||||
<< (request.data.output_parameter_changes_supported
|
||||
? "<IParameterChanges*>"
|
||||
: "nullptr")
|
||||
<< ", input_events = ";
|
||||
if (request.data.input_events) {
|
||||
message << "<IEventList* with "
|
||||
<< request.data.input_events->num_events()
|
||||
<< " events>";
|
||||
} else {
|
||||
message << "nullptr";
|
||||
}
|
||||
message << ", output_events = "
|
||||
<< (request.data.output_events_supported ? "<IEventList*>"
|
||||
: "nullptr")
|
||||
<< ", process_context = "
|
||||
<< (request.data.process_context ? "<ProcessContext*>"
|
||||
: "nullptr")
|
||||
<< ", process_mode = " << request.data.process_mode
|
||||
<< ", symbolic_sample_size = "
|
||||
<< request.data.symbolic_sample_size << ">)";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::GetTailSamples& request) {
|
||||
return log_request_base(
|
||||
is_host_vst, Logger::Verbosity::all_events, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IAudioProcessor::getTailSamples()";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaComponent::GetControllerClassId& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IComponent::getControllerClassId(&classId)";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaComponent::SetIoMode& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IComponent::setIoMode(mode = " << request.mode << ")";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaComponent::GetBusCount& request) {
|
||||
// JUCE-based hosts will call this every processing cycle, for some reason
|
||||
// (it shouldn't be allwoed to change during processing, right?)
|
||||
return log_request_base(
|
||||
is_host_vst, Logger::Verbosity::all_events, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IComponent::getBusCount(type = " << request.type
|
||||
<< ", dir = " << request.dir << ")";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaComponent::GetBusInfo& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IComponent::getBusInfo(type = " << request.type
|
||||
<< ", dir = " << request.dir << ", index = " << request.index
|
||||
<< ", &bus)";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaComponent::GetRoutingInfo& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message
|
||||
<< request.instance_id
|
||||
<< ": IComponent::getRoutingInfo(inInfo = <RoutingInfo& for bus "
|
||||
<< request.in_info.busIndex << " and channel "
|
||||
<< request.in_info.channel << ">, outInfo = <RoutingInfo& for bus "
|
||||
<< request.out_info.busIndex << " and channel "
|
||||
<< request.out_info.channel << ">)";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaComponent::ActivateBus& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IComponent::activateBus(type = " << request.type
|
||||
<< ", dir = " << request.dir << ", index = " << request.index
|
||||
<< ", state = " << (request.state ? "true" : "false") << ")";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaComponent::SetActive& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id << ": IComponent::setActive(state = "
|
||||
<< (request.state ? "true" : "false") << ")";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaConnectionPoint::Connect& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
@@ -508,6 +304,210 @@ bool Vst3Logger::log_request(bool is_host_vst,
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(
|
||||
bool is_host_vst,
|
||||
const YaAudioProcessor::SetBusArrangements& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IAudioProcessor::setBusArrangements(inputs = "
|
||||
"[SpeakerArrangement; "
|
||||
<< request.inputs.size() << "], numIns = " << request.num_ins
|
||||
<< ", outputs = [SpeakerArrangement; " << request.outputs.size()
|
||||
<< "], numOuts = " << request.num_outs << ")";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(
|
||||
bool is_host_vst,
|
||||
const YaAudioProcessor::GetBusArrangement& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IAudioProcessor::getBusArrangement(dir = " << request.dir
|
||||
<< ", index = " << request.index << ", &arr)";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(
|
||||
bool is_host_vst,
|
||||
const YaAudioProcessor::CanProcessSampleSize& request) {
|
||||
return log_request_base(
|
||||
is_host_vst, Logger::Verbosity::all_events, [&](auto& message) {
|
||||
message
|
||||
<< request.instance_id
|
||||
<< ": IAudioProcessor::canProcessSampleSize(symbolicSampleSize "
|
||||
"= "
|
||||
<< request.symbolic_sample_size << ")";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(
|
||||
bool is_host_vst,
|
||||
const YaAudioProcessor::GetLatencySamples& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IAudioProcessor::getLatencySamples()";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::SetupProcessing& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IAudioProcessor::setupProcessing(setup = "
|
||||
"<SetupProcessing with mode = "
|
||||
<< request.setup.processMode << ", symbolic_sample_size = "
|
||||
<< request.setup.symbolicSampleSize
|
||||
<< ", max_buffer_size = " << request.setup.maxSamplesPerBlock
|
||||
<< " and sample_rate = " << request.setup.sampleRate << ">)";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::SetProcessing& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IAudioProcessor::setProcessing(state = "
|
||||
<< (request.state ? "true" : "false") << ")";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::Process& request) {
|
||||
return log_request_base(
|
||||
is_host_vst, Logger::Verbosity::all_events, [&](auto& message) {
|
||||
// This is incredibly verbose, but if you're really a plugin that
|
||||
// handles processing in a weird way you're going to need all of
|
||||
// this
|
||||
|
||||
std::ostringstream num_input_channels;
|
||||
num_input_channels << "[";
|
||||
for (bool is_first = true;
|
||||
const auto& buffers : request.data.inputs) {
|
||||
num_input_channels << (is_first ? "" : ", ")
|
||||
<< buffers.num_channels();
|
||||
is_first = false;
|
||||
}
|
||||
num_input_channels << "]";
|
||||
|
||||
std::ostringstream num_output_channels;
|
||||
num_output_channels << "[";
|
||||
for (bool is_first = true;
|
||||
const auto& num_channels : request.data.outputs_num_channels) {
|
||||
num_output_channels << (is_first ? "" : ", ") << num_channels;
|
||||
is_first = false;
|
||||
}
|
||||
num_output_channels << "]";
|
||||
|
||||
message << request.instance_id
|
||||
<< ": IAudioProcessor::process(data = <ProcessData with "
|
||||
"input_channels = "
|
||||
<< num_input_channels.str()
|
||||
<< ", output_channels = " << num_output_channels.str()
|
||||
<< ", num_samples = " << request.data.process_mode
|
||||
<< ", input_parameter_changes = <IParameterChanges* for "
|
||||
<< request.data.input_parameter_changes.num_parameters()
|
||||
<< " parameters>, output_parameter_changes = "
|
||||
<< (request.data.output_parameter_changes_supported
|
||||
? "<IParameterChanges*>"
|
||||
: "nullptr")
|
||||
<< ", input_events = ";
|
||||
if (request.data.input_events) {
|
||||
message << "<IEventList* with "
|
||||
<< request.data.input_events->num_events()
|
||||
<< " events>";
|
||||
} else {
|
||||
message << "nullptr";
|
||||
}
|
||||
message << ", output_events = "
|
||||
<< (request.data.output_events_supported ? "<IEventList*>"
|
||||
: "nullptr")
|
||||
<< ", process_context = "
|
||||
<< (request.data.process_context ? "<ProcessContext*>"
|
||||
: "nullptr")
|
||||
<< ", process_mode = " << request.data.process_mode
|
||||
<< ", symbolic_sample_size = "
|
||||
<< request.data.symbolic_sample_size << ">)";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::GetTailSamples& request) {
|
||||
return log_request_base(
|
||||
is_host_vst, Logger::Verbosity::all_events, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IAudioProcessor::getTailSamples()";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaComponent::GetControllerClassId& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IComponent::getControllerClassId(&classId)";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaComponent::SetIoMode& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IComponent::setIoMode(mode = " << request.mode << ")";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaComponent::GetBusCount& request) {
|
||||
// JUCE-based hosts will call this every processing cycle, for some reason
|
||||
// (it shouldn't be allwoed to change during processing, right?)
|
||||
return log_request_base(
|
||||
is_host_vst, Logger::Verbosity::all_events, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IComponent::getBusCount(type = " << request.type
|
||||
<< ", dir = " << request.dir << ")";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaComponent::GetBusInfo& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IComponent::getBusInfo(type = " << request.type
|
||||
<< ", dir = " << request.dir << ", index = " << request.index
|
||||
<< ", &bus)";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaComponent::GetRoutingInfo& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message
|
||||
<< request.instance_id
|
||||
<< ": IComponent::getRoutingInfo(inInfo = <RoutingInfo& for bus "
|
||||
<< request.in_info.busIndex << " and channel "
|
||||
<< request.in_info.channel << ">, outInfo = <RoutingInfo& for bus "
|
||||
<< request.out_info.busIndex << " and channel "
|
||||
<< request.out_info.channel << ">)";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaComponent::ActivateBus& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id
|
||||
<< ": IComponent::activateBus(type = " << request.type
|
||||
<< ", dir = " << request.dir << ", index = " << request.index
|
||||
<< ", state = " << (request.state ? "true" : "false") << ")";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst,
|
||||
const YaComponent::SetActive& request) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << request.instance_id << ": IComponent::setActive(state = "
|
||||
<< (request.state ? "true" : "false") << ")";
|
||||
});
|
||||
}
|
||||
|
||||
bool Vst3Logger::log_request(bool is_host_vst, const WantsConfiguration&) {
|
||||
return log_request_base(is_host_vst, [&](auto& message) {
|
||||
message << "Requesting <Configuration>";
|
||||
@@ -593,6 +593,75 @@ void Vst3Logger::log_response(
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(
|
||||
bool is_host_vst,
|
||||
const YaEditController::GetParameterInfoResponse& response) {
|
||||
log_response_base(is_host_vst, [&](auto& message) {
|
||||
message << response.result.string();
|
||||
if (response.result == Steinberg::kResultOk) {
|
||||
std::string param_title =
|
||||
VST3::StringConvert::convert(response.updated_info.title);
|
||||
message << ", <ParameterInfo for '" << param_title << "'>";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(
|
||||
bool is_host_vst,
|
||||
const YaEditController::GetParamStringByValueResponse& response) {
|
||||
log_response_base(is_host_vst, [&](auto& message) {
|
||||
message << response.result.string();
|
||||
if (response.result == Steinberg::kResultOk) {
|
||||
std::string value = VST3::StringConvert::convert(response.string);
|
||||
message << ", \"" << value << "\"";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(
|
||||
bool is_host_vst,
|
||||
const YaEditController::GetParamValueByStringResponse& response) {
|
||||
log_response_base(is_host_vst, [&](auto& message) {
|
||||
message << response.result.string();
|
||||
if (response.result == Steinberg::kResultOk) {
|
||||
message << ", " << response.value_normalized;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(
|
||||
bool is_host_vst,
|
||||
const YaEditController::CreateViewResponse& response) {
|
||||
log_response_base(is_host_vst, [&](auto& message) {
|
||||
if (response.plug_view_args) {
|
||||
message << "<IPlugView*>";
|
||||
} else {
|
||||
message << "<nullptr>";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(bool is_host_vst,
|
||||
const YaPlugView::GetSizeResponse& response) {
|
||||
log_response_base(is_host_vst, [&](auto& message) {
|
||||
message << response.result.string();
|
||||
if (response.result == Steinberg::kResultOk) {
|
||||
message << ", <ViewRect* with left = " << response.updated_size.left
|
||||
<< ", top = " << response.updated_size.top
|
||||
<< ", right = " << response.updated_size.right
|
||||
<< ", bottom = " << response.updated_size.bottom << ">";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(bool is_host_vst,
|
||||
const YaPluginFactory::ConstructArgs& args) {
|
||||
log_response_base(is_host_vst, [&](auto& message) {
|
||||
message << "<IPluginFactory*> with " << args.num_classes
|
||||
<< " registered classes";
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(
|
||||
bool is_host_vst,
|
||||
const YaAudioProcessor::GetBusArrangementResponse& response) {
|
||||
@@ -684,75 +753,6 @@ void Vst3Logger::log_response(
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(
|
||||
bool is_host_vst,
|
||||
const YaEditController::GetParameterInfoResponse& response) {
|
||||
log_response_base(is_host_vst, [&](auto& message) {
|
||||
message << response.result.string();
|
||||
if (response.result == Steinberg::kResultOk) {
|
||||
std::string param_title =
|
||||
VST3::StringConvert::convert(response.updated_info.title);
|
||||
message << ", <ParameterInfo for '" << param_title << "'>";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(
|
||||
bool is_host_vst,
|
||||
const YaEditController::GetParamStringByValueResponse& response) {
|
||||
log_response_base(is_host_vst, [&](auto& message) {
|
||||
message << response.result.string();
|
||||
if (response.result == Steinberg::kResultOk) {
|
||||
std::string value = VST3::StringConvert::convert(response.string);
|
||||
message << ", \"" << value << "\"";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(
|
||||
bool is_host_vst,
|
||||
const YaEditController::GetParamValueByStringResponse& response) {
|
||||
log_response_base(is_host_vst, [&](auto& message) {
|
||||
message << response.result.string();
|
||||
if (response.result == Steinberg::kResultOk) {
|
||||
message << ", " << response.value_normalized;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(
|
||||
bool is_host_vst,
|
||||
const YaEditController::CreateViewResponse& response) {
|
||||
log_response_base(is_host_vst, [&](auto& message) {
|
||||
if (response.plug_view_args) {
|
||||
message << "<IPlugView*>";
|
||||
} else {
|
||||
message << "<nullptr>";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(bool is_host_vst,
|
||||
const YaPlugView::GetSizeResponse& response) {
|
||||
log_response_base(is_host_vst, [&](auto& message) {
|
||||
message << response.result.string();
|
||||
if (response.result == Steinberg::kResultOk) {
|
||||
message << ", <ViewRect* with left = " << response.updated_size.left
|
||||
<< ", top = " << response.updated_size.top
|
||||
<< ", right = " << response.updated_size.right
|
||||
<< ", bottom = " << response.updated_size.bottom << ">";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(bool is_host_vst,
|
||||
const YaPluginFactory::ConstructArgs& args) {
|
||||
log_response_base(is_host_vst, [&](auto& message) {
|
||||
message << "<IPluginFactory*> with " << args.num_classes
|
||||
<< " registered classes";
|
||||
});
|
||||
}
|
||||
|
||||
void Vst3Logger::log_response(bool is_host_vst, const Configuration&) {
|
||||
log_response_base(is_host_vst,
|
||||
[&](auto& message) { message << "<Configuration>"; });
|
||||
|
||||
+32
-30
@@ -64,27 +64,6 @@ class Vst3Logger {
|
||||
bool log_request(bool is_host_vst, const Vst3PluginProxy::Destruct&);
|
||||
bool log_request(bool is_host_vst, const Vst3PluginProxy::SetState&);
|
||||
bool log_request(bool is_host_vst, const Vst3PluginProxy::GetState&);
|
||||
bool log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::SetBusArrangements&);
|
||||
bool log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::GetBusArrangement&);
|
||||
bool log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::CanProcessSampleSize&);
|
||||
bool log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::GetLatencySamples&);
|
||||
bool log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::SetupProcessing&);
|
||||
bool log_request(bool is_host_vst, const YaAudioProcessor::SetProcessing&);
|
||||
bool log_request(bool is_host_vst, const YaAudioProcessor::Process&);
|
||||
bool log_request(bool is_host_vst, const YaAudioProcessor::GetTailSamples&);
|
||||
bool log_request(bool is_host_vst,
|
||||
const YaComponent::GetControllerClassId&);
|
||||
bool log_request(bool is_host_vst, const YaComponent::SetIoMode&);
|
||||
bool log_request(bool is_host_vst, const YaComponent::GetBusCount&);
|
||||
bool log_request(bool is_host_vst, const YaComponent::GetBusInfo&);
|
||||
bool log_request(bool is_host_vst, const YaComponent::GetRoutingInfo&);
|
||||
bool log_request(bool is_host_vst, const YaComponent::ActivateBus&);
|
||||
bool log_request(bool is_host_vst, const YaComponent::SetActive&);
|
||||
bool log_request(bool is_host_vst, const YaConnectionPoint::Connect&);
|
||||
bool log_request(bool is_host_vst, const YaConnectionPoint::Disconnect&);
|
||||
bool log_request(bool is_host_vst,
|
||||
@@ -117,6 +96,28 @@ class Vst3Logger {
|
||||
bool log_request(bool is_host_vst, const YaPluginFactory::Construct&);
|
||||
bool log_request(bool is_host_vst, const YaPluginFactory::SetHostContext&);
|
||||
|
||||
bool log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::SetBusArrangements&);
|
||||
bool log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::GetBusArrangement&);
|
||||
bool log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::CanProcessSampleSize&);
|
||||
bool log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::GetLatencySamples&);
|
||||
bool log_request(bool is_host_vst,
|
||||
const YaAudioProcessor::SetupProcessing&);
|
||||
bool log_request(bool is_host_vst, const YaAudioProcessor::SetProcessing&);
|
||||
bool log_request(bool is_host_vst, const YaAudioProcessor::Process&);
|
||||
bool log_request(bool is_host_vst, const YaAudioProcessor::GetTailSamples&);
|
||||
bool log_request(bool is_host_vst,
|
||||
const YaComponent::GetControllerClassId&);
|
||||
bool log_request(bool is_host_vst, const YaComponent::SetIoMode&);
|
||||
bool log_request(bool is_host_vst, const YaComponent::GetBusCount&);
|
||||
bool log_request(bool is_host_vst, const YaComponent::GetBusInfo&);
|
||||
bool log_request(bool is_host_vst, const YaComponent::GetRoutingInfo&);
|
||||
bool log_request(bool is_host_vst, const YaComponent::ActivateBus&);
|
||||
bool log_request(bool is_host_vst, const YaComponent::SetActive&);
|
||||
|
||||
bool log_request(bool is_host_vst, const WantsConfiguration&);
|
||||
bool log_request(bool is_host_vst, const YaComponentHandler::BeginEdit&);
|
||||
bool log_request(bool is_host_vst, const YaComponentHandler::PerformEdit&);
|
||||
@@ -131,15 +132,6 @@ class Vst3Logger {
|
||||
const std::variant<Vst3PluginProxy::ConstructArgs, UniversalTResult>&);
|
||||
void log_response(bool is_host_vst,
|
||||
const Vst3PluginProxy::GetStateResponse&);
|
||||
void log_response(bool is_host_vst,
|
||||
const YaAudioProcessor::GetBusArrangementResponse&);
|
||||
void log_response(bool is_host_vst,
|
||||
const YaAudioProcessor::ProcessResponse&);
|
||||
void log_response(bool is_host_vst,
|
||||
const YaComponent::GetControllerClassIdResponse&);
|
||||
void log_response(bool is_host_vst, const YaComponent::GetBusInfoResponse&);
|
||||
void log_response(bool is_host_vst,
|
||||
const YaComponent::GetRoutingInfoResponse&);
|
||||
void log_response(bool is_host_vst,
|
||||
const YaEditController::GetParameterInfoResponse&);
|
||||
void log_response(bool is_host_vst,
|
||||
@@ -152,6 +144,16 @@ class Vst3Logger {
|
||||
void log_response(bool is_host_vst, const YaPluginFactory::ConstructArgs&);
|
||||
void log_response(bool is_host_vst, const Configuration&);
|
||||
|
||||
void log_response(bool is_host_vst,
|
||||
const YaAudioProcessor::GetBusArrangementResponse&);
|
||||
void log_response(bool is_host_vst,
|
||||
const YaAudioProcessor::ProcessResponse&);
|
||||
void log_response(bool is_host_vst,
|
||||
const YaComponent::GetControllerClassIdResponse&);
|
||||
void log_response(bool is_host_vst, const YaComponent::GetBusInfoResponse&);
|
||||
void log_response(bool is_host_vst,
|
||||
const YaComponent::GetRoutingInfoResponse&);
|
||||
|
||||
void log_response(bool is_host_vst,
|
||||
const YaHostApplication::GetNameResponse&);
|
||||
|
||||
|
||||
@@ -64,21 +64,6 @@ using ControlRequest = std::variant<Vst3PlugViewProxy::Destruct,
|
||||
Vst3PluginProxy::Destruct,
|
||||
Vst3PluginProxy::SetState,
|
||||
Vst3PluginProxy::GetState,
|
||||
YaAudioProcessor::SetBusArrangements,
|
||||
YaAudioProcessor::GetBusArrangement,
|
||||
YaAudioProcessor::CanProcessSampleSize,
|
||||
YaAudioProcessor::GetLatencySamples,
|
||||
YaAudioProcessor::SetupProcessing,
|
||||
YaAudioProcessor::SetProcessing,
|
||||
YaAudioProcessor::Process,
|
||||
YaAudioProcessor::GetTailSamples,
|
||||
YaComponent::GetControllerClassId,
|
||||
YaComponent::SetIoMode,
|
||||
YaComponent::GetBusCount,
|
||||
YaComponent::GetBusInfo,
|
||||
YaComponent::GetRoutingInfo,
|
||||
YaComponent::ActivateBus,
|
||||
YaComponent::SetActive,
|
||||
YaConnectionPoint::Connect,
|
||||
YaConnectionPoint::Disconnect,
|
||||
YaEditController::SetComponentState,
|
||||
@@ -107,6 +92,35 @@ void serialize(S& s, ControlRequest& payload) {
|
||||
s.ext(payload, bitsery::ext::StdVariant{});
|
||||
}
|
||||
|
||||
/**
|
||||
* A subset of all functions a host can call on a plugin. These functions are
|
||||
* called from a hot loop every processing cycle, so we want a dedicated socket
|
||||
* for these for every plugin instance.
|
||||
*/
|
||||
using AudioProcessorRequest =
|
||||
std::variant<YaAudioProcessor::SetBusArrangements,
|
||||
YaAudioProcessor::GetBusArrangement,
|
||||
YaAudioProcessor::CanProcessSampleSize,
|
||||
YaAudioProcessor::GetLatencySamples,
|
||||
YaAudioProcessor::SetupProcessing,
|
||||
YaAudioProcessor::SetProcessing,
|
||||
YaAudioProcessor::Process,
|
||||
YaAudioProcessor::GetTailSamples,
|
||||
YaComponent::GetControllerClassId,
|
||||
YaComponent::SetIoMode,
|
||||
YaComponent::GetBusCount,
|
||||
YaComponent::GetBusInfo,
|
||||
YaComponent::GetRoutingInfo,
|
||||
YaComponent::ActivateBus,
|
||||
YaComponent::SetActive>;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s, AudioProcessorRequest& payload) {
|
||||
// All of the objects in `AudioProcessorRequest` should have their own
|
||||
// serialization function.
|
||||
s.ext(payload, bitsery::ext::StdVariant{});
|
||||
}
|
||||
|
||||
/**
|
||||
* When we do a callback from the Wine VST host to the plugin, this encodes the
|
||||
* information we want or the operation we want to perform. A request of type
|
||||
|
||||
@@ -27,7 +27,7 @@ Vst3PluginProxyImpl::Vst3PluginProxyImpl(Vst3PluginBridge& bridge,
|
||||
Vst3PluginProxyImpl::~Vst3PluginProxyImpl() {
|
||||
bridge.send_message(
|
||||
Vst3PluginProxy::Destruct{.instance_id = instance_id()});
|
||||
bridge.unregister_plugin_proxy(instance_id());
|
||||
bridge.unregister_plugin_proxy(*this);
|
||||
}
|
||||
|
||||
tresult PLUGIN_API
|
||||
@@ -49,29 +49,32 @@ tresult PLUGIN_API Vst3PluginProxyImpl::setBusArrangements(
|
||||
int32 numOuts) {
|
||||
// NOTE: Ardour passes a null pointer when `numIns` or `numOuts` is 0, so we
|
||||
// need to work around that
|
||||
return bridge.send_message(YaAudioProcessor::SetBusArrangements{
|
||||
.instance_id = instance_id(),
|
||||
.inputs = (inputs ? std::vector<Steinberg::Vst::SpeakerArrangement>(
|
||||
inputs, &inputs[numIns])
|
||||
: std::vector<Steinberg::Vst::SpeakerArrangement>()),
|
||||
.num_ins = numIns,
|
||||
.outputs =
|
||||
(outputs ? std::vector<Steinberg::Vst::SpeakerArrangement>(
|
||||
outputs, &outputs[numOuts])
|
||||
: std::vector<Steinberg::Vst::SpeakerArrangement>()),
|
||||
.num_outs = numOuts,
|
||||
});
|
||||
return bridge.send_audio_processor_message(
|
||||
YaAudioProcessor::SetBusArrangements{
|
||||
.instance_id = instance_id(),
|
||||
.inputs =
|
||||
(inputs ? std::vector<Steinberg::Vst::SpeakerArrangement>(
|
||||
inputs, &inputs[numIns])
|
||||
: std::vector<Steinberg::Vst::SpeakerArrangement>()),
|
||||
.num_ins = numIns,
|
||||
.outputs =
|
||||
(outputs ? std::vector<Steinberg::Vst::SpeakerArrangement>(
|
||||
outputs, &outputs[numOuts])
|
||||
: std::vector<Steinberg::Vst::SpeakerArrangement>()),
|
||||
.num_outs = numOuts,
|
||||
});
|
||||
}
|
||||
|
||||
tresult PLUGIN_API Vst3PluginProxyImpl::getBusArrangement(
|
||||
Steinberg::Vst::BusDirection dir,
|
||||
int32 index,
|
||||
Steinberg::Vst::SpeakerArrangement& arr) {
|
||||
const GetBusArrangementResponse response = bridge.send_message(
|
||||
YaAudioProcessor::GetBusArrangement{.instance_id = instance_id(),
|
||||
.dir = dir,
|
||||
.index = index,
|
||||
.arr = arr});
|
||||
const GetBusArrangementResponse response =
|
||||
bridge.send_audio_processor_message(
|
||||
YaAudioProcessor::GetBusArrangement{.instance_id = instance_id(),
|
||||
.dir = dir,
|
||||
.index = index,
|
||||
.arr = arr});
|
||||
|
||||
arr = response.updated_arr;
|
||||
|
||||
@@ -80,24 +83,26 @@ tresult PLUGIN_API Vst3PluginProxyImpl::getBusArrangement(
|
||||
|
||||
tresult PLUGIN_API
|
||||
Vst3PluginProxyImpl::canProcessSampleSize(int32 symbolicSampleSize) {
|
||||
return bridge.send_message(YaAudioProcessor::CanProcessSampleSize{
|
||||
.instance_id = instance_id(),
|
||||
.symbolic_sample_size = symbolicSampleSize});
|
||||
return bridge.send_audio_processor_message(
|
||||
YaAudioProcessor::CanProcessSampleSize{
|
||||
.instance_id = instance_id(),
|
||||
.symbolic_sample_size = symbolicSampleSize});
|
||||
}
|
||||
|
||||
uint32 PLUGIN_API Vst3PluginProxyImpl::getLatencySamples() {
|
||||
return bridge.send_message(
|
||||
return bridge.send_audio_processor_message(
|
||||
YaAudioProcessor::GetLatencySamples{.instance_id = instance_id()});
|
||||
}
|
||||
|
||||
tresult PLUGIN_API
|
||||
Vst3PluginProxyImpl::setupProcessing(Steinberg::Vst::ProcessSetup& setup) {
|
||||
return bridge.send_message(YaAudioProcessor::SetupProcessing{
|
||||
.instance_id = instance_id(), .setup = setup});
|
||||
return bridge.send_audio_processor_message(
|
||||
YaAudioProcessor::SetupProcessing{.instance_id = instance_id(),
|
||||
.setup = setup});
|
||||
}
|
||||
|
||||
tresult PLUGIN_API Vst3PluginProxyImpl::setProcessing(TBool state) {
|
||||
return bridge.send_message(YaAudioProcessor::SetProcessing{
|
||||
return bridge.send_audio_processor_message(YaAudioProcessor::SetProcessing{
|
||||
.instance_id = instance_id(), .state = state});
|
||||
}
|
||||
|
||||
@@ -105,7 +110,7 @@ tresult PLUGIN_API
|
||||
Vst3PluginProxyImpl::process(Steinberg::Vst::ProcessData& data) {
|
||||
// TODO: Check whether reusing a `YaProcessData` object make a difference in
|
||||
// terms of performance
|
||||
ProcessResponse response = bridge.send_message(
|
||||
ProcessResponse response = bridge.send_audio_processor_message(
|
||||
YaAudioProcessor::Process{.instance_id = instance_id(), .data = data});
|
||||
|
||||
response.output_data.write_back_outputs(data);
|
||||
@@ -114,14 +119,15 @@ Vst3PluginProxyImpl::process(Steinberg::Vst::ProcessData& data) {
|
||||
}
|
||||
|
||||
uint32 PLUGIN_API Vst3PluginProxyImpl::getTailSamples() {
|
||||
return bridge.send_message(
|
||||
return bridge.send_audio_processor_message(
|
||||
YaAudioProcessor::GetTailSamples{.instance_id = instance_id()});
|
||||
}
|
||||
|
||||
tresult PLUGIN_API
|
||||
Vst3PluginProxyImpl::getControllerClassId(Steinberg::TUID classId) {
|
||||
const GetControllerClassIdResponse response = bridge.send_message(
|
||||
YaComponent::GetControllerClassId{.instance_id = instance_id()});
|
||||
const GetControllerClassIdResponse response =
|
||||
bridge.send_audio_processor_message(
|
||||
YaComponent::GetControllerClassId{.instance_id = instance_id()});
|
||||
|
||||
std::copy(response.editor_cid.begin(), response.editor_cid.end(), classId);
|
||||
|
||||
@@ -129,14 +135,14 @@ Vst3PluginProxyImpl::getControllerClassId(Steinberg::TUID classId) {
|
||||
}
|
||||
|
||||
tresult PLUGIN_API Vst3PluginProxyImpl::setIoMode(Steinberg::Vst::IoMode mode) {
|
||||
return bridge.send_message(
|
||||
return bridge.send_audio_processor_message(
|
||||
YaComponent::SetIoMode{.instance_id = instance_id(), .mode = mode});
|
||||
}
|
||||
|
||||
int32 PLUGIN_API
|
||||
Vst3PluginProxyImpl::getBusCount(Steinberg::Vst::MediaType type,
|
||||
Steinberg::Vst::BusDirection dir) {
|
||||
return bridge.send_message(YaComponent::GetBusCount{
|
||||
return bridge.send_audio_processor_message(YaComponent::GetBusCount{
|
||||
.instance_id = instance_id(), .type = type, .dir = dir});
|
||||
}
|
||||
|
||||
@@ -145,7 +151,7 @@ Vst3PluginProxyImpl::getBusInfo(Steinberg::Vst::MediaType type,
|
||||
Steinberg::Vst::BusDirection dir,
|
||||
int32 index,
|
||||
Steinberg::Vst::BusInfo& bus /*out*/) {
|
||||
const GetBusInfoResponse response = bridge.send_message(
|
||||
const GetBusInfoResponse response = bridge.send_audio_processor_message(
|
||||
YaComponent::GetBusInfo{.instance_id = instance_id(),
|
||||
.type = type,
|
||||
.dir = dir,
|
||||
@@ -159,7 +165,7 @@ Vst3PluginProxyImpl::getBusInfo(Steinberg::Vst::MediaType type,
|
||||
tresult PLUGIN_API Vst3PluginProxyImpl::getRoutingInfo(
|
||||
Steinberg::Vst::RoutingInfo& inInfo,
|
||||
Steinberg::Vst::RoutingInfo& outInfo /*out*/) {
|
||||
const GetRoutingInfoResponse response = bridge.send_message(
|
||||
const GetRoutingInfoResponse response = bridge.send_audio_processor_message(
|
||||
YaComponent::GetRoutingInfo{.instance_id = instance_id(),
|
||||
.in_info = inInfo,
|
||||
.out_info = outInfo});
|
||||
@@ -174,7 +180,7 @@ Vst3PluginProxyImpl::activateBus(Steinberg::Vst::MediaType type,
|
||||
Steinberg::Vst::BusDirection dir,
|
||||
int32 index,
|
||||
TBool state) {
|
||||
return bridge.send_message(
|
||||
return bridge.send_audio_processor_message(
|
||||
YaComponent::ActivateBus{.instance_id = instance_id(),
|
||||
.type = type,
|
||||
.dir = dir,
|
||||
@@ -183,7 +189,7 @@ Vst3PluginProxyImpl::activateBus(Steinberg::Vst::MediaType type,
|
||||
}
|
||||
|
||||
tresult PLUGIN_API Vst3PluginProxyImpl::setActive(TBool state) {
|
||||
return bridge.send_message(
|
||||
return bridge.send_audio_processor_message(
|
||||
YaComponent::SetActive{.instance_id = instance_id(), .state = state});
|
||||
}
|
||||
|
||||
|
||||
@@ -161,13 +161,28 @@ Steinberg::IPluginFactory* Vst3PluginBridge::get_plugin_factory() {
|
||||
return plugin_factory;
|
||||
}
|
||||
|
||||
void Vst3PluginBridge::register_plugin_proxy(Vst3PluginProxyImpl& component) {
|
||||
void Vst3PluginBridge::register_plugin_proxy(
|
||||
Vst3PluginProxyImpl& proxy_object) {
|
||||
std::lock_guard lock(plugin_proxies_mutex);
|
||||
plugin_proxies.emplace(component.instance_id(),
|
||||
std::ref<Vst3PluginProxyImpl>(component));
|
||||
|
||||
plugin_proxies.emplace(proxy_object.instance_id(),
|
||||
std::ref<Vst3PluginProxyImpl>(proxy_object));
|
||||
|
||||
// For optimization reaons we use dedicated sockets for functions that will
|
||||
// be run in the audio processing loop
|
||||
if (proxy_object.YaAudioProcessor::supported() ||
|
||||
proxy_object.YaComponent::supported()) {
|
||||
sockets.add_audio_processor_and_connect(proxy_object.instance_id());
|
||||
}
|
||||
}
|
||||
|
||||
void Vst3PluginBridge::unregister_plugin_proxy(size_t instance_id) {
|
||||
void Vst3PluginBridge::unregister_plugin_proxy(
|
||||
Vst3PluginProxyImpl& proxy_object) {
|
||||
std::lock_guard lock(plugin_proxies_mutex);
|
||||
plugin_proxies.erase(instance_id);
|
||||
|
||||
plugin_proxies.erase(proxy_object.instance_id());
|
||||
if (proxy_object.YaAudioProcessor::supported() ||
|
||||
proxy_object.YaComponent::supported()) {
|
||||
sockets.remove_audio_processor(proxy_object.instance_id());
|
||||
}
|
||||
}
|
||||
|
||||
+20
-10
@@ -76,13 +76,12 @@ class Vst3PluginBridge : PluginBridge<Vst3Sockets<std::jthread>> {
|
||||
/**
|
||||
* Add a `Vst3PluginProxyImpl` to the list of registered proxy objects so we
|
||||
* can handle host callbacks. This function is called in
|
||||
* `Vst3PluginProxyImpl`'s constructor.
|
||||
* `Vst3PluginProxyImpl`'s constructor. If the plugin supports the
|
||||
* `IAudioProcessor` or `IComponent` interfaces, then we'll also connect to
|
||||
* a dedicated audio processing socket.
|
||||
*
|
||||
* @param instance_id The instance ID generated by the plugin host when
|
||||
* instantiating the `IComponent`. Used as a stable name to refer to
|
||||
* these.
|
||||
* @param plugin_proxy The actual proxy object we can access its host
|
||||
* context.
|
||||
* @param proxy_object The proxy object so we can access its host context
|
||||
* and unique instance identifier.
|
||||
*
|
||||
* @see plugin_proxies
|
||||
*/
|
||||
@@ -93,13 +92,12 @@ class Vst3PluginBridge : PluginBridge<Vst3Sockets<std::jthread>> {
|
||||
* registered proxy objects. Called during the object's destructor after
|
||||
* asking the Wine plugin host to destroy the component on its side.
|
||||
*
|
||||
* @param instance_id The instance ID generated by the plugin host when
|
||||
* instantiating the `IComponent`. Used as a stable name to refer to
|
||||
* these.
|
||||
* @param proxy_object The proxy object so we can access its unique instance
|
||||
* identifier.
|
||||
*
|
||||
* @see plugin_proxies
|
||||
*/
|
||||
void unregister_plugin_proxy(size_t instance_id);
|
||||
void unregister_plugin_proxy(Vst3PluginProxyImpl& proxy_object);
|
||||
|
||||
/**
|
||||
* Send a control message to the Wine plugin host return the response. This
|
||||
@@ -112,6 +110,18 @@ class Vst3PluginBridge : PluginBridge<Vst3Sockets<std::jthread>> {
|
||||
object, std::pair<Vst3Logger&, bool>(logger, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an `IAudioProcessor` or `IComponent` control message to a specific
|
||||
* plugin instance. This is separated from the above `send_message()` for
|
||||
* performance reasons, as this way every instance has its own dedicated
|
||||
* socket and thread.
|
||||
*/
|
||||
template <typename T>
|
||||
typename T::Response send_audio_processor_message(const T& object) {
|
||||
return sockets.send_audio_processor_message(
|
||||
object, std::pair<Vst3Logger&, bool>(logger, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* The logging facility used for this instance of yabridge. Wraps around
|
||||
* `PluginBridge::generic_logger`.
|
||||
|
||||
+202
-120
@@ -116,28 +116,215 @@ void Vst3Bridge::run() {
|
||||
})
|
||||
.get();
|
||||
|
||||
if (object) {
|
||||
std::lock_guard lock(object_instances_mutex);
|
||||
|
||||
const size_t instance_id = generate_instance_id();
|
||||
object_instances.emplace(instance_id, std::move(object));
|
||||
|
||||
// This is where the magic happens. Here we deduce which
|
||||
// interfaces are supported by this object so we can create
|
||||
// a one-to-one proxy of it.
|
||||
return Vst3PluginProxy::ConstructArgs(
|
||||
object_instances[instance_id].object, instance_id);
|
||||
} else {
|
||||
if (!object) {
|
||||
return UniversalTResult(Steinberg::kResultFalse);
|
||||
}
|
||||
|
||||
std::lock_guard lock(object_instances_mutex);
|
||||
|
||||
const size_t instance_id = generate_instance_id();
|
||||
object_instances.emplace(instance_id, std::move(object));
|
||||
|
||||
// If the object supports `IComponent` or `IAudioProcessor`,
|
||||
// then we'll set up a dedicated thread for function calls for
|
||||
// those interfaces.
|
||||
if (object_instances[instance_id].audio_processor ||
|
||||
object_instances[instance_id].component) {
|
||||
std::promise<void> socket_listening_latch;
|
||||
object_instances[instance_id]
|
||||
.audio_processor_handler = Win32Thread([&,
|
||||
instance_id]() {
|
||||
// TODO: Move this somewhere else, because this is
|
||||
// obviously ridiculous
|
||||
sockets.add_audio_processor_and_listen(
|
||||
instance_id, socket_listening_latch,
|
||||
overload{
|
||||
[&](YaAudioProcessor::SetBusArrangements&
|
||||
request)
|
||||
-> YaAudioProcessor::SetBusArrangements::
|
||||
Response {
|
||||
return object_instances
|
||||
[request.instance_id]
|
||||
.audio_processor
|
||||
->setBusArrangements(
|
||||
request.inputs.data(),
|
||||
request.num_ins,
|
||||
request.outputs.data(),
|
||||
request.num_outs);
|
||||
},
|
||||
[&](YaAudioProcessor::GetBusArrangement&
|
||||
request)
|
||||
-> YaAudioProcessor::GetBusArrangement::
|
||||
Response {
|
||||
const tresult result =
|
||||
object_instances
|
||||
[request.instance_id]
|
||||
.audio_processor
|
||||
->getBusArrangement(
|
||||
request.dir,
|
||||
request.index,
|
||||
request.arr);
|
||||
|
||||
return YaAudioProcessor::
|
||||
GetBusArrangementResponse{
|
||||
.result = result,
|
||||
.updated_arr = request.arr};
|
||||
},
|
||||
[&](const YaAudioProcessor::
|
||||
CanProcessSampleSize& request)
|
||||
-> YaAudioProcessor::CanProcessSampleSize::
|
||||
Response {
|
||||
return object_instances
|
||||
[request.instance_id]
|
||||
.audio_processor
|
||||
->canProcessSampleSize(
|
||||
request
|
||||
.symbolic_sample_size);
|
||||
},
|
||||
[&](const YaAudioProcessor::GetLatencySamples&
|
||||
request)
|
||||
-> YaAudioProcessor::GetLatencySamples::
|
||||
Response {
|
||||
return object_instances
|
||||
[request.instance_id]
|
||||
.audio_processor
|
||||
->getLatencySamples();
|
||||
},
|
||||
[&](YaAudioProcessor::SetupProcessing& request)
|
||||
-> YaAudioProcessor::SetupProcessing::
|
||||
Response {
|
||||
return object_instances
|
||||
[request.instance_id]
|
||||
.audio_processor
|
||||
->setupProcessing(
|
||||
request.setup);
|
||||
},
|
||||
[&](const YaAudioProcessor::SetProcessing&
|
||||
request)
|
||||
-> YaAudioProcessor::SetProcessing::
|
||||
Response {
|
||||
return object_instances
|
||||
[request.instance_id]
|
||||
.audio_processor
|
||||
->setProcessing(
|
||||
request.state);
|
||||
},
|
||||
[&](YaAudioProcessor::Process& request)
|
||||
-> YaAudioProcessor::Process::Response {
|
||||
const tresult result =
|
||||
object_instances[request.instance_id]
|
||||
.audio_processor->process(
|
||||
request.data.get());
|
||||
|
||||
return YaAudioProcessor::ProcessResponse{
|
||||
.result = result,
|
||||
.output_data =
|
||||
request.data
|
||||
.move_outputs_to_response()};
|
||||
},
|
||||
[&](const YaAudioProcessor::GetTailSamples&
|
||||
request)
|
||||
-> YaAudioProcessor::GetTailSamples::
|
||||
Response {
|
||||
return object_instances
|
||||
[request.instance_id]
|
||||
.audio_processor
|
||||
->getTailSamples();
|
||||
},
|
||||
[&](const YaComponent::GetControllerClassId&
|
||||
request)
|
||||
-> YaComponent::GetControllerClassId::
|
||||
Response {
|
||||
Steinberg::TUID cid;
|
||||
const tresult result =
|
||||
object_instances
|
||||
[request.instance_id]
|
||||
.component
|
||||
->getControllerClassId(
|
||||
cid);
|
||||
|
||||
return YaComponent::
|
||||
GetControllerClassIdResponse{
|
||||
.result = result,
|
||||
.editor_cid =
|
||||
std::to_array(cid)};
|
||||
},
|
||||
[&](const YaComponent::SetIoMode& request)
|
||||
-> YaComponent::SetIoMode::Response {
|
||||
return object_instances[request.instance_id]
|
||||
.component->setIoMode(request.mode);
|
||||
},
|
||||
[&](const YaComponent::GetBusCount& request)
|
||||
-> YaComponent::GetBusCount::Response {
|
||||
return object_instances[request.instance_id]
|
||||
.component->getBusCount(request.type,
|
||||
request.dir);
|
||||
},
|
||||
[&](YaComponent::GetBusInfo& request)
|
||||
-> YaComponent::GetBusInfo::Response {
|
||||
const tresult result =
|
||||
object_instances[request.instance_id]
|
||||
.component->getBusInfo(
|
||||
request.type, request.dir,
|
||||
request.index, request.bus);
|
||||
|
||||
return YaComponent::GetBusInfoResponse{
|
||||
.result = result,
|
||||
.updated_bus = request.bus};
|
||||
},
|
||||
[&](YaComponent::GetRoutingInfo& request)
|
||||
-> YaComponent::GetRoutingInfo::Response {
|
||||
const tresult result =
|
||||
object_instances[request.instance_id]
|
||||
.component->getRoutingInfo(
|
||||
request.in_info,
|
||||
request.out_info);
|
||||
|
||||
return YaComponent::GetRoutingInfoResponse{
|
||||
.result = result,
|
||||
.updated_in_info = request.in_info,
|
||||
.updated_out_info = request.out_info};
|
||||
},
|
||||
[&](const YaComponent::ActivateBus& request)
|
||||
-> YaComponent::ActivateBus::Response {
|
||||
return object_instances[request.instance_id]
|
||||
.component->activateBus(
|
||||
request.type, request.dir,
|
||||
request.index, request.state);
|
||||
},
|
||||
[&](const YaComponent::SetActive& request)
|
||||
-> YaComponent::SetActive::Response {
|
||||
return object_instances[request.instance_id]
|
||||
.component->setActive(request.state);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Wait for the new socket to be listening on before
|
||||
// continuing. Otherwise the native plugin may try to
|
||||
// connect to it before our thread is up and running.
|
||||
socket_listening_latch.get_future().wait();
|
||||
}
|
||||
|
||||
// This is where the magic happens. Here we deduce which
|
||||
// interfaces are supported by this object so we can create
|
||||
// a one-to-one proxy of it.
|
||||
return Vst3PluginProxy::ConstructArgs(
|
||||
object_instances[instance_id].object, instance_id);
|
||||
},
|
||||
[&](const Vst3PluginProxy::Destruct& request)
|
||||
-> Vst3PluginProxy::Destruct::Response {
|
||||
// Tear the dedicated audio processing socket down again if we
|
||||
// created one while handling `Vst3PluginProxy::Construct`
|
||||
if (object_instances[request.instance_id].audio_processor ||
|
||||
object_instances[request.instance_id].component) {
|
||||
sockets.remove_audio_processor(request.instance_id);
|
||||
}
|
||||
|
||||
// Remove the instance from within the main IO context so
|
||||
// removing it doesn't interfere with the Win32 message loop
|
||||
main_context
|
||||
.run_in_context([&]() {
|
||||
// Remove the instance from within the main IO context
|
||||
// so removing it doesn't interfere with the Win32
|
||||
// message loop
|
||||
std::lock_guard lock(object_instances_mutex);
|
||||
object_instances.erase(request.instance_id);
|
||||
})
|
||||
@@ -181,111 +368,6 @@ void Vst3Bridge::run() {
|
||||
return Vst3PluginProxy::GetStateResponse{
|
||||
.result = result, .updated_state = std::move(stream)};
|
||||
},
|
||||
[&](YaAudioProcessor::SetBusArrangements& request)
|
||||
-> YaAudioProcessor::SetBusArrangements::Response {
|
||||
return object_instances[request.instance_id]
|
||||
.audio_processor->setBusArrangements(
|
||||
request.inputs.data(), request.num_ins,
|
||||
request.outputs.data(), request.num_outs);
|
||||
},
|
||||
[&](YaAudioProcessor::GetBusArrangement& request)
|
||||
-> YaAudioProcessor::GetBusArrangement::Response {
|
||||
const tresult result =
|
||||
object_instances[request.instance_id]
|
||||
.audio_processor->getBusArrangement(
|
||||
request.dir, request.index, request.arr);
|
||||
|
||||
return YaAudioProcessor::GetBusArrangementResponse{
|
||||
.result = result, .updated_arr = request.arr};
|
||||
},
|
||||
[&](const YaAudioProcessor::CanProcessSampleSize& request)
|
||||
-> YaAudioProcessor::CanProcessSampleSize::Response {
|
||||
return object_instances[request.instance_id]
|
||||
.audio_processor->canProcessSampleSize(
|
||||
request.symbolic_sample_size);
|
||||
},
|
||||
[&](const YaAudioProcessor::GetLatencySamples& request)
|
||||
-> YaAudioProcessor::GetLatencySamples::Response {
|
||||
return object_instances[request.instance_id]
|
||||
.audio_processor->getLatencySamples();
|
||||
},
|
||||
[&](YaAudioProcessor::SetupProcessing& request)
|
||||
-> YaAudioProcessor::SetupProcessing::Response {
|
||||
return object_instances[request.instance_id]
|
||||
.audio_processor->setupProcessing(request.setup);
|
||||
},
|
||||
[&](const YaAudioProcessor::SetProcessing& request)
|
||||
-> YaAudioProcessor::SetProcessing::Response {
|
||||
return object_instances[request.instance_id]
|
||||
.audio_processor->setProcessing(request.state);
|
||||
},
|
||||
[&](YaAudioProcessor::Process& request)
|
||||
-> YaAudioProcessor::Process::Response {
|
||||
const tresult result =
|
||||
object_instances[request.instance_id]
|
||||
.audio_processor->process(request.data.get());
|
||||
|
||||
return YaAudioProcessor::ProcessResponse{
|
||||
.result = result,
|
||||
.output_data = request.data.move_outputs_to_response()};
|
||||
},
|
||||
[&](const YaAudioProcessor::GetTailSamples& request)
|
||||
-> YaAudioProcessor::GetTailSamples::Response {
|
||||
return object_instances[request.instance_id]
|
||||
.audio_processor->getTailSamples();
|
||||
},
|
||||
[&](const YaComponent::GetControllerClassId& request)
|
||||
-> YaComponent::GetControllerClassId::Response {
|
||||
Steinberg::TUID cid;
|
||||
const tresult result =
|
||||
object_instances[request.instance_id]
|
||||
.component->getControllerClassId(cid);
|
||||
|
||||
return YaComponent::GetControllerClassIdResponse{
|
||||
.result = result, .editor_cid = std::to_array(cid)};
|
||||
},
|
||||
[&](const YaComponent::SetIoMode& request)
|
||||
-> YaComponent::SetIoMode::Response {
|
||||
return object_instances[request.instance_id]
|
||||
.component->setIoMode(request.mode);
|
||||
},
|
||||
[&](const YaComponent::GetBusCount& request)
|
||||
-> YaComponent::GetBusCount::Response {
|
||||
return object_instances[request.instance_id]
|
||||
.component->getBusCount(request.type, request.dir);
|
||||
},
|
||||
[&](YaComponent::GetBusInfo& request)
|
||||
-> YaComponent::GetBusInfo::Response {
|
||||
const tresult result =
|
||||
object_instances[request.instance_id].component->getBusInfo(
|
||||
request.type, request.dir, request.index, request.bus);
|
||||
|
||||
return YaComponent::GetBusInfoResponse{
|
||||
.result = result, .updated_bus = request.bus};
|
||||
},
|
||||
[&](YaComponent::GetRoutingInfo& request)
|
||||
-> YaComponent::GetRoutingInfo::Response {
|
||||
const tresult result =
|
||||
object_instances[request.instance_id]
|
||||
.component->getRoutingInfo(request.in_info,
|
||||
request.out_info);
|
||||
|
||||
return YaComponent::GetRoutingInfoResponse{
|
||||
.result = result,
|
||||
.updated_in_info = request.in_info,
|
||||
.updated_out_info = request.out_info};
|
||||
},
|
||||
[&](const YaComponent::ActivateBus& request)
|
||||
-> YaComponent::ActivateBus::Response {
|
||||
return object_instances[request.instance_id]
|
||||
.component->activateBus(request.type, request.dir,
|
||||
request.index, request.state);
|
||||
},
|
||||
[&](const YaComponent::SetActive& request)
|
||||
-> YaComponent::SetActive::Response {
|
||||
return object_instances[request.instance_id]
|
||||
.component->setActive(request.state);
|
||||
},
|
||||
[&](const YaConnectionPoint::Connect& request)
|
||||
-> YaConnectionPoint::Connect::Response {
|
||||
// We can directly connect the underlying objects
|
||||
|
||||
@@ -38,6 +38,13 @@ struct InstanceInterfaces {
|
||||
|
||||
InstanceInterfaces(Steinberg::IPtr<Steinberg::FUnknown> object);
|
||||
|
||||
/**
|
||||
* A dedicated thread for handling incoming `IAudioProcessor` and
|
||||
* `IComponent` calls. Will be instantiated if `object` supports either of
|
||||
* those interfaces.
|
||||
*/
|
||||
Win32Thread audio_processor_handler;
|
||||
|
||||
/**
|
||||
* If the host passes a host context object during
|
||||
* `IPluginBase::initialize()`, we'll store a proxy object here and then
|
||||
|
||||
Reference in New Issue
Block a user