63#define DSPARK_PLUGIN_VST3_INCLUDED 1
65#include "../DSParkPlugin.h"
67#include "vst3_c_api.h"
78 #include <dispatch/dispatch.h>
84extern "C" __declspec(dllimport) std::uintptr_t __stdcall SetTimer(
85 HWND__* window, std::uintptr_t
id,
unsigned int milliseconds,
86 void (__stdcall* callback)(HWND__*,
unsigned int, std::uintptr_t,
unsigned long));
87extern "C" __declspec(dllimport)
int __stdcall KillTimer(HWND__* window, std::uintptr_t
id);
89#pragma comment(lib, "user32.lib")
121 bool start(Steinberg_FUnknown* hostContext,
Callback callback,
void* context)
noexcept
123 if (running_.load(std::memory_order_relaxed))
return true;
124 callback_ = callback;
126#if defined(__APPLE__)
128 source_ = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
129 dispatch_get_main_queue());
130 if (source_ ==
nullptr)
return false;
131 dispatch_set_context(source_,
this);
132 dispatch_source_set_event_handler_f(source_, &onDispatchTick);
133 dispatch_source_set_timer(source_,
134 dispatch_time(DISPATCH_TIME_NOW,
135 static_cast<int64_t
>(kPeriodMs) * 1000000),
136 static_cast<uint64_t
>(kPeriodMs) * 1000000u,
138 dispatch_resume(source_);
141 timerId_ = SetTimer(
nullptr, 0, kPeriodMs, &onWindowsTick);
142 if (timerId_ == 0)
return false;
143 registry().emplace_back(timerId_,
this);
144#elif defined(__linux__)
145 if (hostContext ==
nullptr)
return false;
146 void* loop =
nullptr;
147 if (hostContext->lpVtbl->queryInterface(hostContext, Steinberg_Linux_IRunLoop_iid,
148 &loop) != Steinberg_kResultOk
151 runLoop_ =
static_cast<Steinberg_Linux_IRunLoop*
>(loop);
152 if (runLoop_->lpVtbl->registerTimer(
153 runLoop_,
reinterpret_cast<Steinberg_Linux_ITimerHandler*
>(&timer_),
154 kPeriodMs) != Steinberg_kResultOk)
156 reinterpret_cast<Steinberg_FUnknown*
>(runLoop_)->lpVtbl->release(runLoop_);
164 running_.store(
true, std::memory_order_release);
171 if (!running_.load(std::memory_order_relaxed))
return;
172 running_.store(
false, std::memory_order_release);
173#if defined(__APPLE__)
176 dispatch_source_cancel(source_);
177 dispatch_release(source_);
180 KillTimer(
nullptr, timerId_);
181 auto& entries = registry();
182 for (
size_t i = 0; i < entries.size(); ++i)
183 if (entries[i].first == timerId_)
185 entries.erase(entries.begin() +
static_cast<std::ptrdiff_t
>(i));
189#elif defined(__linux__)
190 runLoop_->lpVtbl->unregisterTimer(
191 runLoop_,
reinterpret_cast<Steinberg_Linux_ITimerHandler*
>(&timer_));
192 reinterpret_cast<Steinberg_FUnknown*
>(runLoop_)->lpVtbl->release(runLoop_);
199 return running_.load(std::memory_order_acquire);
203 static constexpr unsigned int kPeriodMs = 33;
207 if (running_.load(std::memory_order_relaxed) && callback_ !=
nullptr)
211 std::atomic<bool> running_ {
false };
213 void* context_ =
nullptr;
215#if defined(__APPLE__)
216 dispatch_source_t source_ =
nullptr;
217 static void onDispatchTick(
void* self) {
static_cast<UiTicker*
>(self)->tick(); }
219 std::uintptr_t timerId_ = 0;
221 static std::vector<std::pair<std::uintptr_t, UiTicker*>>& registry()
223 static std::vector<std::pair<std::uintptr_t, UiTicker*>> entries;
226 static void __stdcall onWindowsTick(HWND__*,
unsigned int, std::uintptr_t
id,
229 for (
const auto& entry : registry())
230 if (entry.first == id)
232 entry.second->tick();
236#elif defined(__linux__)
240 const Steinberg_Linux_ITimerHandlerVtbl* vtbl;
243 static Steinberg_tresult SMTG_STDMETHODCALLTYPE timerQuery(
void* self_,
244 const Steinberg_TUID iid,
void** obj)
246 if (obj ==
nullptr)
return Steinberg_kInvalidArgument;
247 if (std::memcmp(iid, Steinberg_FUnknown_iid,
sizeof(Steinberg_TUID)) == 0
248 || std::memcmp(iid, Steinberg_Linux_ITimerHandler_iid,
249 sizeof(Steinberg_TUID)) == 0)
252 return Steinberg_kResultOk;
255 return Steinberg_kNoInterface;
257 static Steinberg_uint32 SMTG_STDMETHODCALLTYPE timerRef(
void*) {
return 1; }
258 static void SMTG_STDMETHODCALLTYPE timerTick(
void* self_)
260 static_cast<TimerObject*
>(self_)->owner->tick();
262 inline static const Steinberg_Linux_ITimerHandlerVtbl kTimerVtbl = {
263 &timerQuery, &timerRef, &timerRef, &timerTick
265 TimerObject timer_ { &kTimerVtbl,
this };
266 Steinberg_Linux_IRunLoop* runLoop_ =
nullptr;
272inline bool tuidEqual(
const Steinberg_TUID a,
const Steinberg_TUID b)
noexcept
274 return std::memcmp(a, b,
sizeof(Steinberg_TUID)) == 0;
281 for (; i < 127 && src[i] !=
'\0'; ++i)
282 dst[i] =
static_cast<Steinberg_char16
>(
static_cast<unsigned char>(src[i]));
286inline void copyAscii(
const char* src,
char* dst,
size_t cap)
noexcept
288 std::snprintf(dst, cap,
"%s", src ? src :
"");
292inline bool streamRead(Steinberg_IBStream* s,
void* dst, Steinberg_int32 bytes)
noexcept
294 auto* p =
static_cast<char*
>(dst);
297 Steinberg_int32 got = 0;
298 if (s->lpVtbl->read(s, p, bytes, &got) != Steinberg_kResultOk || got <= 0)
306inline bool streamWrite(Steinberg_IBStream* s,
const void* src, Steinberg_int32 bytes)
noexcept
308 auto* p =
static_cast<const char*
>(src);
311 Steinberg_int32 put = 0;
312 if (s->lpVtbl->write(s,
const_cast<char*
>(p), bytes, &put) != Steinberg_kResultOk
338 const uint32_t ctrl =
id & 0xFFu;
339 const uint32_t channel = (
id >> 8) & 0xFFu;
341 && (ctrl == 1 || ctrl == 64 || ctrl == 128 || ctrl == 129);
344inline constexpr uint32_t
midiProxyId(
int channel,
int controller)
noexcept
347 |
static_cast<uint32_t
>(controller);
354 ev.
channel =
static_cast<uint8_t
>((
id >> 8) & 0x0Fu);
355 ev.sampleOffset = offset;
356 const uint32_t ctrl =
id & 0xFFu;
360 ev.value =
static_cast<float>(normalized * 2.0 - 1.0);
362 else if (ctrl == 128)
365 ev.value =
static_cast<float>(normalized);
370 ev.note =
static_cast<uint8_t
>(ctrl);
371 ev.value =
static_cast<float>(normalized);
384 "an Instrument has no audio inputs; remove the sidechain "
385 "processBlock or use Category::Fx");
387 "an Instrument needs handleMidiEvent (see HasMidi): it has "
388 "no audio input to process");
389 static_assert(paramIdsUnique<P>(),
390 "two parameter ids share a hash32 (or collide with the "
391 "reserved PRGM/BYPS state ids): automation and state would "
392 "cross-wire. Rename one id.");
404 std::atomic<Steinberg_uint32>
refs { 1 };
407 Steinberg_Vst_ProcessSetup
setup { 0, 0, 0, 48000.0 };
431 std::atomic<Steinberg_Vst_IComponentHandler*>
handler {
nullptr };
437 std::atomic<std::thread::id>
uiThread { std::this_thread::get_id() };
451 std::memory_order_relaxed);
458 return reinterpret_cast<Plugin*
>(
static_cast<char*
>(iface)
459 -
static_cast<ptrdiff_t
>(lens) *
sizeof(
void*));
464 return reinterpret_cast<char*
>(
this) +
static_cast<ptrdiff_t
>(lens) *
sizeof(
void*);
469 Steinberg_tresult
query(
const Steinberg_TUID iid,
void** obj)
noexcept
471 if (obj ==
nullptr)
return Steinberg_kInvalidArgument;
473 if (
tuidEqual(iid, Steinberg_FUnknown_iid)
474 ||
tuidEqual(iid, Steinberg_IPluginBase_iid)
475 ||
tuidEqual(iid, Steinberg_Vst_IComponent_iid))
477 else if (
tuidEqual(iid, Steinberg_Vst_IAudioProcessor_iid))
479 else if (
tuidEqual(iid, Steinberg_Vst_IEditController_iid))
481 else if (
tuidEqual(iid, Steinberg_Vst_IProcessContextRequirements_iid))
487 if (*obj ==
nullptr)
return Steinberg_kNoInterface;
488 refs.fetch_add(1, std::memory_order_relaxed);
489 return Steinberg_kResultOk;
494 return refs.fetch_add(1, std::memory_order_relaxed) + 1;
499 const Steinberg_uint32 left =
refs.fetch_sub(1, std::memory_order_acq_rel) - 1;
500 if (left == 0)
delete this;
505 static Steinberg_tresult SMTG_STDMETHODCALLTYPE
sQuery(
void* self_,
506 const Steinberg_TUID iid,
511 static Steinberg_uint32 SMTG_STDMETHODCALLTYPE
sAddRef(
void* self_)
515 static Steinberg_uint32 SMTG_STDMETHODCALLTYPE
sRelease(
void* self_)
523 if (
hash32(P::parameters[i].
id) ==
id)
return static_cast<int>(i);
531 if (normalized != normalized)
return;
532 const auto& spec = P::parameters[
static_cast<size_t>(index)];
533 shadow[
static_cast<size_t>(index)].store(normalized, std::memory_order_relaxed);
534 user.setParameter(index,
static_cast<float>(
toPlain(spec, normalized)));
540 user.setParameter(
static_cast<int>(i),
541 static_cast<float>(
toPlain(P::parameters[i],
542 shadow[i].load(std::memory_order_relaxed))));
578 if (
auto* h =
handler.exchange(
nullptr, std::memory_order_relaxed); h !=
nullptr)
579 reinterpret_cast<Steinberg_FUnknown*
>(h)->lpVtbl->release(h);
587 Steinberg_int32 flags = extraFlags;
590 const int now =
user.getLatency();
594 flags |= Steinberg_Vst_RestartFlags_kLatencyChanged;
602 return std::this_thread::get_id() ==
uiThread.load(std::memory_order_relaxed);
614 if (
auto* h =
handler.load(std::memory_order_relaxed); h !=
nullptr)
615 h->lpVtbl->restartComponent(h, flags);
625 const Steinberg_int32 flags =
pendingRestart.exchange(0, std::memory_order_acq_rel);
627 if (
auto* h =
handler.load(std::memory_order_relaxed); h !=
nullptr)
628 h->lpVtbl->restartComponent(h, flags);
640 uiThread.store(std::this_thread::get_id(), std::memory_order_relaxed);
662 Steinberg_int32 numSamples,
665 if (changes ==
nullptr)
return;
666 const Steinberg_int32 queues = changes->lpVtbl->getParameterCount(changes);
667 for (Steinberg_int32 q = 0; q < queues; ++q)
669 Steinberg_Vst_IParamValueQueue* queue =
670 changes->lpVtbl->getParameterData(changes, q);
671 if (queue ==
nullptr)
continue;
672 const Steinberg_int32 points = queue->lpVtbl->getPointCount(queue);
673 if (points <= 0)
continue;
674 const Steinberg_Vst_ParamID
id = queue->lpVtbl->getParameterId(queue);
675 const Steinberg_int32 first = sampleAccurateOf<P>() ? 0 : points - 1;
676 for (Steinberg_int32 i = first; i < points; ++i)
678 Steinberg_int32 offset = 0;
679 Steinberg_Vst_ParamValue value = 0.0;
680 if (queue->lpVtbl->getPoint(queue, i, &offset, &value)
681 != Steinberg_kResultOk)
683 if (offset < 0) offset = 0;
684 if (numSamples > 0 && offset >= numSamples) offset = numSamples - 1;
699 Steinberg_int32 numSamples,
704 if (list ==
nullptr)
return;
705 const Steinberg_int32 n = list->lpVtbl->getEventCount(list);
706 for (Steinberg_int32 i = 0; i < n; ++i)
708 Steinberg_Vst_Event raw {};
709 if (list->lpVtbl->getEvent(list, i, &raw) != Steinberg_kResultOk)
712 if (raw.type == Steinberg_Vst_Event_EventTypes_kNoteOnEvent)
715 midi.channel =
static_cast<uint8_t
>(
716 raw.Steinberg_Vst_Event_noteOn.channel & 0x0F);
717 midi.note =
static_cast<uint8_t
>(
718 raw.Steinberg_Vst_Event_noteOn.pitch & 0x7F);
719 midi.value = raw.Steinberg_Vst_Event_noteOn.velocity;
721 else if (raw.type == Steinberg_Vst_Event_EventTypes_kNoteOffEvent)
724 midi.channel =
static_cast<uint8_t
>(
725 raw.Steinberg_Vst_Event_noteOff.channel & 0x0F);
726 midi.note =
static_cast<uint8_t
>(
727 raw.Steinberg_Vst_Event_noteOff.pitch & 0x7F);
728 midi.value = raw.Steinberg_Vst_Event_noteOff.velocity;
730 else if (raw.type == Steinberg_Vst_Event_EventTypes_kPolyPressureEvent)
733 midi.channel =
static_cast<uint8_t
>(
734 raw.Steinberg_Vst_Event_polyPressure.channel & 0x0F);
735 midi.note =
static_cast<uint8_t
>(
736 raw.Steinberg_Vst_Event_polyPressure.pitch & 0x7F);
737 midi.value = raw.Steinberg_Vst_Event_polyPressure.pressure;
741 Steinberg_int32 offset = raw.sampleOffset;
742 if (offset < 0) offset = 0;
743 if (numSamples > 0 && offset >= numSamples) offset = numSamples - 1;
766 bool& programChanged)
noexcept
771 bypass.store(ev.value >= 0.5, std::memory_order_relaxed);
779 user.handleMidiEvent(midi);
789 programChanged =
true;
799 ev.value, std::memory_order_relaxed);
801 ev.offset - blockStart);
803 user.handleMidiEvent(midi);
819 const int channel =
static_cast<int>((
id >> 8) & 0x0Fu);
820 const uint32_t ctrl =
id & 0xFFu;
821 const int slot = ctrl == 1 ? 0 : (ctrl == 64 ? 1 : (ctrl == 128 ? 2 : 3));
822 return channel * 4 + slot;
829 static Steinberg_tresult SMTG_STDMETHODCALLTYPE
sInitialize(
void* self_,
830 Steinberg_FUnknown* context)
833 return Steinberg_kResultOk;
836 static Steinberg_tresult SMTG_STDMETHODCALLTYPE
sTerminate(
void* self_)
839 return Steinberg_kResultOk;
843 {
return Steinberg_kResultFalse; }
845 static Steinberg_tresult SMTG_STDMETHODCALLTYPE
sSetIoMode(
void*, Steinberg_Vst_IoMode)
846 {
return Steinberg_kResultOk; }
857 Steinberg_Vst_MediaType type, Steinberg_Vst_BusDirection dir)
859 if (type == Steinberg_Vst_MediaTypes_kEvent)
860 return (
HasMidi<P> && dir == Steinberg_Vst_BusDirections_kInput) ? 1 : 0;
861 if (type != Steinberg_Vst_MediaTypes_kAudio)
return 0;
862 return dir == Steinberg_Vst_BusDirections_kInput ?
numInputBuses() : 1;
865 static Steinberg_tresult SMTG_STDMETHODCALLTYPE
sGetBusInfo(
void* self_,
866 Steinberg_Vst_MediaType type, Steinberg_Vst_BusDirection dir,
867 Steinberg_int32 index, Steinberg_Vst_BusInfo* bus)
869 if (bus ==
nullptr)
return Steinberg_kInvalidArgument;
870 if (type == Steinberg_Vst_MediaTypes_kEvent)
872 if (!
HasMidi<P> || dir != Steinberg_Vst_BusDirections_kInput
874 return Steinberg_kInvalidArgument;
875 bus->mediaType = Steinberg_Vst_MediaTypes_kEvent;
876 bus->direction = dir;
877 bus->channelCount = 16;
878 bus->busType = Steinberg_Vst_BusTypes_kMain;
879 bus->flags = Steinberg_Vst_BusInfo_BusFlags_kDefaultActive;
881 return Steinberg_kResultOk;
883 const Steinberg_int32 count =
884 dir == Steinberg_Vst_BusDirections_kInput ?
numInputBuses() : 1;
885 if (type != Steinberg_Vst_MediaTypes_kAudio || index < 0 || index >= count)
886 return Steinberg_kInvalidArgument;
887 bus->mediaType = Steinberg_Vst_MediaTypes_kAudio;
888 bus->direction = dir;
890 bus->busType = index == 0 ? Steinberg_Vst_BusTypes_kMain
891 : Steinberg_Vst_BusTypes_kAux;
892 bus->flags = Steinberg_Vst_BusInfo_BusFlags_kDefaultActive;
894 : (dir == Steinberg_Vst_BusDirections_kInput ?
"Input"
897 return Steinberg_kResultOk;
901 Steinberg_Vst_RoutingInfo*, Steinberg_Vst_RoutingInfo*)
902 {
return Steinberg_kNotImplemented; }
905 Steinberg_Vst_MediaType, Steinberg_Vst_BusDirection, Steinberg_int32,
907 {
return Steinberg_kResultOk; }
909 static Steinberg_tresult SMTG_STDMETHODCALLTYPE
sSetActive(
void* self_, Steinberg_TBool state)
914 const int maxBlock = p->setup.maxSamplesPerBlock > 0
915 ? p->setup.maxSamplesPerBlock : 512;
916 const double sr = p->setup.sampleRate > 0.0 ? p->setup.sampleRate : 48000.0;
918 p->user.prepare(spec);
920 p->user.setOfflineRendering(
921 p->setup.processMode == Steinberg_Vst_ProcessModes_kOffline);
922 p->applyAllShadows();
923 p->dryL.assign(
static_cast<size_t>(maxBlock), 0.0f);
924 p->dryR.assign(
static_cast<size_t>(maxBlock), 0.0f);
926 p->silence.assign(
static_cast<size_t>(maxBlock), 0.0f);
927 p->bypassMix = p->bypass.load(std::memory_order_relaxed) ? 1.0f : 0.0f;
930 const int latency = p->user.getLatency();
931 p->cachedLatency.store(latency, std::memory_order_relaxed);
936 return Steinberg_kResultOk;
940 Steinberg_IBStream* state)
943 if (state ==
nullptr)
return Steinberg_kInvalidArgument;
945 std::vector<uint8_t> blob;
949 Steinberg_int32 got = 0;
950 if (state->lpVtbl->read(state, chunk,
static_cast<Steinberg_int32
>(
sizeof(chunk)),
951 &got) != Steinberg_kResultOk || got <= 0)
953 blob.insert(blob.end(), chunk, chunk + got);
954 if (blob.size() > (16u << 20))
return Steinberg_kResultFalse;
958 norm[i] = p->shadow[i].load(std::memory_order_relaxed);
960 int bypassState = -1;
961 if (!
applyState(p->user, blob.data(), blob.size(), norm, &program,
963 return Steinberg_kResultFalse;
965 p->applyNormalized(
static_cast<int>(i), norm[i]);
967 p->currentProgram.store(program, std::memory_order_relaxed);
968 if (bypassState >= 0)
969 p->bypass.store(bypassState != 0, std::memory_order_relaxed);
971 return Steinberg_kResultOk;
975 Steinberg_IBStream* state)
978 if (state ==
nullptr)
return Steinberg_kInvalidArgument;
981 norm[i] = p->shadow[i].load(std::memory_order_relaxed);
984 kNumPresets > 0 ? p->currentProgram.load(std::memory_order_relaxed) : -1,
985 p->bypass.load(std::memory_order_relaxed) ? 1 : 0);
986 return streamWrite(state, blob.data(),
static_cast<Steinberg_int32
>(blob.size()))
987 ? Steinberg_kResultOk : Steinberg_kResultFalse;
991 &sQuery<0>, &sAddRef<0>, &sRelease<0>,
1005 Steinberg_Vst_SpeakerArrangement* inputs, Steinberg_int32 numIns,
1006 Steinberg_Vst_SpeakerArrangement* outputs, Steinberg_int32 numOuts)
1009 || (numIns > 0 && inputs ==
nullptr) || outputs ==
nullptr)
1010 return Steinberg_kResultFalse;
1011 const Steinberg_Vst_SpeakerArrangement want = outputs[0];
1013 if (want == Steinberg_Vst_SpeakerArr_kMono) channels = 1;
1014 else if (want == Steinberg_Vst_SpeakerArr_kStereo) channels = 2;
1015 if (channels == 0 || !supportsChannelCount<P>(channels))
1016 return Steinberg_kResultFalse;
1017 for (Steinberg_int32 i = 0; i < numIns; ++i)
1018 if (inputs[i] != want)
1019 return Steinberg_kResultFalse;
1021 return Steinberg_kResultTrue;
1025 Steinberg_Vst_BusDirection dir, Steinberg_int32 index,
1026 Steinberg_Vst_SpeakerArrangement* arr)
1028 const Steinberg_int32 count =
1029 dir == Steinberg_Vst_BusDirections_kInput ?
numInputBuses() : 1;
1030 if (index < 0 || index >= count || arr ==
nullptr)
1031 return Steinberg_kInvalidArgument;
1033 ? Steinberg_Vst_SpeakerArr_kMono : Steinberg_Vst_SpeakerArr_kStereo;
1034 return Steinberg_kResultOk;
1038 Steinberg_int32 size)
1040 return size == Steinberg_Vst_SymbolicSampleSizes_kSample32
1041 ? Steinberg_kResultTrue : Steinberg_kResultFalse;
1046 return static_cast<Steinberg_uint32
>(
1051 Steinberg_Vst_ProcessSetup*
setup)
1053 if (
setup ==
nullptr)
return Steinberg_kInvalidArgument;
1055 return Steinberg_kResultOk;
1058 static Steinberg_tresult SMTG_STDMETHODCALLTYPE
sSetProcessing(
void*, Steinberg_TBool)
1059 {
return Steinberg_kResultOk; }
1066 if (ctx ==
nullptr)
return;
1068 const Steinberg_uint32 s = ctx->state;
1069 t.
playing = (s & Steinberg_Vst_ProcessContext_StatesAndFlags_kPlaying) != 0;
1070 t.recording = (s & Steinberg_Vst_ProcessContext_StatesAndFlags_kRecording) != 0;
1071 t.looping = (s & Steinberg_Vst_ProcessContext_StatesAndFlags_kCycleActive) != 0;
1072 if ((s & Steinberg_Vst_ProcessContext_StatesAndFlags_kTempoValid) != 0)
1074 t.tempoBpm = ctx->tempo;
1075 t.tempoValid =
true;
1077 if ((s & Steinberg_Vst_ProcessContext_StatesAndFlags_kProjectTimeMusicValid) != 0)
1079 t.ppqPosition = ctx->projectTimeMusic;
1080 t.positionValid =
true;
1082 if ((s & Steinberg_Vst_ProcessContext_StatesAndFlags_kBarPositionValid) != 0)
1083 t.barStartPpq = ctx->barPositionMusic;
1084 if ((s & Steinberg_Vst_ProcessContext_StatesAndFlags_kTimeSigValid) != 0)
1086 t.timeSigNumerator = ctx->timeSigNumerator;
1087 t.timeSigDenominator = ctx->timeSigDenominator;
1088 t.timeSigValid =
true;
1090 if ((s & Steinberg_Vst_ProcessContext_StatesAndFlags_kCycleValid) != 0)
1092 t.loopStartPpq = ctx->cycleStartMusic;
1093 t.loopEndPpq = ctx->cycleEndMusic;
1096 user.setTransport(t);
1102 static Steinberg_tresult SMTG_STDMETHODCALLTYPE
sProcess(
void* self_,
1103 Steinberg_Vst_ProcessData* data)
1106 if (data ==
nullptr)
return Steinberg_kInvalidArgument;
1112 const Steinberg_int32 n = data->numSamples;
1117 p->collectParameterChanges(data->inputParameterChanges, n, events, eventCount);
1118 p->collectInputEvents(data->inputEvents, n, events, eventCount);
1121 bool paramsChanged =
false;
1122 bool programChanged =
false;
1124 const bool canRender = n > 0
1125 && data->symbolicSampleSize == Steinberg_Vst_SymbolicSampleSizes_kSample32
1126 && data->numOutputs >= 1 && data->outputs !=
nullptr && p->prepared;
1131 for (
int i = 0; i < eventCount; ++i)
1132 paramsChanged |= p->applyBlockEvent(events[i], events[i].
offset,
1134 if (paramsChanged || programChanged)
1135 p->refreshLatency(programChanged
1136 ? Steinberg_Vst_RestartFlags_kParamValuesChanged : 0);
1137 if (n <= 0)
return Steinberg_kResultOk;
1138 return data->symbolicSampleSize
1139 == Steinberg_Vst_SymbolicSampleSizes_kSample32
1140 ? Steinberg_kResultOk : Steinberg_kResultFalse;
1143 p->forwardTransport(data->processContext);
1145 auto& outBus = data->outputs[0];
1146 float** out = outBus.Steinberg_Vst_AudioBusBuffers_channelBuffers32;
1147 if (out ==
nullptr || outBus.numChannels < 1)
return Steinberg_kResultOk;
1148 const int width = p->currentChannels;
1149 int nCh = outBus.numChannels < width ? outBus.numChannels : width;
1150 while (nCh > 0 && out[nCh - 1] ==
nullptr) --nCh;
1151 if (nCh < 1)
return Steinberg_kResultOk;
1154 && data->numInputs >= 1 && data->inputs !=
nullptr
1155 && data->inputs[0].Steinberg_Vst_AudioBusBuffers_channelBuffers32 !=
nullptr
1156 && data->inputs[0].numChannels >= 1;
1158 ? data->inputs[0].Steinberg_Vst_AudioBusBuffers_channelBuffers32 :
nullptr;
1159 const int inCh = haveIn ?
static_cast<int>(data->inputs[0].numChannels) : 0;
1163 float* scHost[2] = {
nullptr,
nullptr };
1166 if (data->numInputs >= 2 && data->inputs !=
nullptr
1167 && data->inputs[1].Steinberg_Vst_AudioBusBuffers_channelBuffers32
1170 const auto& scBus = data->inputs[1];
1171 float** sc = scBus.Steinberg_Vst_AudioBusBuffers_channelBuffers32;
1172 const int scCh = scBus.numChannels < 2 ? scBus.numChannels : 2;
1173 for (
int ch = 0; ch < scCh; ++ch)
1174 scHost[ch] = sc[ch];
1183 const int chunkMax =
static_cast<int>(p->dryL.size());
1184 if (chunkMax < 1)
return Steinberg_kResultOk;
1185 float* dry[2] = { p->dryL.data(), p->dryR.data() };
1188 for (
int c0 = 0; c0 < n; c0 += chunkMax)
1190 const int len = n - c0 < chunkMax ? n - c0 : chunkMax;
1191 const int cEnd = c0 + len;
1199 const size_t bytes =
sizeof(float) *
static_cast<size_t>(len);
1200 for (
int ch = 0; ch < nCh; ++ch)
1204 std::memset(out[ch] + c0, 0, bytes);
1207 const float* src = out[ch];
1210 const float* s = in[ch < inCh ? ch : inCh - 1];
1211 if (s !=
nullptr) src = s;
1213 std::memcpy(dry[ch], src + c0, bytes);
1215 std::memcpy(out[ch] + c0, src + c0, bytes);
1218 p->dryDelay.process(dry, nCh, len,
1219 p->cachedLatency.load(std::memory_order_relaxed));
1221 float* scChunk[2] = {
nullptr,
nullptr };
1223 for (
int ch = 0; ch < 2; ++ch)
1224 scChunk[ch] = scHost[ch] !=
nullptr ? scHost[ch] + c0
1225 : p->silence.data();
1230 auto processSegment = [&](
int start,
int length)
noexcept {
1231 float* sub[2] = { out[0] + start,
1232 nCh > 1 ? out[1] + start : out[0] + start };
1237 float* scSub[2] = { scChunk[0] + (start - c0),
1238 scChunk[1] + (start - c0) };
1240 p->user.processBlock(view, scView);
1243 p->user.processBlock(view);
1246 if (!sampleAccurateOf<P>())
1248 for (; evIdx < eventCount && events[evIdx].
offset < cEnd; ++evIdx)
1249 paramsChanged |= p->applyBlockEvent(events[evIdx], c0, programChanged);
1250 processSegment(c0, len);
1257 while (evIdx < eventCount
1260 paramsChanged |= p->applyBlockEvent(events[evIdx++], pos,
1263 if (evIdx < eventCount)
1267 if (snapped < next) next = snapped;
1271 processSegment(pos, next - pos);
1278 const float target = p->bypass.load(std::memory_order_relaxed) ? 1.0f : 0.0f;
1279 if (p->bypassMix != target || target > 0.0f)
1282 float mix = p->bypassMix;
1283 for (
int i = 0; i < len; ++i)
1285 mix += (target > mix) ? step : ((target < mix) ? -step : 0.0f);
1286 mix = mix < 0.0f ? 0.0f : (mix > 1.0f ? 1.0f : mix);
1287 for (
int ch = 0; ch < nCh; ++ch)
1288 out[ch][c0 + i] += (dry[ch][i] - out[ch][c0 + i]) * mix;
1293 for (; evIdx < eventCount; ++evIdx)
1294 paramsChanged |= p->applyBlockEvent(events[evIdx], n, programChanged);
1296 if (paramsChanged || programChanged)
1297 p->refreshLatency(programChanged
1298 ? Steinberg_Vst_RestartFlags_kParamValuesChanged : 0);
1300 outBus.silenceFlags = 0;
1301 return Steinberg_kResultOk;
1309 const double seconds = p->user.getTailSeconds();
1310 if (!(seconds > 0.0))
return 0;
1311 const double samples = seconds
1312 * (p->setup.sampleRate > 0 ? p->setup.sampleRate : 48000.0);
1315 if (samples >= 4294967295.0)
return 0xFFFFFFFFu;
1316 return static_cast<Steinberg_uint32
>(samples);
1326 &sQuery<1>, &sAddRef<1>, &sRelease<1>,
1337 Steinberg_FUnknown* context)
1340 return Steinberg_kResultOk;
1346 return Steinberg_kResultOk;
1350 Steinberg_IBStream* state)
1356 static Steinberg_tresult SMTG_STDMETHODCALLTYPE
sCtrlSetState(
void*, Steinberg_IBStream*)
1357 {
return Steinberg_kResultOk; }
1359 static Steinberg_tresult SMTG_STDMETHODCALLTYPE
sCtrlGetState(
void*, Steinberg_IBStream*)
1360 {
return Steinberg_kResultOk; }
1377 Steinberg_int32 index, Steinberg_Vst_ParameterInfo* info)
1381 return Steinberg_kInvalidArgument;
1382 std::memset(info, 0,
sizeof(*info));
1388 info->stepCount = 1;
1389 info->defaultNormalizedValue = 0.0;
1390 info->flags = Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate
1391 | Steinberg_Vst_ParameterInfo_ParameterFlags_kIsBypass;
1392 return Steinberg_kResultOk;
1400 info->defaultNormalizedValue = 0.0;
1401 info->unitId = Steinberg_Vst_kRootUnitId;
1402 info->flags = Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate
1403 | Steinberg_Vst_ParameterInfo_ParameterFlags_kIsList
1404 | Steinberg_Vst_ParameterInfo_ParameterFlags_kIsProgramChange;
1405 return Steinberg_kResultOk;
1412 const int channel = slot / 4;
1417 info->stepCount = 0;
1418 info->defaultNormalizedValue = ctrl == 129 ? 0.5 : 0.0;
1419 info->flags = Steinberg_Vst_ParameterInfo_ParameterFlags_kIsHidden;
1420 return Steinberg_kResultOk;
1422 const auto& spec = P::parameters[
static_cast<size_t>(index)];
1423 info->id =
hash32(spec.id);
1427 info->stepCount = spec.steps;
1428 info->defaultNormalizedValue =
toNormalized(spec, spec.defValue);
1429 info->flags = Steinberg_Vst_ParameterInfo_ParameterFlags_kCanAutomate;
1430 if (spec.labels !=
nullptr)
1431 info->flags |= Steinberg_Vst_ParameterInfo_ParameterFlags_kIsList;
1432 return Steinberg_kResultOk;
1436 Steinberg_Vst_ParamID
id, Steinberg_Vst_ParamValue normalized,
1437 Steinberg_Vst_String128 out)
1440 if (out ==
nullptr)
return Steinberg_kInvalidArgument;
1443 std::snprintf(text,
sizeof(text),
"%s", normalized >= 0.5 ?
"On" :
"Off");
1449 std::snprintf(text,
sizeof(text),
"%s",
1450 P::factoryPresets[
static_cast<size_t>(idx)].name);
1454 std::snprintf(text,
sizeof(text),
"%.3f", normalized);
1458 if (idx < 0)
return Steinberg_kInvalidArgument;
1459 const auto& spec = P::parameters[
static_cast<size_t>(idx)];
1463 return Steinberg_kResultOk;
1467 Steinberg_Vst_ParamID
id, Steinberg_Vst_TChar*
string,
1468 Steinberg_Vst_ParamValue* normalized)
1471 if (
string ==
nullptr || normalized ==
nullptr)
return Steinberg_kInvalidArgument;
1474 for (; i < 63 &&
string[i] != 0; ++i)
1475 ascii[i] =
static_cast<char>(
string[i]);
1481 : (std::strtod(ascii,
nullptr) >= 0.5 ? 1.0 : 0.0);
1482 return Steinberg_kResultOk;
1488 for (
int presetIdx = 0; presetIdx <
kNumPresets; ++presetIdx)
1489 if (std::strcmp(ascii,
1490 P::factoryPresets[
static_cast<size_t>(presetIdx)].name)
1494 ?
static_cast<double>(presetIdx) / (
kNumPresets - 1)
1496 return Steinberg_kResultOk;
1499 const double v = std::strtod(ascii,
nullptr);
1501 return Steinberg_kResultOk;
1505 const double v = std::strtod(ascii,
nullptr);
1506 *normalized = v < 0.0 ? 0.0 : (v > 1.0 ? 1.0 : v);
1507 return Steinberg_kResultOk;
1510 if (idx < 0)
return Steinberg_kInvalidArgument;
1511 const Param& spec = P::parameters[
static_cast<size_t>(idx)];
1513 if (!
parseValue(spec, ascii, plain))
return Steinberg_kResultFalse;
1515 return Steinberg_kResultOk;
1519 Steinberg_Vst_ParamID
id, Steinberg_Vst_ParamValue normalized)
1526 return idx < 0 ? 0.0 :
toPlain(P::parameters[
static_cast<size_t>(idx)], normalized);
1530 Steinberg_Vst_ParamID
id, Steinberg_Vst_ParamValue plain)
1537 return idx < 0 ? 0.0 :
toNormalized(P::parameters[
static_cast<size_t>(idx)], plain);
1541 Steinberg_Vst_ParamID
id)
1544 p->deliverPendingRestart();
1546 return p->bypass.load(std::memory_order_relaxed) ? 1.0 : 0.0;
1549 ?
static_cast<double>(p->currentProgram.load(std::memory_order_relaxed))
1555 return p->midiProxyShadow[
static_cast<size_t>(
midiProxySlot(
id))]
1556 .load(std::memory_order_relaxed);
1559 return idx < 0 ? 0.0
1560 : p->shadow[
static_cast<size_t>(idx)].load(std::memory_order_relaxed);
1564 Steinberg_Vst_ParamID
id, Steinberg_Vst_ParamValue value)
1569 p->bypass.store(value >= 0.5, std::memory_order_relaxed);
1570 return Steinberg_kResultOk;
1577 p->refreshLatency(Steinberg_Vst_RestartFlags_kParamValuesChanged);
1578 return Steinberg_kResultOk;
1587 .store(value, std::memory_order_relaxed);
1588 return Steinberg_kResultOk;
1592 if (idx < 0)
return Steinberg_kInvalidArgument;
1593 p->applyNormalized(idx, value);
1594 p->refreshLatency();
1595 return Steinberg_kResultOk;
1599 Steinberg_Vst_IComponentHandler*
handler)
1605 reinterpret_cast<Steinberg_FUnknown*
>(
handler)->lpVtbl->addRef(
handler);
1606 auto* old = p->handler.exchange(
handler, std::memory_order_relaxed);
1608 reinterpret_cast<Steinberg_FUnknown*
>(old)->lpVtbl->release(old);
1609 p->deliverPendingRestart();
1610 return Steinberg_kResultOk;
1613 static Steinberg_IPlugView* SMTG_STDMETHODCALLTYPE
sCreateView(
void* self_,
1614 Steinberg_FIDString name);
1617 &sQuery<2>, &sAddRef<2>, &sRelease<2>,
1634 return Steinberg_Vst_IProcessContextRequirements_Flags_kNeedTempo
1635 | Steinberg_Vst_IProcessContextRequirements_Flags_kNeedTransportState
1636 | Steinberg_Vst_IProcessContextRequirements_Flags_kNeedProjectTimeMusic
1637 | Steinberg_Vst_IProcessContextRequirements_Flags_kNeedBarPositionMusic
1638 | Steinberg_Vst_IProcessContextRequirements_Flags_kNeedTimeSignature
1639 | Steinberg_Vst_IProcessContextRequirements_Flags_kNeedCycleMusic;
1645 &sQuery<3>, &sAddRef<3>, &sRelease<3>,
1654 static Steinberg_int32 SMTG_STDMETHODCALLTYPE
sGetUnitCount(
void*) {
return 1; }
1657 Steinberg_int32 unitIndex, Steinberg_Vst_UnitInfo* info)
1659 if (unitIndex != 0 || info ==
nullptr)
return Steinberg_kInvalidArgument;
1660 std::memset(info, 0,
sizeof(*info));
1661 info->id = Steinberg_Vst_kRootUnitId;
1662 info->parentUnitId = Steinberg_Vst_kNoParentUnitId;
1664 : Steinberg_Vst_kNoProgramListId;
1666 return Steinberg_kResultOk;
1675 Steinberg_int32 listIndex, Steinberg_Vst_ProgramListInfo* info)
1677 if (listIndex != 0 || info ==
nullptr ||
kNumPresets == 0)
1678 return Steinberg_kInvalidArgument;
1679 std::memset(info, 0,
sizeof(*info));
1683 return Steinberg_kResultOk;
1687 Steinberg_Vst_ProgramListID listId, Steinberg_int32 programIndex,
1688 Steinberg_Vst_String128 name)
1693 || programIndex < 0 || programIndex >=
kNumPresets)
1694 return Steinberg_kInvalidArgument;
1696 P::factoryPresets[
static_cast<size_t>(programIndex)].name, name);
1697 return Steinberg_kResultOk;
1702 (void) programIndex;
1704 return Steinberg_kInvalidArgument;
1709 Steinberg_Vst_ProgramListID, Steinberg_int32, Steinberg_Vst_CString,
1710 Steinberg_Vst_String128)
1711 {
return Steinberg_kResultFalse; }
1714 Steinberg_Vst_ProgramListID, Steinberg_int32)
1715 {
return Steinberg_kResultFalse; }
1718 Steinberg_Vst_ProgramListID, Steinberg_int32, Steinberg_int16,
1719 Steinberg_Vst_String128)
1720 {
return Steinberg_kResultFalse; }
1723 {
return Steinberg_Vst_kRootUnitId; }
1726 Steinberg_Vst_UnitID unitId)
1727 {
return unitId == Steinberg_Vst_kRootUnitId ? Steinberg_kResultOk
1728 : Steinberg_kInvalidArgument; }
1731 Steinberg_Vst_MediaType, Steinberg_Vst_BusDirection, Steinberg_int32,
1732 Steinberg_int32, Steinberg_Vst_UnitID* unitId)
1734 if (unitId ==
nullptr)
return Steinberg_kInvalidArgument;
1735 *unitId = Steinberg_Vst_kRootUnitId;
1736 return Steinberg_kResultOk;
1740 Steinberg_int32, Steinberg_int32,
struct Steinberg_IBStream*)
1741 {
return Steinberg_kNotImplemented; }
1743 inline static const Steinberg_Vst_IUnitInfoVtbl
kUnitVtbl = {
1744 &sQuery<4>, &sAddRef<4>, &sRelease<4>,
1757 Steinberg_int32 busIndex, Steinberg_int16 channel,
1758 Steinberg_Vst_CtrlNumber midiControllerNumber, Steinberg_Vst_ParamID*
id)
1760 if (!
HasMidi<P> ||
id ==
nullptr || busIndex != 0
1761 || channel < 0 || channel >= 16)
1762 return Steinberg_kResultFalse;
1763 const int ctrl = midiControllerNumber;
1764 if (ctrl != 1 && ctrl != 64 && ctrl != 128 && ctrl != 129)
1765 return Steinberg_kResultFalse;
1767 return Steinberg_kResultTrue;
1771 &sQuery<5>, &sAddRef<5>, &sRelease<5>,
1782#if defined(DSPARK_PLUGIN_WEBVIEW)
1789template <
typename P>
1792 static constexpr size_t kNumParams = Plugin<P>::kNumParams;
1796 const Steinberg_IPlugViewVtbl* viewVtbl;
1797 const Steinberg_IPlugViewContentScaleSupportVtbl* scaleVtbl;
1798#if defined(__linux__)
1799 const Steinberg_Linux_ITimerHandlerVtbl* timerVtbl;
1802 std::atomic<Steinberg_uint32> refs { 1 };
1804 Steinberg_IPlugFrame* frame =
nullptr;
1805 webview_ui::Editor<P> editor;
1809 bool correctingSize =
false;
1810 bool editActive[kNumParams == 0 ? 1 : kNumParams] {};
1811#if defined(__linux__)
1812 Steinberg_Linux_IRunLoop* runLoop =
nullptr;
1813 bool timerRegistered =
false;
1816 explicit View(Plugin<P>* plugin) noexcept : owner(plugin)
1818 viewVtbl = &kViewVtbl;
1819 scaleVtbl = &kScaleVtbl;
1820#if defined(__linux__)
1821 timerVtbl = &kTimerVtbl;
1823 owner->addRefImpl();
1824 const EditorSize logical = editorSizeOf<P>();
1825 width = logical.width;
1826 height = logical.height;
1831#if defined(__linux__)
1835 if (frame !=
nullptr)
1836 reinterpret_cast<Steinberg_FUnknown*
>(frame)->lpVtbl->release(frame);
1837 owner->releaseImpl();
1840#if defined(__linux__)
1844 void acquireRunLoop() noexcept
1846 if (frame ==
nullptr || runLoop !=
nullptr)
return;
1847 void* loop =
nullptr;
1848 if (
reinterpret_cast<Steinberg_FUnknown*
>(frame)->lpVtbl->queryInterface(
1849 frame, Steinberg_Linux_IRunLoop_iid, &loop) == Steinberg_kResultOk)
1850 runLoop =
static_cast<Steinberg_Linux_IRunLoop*
>(loop);
1853 void startTimer() noexcept
1856 if (runLoop !=
nullptr && !timerRegistered && editor.created())
1857 timerRegistered = runLoop->lpVtbl->registerTimer(runLoop,
1858 reinterpret_cast<Steinberg_Linux_ITimerHandler*
>(lensPtr(2)),
1859 33) == Steinberg_kResultOk;
1862 void stopTimer() noexcept
1864 if (runLoop !=
nullptr && timerRegistered)
1865 runLoop->lpVtbl->unregisterTimer(runLoop,
1866 reinterpret_cast<Steinberg_Linux_ITimerHandler*
>(lensPtr(2)));
1867 timerRegistered =
false;
1870 void releaseRunLoop() noexcept
1873 if (runLoop !=
nullptr)
1875 reinterpret_cast<Steinberg_FUnknown*
>(runLoop)->lpVtbl->release(runLoop);
1880 static void SMTG_STDMETHODCALLTYPE sOnTimer(
void* self_)
1882 View* view = fromLens(self_, 2);
1883 view->editor.pump();
1884 view->owner->deliverPendingRestart();
1889 static View* fromLens(
void* iface,
int lens)
noexcept
1891 return reinterpret_cast<View*
>(
static_cast<char*
>(iface)
1892 -
static_cast<ptrdiff_t
>(lens) *
sizeof(
void*));
1895 void* lensPtr(
int lens)
noexcept
1897 return reinterpret_cast<char*
>(
this) +
static_cast<ptrdiff_t
>(lens) *
sizeof(
void*);
1900 static const char* platformType() noexcept
1903 return Steinberg_kPlatformTypeHWND;
1904#elif defined(__APPLE__)
1905 return Steinberg_kPlatformTypeNSView;
1907 return Steinberg_kPlatformTypeX11EmbedWindowID;
1913 static void cbSetParam(
void* context,
int index,
double plain)
noexcept
1915 auto* view =
static_cast<View*
>(context);
1916 const Param& spec = P::parameters[
static_cast<size_t>(index)];
1918 view->owner->applyNormalized(index, normalized);
1919 if (
auto* handler = view->owner->handler.load(std::memory_order_relaxed))
1921 const Steinberg_Vst_ParamID
id =
hash32(spec.id);
1922 if (view->editActive[
static_cast<size_t>(index)])
1923 handler->lpVtbl->performEdit(handler,
id, normalized);
1927 handler->lpVtbl->beginEdit(handler,
id);
1928 handler->lpVtbl->performEdit(handler,
id, normalized);
1929 handler->lpVtbl->endEdit(handler,
id);
1934 static void cbBeginEdit(
void* context,
int index)
noexcept
1936 auto* view =
static_cast<View*
>(context);
1937 view->editActive[
static_cast<size_t>(index)] =
true;
1938 if (
auto* handler = view->owner->handler.load(std::memory_order_relaxed))
1939 handler->lpVtbl->beginEdit(handler,
1940 hash32(P::parameters[
static_cast<size_t>(index)].id));
1943 static void cbEndEdit(
void* context,
int index)
noexcept
1945 auto* view =
static_cast<View*
>(context);
1946 view->editActive[
static_cast<size_t>(index)] =
false;
1947 if (
auto* handler = view->owner->handler.load(std::memory_order_relaxed))
1948 handler->lpVtbl->endEdit(handler,
1949 hash32(P::parameters[
static_cast<size_t>(index)].id));
1954 Steinberg_tresult query(
const Steinberg_TUID iid,
void** obj)
noexcept
1956 if (obj ==
nullptr)
return Steinberg_kInvalidArgument;
1958 if (
tuidEqual(iid, Steinberg_FUnknown_iid)
1959 ||
tuidEqual(iid, Steinberg_IPlugView_iid))
1961 else if (
tuidEqual(iid, Steinberg_IPlugViewContentScaleSupport_iid))
1963#if defined(__linux__)
1964 else if (
tuidEqual(iid, Steinberg_Linux_ITimerHandler_iid))
1967 if (*obj ==
nullptr)
return Steinberg_kNoInterface;
1968 refs.fetch_add(1, std::memory_order_relaxed);
1969 return Steinberg_kResultOk;
1973 static Steinberg_tresult SMTG_STDMETHODCALLTYPE sQuery(
void* self_,
1974 const Steinberg_TUID iid,
1976 {
return fromLens(self_, Lens)->query(iid, obj); }
1979 static Steinberg_uint32 SMTG_STDMETHODCALLTYPE sAddRef(
void* self_)
1980 {
return fromLens(self_, Lens)->refs.fetch_add(1, std::memory_order_relaxed) + 1; }
1983 static Steinberg_uint32 SMTG_STDMETHODCALLTYPE sRelease(
void* self_)
1985 auto* view = fromLens(self_, Lens);
1986 const Steinberg_uint32 left = view->refs.fetch_sub(1, std::memory_order_acq_rel) - 1;
1987 if (left == 0)
delete view;
1993 static Steinberg_tresult SMTG_STDMETHODCALLTYPE sIsPlatformTypeSupported(
void*,
1994 Steinberg_FIDString type)
1996 return (webview_ui::Editor<P>::available() && type !=
nullptr
1997 && std::strcmp(type, platformType()) == 0)
1998 ? Steinberg_kResultTrue : Steinberg_kResultFalse;
2001 static Steinberg_tresult SMTG_STDMETHODCALLTYPE sAttached(
void* self_,
void* parent,
2002 Steinberg_FIDString type)
2004 auto* view = fromLens(self_, 0);
2005 webview_ui::debugLog(
"vst3 attached(parent=%p type=%s) negotiated=%dx%d scale=%.2f",
2006 parent, type !=
nullptr ? type :
"?",
2007 view->width, view->height, view->scale);
2008 if (sIsPlatformTypeSupported(self_, type) != Steinberg_kResultTrue
2009 || parent ==
nullptr)
2010 return Steinberg_kResultFalse;
2011 const webview_ui::HostCallbacks callbacks {
2012 view, &cbSetParam, &cbBeginEdit, &cbEndEdit
2014 if (!view->editor.create(parent, view->owner->shadow, callbacks))
2015 return Steinberg_kResultFalse;
2019 int parentW = 0, parentH = 0;
2020 if (view->editor.queryParentSize(parentW, parentH))
2022 view->width = parentW;
2023 view->height = parentH;
2025 view->editor.setBounds(view->width, view->height);
2026 view->editor.setVisible(
true);
2027#if defined(__linux__)
2031 if (!view->timerRegistered)
2033 webview_ui::debugLog(
"vst3 attached: no usable IRunLoop -> no editor");
2034 view->editor.destroy();
2035 return Steinberg_kResultFalse;
2038 return Steinberg_kResultOk;
2041 static Steinberg_tresult SMTG_STDMETHODCALLTYPE sRemoved(
void* self_)
2043 auto* view = fromLens(self_, 0);
2044#if defined(__linux__)
2047 view->editor.destroy();
2048 return Steinberg_kResultOk;
2051 static Steinberg_tresult SMTG_STDMETHODCALLTYPE sOnWheel(
void*,
float)
2052 {
return Steinberg_kResultFalse; }
2054 static Steinberg_tresult SMTG_STDMETHODCALLTYPE sOnKeyDown(
void*, Steinberg_char16,
2055 Steinberg_int16, Steinberg_int16)
2056 {
return Steinberg_kResultFalse; }
2058 static Steinberg_tresult SMTG_STDMETHODCALLTYPE sOnKeyUp(
void*, Steinberg_char16,
2059 Steinberg_int16, Steinberg_int16)
2060 {
return Steinberg_kResultFalse; }
2062 static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetSize(
void* self_,
2063 Steinberg_ViewRect* size)
2065 auto* view = fromLens(self_, 0);
2066 if (size ==
nullptr)
return Steinberg_kInvalidArgument;
2069 size->right = view->width;
2070 size->bottom = view->height;
2071 webview_ui::debugLog(
"vst3 getSize -> %dx%d", view->width, view->height);
2072 return Steinberg_kResultOk;
2075 static Steinberg_tresult SMTG_STDMETHODCALLTYPE sOnSize(
void* self_,
2076 Steinberg_ViewRect* newSize)
2078 auto* view = fromLens(self_, 0);
2079 if (newSize ==
nullptr)
return Steinberg_kInvalidArgument;
2080 const double proposedW = newSize->right - newSize->left;
2081 const double proposedH = newSize->bottom - newSize->top;
2082 double w = proposedW;
2083 double h = proposedH;
2087 if (!view->correctingSize)
2088 constrainEditorSize<P>(w, h, view->scale);
2089 webview_ui::debugLog(
"vst3 onSize %.0fx%.0f -> %.0fx%.0f%s",
2090 proposedW, proposedH, w, h,
2091 view->correctingSize ?
" (correction round-trip)" :
"");
2092 view->width =
toBoundedInt(w + 0.5, 1,
static_cast<int>(kEditorMaxPixels));
2093 view->height =
toBoundedInt(h + 0.5, 1,
static_cast<int>(kEditorMaxPixels));
2094 view->editor.setBounds(view->width, view->height);
2095 if (!view->correctingSize && view->frame !=
nullptr
2096 && (view->width - proposedW > 1.0 || proposedW - view->width > 1.0
2097 || view->height - proposedH > 1.0 || proposedH - view->height > 1.0))
2099 view->correctingSize =
true;
2100 Steinberg_ViewRect corrected { 0, 0, view->width, view->height };
2101 view->frame->lpVtbl->resizeView(view->frame,
2102 reinterpret_cast<Steinberg_IPlugView*
>(view->lensPtr(0)), &corrected);
2103 view->correctingSize =
false;
2105 return Steinberg_kResultOk;
2108 static Steinberg_tresult SMTG_STDMETHODCALLTYPE sOnFocus(
void*, Steinberg_TBool)
2109 {
return Steinberg_kResultOk; }
2111 static Steinberg_tresult SMTG_STDMETHODCALLTYPE sSetFrame(
void* self_,
2112 Steinberg_IPlugFrame* frame)
2114 auto* view = fromLens(self_, 0);
2115#if defined(__linux__)
2116 view->releaseRunLoop();
2118 if (view->frame !=
nullptr)
2119 reinterpret_cast<Steinberg_FUnknown*
>(view->frame)
2120 ->lpVtbl->release(view->frame);
2121 view->frame = frame;
2122 if (view->frame !=
nullptr)
2123 reinterpret_cast<Steinberg_FUnknown*
>(view->frame)
2124 ->lpVtbl->addRef(view->frame);
2125#if defined(__linux__)
2126 if (view->editor.created())
2129 return Steinberg_kResultOk;
2132 static Steinberg_tresult SMTG_STDMETHODCALLTYPE sCanResize(
void*)
2134 return editorResizeOf<P>() != EditorResize::Fixed ? Steinberg_kResultTrue
2135 : Steinberg_kResultFalse;
2138 static Steinberg_tresult SMTG_STDMETHODCALLTYPE sCheckSizeConstraint(
void* self_,
2139 Steinberg_ViewRect* rect)
2141 auto* view = fromLens(self_, 0);
2142 if (rect ==
nullptr)
return Steinberg_kInvalidArgument;
2143 const double proposedW = rect->right - rect->left;
2144 const double proposedH = rect->bottom - rect->top;
2145 double w = proposedW;
2146 double h = proposedH;
2147 constrainEditorSize<P>(w, h, view->scale);
2148 rect->right = rect->left
2149 +
toBoundedInt(w + 0.5, 1,
static_cast<int>(kEditorMaxPixels));
2150 rect->bottom = rect->top
2151 +
toBoundedInt(h + 0.5, 1,
static_cast<int>(kEditorMaxPixels));
2152 webview_ui::debugLog(
"vst3 checkSizeConstraint %.0fx%.0f -> %.0fx%.0f",
2153 proposedW, proposedH, w, h);
2154 return Steinberg_kResultTrue;
2157 inline static const Steinberg_IPlugViewVtbl kViewVtbl = {
2158 &sQuery<0>, &sAddRef<0>, &sRelease<0>,
2159 &sIsPlatformTypeSupported, &sAttached, &sRemoved,
2160 &sOnWheel, &sOnKeyDown, &sOnKeyUp,
2161 &sGetSize, &sOnSize, &sOnFocus,
2162 &sSetFrame, &sCanResize, &sCheckSizeConstraint
2167 static Steinberg_tresult SMTG_STDMETHODCALLTYPE sSetContentScaleFactor(
void* self_,
2168 Steinberg_IPlugViewContentScaleSupport_ScaleFactor factor)
2170 auto* view = fromLens(self_, 1);
2171 if (!(factor > 0.0f))
return Steinberg_kInvalidArgument;
2172 const double previous = view->scale;
2173 view->scale = factor;
2174 webview_ui::debugLog(
"vst3 setContentScaleFactor %.2f (was %.2f)",
2175 view->scale, previous);
2178 view->width =
toBoundedInt(view->width * (view->scale / previous) + 0.5,
2179 1,
static_cast<int>(kEditorMaxPixels));
2180 view->height =
toBoundedInt(view->height * (view->scale / previous) + 0.5,
2181 1,
static_cast<int>(kEditorMaxPixels));
2182 if (view->editor.created() && view->frame !=
nullptr)
2186 Steinberg_ViewRect rect { 0, 0, view->width, view->height };
2187 view->frame->lpVtbl->resizeView(view->frame,
2188 reinterpret_cast<Steinberg_IPlugView*
>(view->lensPtr(0)), &rect);
2190 return Steinberg_kResultOk;
2193 inline static const Steinberg_IPlugViewContentScaleSupportVtbl kScaleVtbl = {
2194 &sQuery<1>, &sAddRef<1>, &sRelease<1>,
2195 &sSetContentScaleFactor
2198#if defined(__linux__)
2201 inline static const Steinberg_Linux_ITimerHandlerVtbl kTimerVtbl = {
2202 &sQuery<2>, &sAddRef<2>, &sRelease<2>, &sOnTimer
2209template <
typename P>
2211 Steinberg_FIDString name)
2215#if defined(DSPARK_PLUGIN_WEBVIEW)
2219 && std::strcmp(name,
"editor") == 0)
2221 auto* view =
new (std::nothrow) View<P>(fromLens(self_, 2));
2222 if (view !=
nullptr)
2223 return reinterpret_cast<Steinberg_IPlugView*
>(view->lensPtr(0));
2232template <
typename P>
2235 const Steinberg_IPluginFactory3Vtbl*
vtbl;
2241 return makeUid(P::descriptor.productId, 0x56535433ull);
2244 static Steinberg_tresult SMTG_STDMETHODCALLTYPE
sQuery(
void* self_,
2245 const Steinberg_TUID iid,
void** obj)
2247 if (obj ==
nullptr)
return Steinberg_kInvalidArgument;
2248 if (
tuidEqual(iid, Steinberg_FUnknown_iid)
2249 ||
tuidEqual(iid, Steinberg_IPluginFactory_iid)
2250 ||
tuidEqual(iid, Steinberg_IPluginFactory2_iid)
2251 ||
tuidEqual(iid, Steinberg_IPluginFactory3_iid))
2254 return Steinberg_kResultOk;
2257 return Steinberg_kNoInterface;
2260 static Steinberg_uint32 SMTG_STDMETHODCALLTYPE
sAddRef(
void*) {
return 100; }
2261 static Steinberg_uint32 SMTG_STDMETHODCALLTYPE
sRelease(
void*) {
return 100; }
2264 Steinberg_PFactoryInfo* info)
2266 if (info ==
nullptr)
return Steinberg_kInvalidArgument;
2267 std::memset(info, 0,
sizeof(*info));
2268 copyAscii(P::descriptor.vendor, info->vendor,
sizeof(info->vendor));
2269 copyAscii(P::descriptor.url, info->url,
sizeof(info->url));
2270 copyAscii(P::descriptor.email, info->email,
sizeof(info->email));
2271 info->flags = Steinberg_PFactoryInfo_FactoryFlags_kUnicode;
2272 return Steinberg_kResultOk;
2275 static Steinberg_int32 SMTG_STDMETHODCALLTYPE
sCountClasses(
void*) {
return 1; }
2280 std::memcpy(cid, uid.data(), 16);
2284 Steinberg_int32 index, Steinberg_PClassInfo* info)
2286 if (index != 0 || info ==
nullptr)
return Steinberg_kInvalidArgument;
2287 std::memset(info, 0,
sizeof(*info));
2289 info->cardinality = 0x7FFFFFFF;
2290 copyAscii(
"Audio Module Class", info->category,
sizeof(info->category));
2291 copyAscii(P::descriptor.name, info->name,
sizeof(info->name));
2292 return Steinberg_kResultOk;
2296 Steinberg_int32 index, Steinberg_PClassInfo2* info)
2298 if (index != 0 || info ==
nullptr)
return Steinberg_kInvalidArgument;
2299 std::memset(info, 0,
sizeof(*info));
2301 info->cardinality = 0x7FFFFFFF;
2302 copyAscii(
"Audio Module Class", info->category,
sizeof(info->category));
2303 copyAscii(P::descriptor.name, info->name,
sizeof(info->name));
2304 info->classFlags = 0;
2306 info->subCategories,
sizeof(info->subCategories));
2307 copyAscii(P::descriptor.vendor, info->vendor,
sizeof(info->vendor));
2308 copyAscii(P::descriptor.version, info->version,
sizeof(info->version));
2309 copyAscii(
"VST 3.7.9", info->sdkVersion,
sizeof(info->sdkVersion));
2310 return Steinberg_kResultOk;
2314 Steinberg_int32 index, Steinberg_PClassInfoW* info)
2316 if (index != 0 || info ==
nullptr)
return Steinberg_kInvalidArgument;
2317 std::memset(info, 0,
sizeof(*info));
2319 info->cardinality = 0x7FFFFFFF;
2320 copyAscii(
"Audio Module Class", info->category,
sizeof(info->category));
2322 info->classFlags = 0;
2324 info->subCategories,
sizeof(info->subCategories));
2328 return Steinberg_kResultOk;
2332 Steinberg_FIDString cid, Steinberg_FIDString iid,
void** obj)
2334 if (cid ==
nullptr || iid ==
nullptr || obj ==
nullptr)
2335 return Steinberg_kInvalidArgument;
2338 if (std::memcmp(cid, uid.data(), 16) != 0)
2339 return Steinberg_kNoInterface;
2341 auto* plugin =
new (std::nothrow)
Plugin<P>();
2342 if (plugin ==
nullptr)
return Steinberg_kResultFalse;
2344 Steinberg_TUID requested;
2345 std::memcpy(requested, iid,
sizeof(requested));
2346 const Steinberg_tresult r = plugin->query(requested, obj);
2347 plugin->releaseImpl();
2348 return r == Steinberg_kResultOk ? Steinberg_kResultOk : Steinberg_kNoInterface;
2351 static Steinberg_tresult SMTG_STDMETHODCALLTYPE
sSetHostContext(
void*, Steinberg_FUnknown*)
2352 {
return Steinberg_kResultOk; }
2354 inline static const Steinberg_IPluginFactory3Vtbl
kVtbl = {
2367#define DSPARK_VST3_EXPORT __declspec(dllexport)
2369#define DSPARK_VST3_EXPORT __attribute__((visibility("default")))
2376#define DSPARK_VST3_PLUGIN(PluginClass) \
2377 static dspark::plugin::vst3::Factory<PluginClass> gDsparkVst3Factory; \
2379 DSPARK_VST3_EXPORT Steinberg_IPluginFactory* SMTG_STDMETHODCALLTYPE \
2380 GetPluginFactory() \
2382 return reinterpret_cast<Steinberg_IPluginFactory*>(&gDsparkVst3Factory); \
2384 DSPARK_VST3_EXPORT bool InitDll() { return true; } \
2385 DSPARK_VST3_EXPORT bool ExitDll() { return true; } \
2386 DSPARK_VST3_EXPORT bool ModuleEntry(void*) { return true; } \
2387 DSPARK_VST3_EXPORT bool ModuleExit() { return true; } \
2388 DSPARK_VST3_EXPORT bool bundleEntry(void*) { return true; } \
2389 DSPARK_VST3_EXPORT bool bundleExit() { return true; } \
Non-owning view over audio channel data.
RAII scope guard to disable denormalised (subnormal) floating-point numbers.
The dry signal of the wrappers' soft bypass, delayed by the plugin's latency.
A ~30 Hz tick on the host's UI thread.
bool start(Steinberg_FUnknown *hostContext, Callback callback, void *context) noexcept
bool running() const noexcept
void(*)(void *context) noexcept Callback
UiTicker() noexcept=default
One embedded WebView editor instance for plugin class P. Owned by the format backends (the VST3 IPlug...
Custom-editor switch. With hasEditor = false (or absent) hosts show their generic parameter UI....
Factory-preset capability: a static constexpr auto factoryPresets table built with presets()....
MIDI capability: void handleMidiEvent(const MidiEvent&) noexcept. Its presence grows a note/event inp...
Render-mode capability: void setOfflineRendering(bool) noexcept. The host flips it to true for non-re...
Sidechain capability. Implement the two-buffer process - the same shape DSPark's own dynamics take:
Transport capability: void setTransport(const TransportInfo&) noexcept. Called on the audio thread,...
constexpr uint32_t midiProxyId(int channel, int controller) noexcept
bool streamRead(Steinberg_IBStream *s, void *dst, Steinberg_int32 bytes) noexcept
Reads the full requested range from an IBStream (hosts may chunk).
constexpr uint32_t kBypassParamId
constexpr uint32_t kMidiProxyBase
constexpr int kNumMidiProxies
bool tuidEqual(const Steinberg_TUID a, const Steinberg_TUID b) noexcept
bool streamWrite(Steinberg_IBStream *s, const void *src, Steinberg_int32 bytes) noexcept
constexpr Steinberg_int32 kPresetProgramListId
void asciiToString128(const char *src, Steinberg_Vst_String128 dst) noexcept
constexpr int kMidiProxyControllers[4]
constexpr bool isMidiProxyId(uint32_t id) noexcept
MidiEvent midiProxyToEvent(uint32_t id, double normalized, int offset) noexcept
Proxy parameter (normalized) -> the framework-neutral MidiEvent.
constexpr uint32_t kProgramParamId
constexpr int kBypassRampSamples
void copyAscii(const char *src, char *dst, size_t cap) noexcept
@ Instrument
Audio generator (MIDI in -> audio out, no audio input).
std::vector< uint8_t > buildState(const P &user, const double *normalized, size_t numParams, int programIndex=-1, int bypassState=-1)
constexpr Param toggle(const char *id, const char *name, bool defaultOn) noexcept
On/off parameter helper.
constexpr int kDryDelayMinCapacity
Ring headroom of the bypass dry path: latencies up to this (or twice the latency at activation,...
constexpr std::array< uint8_t, 16 > makeUid(const char *productId, uint64_t salt) noexcept
Deterministic 16-byte class UID from the stable productId. Each backend salts differently so VST3/CLA...
constexpr double toNormalized(const Param &p, double plain) noexcept
Plain [min, max] -> normalized [0, 1] (steps snap on the way back).
constexpr double toPlain(const Param &p, double normalized) noexcept
Normalized [0, 1] -> plain [min, max], snapped for stepped params.
int parseToggleText(const char *text) noexcept
Case-insensitive "On"/"Off" recognition - the inverse of the toggle display below....
constexpr int kAutomationQuantum
Sub-block grain: automation/event splits snap to this many frames (bounds the per-call overhead; ~0....
constexpr uint32_t hash32(const char *s) noexcept
FNV-1a 32-bit over a C string - the per-parameter host id.
constexpr int kMaxBlockEvents
Hard cap of timestamped events handled per block; the (very unlikely) excess applies at the position ...
void formatValue(const Param &p, double plain, char *out, int outSize) noexcept
Formats a plain value for host display ("3.5 dB", "On", "Saw", "4").
void sortBlockEvents(BlockEvent *events, int count) noexcept
Stable insertion sort by offset (tiny N, allocation-free - audio-thread safe; equal offsets keep arri...
constexpr int toBoundedInt(double value, int lo, int hi) noexcept
A floating value from outside converted to an integer, with the range settled BEFORE the conversion.
bool applyState(P &user, const uint8_t *data, size_t size, double *normalized, int *programIndex=nullptr, int *bypassState=nullptr)
Parses a state blob; fills normalized (defaults pre-loaded by the caller), reports a persisted progra...
bool parseValue(const Param &p, const char *text, double &plain) noexcept
Host text -> plain value, the inverse of formatValue(): a choice's label (any case),...
Describes the audio environment for a DSP processor.
One timestamped in-block event, normalised across formats: a parameter point, a bypass point,...
int32_t offset
Frame position within the block.
One incoming MIDI-ish event, normalised across formats.
@ NoteOn
note = key, value = velocity 0..1.
@ PolyPressure
note = key, value = pressure 0..1.
@ NoteOff
note = key, value = release velocity 0..1.
@ ControlChange
note = controller number, value = 0..1.
@ ChannelPressure
value = pressure 0..1 (note unused).
@ PitchBend
value = bend -1..+1 (note unused).
int sampleOffset
Frames into the NEXT processBlock call.
One automatable parameter. Plain values run [min, max]; hosts see the normalized [0,...
Host transport snapshot, delivered once per audio block.
bool playing
Transport is rolling.
static std::array< uint8_t, 16 > classUid() noexcept
static Steinberg_int32 SMTG_STDMETHODCALLTYPE sCountClasses(void *)
static Steinberg_uint32 SMTG_STDMETHODCALLTYPE sRelease(void *)
static void fillClassCommon(Steinberg_TUID cid) noexcept
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sSetHostContext(void *, Steinberg_FUnknown *)
static const Steinberg_IPluginFactory3Vtbl kVtbl
static Steinberg_uint32 SMTG_STDMETHODCALLTYPE sAddRef(void *)
const Steinberg_IPluginFactory3Vtbl * vtbl
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sQuery(void *self_, const Steinberg_TUID iid, void **obj)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetClassInfoUnicode(void *, Steinberg_int32 index, Steinberg_PClassInfoW *info)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sCreateInstance(void *, Steinberg_FIDString cid, Steinberg_FIDString iid, void **obj)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetClassInfo(void *, Steinberg_int32 index, Steinberg_PClassInfo *info)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetClassInfo2(void *, Steinberg_int32 index, Steinberg_PClassInfo2 *info)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetFactoryInfo(void *, Steinberg_PFactoryInfo *info)
static Steinberg_int32 SMTG_STDMETHODCALLTYPE sGetProgramListCount(void *)
static Steinberg_uint32 SMTG_STDMETHODCALLTYPE sGetLatencySamples(void *self_)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sComponentSetState(void *self_, Steinberg_IBStream *state)
static Steinberg_Vst_ParamValue SMTG_STDMETHODCALLTYPE sGetParamNormalized(void *self_, Steinberg_Vst_ParamID id)
std::atomic< double > shadow[kNumParams==0 ? 1 :kNumParams]
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sSetupProcessing(void *self_, Steinberg_Vst_ProcessSetup *setup)
static constexpr Steinberg_int32 kFirstProxyIndex
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetUnitByBus(void *, Steinberg_Vst_MediaType, Steinberg_Vst_BusDirection, Steinberg_int32, Steinberg_int32, Steinberg_Vst_UnitID *unitId)
const Steinberg_Vst_IComponentVtbl * componentVtbl
bool onUiThread() const noexcept
static Steinberg_uint32 SMTG_STDMETHODCALLTYPE sRelease(void *self_)
const Steinberg_Vst_IProcessContextRequirementsVtbl * contextReqVtbl
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetRoutingInfo(void *, Steinberg_Vst_RoutingInfo *, Steinberg_Vst_RoutingInfo *)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetMidiControllerAssignment(void *, Steinberg_int32 busIndex, Steinberg_int16 channel, Steinberg_Vst_CtrlNumber midiControllerNumber, Steinberg_Vst_ParamID *id)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sCtrlSetState(void *, Steinberg_IBStream *)
static Steinberg_Vst_ParamValue SMTG_STDMETHODCALLTYPE sPlainParamToNormalized(void *, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue plain)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sComponentGetState(void *self_, Steinberg_IBStream *state)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sInitialize(void *self_, Steinberg_FUnknown *context)
std::atomic< double > midiProxyShadow[HasMidi< P > ? kNumMidiProxies :1]
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sCtrlGetState(void *, Steinberg_IBStream *)
static int programIndexOf(Steinberg_Vst_ParamValue normalized) noexcept
static constexpr Steinberg_int32 kBypassIndex
const Steinberg_Vst_IMidiMappingVtbl * midiMapVtbl
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sHasProgramPitchNames(void *, Steinberg_Vst_ProgramListID, Steinberg_int32)
static constexpr size_t kNumParams
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sTerminate(void *self_)
static const Steinberg_Vst_IUnitInfoVtbl kUnitVtbl
void applyAllShadows() noexcept
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sCanProcessSampleSize(void *, Steinberg_int32 size)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sSetIoMode(void *, Steinberg_Vst_IoMode)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sCtrlInitialize(void *self_, Steinberg_FUnknown *context)
void applyFactoryPresetIdx(int idx) noexcept
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sQuery(void *self_, const Steinberg_TUID iid, void **obj)
static int indexOfParamId(Steinberg_Vst_ParamID id) noexcept
static Steinberg_int32 numInputBuses() noexcept
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sSetComponentHandler(void *self_, Steinberg_Vst_IComponentHandler *handler)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetProgramName(void *, Steinberg_Vst_ProgramListID listId, Steinberg_int32 programIndex, Steinberg_Vst_String128 name)
std::atomic< std::thread::id > uiThread
static const Steinberg_Vst_IComponentVtbl kComponentVtbl
void refreshLatency(Steinberg_int32 extraFlags=0) noexcept
bool applyBlockEvent(const BlockEvent &ev, int blockStart, bool &programChanged) noexcept
void deliverPendingRestart() noexcept
std::atomic< Steinberg_uint32 > refs
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sSetActive(void *self_, Steinberg_TBool state)
static Steinberg_int32 SMTG_STDMETHODCALLTYPE sGetParameterCount(void *)
Steinberg_tresult query(const Steinberg_TUID iid, void **obj) noexcept
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sSelectUnit(void *, Steinberg_Vst_UnitID unitId)
static Steinberg_uint32 SMTG_STDMETHODCALLTYPE sAddRef(void *self_)
static void onUiTick(void *self) noexcept
const Steinberg_Vst_IUnitInfoVtbl * unitVtbl
Steinberg_uint32 addRefImpl() noexcept
static const Steinberg_Vst_IProcessContextRequirementsVtbl kContextReqVtbl
static Steinberg_int32 SMTG_STDMETHODCALLTYPE sGetUnitCount(void *)
void forwardTransport(const Steinberg_Vst_ProcessContext *ctx) noexcept
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetProgramListInfo(void *, Steinberg_int32 listIndex, Steinberg_Vst_ProgramListInfo *info)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetBusArrangement(void *self_, Steinberg_Vst_BusDirection dir, Steinberg_int32 index, Steinberg_Vst_SpeakerArrangement *arr)
std::vector< float > silence
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetUnitInfo(void *, Steinberg_int32 unitIndex, Steinberg_Vst_UnitInfo *info)
std::atomic< int > currentProgram
std::atomic< int > cachedLatency
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetParamStringByValue(void *self_, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue normalized, Steinberg_Vst_String128 out)
static const Steinberg_Vst_IAudioProcessorVtbl kProcessorVtbl
const Steinberg_Vst_IEditControllerVtbl * controllerVtbl
static const Steinberg_Vst_IMidiMappingVtbl kMidiMapVtbl
static const Steinberg_Vst_IEditControllerVtbl kControllerVtbl
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sActivateBus(void *, Steinberg_Vst_MediaType, Steinberg_Vst_BusDirection, Steinberg_int32, Steinberg_TBool)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetProgramPitchName(void *, Steinberg_Vst_ProgramListID, Steinberg_int32, Steinberg_int16, Steinberg_Vst_String128)
static constexpr Steinberg_int32 kProgramIndex
static constexpr int kNumPresets
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetBusInfo(void *self_, Steinberg_Vst_MediaType type, Steinberg_Vst_BusDirection dir, Steinberg_int32 index, Steinberg_Vst_BusInfo *bus)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetParamValueByString(void *self_, Steinberg_Vst_ParamID id, Steinberg_Vst_TChar *string, Steinberg_Vst_ParamValue *normalized)
static Steinberg_Vst_ParamValue SMTG_STDMETHODCALLTYPE sNormalizedParamToPlain(void *, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue normalized)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetProgramInfo(void *, Steinberg_Vst_ProgramListID, Steinberg_int32, Steinberg_Vst_CString, Steinberg_Vst_String128)
std::vector< float > dryR
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sSetUnitProgramData(void *, Steinberg_int32, Steinberg_int32, struct Steinberg_IBStream *)
Steinberg_Vst_ProcessSetup setup
static Plugin * fromLens(void *iface, int lens) noexcept
void attachToHost(Steinberg_FUnknown *context) noexcept
std::vector< float > dryL
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sCtrlTerminate(void *self_)
void collectParameterChanges(Steinberg_Vst_IParameterChanges *changes, Steinberg_int32 numSamples, BlockEvent *events, int &count) noexcept
void collectInputEvents(Steinberg_Vst_IEventList *list, Steinberg_int32 numSamples, BlockEvent *events, int &count) noexcept
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sProcess(void *self_, Steinberg_Vst_ProcessData *data)
std::atomic< bool > bypass
static Steinberg_uint32 SMTG_STDMETHODCALLTYPE sGetTailSamples(void *self_)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sSetParamNormalized(void *self_, Steinberg_Vst_ParamID id, Steinberg_Vst_ParamValue value)
static Steinberg_Vst_UnitID SMTG_STDMETHODCALLTYPE sGetSelectedUnit(void *)
Steinberg_uint32 releaseImpl() noexcept
void * lensPtr(int lens) noexcept
static Steinberg_IPlugView *SMTG_STDMETHODCALLTYPE sCreateView(void *self_, Steinberg_FIDString name)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetParameterInfo(void *self_, Steinberg_int32 index, Steinberg_Vst_ParameterInfo *info)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sSetComponentState(void *self_, Steinberg_IBStream *state)
std::atomic< Steinberg_int32 > pendingRestart
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sGetControllerClassId(void *, Steinberg_TUID)
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sSetProcessing(void *, Steinberg_TBool)
static constexpr bool kIsInstrument
static Steinberg_uint32 SMTG_STDMETHODCALLTYPE sGetProcessContextRequirements(void *)
const Steinberg_Vst_IAudioProcessorVtbl * processorVtbl
std::atomic< Steinberg_Vst_IComponentHandler * > handler
static Steinberg_int32 SMTG_STDMETHODCALLTYPE sGetBusCount(void *, Steinberg_Vst_MediaType type, Steinberg_Vst_BusDirection dir)
void applyNormalized(int index, double normalized) noexcept
static Steinberg_tresult SMTG_STDMETHODCALLTYPE sSetBusArrangements(void *self_, Steinberg_Vst_SpeakerArrangement *inputs, Steinberg_int32 numIns, Steinberg_Vst_SpeakerArrangement *outputs, Steinberg_int32 numOuts)
static int midiProxySlot(uint32_t id) noexcept
void detachFromHost() noexcept
void requestRestart(Steinberg_int32 flags) noexcept