Audacity 3.2.0
VST3Wrapper.cpp
Go to the documentation of this file.
1#include "VST3Wrapper.h"
2#include <stdexcept>
3#include <optional>
4#include <map>
5
6#include "EffectInterface.h"
7
9
10#include <pluginterfaces/vst/ivsteditcontroller.h>
11#include <pluginterfaces/vst/ivstparameterchanges.h>
12#include <wx/tokenzr.h>
13
14#include "ConfigInterface.h"
15#include "memorystream.h"
16#include "MemoryX.h"
17#include "VST3Utils.h"
19
20namespace
21{
22
23// define some shared registry keys
24constexpr auto processorStateKey = wxT("ProcessorState");
25constexpr auto controllerStateKey = wxT("ControllerState");
26constexpr auto parametersKey = wxT("Parameters");
27
28Steinberg::Vst::SpeakerArrangement GetBusArragementForChannels(
29 int32_t channelsCount, Steinberg::Vst::SpeakerArrangement defaultArragment)
30{
31 if (channelsCount == 1)
32 return defaultArragment;
33
34 return Steinberg::Vst::SpeakerArr::kStereo;
35}
36
38{
40 std::map<Steinberg::Vst::ParamID, Steinberg::Vst::ParamValue> parameterChanges;
41
43 std::optional<wxString> processorState;
45 std::optional<wxString> controllerState;
46};
47
48std::map<Steinberg::Vst::ParamID, Steinberg::Vst::ParamValue> ParametersFromString(const wxString& str)
49{
50 std::map<Steinberg::Vst::ParamID, Steinberg::Vst::ParamValue> result;
51 wxStringTokenizer tokenizer(str, ";");
52 while(tokenizer.HasMoreTokens())
53 {
54 auto token = tokenizer.GetNextToken();
55
56 const auto split = token.Find('=');
57 if(split == wxNOT_FOUND)
58 continue;
59
60 unsigned long id;
61 double value;
62 if(!token.Left(split).ToULong(&id) ||
63 !token.Right(token.Length() - split - 1).ToDouble(&value))
64 continue;
65
66 result[id] = value;
67 }
68 return result;
69}
70
71wxString ParametersToString(const std::map<Steinberg::Vst::ParamID, Steinberg::Vst::ParamValue>& params)
72{
73 wxString result;
74 for(auto& p : params)
75 result.Append(wxString::Format(
76 "%lu=%f;", static_cast<unsigned long>(p.first), p.second));
77 return result;
78}
79
81{
82 auto vst3settings = settings.cast<VST3EffectSettings>();
83 assert(vst3settings);
84 return *vst3settings;
85}
86
88{
89 auto vst3settings = settings.cast<VST3EffectSettings>();
90 assert(vst3settings);
91 return *vst3settings;
92}
93
94
95//Activates main audio input/output buses and disables others (event, audio aux)
96bool ActivateMainAudioBuses(Steinberg::Vst::IComponent& component)
97{
98 using namespace Steinberg;
99
100 constexpr int32 MaxChannelsPerAudioBus = 2;
101
102 std::vector<Vst::SpeakerArrangement> defaultInputSpeakerArrangements;
103 std::vector<Vst::SpeakerArrangement> defaultOutputSpeakerArrangements;
104
105 const auto processor = FUnknownPtr<Vst::IAudioProcessor>(&component);
106
107 for(int i = 0, count = component.getBusCount(Vst::kAudio, Vst::kInput); i < count; ++i)
108 {
109 Vst::BusInfo busInfo {};
110 Vst::SpeakerArrangement arrangement {0ull};
111
112 component.getBusInfo(Vst::kAudio, Vst::kInput, i, busInfo);
113
114 Vst::SpeakerArrangement defaultArragement {};
115 processor->getBusArrangement(Vst::kInput, i, defaultArragement);
116
117 arrangement =
118 GetBusArragementForChannels(busInfo.channelCount, defaultArragement);
119
120 component.activateBus(Vst::kAudio, Vst::kInput, i, busInfo.busType == Vst::kMain);
121
122 defaultInputSpeakerArrangements.push_back(arrangement);
123 }
124 for(int i = 0, count = component.getBusCount(Vst::kAudio, Vst::kOutput); i < count; ++i)
125 {
126 Vst::BusInfo busInfo {};
127 Vst::SpeakerArrangement arrangement {0ull};
128
129 component.getBusInfo(Vst::kAudio, Vst::kOutput, i, busInfo);
130
131 Vst::SpeakerArrangement defaultArragement {};
132 processor->getBusArrangement(Vst::kOutput, i, defaultArragement);
133
134 arrangement =
135 busInfo.busType == Vst::kMain ?
136 GetBusArragementForChannels(busInfo.channelCount, defaultArragement) :
137 Vst::SpeakerArr::kEmpty;
138
139 component.activateBus(Vst::kAudio, Vst::kOutput, i, busInfo.busType == Vst::kMain);
140 defaultOutputSpeakerArrangements.push_back(arrangement);
141 }
142 for(int i = 0, count = component.getBusCount(Vst::kEvent, Vst::kInput); i < count; ++i)
143 component.activateBus(Vst::kEvent, Vst::kInput, i, 0);
144 for(int i = 0, count = component.getBusCount(Vst::kEvent, Vst::kOutput); i < count; ++i)
145 component.activateBus(Vst::kEvent, Vst::kOutput, i, 0);
146
147 auto result = processor->setBusArrangements(
148 defaultInputSpeakerArrangements.empty() ? nullptr : defaultInputSpeakerArrangements.data(), defaultInputSpeakerArrangements.size(),
149 defaultOutputSpeakerArrangements.empty() ? nullptr : defaultOutputSpeakerArrangements.data(), defaultOutputSpeakerArrangements.size()
150 );
151
152 return result == kResultOk;
153}
154
155//The component should be disabled
156bool SetupProcessing(Steinberg::Vst::IComponent& component, Steinberg::Vst::ProcessSetup& setup)
157{
158 using namespace Steinberg;
159 auto processor = FUnknownPtr<Vst::IAudioProcessor>(&component);
160
161 if(processor->setupProcessing(setup) == kResultOk)
162 {
163 //We don't (yet) support custom input/output channel configuration
164 //on the host side. No support for event bus. Use default bus and
165 //channel configuration
166 return ActivateMainAudioBuses(component);
167 }
168 return false;
169}
170
171class InputParameterChanges final : public Steinberg::Vst::IParameterChanges
172{
173 const Steinberg::int32 mParameterCount;
175public:
176
177 InputParameterChanges(const std::vector<std::pair<Steinberg::Vst::ParamID, Steinberg::Vst::ParamValue>>& values, SingleInputParameterValue* queues)
178 : mParameterQueues(queues), mParameterCount(values.size())
179 {
180 FUNKNOWN_CTOR
181
182 int queueIndex{0};
183 for(auto& p : values)
184 queues[queueIndex++].Set(p.first, p.second);
185 }
186
188 {
189 FUNKNOWN_DTOR;
190 }
191
192 Steinberg::Vst::IParamValueQueue* PLUGIN_API
193 addParameterData(const Steinberg::Vst::ParamID& id, Steinberg::int32& index) override
194 {
195 return nullptr;
196 }
197
198 Steinberg::int32 PLUGIN_API getParameterCount() override
199 {
200 return mParameterCount;
201 }
202 Steinberg::Vst::IParamValueQueue* PLUGIN_API getParameterData(Steinberg::int32 index) override
203 {
204 return &mParameterQueues[index];
205 }
206
208};
209
210IMPLEMENT_FUNKNOWN_METHODS(InputParameterChanges, Steinberg::Vst::IParameterChanges,
211 Steinberg::Vst::IParameterChanges::iid);
212
213class ComponentHandler : public Steinberg::Vst::IComponentHandler
214{
216 EffectSettingsAccess* mAccess{nullptr};
217 //Used to prevent calls from non-UI thread
219
220 EffectSettings* mStateChangeSettings {nullptr};
221 std::map<Steinberg::Vst::ParamID, Steinberg::Vst::ParamValue> mParametersCache;
222 std::map<Steinberg::Vst::ParamID, Steinberg::Vst::ParamValue> mCurrentParamValues;
223
224public:
225
227 : mThreadId(std::this_thread::get_id()), mWrapper(wrapper)
228 {
229 FUNKNOWN_CTOR;
230 }
231 virtual ~ComponentHandler() { FUNKNOWN_DTOR; }
232
234 {
235 const auto paramsCount = mWrapper.mEditController->getParameterCount();
236
237 for (int i = 0; i < paramsCount; ++i)
238 {
239 using Steinberg::Vst::ParameterInfo;
240 ParameterInfo info {};
241 mWrapper.mEditController->getParameterInfo(i, info);
242
243 if (info.flags & (ParameterInfo::kIsHidden | ParameterInfo::kIsProgramChange))
244 continue;
245
246 if ((info.flags & ParameterInfo::kCanAutomate) == 0)
247 continue;
248
249 mCurrentParamValues[info.id] =
250 mWrapper.mEditController->getParamNormalized(info.id);
251 }
252 }
253
254 void SetAccess(EffectSettingsAccess* access) { mAccess = access; }
255
256 EffectSettingsAccess* GetAccess() { return mAccess; }
257
259 {
260 mStateChangeSettings = &settings;
261 }
262
264 {
265 assert(mStateChangeSettings != nullptr);
266 FlushCache(*mStateChangeSettings);
267 mStateChangeSettings = nullptr;
268 }
269
271 {
272 if(mParametersCache.empty())
273 return;
274
275 auto& vst3settings = GetSettings(settings);
276 for(auto& p : mParametersCache)
277 vst3settings.parameterChanges[p.first] = p.second;
278 mParametersCache.clear();
279 }
280
282 {
283 mParametersCache.clear();
284 }
285
287 Steinberg::Vst::ParamID id, Steinberg::Vst::ParamValue valueNormalized)
288 {
289 auto it = mCurrentParamValues.find(id);
290
291 if (it == mCurrentParamValues.end())
292 return;
293
294 // Tolerance to avoid unnecessary updates
295 // Waves plugins constantly update several parameters
296 // with very small changes in the values without any
297 // user input
298 constexpr auto epsilon = Steinberg::Vst::ParamValue(1e-5);
299
300 if (std::abs(it->second - valueNormalized) < epsilon)
301 return;
302
303 it->second = valueNormalized;
304
305 if (mStateChangeSettings == nullptr && mWrapper.ParamChangedHandler)
306 mWrapper.ParamChangedHandler(id, valueNormalized);
307 }
308
309 Steinberg::tresult PLUGIN_API beginEdit(Steinberg::Vst::ParamID id) override { return Steinberg::kResultOk; }
310
311 Steinberg::tresult PLUGIN_API performEdit(Steinberg::Vst::ParamID id, Steinberg::Vst::ParamValue valueNormalized) override
312 {
313 if(std::this_thread::get_id() != mThreadId)
314 return Steinberg::kResultFalse;
315
316 NotifyParamChange(id, valueNormalized);
317
318 if(mStateChangeSettings != nullptr || !mWrapper.IsActive())
319 // Collecting edit callbacks from the plug-in, in response to changes
320 // of complete state, while doing FetchSettings() (which may be delayed...)
321 mParametersCache[id] = valueNormalized;
322 else if(mAccess)
323 {
324 mAccess->ModifySettings([&](EffectSettings& settings)
325 {
326 auto& vst3settings = GetSettings(settings);
327 vst3settings.parameterChanges[id] = valueNormalized;
328 return nullptr;
329 });
330 }
331
332 return Steinberg::kResultOk;
333 }
334
335 Steinberg::tresult PLUGIN_API endEdit(Steinberg::Vst::ParamID id) override { return Steinberg::kResultOk; }
336
337 Steinberg::tresult PLUGIN_API restartComponent(Steinberg::int32 flags) override { return Steinberg::kNotImplemented; }
338
339 DECLARE_FUNKNOWN_METHODS
340};
341
342IMPLEMENT_FUNKNOWN_METHODS(ComponentHandler, Steinberg::Vst::IComponentHandler, Steinberg::Vst::IComponentHandler::iid)
343
344}
345
346
347void SingleInputParameterValue::Set(Steinberg::Vst::ParamID id, const Steinberg::Vst::ParamValue value)
348{
350 mValue = value;
351}
352
353Steinberg::tresult SingleInputParameterValue::addPoint(Steinberg::int32 sampleOffset, Steinberg::Vst::ParamValue value,
354 Steinberg::int32& index)
355{
356 return Steinberg::kResultFalse;
357}
358
360{
361 return mParameterId;
362}
363
364Steinberg::tresult SingleInputParameterValue::getPoint(Steinberg::int32 index, Steinberg::int32& sampleOffset,
365 Steinberg::Vst::ParamValue& value)
366{
367 sampleOffset = 0;
368 value = mValue;
369 return Steinberg::kResultOk;
370}
371
373{
374 return 1;
375}
376
377IMPLEMENT_FUNKNOWN_METHODS(SingleInputParameterValue, Steinberg::Vst::IParamValueQueue, Steinberg::Vst::IParamValueQueue::iid);
378
379VST3Wrapper::VST3Wrapper(VST3::Hosting::Module& module, VST3::UID effectUID)
380 : mEffectUID(std::move(effectUID))
381 , mModule{ module }
382{
383 using namespace Steinberg;
384
385 //Preinitialize with some default values in case if parameters
386 //flush happens before processing initialized
387 mSetup.maxSamplesPerBlock = 512;
388 mSetup.processMode = Vst::kOffline;
389 mSetup.symbolicSampleSize = Vst::kSample32;
390 mSetup.sampleRate = 44100.0;
391
392 const auto& pluginFactory = module.getFactory();
393
394 auto effectComponent = pluginFactory.createInstance<Vst::IComponent>(mEffectUID);
395 if(!effectComponent)
396 throw std::runtime_error("Cannot create VST3 effect component");
397 if(effectComponent->initialize(&AudacityVst3HostApplication::Get()) != kResultOk)
398 throw std::runtime_error("Cannot initialize VST3 effect component");
399
400 auto audioProcessor = FUnknownPtr<Vst::IAudioProcessor>(effectComponent);
401 if(!audioProcessor)
402 //It's stated that "This interface must always be supported by audio processing plug-ins."
403 throw std::runtime_error("VST3 plugin does not provide audio processor interface");
404
405 if(audioProcessor->canProcessSampleSize(Vst::kSample32) != kResultTrue)
406 throw std::runtime_error("32-bit sample size not supported");
407
408 mEffectComponent = effectComponent;
409 mAudioProcessor = audioProcessor;
410
412 throw std::runtime_error("bus configuration not supported");
413
414 auto editController = FUnknownPtr<Vst::IEditController>(mEffectComponent);
415 if(editController.get() == nullptr)
416 {
417 TUID controllerCID;
418
419 if (mEffectComponent->getControllerClassId (controllerCID) == kResultTrue)
420 editController = pluginFactory.createInstance<Vst::IEditController>(VST3::UID(controllerCID));
421 }
422
423 if(editController.get() == nullptr)
424 throw std::runtime_error("Failed to instantiate edit controller");
425
426 mEditController = editController;
428
429 mComponentHandler = owned(safenew ComponentHandler(*this));
430 mEditController->setComponentHandler(mComponentHandler);
431
432 const auto componentConnectionPoint = FUnknownPtr<Vst::IConnectionPoint>{ mEffectComponent };
433 const auto controllerConnectionPoint = FUnknownPtr<Vst::IConnectionPoint>{ mEditController };
434
435 if (componentConnectionPoint && controllerConnectionPoint)
436 {
437 mComponentConnectionProxy = owned(safenew internal::ConnectionProxy(componentConnectionPoint));
438 mControllerConnectionProxy = owned(safenew internal::ConnectionProxy(controllerConnectionPoint));
439
440 mComponentConnectionProxy->connect(controllerConnectionPoint);
441 mControllerConnectionProxy->connect(componentConnectionPoint);
442 }
443
444 mParameterQueues = std::make_unique<SingleInputParameterValue[]>(mEditController->getParameterCount());
445 mParameters.reserve(mEditController->getParameterCount());
446
447 Steinberg::MemoryStream stateStream;
448 if(mEffectComponent->getState(&stateStream) == kResultOk)
449 {
450 int64 unused;
451 stateStream.seek(0, IBStream::kIBSeekSet, &unused);
452 mEditController->setComponentState(&stateStream);
453 }
454
457
458
459 static_cast<ComponentHandler*>(mComponentHandler.get())
460 ->LoadCurrentParamValues();
461}
462
464{
465 using namespace Steinberg;
466
468 mComponentConnectionProxy->disconnect(FUnknownPtr<Vst::IConnectionPoint>(mEditController));
470 mControllerConnectionProxy->disconnect(FUnknownPtr<Vst::IConnectionPoint>(mEffectComponent));
471
473 {
474 mEditController->setComponentHandler(nullptr);
475 mEditController->terminate();
476 }
478 mEffectComponent->terminate();
479}
480
481bool VST3Wrapper::IsActive() const noexcept
482{
483 return mActive;
484}
485
487{
488 //TODO: perform version check
489 {
490 auto componentHandler = static_cast<ComponentHandler*>(mComponentHandler.get());
491 componentHandler->ResetCache();
492 componentHandler->BeginStateChange(settings);
493 auto cleanup = finally([&] { componentHandler->EndStateChange(); });
494
495 //Restore state
496 const auto* vst3settings = &GetSettings(settings);
497 if(!vst3settings->processorState.has_value())
498 vst3settings = &GetSettings(mDefaultSettings);
499
500 if(vst3settings->processorState.has_value())
501 {
502 auto processorState = PresetsBufferStream::fromString(*vst3settings->processorState);
503 processorState->seek(0, Steinberg::IBStream::kIBSeekSet);
504 if(mEffectComponent->setState(processorState) == Steinberg::kResultOk)
505 {
506 processorState->seek(0, Steinberg::IBStream::kIBSeekSet);
507 if(mEditController->setComponentState(processorState) == Steinberg::kResultOk)
508 {
509 if(vst3settings->controllerState.has_value())
510 {
511 auto controllerState = PresetsBufferStream::fromString(*vst3settings->controllerState);
512 controllerState->seek(0, Steinberg::IBStream::kIBSeekSet);
513 mEditController->setState(controllerState);
514 }
515 }
516 }
517 }
518 }
519 //restore parameters if present
520 auto& vst3setting = GetSettings(settings);
521 for(auto& p : vst3setting.parameterChanges)
522 mEditController->setParamNormalized(p.first, p.second);
523}
524
526{
527 using namespace Steinberg;
528
529 VST3EffectSettings vst3settings;
530
531 {
532 PresetsBufferStream processorState;
533 if(mEffectComponent->getState(&processorState) == kResultOk)
534 vst3settings.processorState = processorState.toString();
535 }
536 {
537 PresetsBufferStream controllerState;
538 if(mEditController->getState(&controllerState) == kResultOk)
539 vst3settings.controllerState = controllerState.toString();
540 }
541
542 std::swap(vst3settings, GetSettings(settings));
543}
544
545bool VST3Wrapper::LoadPreset(Steinberg::IBStream* fileStream)
546{
547 using namespace Steinberg;
548
549 return Vst::PresetFile::loadPreset
550 (
551 fileStream,
552 FUID::fromTUID(mEffectUID.data()),
553 mEffectComponent.get(),
554 mEditController.get()
555 );
556}
557
558
559bool VST3Wrapper::SavePreset(Steinberg::IBStream* fileStream) const
560{
561 using namespace Steinberg;
562
563 return Vst::PresetFile::savePreset
564 (
565 fileStream,
566 FUID::fromTUID(mEffectUID.data()),
567 mEffectComponent.get(),
568
569 mEditController.get()
570 );
571}
572
573bool VST3Wrapper::Initialize(EffectSettings& settings, Steinberg::Vst::SampleRate sampleRate, Steinberg::int32 processMode, Steinberg::int32 maxSamplesPerBlock)
574{
575 using namespace Steinberg;
576
577 Vst::ProcessSetup setup = {
578 processMode,
579 Vst::kSample32,
580 maxSamplesPerBlock,
581 sampleRate
582 };
583
584 if(!SetupProcessing(*mEffectComponent.get(), setup))
585 return false;
586
587 mSetup = setup;
588
590
591 if(mEffectComponent->setActive(true) == kResultOk)
592 {
593 if(mAudioProcessor->setProcessing(true) != kResultFalse)
594 {
595 mActive = true;
597 //make zero-flush, to make sure parameters are delivered to the processor...
598 Process(nullptr, nullptr, 0);
600 return true;
601 }
602 }
603 return false;
604}
605
607{
608 //Could be FlushParameters but processor is already configured
609 //If no calls to process were performed deliver changes here
610 if(settings != nullptr)
611 {
613 Process(nullptr, nullptr, 0);
614 }
615 mAudioProcessor->setProcessing(false);
616 mEffectComponent->setActive(false);
617 mActive = false;
618
619 if(settings != nullptr)
621}
622
624{
626}
627
629{
630 const auto& vst3settings = GetSettings(settings);
631 for(auto& p : vst3settings.parameterChanges)
632 {
633 auto it = std::find_if(mParameters.begin(), mParameters.end(), [&p](const auto& v) { return v.first == p.first; });
634 if(it != mParameters.end())
635 it->second = p.second;
636 else
637 mParameters.push_back(p);
638 }
639}
640
641//Used as a workaround for issue #2555: some plugins do not accept changes
642//via IEditController::setParamNormalized, but seem to read current
643//parameter values directly from the DSP model.
644//
645//When processing is disabled this call helps synchronize internal state of the
646//IEditController, so that next time UI is opened it displays correct values.
647//
648//As a side effect subsequent call to IAudioProcessor::process may flush
649//plugin internal buffers
651{
652 if(!mActive)
653 {
654 auto componentHandler = static_cast<ComponentHandler*>(mComponentHandler.get());
655 componentHandler->FlushCache(settings);
656
657 const auto doProcessing = !GetSettings(settings).parameterChanges.empty();
658
659 if(hasChanges != nullptr)
660 *hasChanges = doProcessing;
661
662 if(!doProcessing)
663 return;
664
666 mActive = true;
667 if(mEffectComponent->setActive(true) == Steinberg::kResultOk)
668 {
670 if(mAudioProcessor->setProcessing(true) != Steinberg::kResultFalse)
671 {
672 Process(nullptr, nullptr, 0);
673 mAudioProcessor->setProcessing(false);
674 }
675 }
676 mEffectComponent->setActive(false);
677 mActive = false;
678 }
679 else if(hasChanges != nullptr)
680 *hasChanges = false;
681}
682
683size_t VST3Wrapper::Process(const float* const* inBlock, float* const* outBlock, size_t blockLen)
684{
685 using namespace Steinberg;
686
687 InputParameterChanges inputParameterChanges(mParameters, mParameterQueues.get());
688 mParameters.clear();
689
690 Vst::ProcessData data;
691 data.processMode = mSetup.processMode;
692 data.symbolicSampleSize = mSetup.symbolicSampleSize;
693 data.inputParameterChanges = &inputParameterChanges;
694
695 static_assert(std::numeric_limits<decltype(blockLen)>::max()
696 >= std::numeric_limits<decltype(data.numSamples)>::max());
697
698 data.numSamples = static_cast<decltype(data.numSamples)>(std::min(
699 blockLen,
700 static_cast<decltype(blockLen)>(mSetup.maxSamplesPerBlock)
701 ));
702
703 data.numInputs = inBlock == nullptr ? 0 : mEffectComponent->getBusCount(Vst::kAudio, Vst::kInput);
704 data.numOutputs = outBlock == nullptr ? 0 : mEffectComponent->getBusCount(Vst::kAudio, Vst::kOutput);
705
706 if(data.numInputs > 0)
707 {
708 int inputBlocksOffset {0};
709
710 data.inputs = static_cast<Vst::AudioBusBuffers*>(
711 alloca(sizeof(Vst::AudioBusBuffers) * data.numInputs));
712
713 for(int busIndex = 0; busIndex < data.numInputs; ++busIndex)
714 {
715 Vst::BusInfo busInfo { };
716 if(mEffectComponent->getBusInfo(Vst::kAudio, Vst::kInput, busIndex, busInfo) != kResultOk)
717 {
718 return 0;
719 }
720 if(busInfo.busType == Vst::kMain)
721 {
722 data.inputs[busIndex].numChannels = busInfo.channelCount;
723 data.inputs[busIndex].channelBuffers32 = const_cast<float**>(inBlock + inputBlocksOffset);
724 inputBlocksOffset += busInfo.channelCount;
725 }
726 else
727 {
728 //aux is not yet supported
729 data.inputs[busIndex].numChannels = 0;
730 data.inputs[busIndex].channelBuffers32 = nullptr;
731 }
732 data.inputs[busIndex].silenceFlags = 0UL;
733 }
734 }
735 if(data.numOutputs > 0)
736 {
737 int outputBlocksOffset {0};
738
739 data.outputs = static_cast<Vst::AudioBusBuffers*>(
740 alloca(sizeof(Vst::AudioBusBuffers) * data.numOutputs));
741 for(int busIndex = 0; busIndex < data.numOutputs; ++busIndex)
742 {
743 Vst::BusInfo busInfo { };
744 if(mEffectComponent->getBusInfo(Vst::kAudio, Vst::kOutput, busIndex, busInfo) != kResultOk)
745 {
746 return 0;
747 }
748 if(busInfo.busType == Vst::kMain)
749 {
750 data.outputs[busIndex].numChannels = busInfo.channelCount;
751 data.outputs[busIndex].channelBuffers32 = const_cast<float**>(outBlock + outputBlocksOffset);
752 outputBlocksOffset += busInfo.channelCount;
753 }
754 else
755 {
756 //aux is not yet supported
757 data.outputs[busIndex].numChannels = 0;
758 data.outputs[busIndex].channelBuffers32 = nullptr;
759 }
760 data.outputs[busIndex].silenceFlags = 0UL;
761 }
762 }
763
764 const auto processResult = mAudioProcessor->process(data);
765
766 return processResult == kResultOk ?
767 data.numSamples : 0;
768}
769
770
772{
773 mAudioProcessor->setProcessing(false);
774}
775
777{
778 mAudioProcessor->setProcessing(true);
779}
780
782{
783 static_cast<ComponentHandler*>(mComponentHandler.get())->SetAccess(&access);
784}
785
787{
788 auto componentHandler = static_cast<ComponentHandler*>(mComponentHandler.get());
789 componentHandler->SetAccess(nullptr);
790}
791
792
793
794Steinberg::int32 VST3Wrapper::GetLatencySamples() const
795{
796 return mAudioProcessor->getLatencySamples();
797}
798
800{
801 return EffectSettings::Make<VST3EffectSettings>();
802}
803
805{
806 VST3EffectSettings vst3settings;
807
808 if(parms.HasEntry(processorStateKey))
809 {
810 vst3settings.processorState = parms.Read(processorStateKey);
812 vst3settings.controllerState = parms.Read(controllerStateKey);
813 }
814 if(parms.HasEntry(parametersKey))
815 vst3settings.parameterChanges = ParametersFromString(parms.Read(parametersKey));
816
817 std::swap(vst3settings, GetSettings(settings));
818}
819
821{
822 const auto& vst3settings = GetSettings(settings);
823
824 if(vst3settings.processorState.has_value())
825 parms.Write(processorStateKey, *vst3settings.processorState);
826 if(vst3settings.controllerState.has_value())
827 parms.Write(controllerStateKey, *vst3settings.controllerState);
828 if(!vst3settings.parameterChanges.empty())
829 parms.Write(parametersKey, ParametersToString(vst3settings.parameterChanges));
830}
831
834{
835 VST3EffectSettings vst3settings;
836
837 wxString processorStateStr;
838 if(GetConfig(effect, PluginSettings::Private, name, processorStateKey, processorStateStr, wxEmptyString))
839 {
840 vst3settings.processorState = processorStateStr;
841 wxString controllerStateStr;
842 if(GetConfig(effect, PluginSettings::Private, name, controllerStateKey, controllerStateStr, wxEmptyString))
843 vst3settings.controllerState = controllerStateStr;
844 }
845 wxString parametersStr;
846 if(GetConfig(effect, PluginSettings::Private, name, parametersKey, parametersStr, wxEmptyString))
847 vst3settings.parameterChanges = ParametersFromString(parametersStr);
848
849 std::swap(vst3settings, GetSettings(settings));
850 return { nullptr };
851}
852
854{
855 using namespace Steinberg;
856
857 const auto& vst3settings = GetSettings(settings);
858 if(vst3settings.processorState.has_value())
859 {
860 SetConfig(effect, PluginSettings::Private, name, processorStateKey, *vst3settings.processorState);
861 if(vst3settings.controllerState.has_value())
862 SetConfig(effect, PluginSettings::Private, name, controllerStateKey, *vst3settings.controllerState);
863 }
864 if(!vst3settings.parameterChanges.empty())
865 SetConfig(effect, PluginSettings::Private, name, parametersKey, ParametersToString(vst3settings.parameterChanges));
866}
867
869{
870 auto& from = GetSettings(*const_cast<EffectSettings*>(&src));
871 auto& to = GetSettings(dst);
872
873 //Don't allocate in worker
874 std::swap(from.parameterChanges, to.parameterChanges);
875}
wxT("CloseDown"))
int min(int a, int b)
#define str(a)
EffectDistortion::Params params
Definition: Distortion.cpp:83
const TranslatableString name
Definition: Distortion.cpp:82
std::optional< std::unique_ptr< EffectSettingsAccess::Message > > OptionalMessage
const wxChar * values
wxString RegistryPath
Definition: Identifier.h:218
#define safenew
Definition: MemoryX.h:10
static Settings & settings()
Definition: TrackInfo.cpp:87
IMPLEMENT_FUNKNOWN_METHODS(SingleInputParameterValue, Steinberg::Vst::IParamValueQueue, Steinberg::Vst::IParamValueQueue::iid)
int id
CommandParameters, derived from wxFileConfig, is essentially doing the same things as the SettingsVis...
virtual bool HasEntry(const wxString &strName) const override
EffectDefinitionInterface is a ComponentInterface that adds some basic read-only information about ef...
static Steinberg::IPtr< PresetsBufferStream > fromString(const wxString &str)
Definition: VST3Utils.cpp:80
wxString toString() const
Definition: VST3Utils.cpp:94
Steinberg::int32 PLUGIN_API getPointCount() override
void Set(Steinberg::Vst::ParamID id, const Steinberg::Vst::ParamValue value)
Steinberg::Vst::ParamValue mValue
Definition: VST3Wrapper.h:32
Steinberg::Vst::ParamID PLUGIN_API getParameterId() override
Steinberg::tresult PLUGIN_API getPoint(Steinberg::int32 index, Steinberg::int32 &sampleOffset, Steinberg::Vst::ParamValue &value) override
Steinberg::tresult PLUGIN_API addPoint(Steinberg::int32 sampleOffset, Steinberg::Vst::ParamValue value, Steinberg::int32 &index) override
Steinberg::Vst::ParamID mParameterId
Definition: VST3Wrapper.h:31
tresult PLUGIN_API seek(int64 pos, int32 mode, int64 *result) SMTG_OVERRIDE
void SuspendProcessing()
void BeginParameterEdit(EffectSettingsAccess &access)
size_t Process(const float *const *inBlock, float *const *outBlock, size_t blockLen)
void ResumeProcessing()
static EffectSettings MakeSettings()
bool LoadPreset(Steinberg::IBStream *fileStream)
Steinberg::IPtr< Steinberg::Vst::IConnectionPoint > mControllerConnectionProxy
Definition: VST3Wrapper.h:63
bool SavePreset(Steinberg::IBStream *fileStream) const
void ProcessBlockStart(const EffectSettings &settings)
Prepares effect to process next block with changes written to the settings object.
Steinberg::IPtr< Steinberg::Vst::IEditController > mEditController
Definition: VST3Wrapper.h:61
Steinberg::IPtr< Steinberg::Vst::IComponent > mEffectComponent
Definition: VST3Wrapper.h:60
static void SaveUserPreset(const EffectDefinitionInterface &effect, const RegistryPath &name, const EffectSettings &settings)
Steinberg::IPtr< Steinberg::Vst::IComponentHandler > mComponentHandler
Definition: VST3Wrapper.h:64
Steinberg::int32 GetLatencySamples() const
void FetchSettings(EffectSettings &)
Fetch state from settings object, may change internal runtime data.
static void SaveSettings(const EffectSettings &settings, CommandParameters &parms)
std::unique_ptr< SingleInputParameterValue[]> mParameterQueues
Definition: VST3Wrapper.h:141
std::function< void(Steinberg::Vst::ParamID, Steinberg::Vst::ParamValue)> ParamChangedHandler
Definition: VST3Wrapper.h:126
static void CopySettingsContents(const EffectSettings &src, EffectSettings &dst)
bool IsActive() const noexcept
bool Initialize(EffectSettings &settings, Steinberg::Vst::SampleRate sampleRate, Steinberg::int32 processMode, Steinberg::int32 maxSamplesPerBlock)
Initializes effect for processing using settings.
VST3Wrapper(VST3::Hosting::Module &module, VST3::UID effectUID)
void StoreSettings(EffectSettings &) const
Saves current state inside settings object, clears all runtime data.
static void LoadSettings(const CommandParameters &parms, EffectSettings &settings)
Steinberg::IPtr< Steinberg::Vst::IConnectionPoint > mComponentConnectionProxy
Definition: VST3Wrapper.h:62
Steinberg::IPtr< Steinberg::Vst::IAudioProcessor > mAudioProcessor
Definition: VST3Wrapper.h:58
void ConsumeChanges(const EffectSettings &settings)
void EndParameterEdit()
Steinberg::Vst::ProcessSetup mSetup
Definition: VST3Wrapper.h:59
std::vector< std::pair< Steinberg::Vst::ParamID, Steinberg::Vst::ParamValue > > mParameters
Definition: VST3Wrapper.h:136
void FlushParameters(EffectSettings &settings, bool *hasChanges=nullptr)
const VST3::UID mEffectUID
Definition: VST3Wrapper.h:134
EffectSettings mDefaultSettings
Definition: VST3Wrapper.h:55
void Finalize(EffectSettings *settings)
static OptionalMessage LoadUserPreset(const EffectDefinitionInterface &effect, const RegistryPath &name, EffectSettings &settings)
std::map< Steinberg::Vst::ParamID, Steinberg::Vst::ParamValue > mParametersCache
Steinberg::tresult PLUGIN_API endEdit(Steinberg::Vst::ParamID id) override
void SetAccess(EffectSettingsAccess *access)
std::map< Steinberg::Vst::ParamID, Steinberg::Vst::ParamValue > mCurrentParamValues
void NotifyParamChange(Steinberg::Vst::ParamID id, Steinberg::Vst::ParamValue valueNormalized)
Steinberg::tresult PLUGIN_API beginEdit(Steinberg::Vst::ParamID id) override
Steinberg::tresult PLUGIN_API performEdit(Steinberg::Vst::ParamID id, Steinberg::Vst::ParamValue valueNormalized) override
Steinberg::tresult PLUGIN_API restartComponent(Steinberg::int32 flags) override
InputParameterChanges(const std::vector< std::pair< Steinberg::Vst::ParamID, Steinberg::Vst::ParamValue > > &values, SingleInputParameterValue *queues)
Steinberg::Vst::IParamValueQueue *PLUGIN_API addParameterData(const Steinberg::Vst::ParamID &id, Steinberg::int32 &index) override
Steinberg::int32 PLUGIN_API getParameterCount() override
Steinberg::Vst::IParamValueQueue *PLUGIN_API getParameterData(Steinberg::int32 index) override
Host's proxy object between connection points.
bool SetConfig(const EffectDefinitionInterface &ident, ConfigurationType type, const RegistryPath &group, const RegistryPath &key, const Value &value)
bool GetConfig(const EffectDefinitionInterface &ident, ConfigurationType type, const RegistryPath &group, const RegistryPath &key, Value &var, const Value &defval)
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:753
bool ActivateMainAudioBuses(Steinberg::Vst::IComponent &component)
Definition: VST3Wrapper.cpp:96
std::map< Steinberg::Vst::ParamID, Steinberg::Vst::ParamValue > ParametersFromString(const wxString &str)
Definition: VST3Wrapper.cpp:48
Steinberg::Vst::SpeakerArrangement GetBusArragementForChannels(int32_t channelsCount, Steinberg::Vst::SpeakerArrangement defaultArragment)
Definition: VST3Wrapper.cpp:28
bool SetupProcessing(Steinberg::Vst::IComponent &component, Steinberg::Vst::ProcessSetup &setup)
const VST3EffectSettings & GetSettings(const EffectSettings &settings)
Definition: VST3Wrapper.cpp:87
wxString ParametersToString(const std::map< Steinberg::Vst::ParamID, Steinberg::Vst::ParamValue > &params)
Definition: VST3Wrapper.cpp:71
STL namespace.
Externalized state of a plug-in.
std::optional< wxString > controllerState
Holds the last known controller state, rarely updates (usually only on UI or preset change)
Definition: VST3Wrapper.cpp:45
std::map< Steinberg::Vst::ParamID, Steinberg::Vst::ParamValue > parameterChanges
Holds the parameter that has been changed since last processing pass.
Definition: VST3Wrapper.cpp:40
std::optional< wxString > processorState
Holds the last known processor/component state, rarely updates (usually only on UI or preset change)
Definition: VST3Wrapper.cpp:43