Files
yabridge/src/common/serialization/vst3/process-data.cpp
T
Robbert van der Helm 1c7a5e08a0 Check for null pointers in input parameter changes
This is not allowed to be a null pointer, but the SDK's plugin validator
will pass a null pointer anyways.
2020-12-29 17:32:33 +01:00

241 lines
9.7 KiB
C++

// yabridge: a Wine VST bridge
// Copyright (C) 2020 Robbert van der Helm
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "process-data.h"
#include "src/common/utils.h"
YaAudioBusBuffers::YaAudioBusBuffers() {}
YaAudioBusBuffers::YaAudioBusBuffers(int32 sample_size,
size_t num_channels,
size_t num_samples)
: buffers(sample_size == Steinberg::Vst::SymbolicSampleSizes::kSample64
? decltype(buffers)(std::vector<std::vector<double>>(
num_channels,
std::vector<double>(num_samples, 0.0)))
: decltype(buffers)(std::vector<std::vector<float>>(
num_channels,
std::vector<float>(num_samples, 0.0)))) {}
YaAudioBusBuffers::YaAudioBusBuffers(
int32 sample_size,
int32 num_samples,
const Steinberg::Vst::AudioBusBuffers& data)
: silence_flags(data.silenceFlags) {
switch (sample_size) {
case Steinberg::Vst::kSample64: {
std::vector<std::vector<double>> vector_buffers(data.numChannels);
for (int channel = 0; channel < data.numChannels; channel++) {
vector_buffers[channel].assign(
&data.channelBuffers64[channel][0],
&data.channelBuffers64[channel][num_samples]);
}
buffers = std::move(vector_buffers);
} break;
case Steinberg::Vst::kSample32:
// I don't think they'll add any other sample sizes any time soon
default: {
std::vector<std::vector<float>> vector_buffers(data.numChannels);
for (int channel = 0; channel < data.numChannels; channel++) {
vector_buffers[channel].assign(
&data.channelBuffers32[channel][0],
&data.channelBuffers32[channel][num_samples]);
}
buffers = std::move(vector_buffers);
} break;
}
}
Steinberg::Vst::AudioBusBuffers YaAudioBusBuffers::get() {
Steinberg::Vst::AudioBusBuffers reconstructed_buffers;
reconstructed_buffers.silenceFlags = silence_flags;
std::visit(overload{
[&](std::vector<std::vector<double>>& buffers) {
buffer_pointers.clear();
for (auto& buffer : buffers) {
buffer_pointers.push_back(buffer.data());
}
reconstructed_buffers.numChannels = buffers.size();
reconstructed_buffers.channelBuffers64 =
reinterpret_cast<double**>(buffer_pointers.data());
},
[&](std::vector<std::vector<float>>& buffers) {
buffer_pointers.clear();
for (auto& buffer : buffers) {
buffer_pointers.push_back(buffer.data());
}
reconstructed_buffers.numChannels = buffers.size();
reconstructed_buffers.channelBuffers32 =
reinterpret_cast<float**>(buffer_pointers.data());
},
},
buffers);
return reconstructed_buffers;
}
size_t YaAudioBusBuffers::num_channels() const {
return std::visit([&](const auto& buffers) { return buffers.size(); },
buffers);
}
void YaAudioBusBuffers::write_back_outputs(
Steinberg::Vst::AudioBusBuffers& output_buffers) const {
output_buffers.silenceFlags = silence_flags;
std::visit(
overload{
[&](const std::vector<std::vector<double>>& buffers) {
for (int channel = 0; channel < output_buffers.numChannels;
channel++) {
std::copy(buffers[channel].begin(), buffers[channel].end(),
output_buffers.channelBuffers64[channel]);
}
},
[&](const std::vector<std::vector<float>>& buffers) {
for (int channel = 0; channel < output_buffers.numChannels;
channel++) {
std::copy(buffers[channel].begin(), buffers[channel].end(),
output_buffers.channelBuffers32[channel]);
}
},
},
buffers);
}
void YaProcessDataResponse::write_back_outputs(
Steinberg::Vst::ProcessData& process_data) {
for (int i = 0; i < process_data.numOutputs; i++) {
outputs[i].write_back_outputs(process_data.outputs[i]);
}
if (output_parameter_changes && process_data.outputParameterChanges) {
output_parameter_changes->write_back_outputs(
*process_data.outputParameterChanges);
}
if (output_events && process_data.outputEvents) {
output_events->write_back_outputs(*process_data.outputEvents);
}
}
YaProcessData::YaProcessData() {}
YaProcessData::YaProcessData(const Steinberg::Vst::ProcessData& process_data)
: process_mode(process_data.processMode),
symbolic_sample_size(process_data.symbolicSampleSize),
num_samples(process_data.numSamples),
outputs_num_channels(process_data.numOutputs),
// Even though `ProcessData::inputParamterChanges` is mandatory, the VST3
// validator will pass a null pointer here
input_parameter_changes(
process_data.inputParameterChanges
? YaParameterChanges(*process_data.inputParameterChanges)
: YaParameterChanges()),
output_parameter_changes_supported(process_data.outputParameterChanges),
input_events(process_data.inputEvents ? std::make_optional<YaEventList>(
*process_data.inputEvents)
: std::nullopt),
output_events_supported(process_data.outputEvents),
process_context(process_data.processContext
? std::make_optional<Steinberg::Vst::ProcessContext>(
*process_data.processContext)
: std::nullopt) {
for (int i = 0; i < process_data.numInputs; i++) {
inputs.emplace_back(symbolic_sample_size, num_samples,
process_data.inputs[i]);
}
// Fetch the number of channels for each output so we can recreate these
// buffers in the Wine plugin host
for (int i = 0; i < process_data.numOutputs; i++) {
outputs_num_channels[i] = process_data.outputs[i].numChannels;
}
}
Steinberg::Vst::ProcessData& YaProcessData::get() {
// 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_channels, num_samples);
outputs_audio_bus_buffers.push_back(buffers.get());
}
reconstructed_process_data.processMode = process_mode;
reconstructed_process_data.symbolicSampleSize = symbolic_sample_size;
reconstructed_process_data.numSamples = num_samples;
reconstructed_process_data.numInputs = inputs.size();
reconstructed_process_data.numOutputs = outputs_num_channels.size();
reconstructed_process_data.inputs = inputs_audio_bus_buffers.data();
reconstructed_process_data.outputs = outputs_audio_bus_buffers.data();
reconstructed_process_data.inputParameterChanges = &input_parameter_changes;
if (output_parameter_changes_supported) {
output_parameter_changes.emplace();
reconstructed_process_data.outputParameterChanges =
&*output_parameter_changes;
} else {
output_parameter_changes.reset();
reconstructed_process_data.outputParameterChanges = nullptr;
}
if (input_events) {
reconstructed_process_data.inputEvents = &*input_events;
} else {
reconstructed_process_data.inputEvents = nullptr;
}
if (output_events_supported) {
output_events.emplace();
reconstructed_process_data.outputEvents = &*output_events;
} else {
output_events.reset();
reconstructed_process_data.outputEvents = nullptr;
}
if (process_context) {
reconstructed_process_data.processContext = &*process_context;
} else {
reconstructed_process_data.processContext = nullptr;
}
return reconstructed_process_data;
}
YaProcessDataResponse YaProcessData::move_outputs_to_response() {
return YaProcessDataResponse{
.outputs = std::move(outputs),
.output_parameter_changes = std::move(output_parameter_changes),
.output_events = std::move(output_events)};
}