mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-06-20 19:03:56 +02:00
Avoid allocations when reconstructing process data
This commit is contained in:
@@ -236,7 +236,7 @@ class YaAudioProcessor : public Steinberg::Vst::IAudioProcessor {
|
|||||||
* Wine plugin host. This `YaProcessData` object wraps around all input
|
* Wine plugin host. This `YaProcessData` object wraps around all input
|
||||||
* audio buffers, parameter changes and events along with all context data
|
* audio buffers, parameter changes and events along with all context data
|
||||||
* provided by the host so we can send it to the Wine plugin host. We can
|
* provided by the host so we can send it to the Wine plugin host. We can
|
||||||
* then use `YaProcessData::get()` on the Wine plugin host side to
|
* then use `YaProcessData::reconstruct()` on the Wine plugin host side to
|
||||||
* reconstruct the original `ProcessData` object, and we then finally use
|
* reconstruct the original `ProcessData` object, and we then finally use
|
||||||
* `YaProcessData::move_outputs_to_response()` to create a response object
|
* `YaProcessData::move_outputs_to_response()` to create a response object
|
||||||
* that we can write back to the `ProcessData` object provided by the host.
|
* that we can write back to the `ProcessData` object provided by the host.
|
||||||
|
|||||||
@@ -20,16 +20,34 @@
|
|||||||
|
|
||||||
YaAudioBusBuffers::YaAudioBusBuffers() {}
|
YaAudioBusBuffers::YaAudioBusBuffers() {}
|
||||||
|
|
||||||
YaAudioBusBuffers::YaAudioBusBuffers(int32 sample_size,
|
void YaAudioBusBuffers::clear(int32 sample_size,
|
||||||
size_t num_samples,
|
size_t num_samples,
|
||||||
size_t num_channels)
|
size_t num_channels) {
|
||||||
: buffers(sample_size == Steinberg::Vst::SymbolicSampleSizes::kSample64
|
if (sample_size == Steinberg::Vst::SymbolicSampleSizes::kSample64) {
|
||||||
? decltype(buffers)(std::vector<std::vector<double>>(
|
if (!std::holds_alternative<std::vector<std::vector<double>>>(
|
||||||
num_channels,
|
buffers)) {
|
||||||
std::vector<double>(num_samples, 0.0)))
|
buffers.emplace<std::vector<std::vector<double>>>();
|
||||||
: decltype(buffers)(std::vector<std::vector<float>>(
|
}
|
||||||
num_channels,
|
|
||||||
std::vector<float>(num_samples, 0.0)))) {}
|
std::vector<std::vector<double>>& vector_buffers =
|
||||||
|
std::get<std::vector<std::vector<double>>>(buffers);
|
||||||
|
vector_buffers.resize(num_channels);
|
||||||
|
for (size_t i = 0; i < vector_buffers.size(); i++) {
|
||||||
|
vector_buffers[i].assign(num_samples, 0.0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!std::holds_alternative<std::vector<std::vector<float>>>(buffers)) {
|
||||||
|
buffers.emplace<std::vector<std::vector<float>>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<float>>& vector_buffers =
|
||||||
|
std::get<std::vector<std::vector<float>>>(buffers);
|
||||||
|
vector_buffers.resize(num_channels);
|
||||||
|
for (size_t i = 0; i < vector_buffers.size(); i++) {
|
||||||
|
vector_buffers[i].assign(num_samples, 0.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void YaAudioBusBuffers::repopulate(
|
void YaAudioBusBuffers::repopulate(
|
||||||
int32 sample_size,
|
int32 sample_size,
|
||||||
@@ -73,14 +91,16 @@ void YaAudioBusBuffers::repopulate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Steinberg::Vst::AudioBusBuffers YaAudioBusBuffers::get() {
|
void YaAudioBusBuffers::reconstruct(
|
||||||
Steinberg::Vst::AudioBusBuffers reconstructed_buffers;
|
Steinberg::Vst::AudioBusBuffers& reconstructed_buffers) {
|
||||||
|
// We'll update the `AudioBusBuffers` object in place to point to our new
|
||||||
|
// data
|
||||||
reconstructed_buffers.silenceFlags = silence_flags;
|
reconstructed_buffers.silenceFlags = silence_flags;
|
||||||
std::visit(overload{
|
std::visit(overload{
|
||||||
[&](std::vector<std::vector<double>>& buffers) {
|
[&](std::vector<std::vector<double>>& buffers) {
|
||||||
buffer_pointers.clear();
|
buffer_pointers.resize(buffers.size());
|
||||||
for (auto& buffer : buffers) {
|
for (size_t i = 0; i < buffers.size(); i++) {
|
||||||
buffer_pointers.push_back(buffer.data());
|
buffer_pointers[i] = buffers[i].data();
|
||||||
}
|
}
|
||||||
|
|
||||||
reconstructed_buffers.numChannels =
|
reconstructed_buffers.numChannels =
|
||||||
@@ -89,9 +109,9 @@ Steinberg::Vst::AudioBusBuffers YaAudioBusBuffers::get() {
|
|||||||
reinterpret_cast<double**>(buffer_pointers.data());
|
reinterpret_cast<double**>(buffer_pointers.data());
|
||||||
},
|
},
|
||||||
[&](std::vector<std::vector<float>>& buffers) {
|
[&](std::vector<std::vector<float>>& buffers) {
|
||||||
buffer_pointers.clear();
|
buffer_pointers.resize(buffers.size());
|
||||||
for (auto& buffer : buffers) {
|
for (size_t i = 0; i < buffers.size(); i++) {
|
||||||
buffer_pointers.push_back(buffer.data());
|
buffer_pointers[i] = buffers[i].data();
|
||||||
}
|
}
|
||||||
|
|
||||||
reconstructed_buffers.numChannels =
|
reconstructed_buffers.numChannels =
|
||||||
@@ -101,8 +121,6 @@ Steinberg::Vst::AudioBusBuffers YaAudioBusBuffers::get() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
buffers);
|
buffers);
|
||||||
|
|
||||||
return reconstructed_buffers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t YaAudioBusBuffers::num_channels() const {
|
size_t YaAudioBusBuffers::num_channels() const {
|
||||||
@@ -204,52 +222,47 @@ void YaProcessData::repopulate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Steinberg::Vst::ProcessData& YaProcessData::get() {
|
Steinberg::Vst::ProcessData& YaProcessData::reconstruct() {
|
||||||
// TODO: Also optimize this to make use of the reused objects
|
|
||||||
|
|
||||||
// We'll have to transform out `YaAudioBusBuffers` objects into an array of
|
|
||||||
// `AudioBusBuffers` object so the plugin can deal with them. These objects
|
|
||||||
// contain pointers to those original objects and thus don't store any
|
|
||||||
// buffer data themselves.
|
|
||||||
inputs_audio_bus_buffers.clear();
|
|
||||||
for (auto& buffers : inputs) {
|
|
||||||
inputs_audio_bus_buffers.push_back(buffers.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
// We'll do the same with with the outputs, but we'll first have to
|
|
||||||
// initialize zeroed out buffers for the plugin to work with since we didn't
|
|
||||||
// serialize those directly
|
|
||||||
outputs.clear();
|
|
||||||
outputs_audio_bus_buffers.clear();
|
|
||||||
for (auto& num_channels : outputs_num_channels) {
|
|
||||||
YaAudioBusBuffers& buffers = outputs.emplace_back(
|
|
||||||
symbolic_sample_size, num_samples, num_channels);
|
|
||||||
outputs_audio_bus_buffers.push_back(buffers.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (output_parameter_changes) {
|
|
||||||
output_parameter_changes->clear();
|
|
||||||
}
|
|
||||||
if (output_events) {
|
|
||||||
output_events->clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
reconstructed_process_data.processMode = process_mode;
|
reconstructed_process_data.processMode = process_mode;
|
||||||
reconstructed_process_data.symbolicSampleSize = symbolic_sample_size;
|
reconstructed_process_data.symbolicSampleSize = symbolic_sample_size;
|
||||||
reconstructed_process_data.numSamples = num_samples;
|
reconstructed_process_data.numSamples = num_samples;
|
||||||
reconstructed_process_data.numInputs = static_cast<int32>(inputs.size());
|
reconstructed_process_data.numInputs = static_cast<int32>(inputs.size());
|
||||||
reconstructed_process_data.numOutputs =
|
reconstructed_process_data.numOutputs =
|
||||||
static_cast<int32>(outputs_num_channels.size());
|
static_cast<int32>(outputs_num_channels.size());
|
||||||
reconstructed_process_data.inputs = inputs_audio_bus_buffers.data();
|
|
||||||
reconstructed_process_data.outputs = outputs_audio_bus_buffers.data();
|
|
||||||
|
|
||||||
|
// We'll have to transform our `YaAudioBusBuffers` objects into an array of
|
||||||
|
// `AudioBusBuffers` object so the plugin can deal with them. These objects
|
||||||
|
// contain pointers to those original objects and thus don't store any
|
||||||
|
// buffer data themselves.
|
||||||
|
inputs_audio_bus_buffers.resize(inputs.size());
|
||||||
|
for (size_t i = 0; i < inputs.size(); i++) {
|
||||||
|
inputs[i].reconstruct(inputs_audio_bus_buffers[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
reconstructed_process_data.inputs = inputs_audio_bus_buffers.data();
|
||||||
|
|
||||||
|
// We'll do the same with with the outputs, but we'll first have to
|
||||||
|
// initialize zeroed out buffers for the plugin to work with since we didn't
|
||||||
|
// serialize those directly
|
||||||
|
outputs.resize(outputs_num_channels.size());
|
||||||
|
outputs_audio_bus_buffers.resize(outputs_num_channels.size());
|
||||||
|
for (size_t i = 0; i < outputs_num_channels.size(); i++) {
|
||||||
|
outputs[i].clear(symbolic_sample_size, num_samples,
|
||||||
|
outputs_num_channels[i]);
|
||||||
|
outputs[i].reconstruct(outputs_audio_bus_buffers[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
reconstructed_process_data.outputs = outputs_audio_bus_buffers.data();
|
||||||
reconstructed_process_data.inputParameterChanges = &input_parameter_changes;
|
reconstructed_process_data.inputParameterChanges = &input_parameter_changes;
|
||||||
|
|
||||||
if (output_parameter_changes_supported) {
|
if (output_parameter_changes_supported) {
|
||||||
output_parameter_changes.emplace();
|
if (!output_parameter_changes) {
|
||||||
|
output_parameter_changes.emplace();
|
||||||
|
}
|
||||||
|
output_parameter_changes->clear();
|
||||||
reconstructed_process_data.outputParameterChanges =
|
reconstructed_process_data.outputParameterChanges =
|
||||||
&*output_parameter_changes;
|
&*output_parameter_changes;
|
||||||
} else {
|
} else {
|
||||||
output_parameter_changes.reset();
|
|
||||||
reconstructed_process_data.outputParameterChanges = nullptr;
|
reconstructed_process_data.outputParameterChanges = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,10 +273,12 @@ Steinberg::Vst::ProcessData& YaProcessData::get() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (output_events_supported) {
|
if (output_events_supported) {
|
||||||
output_events.emplace();
|
if (!output_events) {
|
||||||
|
output_events.emplace();
|
||||||
|
}
|
||||||
|
output_events->clear();
|
||||||
reconstructed_process_data.outputEvents = &*output_events;
|
reconstructed_process_data.outputEvents = &*output_events;
|
||||||
} else {
|
} else {
|
||||||
output_events.reset();
|
|
||||||
reconstructed_process_data.outputEvents = nullptr;
|
reconstructed_process_data.outputEvents = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,14 +47,9 @@ class YaAudioBusBuffers {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new, zero initialize audio bus buffers object. Used to
|
* Create a new, zero initialize audio bus buffers object. Used to
|
||||||
* reconstruct the output buffers during `YaProcessData::get()`.
|
* reconstruct the output buffers during `YaProcessData::reconstruct()`.
|
||||||
*
|
|
||||||
* TODO: Replace with a function similar to `repopulate` that just reassigns
|
|
||||||
* the existing buffers for the outputs created on the Wine side.
|
|
||||||
*/
|
*/
|
||||||
YaAudioBusBuffers(int32 sample_size,
|
void clear(int32 sample_size, size_t num_samples, size_t num_channels);
|
||||||
size_t num_samples,
|
|
||||||
size_t num_channels);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy data from a host provided `AudioBusBuffers` object during a process
|
* Copy data from a host provided `AudioBusBuffers` object during a process
|
||||||
@@ -71,13 +66,13 @@ class YaAudioBusBuffers {
|
|||||||
/**
|
/**
|
||||||
* Reconstruct the original `AudioBusBuffers` object passed to the
|
* Reconstruct the original `AudioBusBuffers` object passed to the
|
||||||
* constructor and return it. This is used as part of
|
* constructor and return it. This is used as part of
|
||||||
* `YaProcessData::get()`. The object contains pointers to `buffers`, so it
|
* `YaProcessData::reconstruct()`. The object contains pointers to
|
||||||
* may not outlive this object.
|
* `buffers`, so it may not outlive this object.
|
||||||
*
|
*
|
||||||
* NOTE: The `silenceFlags` field is of course not a reference, so writing
|
* NOTE: The `silenceFlags` field is of course not a reference, so writing
|
||||||
* to that will not modify `silence_flags`.
|
* to that will not modify `silence_flags`.
|
||||||
*/
|
*/
|
||||||
Steinberg::Vst::AudioBusBuffers get();
|
void reconstruct(Steinberg::Vst::AudioBusBuffers& reconstructed_buffers);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the number of channels in `buffers`. Only used for debug logs.
|
* Return the number of channels in `buffers`. Only used for debug logs.
|
||||||
@@ -183,10 +178,11 @@ class YaProcessData {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy data from a host provided `ProcessData` object during a process
|
* Copy data from a host provided `ProcessData` object during a process
|
||||||
* call. This struct can then be serialized, and `YaProcessData::get()` can
|
* call. This struct can then be serialized, and
|
||||||
* then be used again to recreate the original `ProcessData` object. This
|
* `YaProcessData::reconstruct()` can then be used again to recreate the
|
||||||
* will avoid allocating unless it's absolutely necessary (e.g. when we
|
* original `ProcessData` object. This will avoid allocating unless it's
|
||||||
* receive more parameter changes than we've received in previous calls).
|
* absolutely necessary (e.g. when we receive more parameter changes than
|
||||||
|
* we've received in previous calls).
|
||||||
*/
|
*/
|
||||||
void repopulate(const Steinberg::Vst::ProcessData& process_data);
|
void repopulate(const Steinberg::Vst::ProcessData& process_data);
|
||||||
|
|
||||||
@@ -195,7 +191,7 @@ class YaProcessData {
|
|||||||
* and return it. This is used in the Wine plugin host when processing an
|
* and return it. This is used in the Wine plugin host when processing an
|
||||||
* `IAudioProcessor::process()` call.
|
* `IAudioProcessor::process()` call.
|
||||||
*/
|
*/
|
||||||
Steinberg::Vst::ProcessData& get();
|
Steinberg::Vst::ProcessData& reconstruct();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* **Move** all output written by the Windows VST3 plugin to a response
|
* **Move** all output written by the Windows VST3 plugin to a response
|
||||||
|
|||||||
@@ -1261,12 +1261,10 @@ size_t Vst3Bridge::register_object_instance(
|
|||||||
true, *request.new_realtime_priority);
|
true, *request.new_realtime_priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This `get()` now moves data. We should avoid
|
|
||||||
// that, since that would require reallocating the
|
|
||||||
// process data next iteration.
|
|
||||||
const tresult result =
|
const tresult result =
|
||||||
object_instances[request.instance_id]
|
object_instances[request.instance_id]
|
||||||
.audio_processor->process(request.data.get());
|
.audio_processor->process(
|
||||||
|
request.data.reconstruct());
|
||||||
|
|
||||||
return YaAudioProcessor::ProcessResponse{
|
return YaAudioProcessor::ProcessResponse{
|
||||||
.result = result,
|
.result = result,
|
||||||
|
|||||||
Reference in New Issue
Block a user