mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +02:00
Reuse request on Wine side during VST2 processing
The object was constantly being recreated, resulting in memory allocations caused by creating and destroying the audio buffer vectors.
This commit is contained in:
@@ -30,6 +30,8 @@ Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
- Redesigned the VST3 audio socket handling to be able to reuse the process data
|
||||
objects on both sides. This greatly reduces the overhead of our VST3 bridging
|
||||
by getting rid of all memory allocations during audio processing.
|
||||
- VST2 audio processing also received the same optimizations. In a few places
|
||||
yabridge would still reallocate heap data during audio processing.
|
||||
- Considerably optimized both VST2 and VST3 audio processing by preventing
|
||||
unnecessary memory operations. As it turns out, the underlying binary
|
||||
serialization library used by yabridge would always reinitialize the type-safe
|
||||
|
||||
@@ -443,8 +443,10 @@ class SocketHandler {
|
||||
* Read a serialized object from the socket sent using `send()`. This will
|
||||
* block until the object is available.
|
||||
*
|
||||
* @param buffer The buffer to read into. This is used to prevent excess
|
||||
* allocations when sending audio.
|
||||
* @param object The object to serialize into. There are also overrides that
|
||||
* create a new default initialized `T`
|
||||
* @param buffer The buffer to read into. This is useful for sending audio
|
||||
* and chunk data since that can vary in size by a lot.
|
||||
*
|
||||
* @return The deserialized object.
|
||||
*
|
||||
@@ -467,13 +469,13 @@ class SocketHandler {
|
||||
* @see SocketHandler::receive_multi
|
||||
*/
|
||||
template <typename T>
|
||||
inline T receive_single(SerializationBufferBase& buffer) {
|
||||
return read_object<T>(socket, buffer);
|
||||
inline T& receive_single(T& object, SerializationBufferBase& buffer) {
|
||||
return read_object<T>(socket, object, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* `SocketHandler::receive_single()` with a small default buffer for
|
||||
* convenience.
|
||||
* `SocketHandler::receive_single()` into a new default initialized object
|
||||
* with a small default buffer for convenience.
|
||||
*
|
||||
* @overload
|
||||
*/
|
||||
@@ -500,14 +502,15 @@ class SocketHandler {
|
||||
* @see read_object
|
||||
* @see SocketHandler::receive_single
|
||||
*/
|
||||
template <typename T, std::invocable<T, SerializationBufferBase&> F>
|
||||
template <typename T, std::invocable<T&, SerializationBufferBase&> F>
|
||||
void receive_multi(F&& callback) {
|
||||
SerializationBuffer<64> buffer{};
|
||||
T object;
|
||||
while (true) {
|
||||
try {
|
||||
auto object = receive_single<T>(buffer);
|
||||
receive_single<T>(object, buffer);
|
||||
|
||||
callback(std::move(object), buffer);
|
||||
callback(object, buffer);
|
||||
} catch (const boost::system::system_error&) {
|
||||
// This happens when the sockets got closed because the plugin
|
||||
// is being shut down
|
||||
|
||||
@@ -601,17 +601,17 @@ void Vst2PluginBridge::do_process(T** inputs, T** outputs, int sample_frames) {
|
||||
input_buffers[channel].begin());
|
||||
}
|
||||
|
||||
const AudioBuffers request{.buffers = input_buffers,
|
||||
.sample_frames = sample_frames,
|
||||
.current_time_info = current_time_info,
|
||||
.current_process_level = current_process_level,
|
||||
.new_realtime_priority = new_realtime_priority};
|
||||
AudioBuffers request{.buffers = input_buffers,
|
||||
.sample_frames = sample_frames,
|
||||
.current_time_info = current_time_info,
|
||||
.current_process_level = current_process_level,
|
||||
.new_realtime_priority = new_realtime_priority};
|
||||
sockets.host_vst_process_replacing.send(request, process_buffer);
|
||||
|
||||
// Write the results back to the `outputs` arrays
|
||||
const auto response =
|
||||
const auto& response =
|
||||
sockets.host_vst_process_replacing.receive_single<AudioBuffers>(
|
||||
process_buffer);
|
||||
request, process_buffer);
|
||||
const auto& response_buffers =
|
||||
std::get<std::vector<std::vector<T>>>(response.buffers);
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ Vst2Bridge::Vst2Bridge(MainContext& main_context,
|
||||
|
||||
parameters_handler = Win32Thread([&]() {
|
||||
sockets.host_vst_parameters.receive_multi<Parameter>(
|
||||
[&](Parameter request, SerializationBufferBase& buffer) {
|
||||
[&](Parameter& request, SerializationBufferBase& buffer) {
|
||||
// Both `getParameter` and `setParameter` functions are passed
|
||||
// through on this socket since they have a lot of overlap. The
|
||||
// presence of the `value` field tells us which one we're
|
||||
@@ -216,7 +216,7 @@ Vst2Bridge::Vst2Bridge(MainContext& main_context,
|
||||
plugin->numOutputs);
|
||||
|
||||
sockets.host_vst_process_replacing.receive_multi<AudioBuffers>(
|
||||
[&](AudioBuffers request, SerializationBufferBase& buffer) {
|
||||
[&](AudioBuffers& request, SerializationBufferBase& buffer) {
|
||||
// Since the value cannot change during this processing cycle,
|
||||
// we'll send the current transport information as part of the
|
||||
// request so we prefetch it to avoid unnecessary callbacks from
|
||||
|
||||
Reference in New Issue
Block a user