Loading...
Searching...
No Matches
Plugin.hpp
1#pragma once
2
3#define RELEASE 1
4#include <ossia/detail/flat_map.hpp>
5#include <ossia/detail/flat_set.hpp>
6
7#include <QString>
8
9#include <score_plugin_vst3_export.h>
10
11#include <pluginterfaces/base/funknown.h>
12#include <pluginterfaces/gui/iplugview.h>
13#include <pluginterfaces/vst/ivstaudioprocessor.h>
14#include <pluginterfaces/vst/ivstcomponent.h>
15#include <pluginterfaces/vst/ivstmessage.h>
16#include <pluginterfaces/vst/ivstunits.h>
17
18#include <string_view>
19
20#include <public.sdk/source/vst/hosting/hostclasses.h>
21#include <public.sdk/source/vst/hosting/module.h>
22#include <public.sdk/source/vst/hosting/plugprovider.h>
23
24namespace vst3
25{
26class ApplicationPlugin;
27class Model;
28
29inline QString fromString(const Steinberg::Vst::String128& str)
30{
31#if defined(_WIN32)
32 return [] <typename T>(const T& str) {
33 using char_type = std::decay_t<decltype(str[0])>;
34 if constexpr(std::is_same_v<char_type, char16_t>)
35 return QString::fromUtf16(str);
36 else
37 return QString::fromWCharArray(str);
38 }(str);
39#else
40 return QString::fromUtf16(str);
41#endif
42}
43
44// Keyed by (event bus, MIDI channel 0-15, controller number). The channel is
45// part of the key because VST3 lets a plug-in assign a different parameter per
46// channel, which is how an MPE-aware plug-in exposes per-voice pitch bend -
47// and MPE senders put every note on its own member channel, never channel 0.
48using MIDIControls
49 = ossia::flat_map<std::tuple<int, int, int>, Steinberg::Vst::ParamID>;
50class PlugFrame;
51class ComponentHandler;
52struct SCORE_PLUGIN_VST3_TEST_EXPORT Plugin
53{
54 Plugin() = default;
55 Plugin(const Plugin&) = default;
56 Plugin(Plugin&&) = default;
57 Plugin& operator=(const Plugin&) = default;
58 Plugin& operator=(Plugin&&) = default;
59
60 ~Plugin() = default;
61
62 void load(
63 Model& model, ApplicationPlugin& ctx, const std::string& path,
64 const VST3::UID& uid, double sr, int max_bs);
65 operator bool() const noexcept { return component && processor; }
66
67 std::string path;
68 VST3::Hosting::Module::Ptr mdl;
69 Steinberg::Vst::IComponent* component{};
70 Steinberg::Vst::IAudioProcessor* processor{};
71 Steinberg::Vst::IEditController* controller{};
72 Steinberg::Vst::IUnitInfo* units{};
73 Steinberg::IPlugView* view{};
74 PlugFrame* plugFrame{};
75
76 // Connection points of the component and the controller, kept around because
77 // VST3 mandates disconnecting them before terminating the plug-in
78 Steinberg::Vst::IConnectionPoint* componentCP{};
79 Steinberg::Vst::IConnectionPoint* controllerCP{};
80
81 // Given to the controller: it points to the Model, so it has to be unset
82 // before the Model dies
83 ComponentHandler* componentHandler{};
84
85 void loadAudioProcessor(ApplicationPlugin& ctx);
86 void loadEditController(Model& model, ApplicationPlugin& ctx);
87 void loadView(Model& model);
88 void loadBuses();
89 void loadPresets();
90
91 void loadProcessorStateToController();
92
94 void activateBuses();
95 void start(double sample_rate, int max_bs);
97 void restart(double sample_rate, int max_bs);
98 void stop();
99
101 double sample_rate{};
102 int max_block{};
103
104 bool supports_double{};
105 bool ui_available{};
106 bool ui_owned{};
107
108 // initialize() succeeded and must be paired with exactly one terminate()
109 bool component_initialized{};
110 bool controller_initialized{};
111
112 // Single-component effect: the component *is* the edit controller, thus it
113 // must only be initialized and terminated once
114 bool controller_is_component{};
115 int audio_ins = 0;
116 int event_ins = 0;
117 int audio_outs = 0;
118 int event_outs = 0;
119
120 MIDIControls midiControls{};
121
125 ossia::flat_set<Steinberg::Vst::ParamID> unsentParameters;
126
127 Steinberg::Vst::ProgramListInfo programs;
128};
129
130#if __cpp_concepts >= 201907
131template <typename T>
132concept BusVisitor = requires(T&& vis)
133{
134 vis.audioIn(Steinberg::Vst::BusInfo{}, int{});
135 vis.audioOut(Steinberg::Vst::BusInfo{}, int{});
136 vis.eventIn(Steinberg::Vst::BusInfo{}, int{});
137 vis.eventOut(Steinberg::Vst::BusInfo{}, int{});
138};
139void forEachBus(BusVisitor auto&& visitor, Steinberg::Vst::IComponent& component)
140#else
141template <typename T>
142void forEachBus(T&& visitor, Steinberg::Vst::IComponent& component)
143#endif
144{
145 const int audio_ins
146 = component.getBusCount(Steinberg::Vst::kAudio, Steinberg::Vst::kInput);
147 const int event_ins
148 = component.getBusCount(Steinberg::Vst::kEvent, Steinberg::Vst::kInput);
149 const int audio_outs
150 = component.getBusCount(Steinberg::Vst::kAudio, Steinberg::Vst::kOutput);
151 const int event_outs
152 = component.getBusCount(Steinberg::Vst::kEvent, Steinberg::Vst::kOutput);
153
154 Steinberg::Vst::BusInfo bus;
155 for(int i = 0; i < audio_ins; i++)
156 {
157 component.getBusInfo(Steinberg::Vst::kAudio, Steinberg::Vst::kInput, i, bus);
158 visitor.audioIn(bus, i);
159 }
160
161 for(int i = 0; i < event_ins; i++)
162 {
163 component.getBusInfo(Steinberg::Vst::kEvent, Steinberg::Vst::kInput, i, bus);
164 visitor.eventIn(bus, i);
165 }
166
167 for(int i = 0; i < audio_outs; i++)
168 {
169 component.getBusInfo(Steinberg::Vst::kAudio, Steinberg::Vst::kOutput, i, bus);
170 visitor.audioOut(bus, i);
171 }
172
173 for(int i = 0; i < event_outs; i++)
174 {
175 component.getBusInfo(Steinberg::Vst::kEvent, Steinberg::Vst::kOutput, i, bus);
176 visitor.eventOut(bus, i);
177 }
178}
179}
Definition score-plugin-vst3/Vst3/ApplicationPlugin.hpp:131
Definition EditHandler.hpp:17
Definition score-plugin-vst3/Vst3/EffectModel.hpp:45
Definition PlugFrame.hpp:14
Definition Plugin.hpp:53
ossia::flat_set< Steinberg::Vst::ParamID > unsentParameters
Definition Plugin.hpp:125