@soundor/vst3-host 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CMakeLists.txt +194 -0
  2. package/CMakePresets.json +115 -0
  3. package/LICENSE +21 -0
  4. package/NOTICE.md +17 -0
  5. package/README.md +40 -2
  6. package/cmake/Vst3HostDependencies.cmake +179 -0
  7. package/dist/cli.d.mts +1 -0
  8. package/dist/cli.mjs +88 -0
  9. package/dist/cli.mjs.map +1 -0
  10. package/dist/index.d.mts +586 -0
  11. package/dist/index.d.mts.map +1 -0
  12. package/dist/index.mjs +1 -0
  13. package/dist/prepare-ayjTVhfk.mjs +1149 -0
  14. package/dist/prepare-ayjTVhfk.mjs.map +1 -0
  15. package/native/src/Bundle.cpp +253 -0
  16. package/native/src/Bundle.h +32 -0
  17. package/native/src/EditorArea.h +92 -0
  18. package/native/src/EditorAreaMac.mm +52 -0
  19. package/native/src/EditorAreaWindows.cpp +66 -0
  20. package/native/src/EditorAreaX11.cpp +512 -0
  21. package/native/src/EditorWindow.h +72 -0
  22. package/native/src/EditorWindowMac.mm +134 -0
  23. package/native/src/EditorWindowWindows.cpp +148 -0
  24. package/native/src/EventLoop.h +50 -0
  25. package/native/src/EventLoopLinux.cpp +204 -0
  26. package/native/src/EventLoopMac.mm +146 -0
  27. package/native/src/EventLoopWindows.cpp +132 -0
  28. package/native/src/HostContext.cpp +217 -0
  29. package/native/src/HostContext.h +116 -0
  30. package/native/src/HostInterface.cpp +1026 -0
  31. package/native/src/HostInterface.h +138 -0
  32. package/native/src/HostWindow.cpp +901 -0
  33. package/native/src/HostWindow.h +187 -0
  34. package/native/src/Ipc.cpp +158 -0
  35. package/native/src/Ipc.h +91 -0
  36. package/native/src/Json.cpp +426 -0
  37. package/native/src/Json.h +85 -0
  38. package/native/src/LiveAudio.cpp +416 -0
  39. package/native/src/LiveAudio.h +121 -0
  40. package/native/src/Main.cpp +178 -0
  41. package/native/src/Paths.h +20 -0
  42. package/native/src/Plugin.cpp +1865 -0
  43. package/native/src/Plugin.h +321 -0
  44. package/native/src/Session.cpp +656 -0
  45. package/native/src/Session.h +82 -0
  46. package/native/src/Settings.cpp +93 -0
  47. package/native/src/Settings.h +23 -0
  48. package/native/src/Version.h.in +11 -0
  49. package/native/src/X11Library.cpp +127 -0
  50. package/native/src/X11Library.h +83 -0
  51. package/native/tools/Embed.cpp +49 -0
  52. package/package.json +58 -3
  53. package/protocol.json +7 -0
  54. package/targets.json +33 -0
