From 2bf98d0a97e2e3d823f98ea52912f2fbcaab7c30 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Wed, 16 Dec 2020 18:14:28 +0100 Subject: [PATCH] Implement writing back YaProcessDataResponse Everything around `ProcessData` is now fully implemented and should in theory work. --- README.md | 2 + src/common/serialization/vst3/event-list.cpp | 11 ++++++ src/common/serialization/vst3/event-list.h | 6 +++ .../serialization/vst3/param-value-queue.cpp | 10 +++++ .../serialization/vst3/param-value-queue.h | 9 ++++- .../serialization/vst3/parameter-changes.cpp | 13 +++++++ .../serialization/vst3/parameter-changes.h | 7 ++++ .../serialization/vst3/process-data.cpp | 39 +++++++++++++++++++ src/common/serialization/vst3/process-data.h | 12 +++++- 9 files changed, 107 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ae07010a..7a981a79 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ incomplete list of things that still have to be done before this can be used: - Fully implemented: - `GetPluginFactory()` and `IPluginFactory{,2,3}` - `IPluginBase` and `IComponent` + - Everything surrounding `ProcessData`, `ProcessingContext` and + `AudioBusBuffers` - `IParameterChanges`, `IParamValueQueue`, `IEventList`, and all event types in VST 3.7.1 - `IBStream` diff --git a/src/common/serialization/vst3/event-list.cpp b/src/common/serialization/vst3/event-list.cpp index ea084f95..a60f7023 100644 --- a/src/common/serialization/vst3/event-list.cpp +++ b/src/common/serialization/vst3/event-list.cpp @@ -194,6 +194,17 @@ YaEventList::~YaEventList() { FUNKNOWN_DTOR } +void YaEventList::write_back_outputs( + Steinberg::Vst::IEventList& output_events) const { + // TODO: I assume the host is responsible for directly copying heap data + // (e.g. text) in these events and they're not supposed to stay + // around, right? If not, then we'll find out very quickly. + for (auto& event : events) { + Steinberg::Vst::Event reconstructed_event = event.get(); + output_events.addEvent(reconstructed_event); + } +} + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" IMPLEMENT_FUNKNOWN_METHODS(YaEventList, diff --git a/src/common/serialization/vst3/event-list.h b/src/common/serialization/vst3/event-list.h index 0d5459cf..84ae6059 100644 --- a/src/common/serialization/vst3/event-list.h +++ b/src/common/serialization/vst3/event-list.h @@ -227,6 +227,12 @@ class YaEventList : public Steinberg::Vst::IEventList { DECLARE_FUNKNOWN_METHODS + /** + * Write these events an output events queue on the `ProcessData` object + * provided by the host. + */ + void write_back_outputs(Steinberg::Vst::IEventList& output_events) const; + // From `IEventList` virtual int32 PLUGIN_API getEventCount() override; virtual tresult PLUGIN_API diff --git a/src/common/serialization/vst3/param-value-queue.cpp b/src/common/serialization/vst3/param-value-queue.cpp index 25e93375..45c4897a 100644 --- a/src/common/serialization/vst3/param-value-queue.cpp +++ b/src/common/serialization/vst3/param-value-queue.cpp @@ -47,6 +47,16 @@ IMPLEMENT_FUNKNOWN_METHODS(YaParamValueQueue, Steinberg::Vst::IParamValueQueue::iid) #pragma GCC diagnostic pop +void YaParamValueQueue::write_back_outputs( + Steinberg::Vst::IParamValueQueue& output_queue) const { + // We don't need this value + int32 index; + for (const auto& [sample_offset, value] : queue) { + // We don't check for `kResultOk` here + output_queue.addPoint(sample_offset, value, index); + } +} + Steinberg::Vst::ParamID PLUGIN_API YaParamValueQueue::getParameterId() { return parameter_id; } diff --git a/src/common/serialization/vst3/param-value-queue.h b/src/common/serialization/vst3/param-value-queue.h index 9b3b02fe..dfb13464 100644 --- a/src/common/serialization/vst3/param-value-queue.h +++ b/src/common/serialization/vst3/param-value-queue.h @@ -51,6 +51,13 @@ class YaParamValueQueue : public Steinberg::Vst::IParamValueQueue { DECLARE_FUNKNOWN_METHODS + /** + * Write this queue back the output parameter changes object on the + * `ProcessData` object provided by the host. + */ + void write_back_outputs( + Steinberg::Vst::IParamValueQueue& output_queue) const; + // From `IParamValueQueue` Steinberg::Vst::ParamID PLUGIN_API getParameterId() override; int32 PLUGIN_API getPointCount() override; @@ -72,12 +79,12 @@ class YaParamValueQueue : public Steinberg::Vst::IParamValueQueue { }); } - private: /** * For `IParamValueQueue::getParameterId`. */ Steinberg::Vst::ParamID parameter_id; + private: /** * The actual parameter changes queue. The specification doesn't mention * that this should be a priority queue or something, but I'd assume both diff --git a/src/common/serialization/vst3/parameter-changes.cpp b/src/common/serialization/vst3/parameter-changes.cpp index bb6d27ce..44c4bb62 100644 --- a/src/common/serialization/vst3/parameter-changes.cpp +++ b/src/common/serialization/vst3/parameter-changes.cpp @@ -41,6 +41,19 @@ IMPLEMENT_FUNKNOWN_METHODS(YaParameterChanges, Steinberg::Vst::IParameterChanges::iid) #pragma GCC diagnostic pop +void YaParameterChanges::write_back_outputs( + Steinberg::Vst::IParameterChanges& output_queues) const { + for (auto& queue : queues) { + // We don't need this, but the SDK requires us to need this + int32 output_queue_index; + if (Steinberg::Vst::IParamValueQueue* output_queue = + output_queues.addParameterData(queue.parameter_id, + output_queue_index)) { + queue.write_back_outputs(*output_queue); + } + } +} + int32 PLUGIN_API YaParameterChanges::getParameterCount() { return queues.size(); } diff --git a/src/common/serialization/vst3/parameter-changes.h b/src/common/serialization/vst3/parameter-changes.h index 0eabaa30..600dd08b 100644 --- a/src/common/serialization/vst3/parameter-changes.h +++ b/src/common/serialization/vst3/parameter-changes.h @@ -45,6 +45,13 @@ class YaParameterChanges : public Steinberg::Vst::IParameterChanges { DECLARE_FUNKNOWN_METHODS + /** + * Write these changes back to an output parameter changes queue on the + * `ProcessData` object provided by the host. + */ + void write_back_outputs( + Steinberg::Vst::IParameterChanges& output_queues) const; + // From `IParameterChanges` int32 PLUGIN_API getParameterCount() override; Steinberg::Vst::IParamValueQueue* PLUGIN_API diff --git a/src/common/serialization/vst3/process-data.cpp b/src/common/serialization/vst3/process-data.cpp index 50e4cc77..b733ee4c 100644 --- a/src/common/serialization/vst3/process-data.cpp +++ b/src/common/serialization/vst3/process-data.cpp @@ -92,6 +92,45 @@ Steinberg::Vst::AudioBusBuffers YaAudioBusBuffers::get() { return reconstructed_buffers; } +void YaAudioBusBuffers::write_back_outputs( + Steinberg::Vst::AudioBusBuffers& output_buffers) const { + output_buffers.silenceFlags = silence_flags; + std::visit( + overload{ + [&](const std::vector>& 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>& 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) diff --git a/src/common/serialization/vst3/process-data.h b/src/common/serialization/vst3/process-data.h index c3a10167..6216d672 100644 --- a/src/common/serialization/vst3/process-data.h +++ b/src/common/serialization/vst3/process-data.h @@ -72,6 +72,13 @@ class YaAudioBusBuffers { */ Steinberg::Vst::AudioBusBuffers get(); + /** + * Write these buffers and the silence flag back to an `AudioBusBuffers + * object provided by the host. + */ + void write_back_outputs( + Steinberg::Vst::AudioBusBuffers& output_buffers) const; + template void serialize(S& s) { s.value8b(silence_flags); @@ -134,7 +141,10 @@ struct YaProcessDataResponse { std::optional output_parameter_changes; std::optional output_events; - // TODO: Add function to write these back to the host's `ProcessData` + /** + * Write all of this output data back to the host's `ProcessData` object. + */ + void write_back_outputs(Steinberg::Vst::ProcessData& process_data); template void serialize(S& s) {