@@ -0,0 +1,321 @@
1
+ // One loaded VST3 plugin: its module, the chosen class's component and
2
+ // controller, the negotiated buses and processing setup, the editor, and
3
+ // offline processing. Every method but runProcess() is for the main thread.
4
+ #pragma once
5
+
6
+ #include "EditorWindow.h"
7
+ #include "EventLoop.h"
8
+ #include "HostContext.h"
9
+ #include "Ipc.h"
10
+ #include "Json.h"
11
+ #include "pluginterfaces/gui/iplugview.h"
12
+ #include "pluginterfaces/vst/ivstaudioprocessor.h"
13
+ #include "pluginterfaces/vst/ivstcomponent.h"
14
+ #include "pluginterfaces/vst/ivsteditcontroller.h"
15
+ #include "pluginterfaces/vst/ivstevents.h"
16
+ #include "pluginterfaces/vst/ivstmessage.h"
17
+ #include "pluginterfaces/vst/ivstmidicontrollers.h"
18
+ #include "public.sdk/source/vst/hosting/module.h"
19
+
20
+ #include <functional>
21
+ #include <memory>
22
+ #include <mutex>
23
+ #include <optional>
24
+ #include <stdexcept>
25
+ #include <string>
26
+ #include <vector>
27
+
28
+ namespace soundor::vst3host
29
+ {
30
+ /** An error reported to the client as { code, message, details }. */
31
+ class Failure : public std::runtime_error
32
+ {
33
+ public:
34
+ Failure(std::string code, const std::string& message, Json details = {})
35
+ : std::runtime_error(message), code_(std::move(code)), details_(std::move(details))
36
+ {
37
+ }
38
+ const std::string& code() const { return code_; }
39
+ const Json& details() const { return details_; }
40
+
41
+ private:
42
+ std::string code_;
43
+ Json details_;
44
+ };
45
+
46
+ struct BusRequest
47
+ {
48
+ std::optional<Vst::SpeakerArrangement> arrangement;
49
+ std::optional<bool> active;
50
+ };
51
+
52
+ struct Setup
53
+ {
54
+ double sampleRate = 48000;
55
+ int32 blockSize = 512;
56
+ int32 processMode = Vst::kRealtime;
57
+ /** By audio bus index; a bus without a request keeps the plugin's
58
+ default arrangement and activation. */
59
+ std::vector<std::optional<BusRequest>> inputs;
60
+ std::vector<std::optional<BusRequest>> outputs;
61
+ };
62
+
63
+ struct Transport
64
+ {
65
+ bool playing = false;
66
+ double tempo = 120;
67
+ int32 numerator = 4;
68
+ int32 denominator = 4;
69
+ int64 samplePosition = 0;
70
+ };
71
+
72
+ struct ParameterChange
73
+ {
74
+ Vst::ParamID id = 0;
75
+ double value = 0;
76
+ int64 time = 0;
77
+ };
78
+
79
+ /** One process request, validated on the main thread and run on the
80
+ audio worker (runProcess). Times are frames from the request's start. */
81
+ struct ProcessJob
82
+ {
83
+ int64 frames = 0;
84
+ Payload input;
85
+ /** Per audio input bus, per channel: a channel of `input`, or nullptr
86
+ for silence (an omitted bus). */
87
+ std::vector<std::vector<const float*>> inputs;
88
+ std::vector<ParameterChange> changes;
89
+ std::vector<Vst::Event> events;
90
+ std::optional<Transport> transport;
91
+
92
+ // Results.
93
+ Payload output;
94
+ std::vector<int32> outputChannels;
95
+ std::vector<Vst::Event> outputEvents;
96
+ std::vector<ParameterChange> outputChanges;
97
+ std::vector<std::string> warnings;
98
+ std::string error;
99
+ };
100
+
101
+ /** A parameter as the host interface shows it. */
102
+ struct HostParameter
103
+ {
104
+ Vst::ParamID id = 0;
105
+ std::string title;
106
+ std::string units;
107
+ int32 stepCount = 0;
108
+ double defaultValue = 0;
109
+ bool readOnly = false;
110
+ bool list = false;
111
+ bool bypass = false;
112
+ };
113
+
114
+ struct PluginCallbacks
115
+ {
116
+ std::function<void(const std::string& event, Json params)> emit;
117
+ /** Makes the window an editor opens in (the presentation's). */
118
+ std::function<std::unique_ptr<EditorWindow>(const std::string& title, int width, int height,
119
+ EditorWindow::Callbacks callbacks, std::string& error)>
120
+ createWindow;
121
+ };
122
+
123
+ class Plugin final : public HostListener
124
+ {
125
+ public:
126
+ Plugin(EventLoop& loop, bool ui, PluginCallbacks callbacks);
127
+ ~Plugin() override;
128
+
129
+ Plugin(const Plugin&) = delete;
130
+ Plugin& operator=(const Plugin&) = delete;
131
+
132
+ /** Loads the module (once) and creates, connects, configures and
133
+ activates an instance of the selected class. */
134
+ void load(const std::string& path, const std::string& className, const std::string& classId,
135
+ const Setup& setup);
136
+ /** A new instance of the same class and setup; the module stays. */
137
+ void recreate();
138
+ /** Deactivates and reactivates: the plugin clears its processing state. */
139
+ void reset();
140
+ /** Applies a new setup (sample rate, block size, mode, buses). */
141
+ void configure(const Setup& setup);
142
+ void unload();
143
+
144
+ Json info() const;
145
+ Json parameters() const;
146
+ Json parameter(Vst::ParamID id) const;
147
+ Json setParameter(Vst::ParamID id, std::optional<double> normalized, std::optional<double> plain);
148
+ Json latencyAndTail() const;
149
+
150
+ /** Component state, then controller state; sizes in `sizes`. */
151
+ std::vector<uint8_t> getState(size_t& componentSize);
152
+ void setState(const uint8_t* component, size_t componentSize, const uint8_t* controller, size_t controllerSize);
153
+
154
+ /** Validates a process request and prepares its job. */
155
+ std::unique_ptr<ProcessJob> prepareProcess(const Json& params, Payload&& payload);
156
+ /** On the audio worker. */
157
+ void runProcess(ProcessJob& job);
158
+ /** Back on the main thread: results to JSON, controller in sync. */
159
+ Json finishProcess(ProcessJob& job);
160
+ /** The transport as the protocol reports it. */
161
+ Json transportJson() const;
162
+ /** Changes the transport (setTransport's params); returns it. */
163
+ Json setTransport(const Json& params);
164
+
165
+ Json openEditor(std::optional<double> scale);
166
+ void closeEditor();
167
+ Json resizeEditor(int width, int height);
168
+ /** The size the window shows the editor at, within its limits, before
169
+ it is asked to take it (resizeEditor). */
170
+ void previewEditorSize(int width, int height);
171
+ /** The size a window that is the editor shows it at, in a drag that
172
+ has just changed the window from `lastWidth`×`lastHeight`. A window
173
+ edge moves one side, and an editor that keeps its shape answers
174
+ that with the other side's size: it is asked again, with the other
175
+ side left to it. VST3 does not say which edge moved. */
176
+ void previewEditorDrag(int width, int height, int lastWidth, int lastHeight);
177
+ /** A size the user or the editor asks for: within the editor's limits
178
+ and the room the window has. resizeEditor() is the caller's: the
179
+ window grows for it. */
180
+ Json requestEditorSize(int width, int height);
181
+ Json editorInfo() const;
182
+ [[nodiscard]] bool editorOpen() const { return view_ != nullptr; }
183
+ [[nodiscard]] double editorScale() const { return scale_; }
184
+
185
+ // ── For live audio and the host interface (main thread unless noted)
186
+
187
+ /** Held while the plugin processes, and while anything changes what
188
+ processing uses (activation, buses, state). Live audio only tries
189
+ it, and plays silence when it is taken. */
190
+ std::recursive_mutex& processing() { return processing_; }
191
+ [[nodiscard]] bool active() const { return active_; }
192
+ [[nodiscard]] std::string name() const { return class_.name(); }
193
+ [[nodiscard]] std::string vendor() const { return class_.vendor(); }
194
+ [[nodiscard]] std::string version() const { return class_.version(); }
195
+ /** Channels of the main audio input and output buses (0: none or inactive). */
196
+ [[nodiscard]] int32 mainInputChannels() const;
197
+ [[nodiscard]] int32 mainOutputChannels() const;
198
+ /** Each audio bus's channel count, as process() lays them out. */
199
+ [[nodiscard]] std::vector<int32> inputBusChannels() const;
200
+ [[nodiscard]] std::vector<int32> outputBusChannels() const;
201
+ [[nodiscard]] bool hasEventInput() const { return ! eventInputs_.empty(); }
202
+ [[nodiscard]] uint32 latencySamples() const;
203
+ /** Bumped when the plugin's parameter list or titles may have changed. */
204
+ [[nodiscard]] int parameterGeneration() const { return parameterGeneration_; }
205
+ std::vector<HostParameter> hostParameters() const;
206
+ [[nodiscard]] double normalizedValue(Vst::ParamID id) const;
207
+ std::string valueText(Vst::ParamID id, double normalized) const;
208
+ /** An edit from the host interface: the controller now, the processor
209
+ at the next process call. Ignored for an unknown or read-only id. */
210
+ void editParameter(Vst::ParamID id, double normalized);
211
+ /** A note for the next process call's first frame (the on-screen keyboard). */
212
+ void queueEvent(const Vst::Event& event);
213
+ /** The transport, any thread. */
214
+ [[nodiscard]] Transport transport() const;
215
+ /** Changes the transport; any thread. Processing in progress keeps
216
+ what it had for its call and does not overwrite the change. */
217
+ void editTransport(const std::function<void(Transport&)>& edit);
218
+ /** Applies output parameter values the processor reported (meters)
219
+ to the controller. */
220
+ void applyOutputChanges(const std::vector<ParameterChange>& changes);
221
+
222
+ /** Runs restarts the plugin asked for while busy. */
223
+ void applyPendingRestarts();
224
+ bool loaded() const { return component_ != nullptr; }
225
+ const Setup& setup() const { return setup_; }
226
+
227
+ // HostListener
228
+ void beginEdit(Vst::ParamID id) override;
229
+ void performEdit(Vst::ParamID id, Vst::ParamValue value) override;
230
+ void endEdit(Vst::ParamID id) override;
231
+ void restartComponent(int32 flags) override;
232
+ void setDirty(bool dirty) override;
233
+ void requestOpenEditor() override;
234
+ bool resizeView(IPlugView* view, ViewRect* size) override;
235
+
236
+ /** True while the audio worker runs a job: lifecycle waits. */
237
+ bool busy = false;
238
+
239
+ private:
240
+ /** `size`, held to the room the window has for the editor, if it keeps to one. */
241
+ void keepToRoom(Steinberg::ViewRect& size);
242
+ /** The window keeps no smaller than the editor's smallest size takes. */
243
+ void tellWindowEditorLimits();
244
+ struct Bus
245
+ {
246
+ std::string name;
247
+ int32 type = Vst::kMain;
248
+ int32 channels = 0;
249
+ Vst::SpeakerArrangement arrangement = 0;
250
+ bool active = false;
251
+ bool defaultActive = false;
252
+ };
253
+
254
+ void createInstance();
255
+ void destroyInstance();
256
+ void negotiate(const Setup& setup);
257
+ void activate();
258
+ void deactivate();
259
+ void flushParameters();
260
+ void readBuses();
261
+ Json busesJson() const;
262
+ Json parameterJson(int32 index) const;
263
+ void requireLoaded() const;
264
+ Transport parseTransport(const Json& transport, const std::string& where) const;
265
+
266
+ EventLoop& loop_;
267
+ bool ui_;
268
+ PluginCallbacks callbacks_;
269
+
270
+ #if defined(__linux__)
271
+ IPtr<RunLoop> runLoop_;
272
+ #endif
273
+ IPtr<HostApplication> hostContext_;
274
+ IPtr<ComponentHandler> componentHandler_;
275
+
276
+ VST3::Hosting::Module::Ptr module_;
277
+ std::string path_;
278
+ VST3::Hosting::ClassInfo class_;
279
+ Setup setup_;
280
+
281
+ IPtr<Vst::IComponent> component_;
282
+ IPtr<Vst::IAudioProcessor> processor_;
283
+ IPtr<Vst::IEditController> controller_;
284
+ IPtr<Vst::IMidiMapping> midiMapping_;
285
+ IPtr<Vst::IConnectionPoint> componentConnection_;
286
+ IPtr<Vst::IConnectionPoint> controllerConnection_;
287
+ bool separateController_ = false;
288
+ bool active_ = false;
289
+
290
+ std::vector<Bus> audioInputs_;
291
+ std::vector<Bus> audioOutputs_;
292
+ std::vector<Bus> eventInputs_;
293
+ std::vector<Bus> eventOutputs_;
294
+
295
+ // The transport, shared by offline and live processing and changed
296
+ // by the interface; `transportEdits_` counts its outside changes.
297
+ mutable std::mutex transportMutex_;
298
+ Transport transport_;
299
+ uint64_t transportEdits_ = 0;
300
+ int64 continuousSamples_ = 0;
301
+ int32 parameterCount_ = 0;
302
+ int parameterGeneration_ = 0;
303
+
304
+ std::recursive_mutex processing_;
305
+
306
+ // Edits from setParameter, the editor and the interface, and notes
307
+ // from the interface, for the next process call.
308
+ std::mutex pendingMutex_;
309
+ std::vector<ParameterChange> pending_;
310
+ std::vector<Vst::Event> pendingEvents_;
311
+ int32 pendingRestart_ = 0;
312
+
313
+ // The editor (UI mode).
314
+ IPtr<IPlugView> view_;
315
+ IPtr<PlugFrame> frame_;
316
+ std::unique_ptr<EditorWindow> window_;
317
+ double scale_ = 1;
318
+ bool resizing_ = false;
319
+ int editorGeneration_ = 0;
320
+ };
321
+ } // namespace soundor::vst3host