@soundor/vst3-host 0.0.0-stage → 0.1.1

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,416 @@
1
+ #include "LiveAudio.h"
2
+
3
+ #include <SDL3/SDL.h>
4
+
5
+ #include <algorithm>
6
+ #include <cmath>
7
+ #include <cstring>
8
+ #include <filesystem>
9
+
10
+ namespace soundor::vst3host
11
+ {
12
+ namespace
13
+ {
14
+ constexpr double oscillatorFrequency = 220;
15
+ constexpr float oscillatorLevel = 0.3F;
16
+ // How far the volume moves per sample: a 10 ms ramp from 0 to 1.
17
+ constexpr float gainStep = 1.0F / 480.0F;
18
+
19
+ SDL_AudioSpec specAt(double rate)
20
+ {
21
+ SDL_AudioSpec spec {};
22
+ spec.format = SDL_AUDIO_F32;
23
+ spec.channels = 2;
24
+ spec.freq = static_cast<int>(std::lround(rate));
25
+ return spec;
26
+ }
27
+ } // namespace
28
+
29
+ const char* inputName(AudioInput input)
30
+ {
31
+ switch (input)
32
+ {
33
+ case AudioInput::oscillator:
34
+ return "oscillator";
35
+ case AudioInput::file:
36
+ return "file";
37
+ case AudioInput::microphone:
38
+ return "microphone";
39
+ case AudioInput::none:
40
+ return "none";
41
+ }
42
+ return "none";
43
+ }
44
+
45
+ std::optional<AudioInput> parseInput(const std::string& name)
46
+ {
47
+ for (const auto input : { AudioInput::oscillator, AudioInput::file, AudioInput::microphone, AudioInput::none })
48
+ if (name == inputName(input))
49
+ return input;
50
+ return std::nullopt;
51
+ }
52
+
53
+ LiveAudio::LiveAudio(EventLoop& loop, Plugin& plugin) : loop_(loop), plugin_(plugin) {}
54
+
55
+ LiveAudio::~LiveAudio()
56
+ {
57
+ stop();
58
+ }
59
+
60
+ void LiveAudio::start()
61
+ {
62
+ if (output_ != nullptr)
63
+ return;
64
+ if (! plugin_.loaded())
65
+ throw Failure("invalid-state", "no plugin is loaded");
66
+ if (! SDL_WasInit(SDL_INIT_AUDIO) && ! SDL_InitSubSystem(SDL_INIT_AUDIO))
67
+ throw Failure("audio-unavailable", std::string("audio could not start: ") + SDL_GetError());
68
+ sampleRate_ = plugin_.setup().sampleRate;
69
+ const SDL_AudioSpec spec = specAt(sampleRate_);
70
+ output_ = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, &LiveAudio::feed, this);
71
+ if (output_ == nullptr)
72
+ throw Failure("audio-unavailable",
73
+ std::string("no audio output device could be opened: ") + SDL_GetError());
74
+ {
75
+ const std::scoped_lock lock(errorMutex_);
76
+ error_.clear();
77
+ }
78
+ gain_ = 0;
79
+ convertFile();
80
+ if (input_ == AudioInput::microphone)
81
+ openMicrophone();
82
+ playingInput_ = input_;
83
+ SDL_ResumeAudioStreamDevice(output_);
84
+ }
85
+
86
+ void LiveAudio::stop()
87
+ {
88
+ if (output_ == nullptr)
89
+ return;
90
+ // Waits for a callback in progress.
91
+ SDL_DestroyAudioStream(output_);
92
+ output_ = nullptr;
93
+ closeMicrophone();
94
+ peaks_[0] = 0;
95
+ peaks_[1] = 0;
96
+ drainOutputChanges();
97
+ }
98
+
99
+ void LiveAudio::setVolume(double volume)
100
+ {
101
+ volume_ = std::isfinite(volume) ? std::clamp(volume, 0.0, 1.0) : 0.0;
102
+ }
103
+
104
+ void LiveAudio::setInput(AudioInput input, const std::string& file)
105
+ {
106
+ if (input == AudioInput::file)
107
+ {
108
+ if (file.empty() && filePath_.empty())
109
+ throw Failure("invalid-request", "the file input needs a WAV file");
110
+ if (! file.empty())
111
+ {
112
+ SDL_AudioSpec spec {};
113
+ Uint8* bytes = nullptr;
114
+ Uint32 length = 0;
115
+ if (! SDL_LoadWAV(file.c_str(), &spec, &bytes, &length))
116
+ throw Failure("invalid-request",
117
+ "\"" + file + "\" could not be read as a WAV file: " + SDL_GetError());
118
+ std::vector<unsigned char> decoded(bytes, bytes + length);
119
+ SDL_free(bytes);
120
+ if (output_ != nullptr)
121
+ SDL_LockAudioStream(output_);
122
+ fileBytes_ = std::move(decoded);
123
+ fileFormat_ = static_cast<int>(spec.format);
124
+ fileChannels_ = spec.channels;
125
+ fileRate_ = spec.freq;
126
+ filePath_ = file;
127
+ if (output_ != nullptr)
128
+ SDL_UnlockAudioStream(output_);
129
+ convertFile();
130
+ }
131
+ }
132
+ inputError_.clear();
133
+ if (output_ != nullptr)
134
+ SDL_LockAudioStream(output_);
135
+ input_ = input;
136
+ playingInput_ = input;
137
+ filePosition_ = 0;
138
+ if (output_ != nullptr)
139
+ SDL_UnlockAudioStream(output_);
140
+ if (output_ != nullptr && input == AudioInput::microphone)
141
+ openMicrophone();
142
+ else
143
+ closeMicrophone();
144
+ }
145
+
146
+ std::string LiveAudio::fileName() const
147
+ {
148
+ return filePath_.empty() ? std::string() : std::filesystem::path(filePath_).filename().string();
149
+ }
150
+
151
+ std::string LiveAudio::device() const
152
+ {
153
+ if (output_ == nullptr)
154
+ return {};
155
+ const char* name = SDL_GetAudioDeviceName(SDL_GetAudioStreamDevice(output_));
156
+ return name != nullptr ? name : "";
157
+ }
158
+
159
+ std::string LiveAudio::error() const
160
+ {
161
+ const std::scoped_lock lock(errorMutex_);
162
+ return error_;
163
+ }
164
+
165
+ void LiveAudio::formatChanged()
166
+ {
167
+ if (output_ == nullptr)
168
+ return;
169
+ sampleRate_ = plugin_.setup().sampleRate;
170
+ const SDL_AudioSpec spec = specAt(sampleRate_);
171
+ SDL_SetAudioStreamFormat(output_, &spec, nullptr);
172
+ if (microphone_ != nullptr)
173
+ SDL_SetAudioStreamFormat(microphone_, nullptr, &spec);
174
+ convertFile();
175
+ }
176
+
177
+ Json LiveAudio::status() const
178
+ {
179
+ Json result = Json::object();
180
+ result.set("running", running());
181
+ result.set("muted", muted());
182
+ result.set("volume", volume());
183
+ result.set("input", inputName(input_));
184
+ result.set("file", filePath_.empty() ? Json() : Json(filePath_));
185
+ result.set("device", running() ? Json(device()) : Json());
186
+ const std::string failure = error();
187
+ result.set("error", failure.empty() ? Json() : Json(failure));
188
+ result.set("inputError", inputError_.empty() ? Json() : Json(inputError_));
189
+ return result;
190
+ }
191
+
192
+ void LiveAudio::openMicrophone()
193
+ {
194
+ if (microphone_ != nullptr)
195
+ return;
196
+ const SDL_AudioSpec spec = specAt(sampleRate_);
197
+ SDL_AudioStream* stream =
198
+ SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_RECORDING, &spec, nullptr, nullptr);
199
+ if (stream == nullptr)
200
+ {
201
+ inputError_ = std::string("the microphone could not be opened: ") + SDL_GetError();
202
+ return;
203
+ }
204
+ SDL_ResumeAudioStreamDevice(stream);
205
+ SDL_LockAudioStream(output_);
206
+ microphone_ = stream;
207
+ SDL_UnlockAudioStream(output_);
208
+ }
209
+
210
+ void LiveAudio::closeMicrophone()
211
+ {
212
+ if (microphone_ == nullptr)
213
+ return;
214
+ SDL_AudioStream* stream = microphone_;
215
+ if (output_ != nullptr)
216
+ SDL_LockAudioStream(output_);
217
+ microphone_ = nullptr;
218
+ if (output_ != nullptr)
219
+ SDL_UnlockAudioStream(output_);
220
+ SDL_DestroyAudioStream(stream);
221
+ }
222
+
223
+ void LiveAudio::convertFile()
224
+ {
225
+ if (fileBytes_.empty())
226
+ return;
227
+ SDL_AudioSpec source {};
228
+ source.format = static_cast<SDL_AudioFormat>(fileFormat_);
229
+ source.channels = fileChannels_;
230
+ source.freq = fileRate_;
231
+ const SDL_AudioSpec target = specAt(plugin_.setup().sampleRate);
232
+ Uint8* converted = nullptr;
233
+ int length = 0;
234
+ if (! SDL_ConvertAudioSamples(&source, fileBytes_.data(), static_cast<int>(fileBytes_.size()), &target,
235
+ &converted, &length))
236
+ {
237
+ inputError_ = std::string("the file could not be converted: ") + SDL_GetError();
238
+ return;
239
+ }
240
+ std::vector<float> samples(static_cast<size_t>(length) / sizeof(float));
241
+ std::memcpy(samples.data(), converted, samples.size() * sizeof(float));
242
+ SDL_free(converted);
243
+ if (output_ != nullptr)
244
+ SDL_LockAudioStream(output_);
245
+ file_ = std::move(samples);
246
+ filePosition_ = 0;
247
+ if (output_ != nullptr)
248
+ SDL_UnlockAudioStream(output_);
249
+ }
250
+
251
+ // ── The audio thread ─────────────────────────────────────────────────
252
+
253
+ void LiveAudio::feed(void* self, SDL_AudioStream* stream, int additional, int /*total*/)
254
+ {
255
+ auto& audio = *static_cast<LiveAudio*>(self);
256
+ const int block = std::max(1, static_cast<int>(audio.plugin_.setup().blockSize));
257
+ int frames = additional / static_cast<int>(2 * sizeof(float));
258
+ while (frames > 0)
259
+ {
260
+ const int chunk = std::min(frames, block);
261
+ audio.render(chunk);
262
+ SDL_PutAudioStreamData(stream, audio.mix_.data(), static_cast<int>(audio.mix_.size() * sizeof(float)));
263
+ frames -= chunk;
264
+ }
265
+ }
266
+
267
+ void LiveAudio::readInput(int frames, bool playing)
268
+ {
269
+ stereoInput_.assign(static_cast<size_t>(frames) * 2, 0.0F);
270
+ if (! playing)
271
+ return;
272
+ switch (playingInput_)
273
+ {
274
+ case AudioInput::oscillator:
275
+ for (int frame = 0; frame < frames; ++frame)
276
+ {
277
+ // A triangle wave, as the web runtime's.
278
+ const double value = 4.0 * std::abs(phase_ - std::floor(phase_ + 0.5)) - 1.0;
279
+ const auto sample = static_cast<float>(value) * oscillatorLevel;
280
+ stereoInput_[static_cast<size_t>(frame) * 2] = sample;
281
+ stereoInput_[static_cast<size_t>(frame) * 2 + 1] = sample;
282
+ phase_ += oscillatorFrequency / sampleRate_;
283
+ phase_ -= std::floor(phase_);
284
+ }
285
+ break;
286
+ case AudioInput::file:
287
+ if (file_.size() >= 2)
288
+ for (float& sample : stereoInput_)
289
+ {
290
+ sample = file_[filePosition_];
291
+ filePosition_ = (filePosition_ + 1) % file_.size();
292
+ }
293
+ break;
294
+ case AudioInput::microphone:
295
+ if (microphone_ != nullptr)
296
+ {
297
+ const int wanted = static_cast<int>(stereoInput_.size() * sizeof(float));
298
+ // Never fall behind the device by more than a few blocks.
299
+ const int available = SDL_GetAudioStreamAvailable(microphone_);
300
+ if (available > wanted * 4)
301
+ {
302
+ std::vector<float> discard(static_cast<size_t>(available - wanted) / sizeof(float));
303
+ SDL_GetAudioStreamData(microphone_, discard.data(),
304
+ static_cast<int>(discard.size() * sizeof(float)));
305
+ }
306
+ SDL_GetAudioStreamData(microphone_, stereoInput_.data(), wanted);
307
+ }
308
+ break;
309
+ case AudioInput::none:
310
+ break;
311
+ }
312
+ }
313
+
314
+ void LiveAudio::render(int frames)
315
+ {
316
+ mix_.assign(static_cast<size_t>(frames) * 2, 0.0F);
317
+ const std::unique_lock processing(plugin_.processing(), std::try_to_lock);
318
+ if (processing.owns_lock() && plugin_.loaded() && plugin_.active())
319
+ {
320
+ const bool playing = plugin_.transport().playing;
321
+ readInput(frames, playing);
322
+
323
+ const auto inputs = plugin_.inputBusChannels();
324
+ const auto outputs = plugin_.outputBusChannels();
325
+ const int32 inChannels = plugin_.mainInputChannels();
326
+ const int32 outChannels = plugin_.mainOutputChannels();
327
+ inputChannels_.resize(static_cast<size_t>(std::max(inChannels, 0)));
328
+ job_.inputs.assign(inputs.size(), {});
329
+ for (size_t bus = 0; bus < inputs.size(); ++bus)
330
+ job_.inputs[bus].assign(static_cast<size_t>(inputs[bus]), nullptr);
331
+ for (int32 channel = 0; channel < inChannels && ! inputs.empty(); ++channel)
332
+ {
333
+ auto& samples = inputChannels_[static_cast<size_t>(channel)];
334
+ samples.assign(static_cast<size_t>(frames), 0.0F);
335
+ for (int frame = 0; frame < frames; ++frame)
336
+ {
337
+ const float left = stereoInput_[static_cast<size_t>(frame) * 2];
338
+ const float right = stereoInput_[static_cast<size_t>(frame) * 2 + 1];
339
+ samples[static_cast<size_t>(frame)] = inChannels == 1 ? (left + right) * 0.5F
340
+ : channel == 0 ? left
341
+ : channel == 1 ? right
342
+ : 0.0F;
343
+ }
344
+ job_.inputs[0][static_cast<size_t>(channel)] = samples.data();
345
+ }
346
+ job_.frames = frames;
347
+ job_.outputChannels.assign(outputs.size(), 0);
348
+ if (! outputs.empty())
349
+ job_.outputChannels[0] = outChannels;
350
+ job_.output.resize(static_cast<size_t>(std::max(outChannels, 0)) * static_cast<size_t>(frames)
351
+ * sizeof(float));
352
+ job_.outputChanges.clear();
353
+ job_.outputEvents.clear();
354
+ job_.error.clear();
355
+ plugin_.runProcess(job_);
356
+
357
+ if (! job_.error.empty())
358
+ {
359
+ const std::scoped_lock lock(errorMutex_);
360
+ error_ = job_.error;
361
+ }
362
+ else if (outChannels > 0)
363
+ {
364
+ const auto* output = reinterpret_cast<const float*>(job_.output.data());
365
+ const float* left = output;
366
+ const float* right = outChannels > 1 ? output + frames : output;
367
+ for (int frame = 0; frame < frames; ++frame)
368
+ {
369
+ mix_[static_cast<size_t>(frame) * 2] = left[frame];
370
+ mix_[static_cast<size_t>(frame) * 2 + 1] = right[frame];
371
+ }
372
+ }
373
+ if (! job_.outputChanges.empty())
374
+ {
375
+ {
376
+ const std::unique_lock lock(changesMutex_, std::try_to_lock);
377
+ if (lock.owns_lock())
378
+ changes_.insert(changes_.end(), job_.outputChanges.begin(), job_.outputChanges.end());
379
+ }
380
+ if (! drainPosted_.exchange(true))
381
+ loop_.post([this] { drainOutputChanges(); });
382
+ }
383
+ }
384
+
385
+ const float target = muted_ ? 0.0F : static_cast<float>(volume_.load());
386
+ float peaks[2] = { 0, 0 };
387
+ for (int frame = 0; frame < frames; ++frame)
388
+ {
389
+ gain_ += std::clamp(target - gain_, -gainStep, gainStep);
390
+ for (int side = 0; side < 2; ++side)
391
+ {
392
+ float& sample = mix_[static_cast<size_t>(frame) * 2 + static_cast<size_t>(side)];
393
+ sample = std::isfinite(sample) ? std::clamp(sample * gain_, -1.0F, 1.0F) : 0.0F;
394
+ peaks[side] = std::max(peaks[side], std::abs(sample));
395
+ }
396
+ }
397
+ for (int side = 0; side < 2; ++side)
398
+ {
399
+ float previous = peaks_[side].load();
400
+ while (peaks[side] > previous && ! peaks_[side].compare_exchange_weak(previous, peaks[side]))
401
+ {
402
+ }
403
+ }
404
+ }
405
+
406
+ void LiveAudio::drainOutputChanges()
407
+ {
408
+ drainPosted_ = false;
409
+ std::vector<ParameterChange> changes;
410
+ {
411
+ const std::scoped_lock lock(changesMutex_);
412
+ changes.swap(changes_);
413
+ }
414
+ plugin_.applyOutputChanges(changes);
415
+ }
416
+ } // namespace soundor::vst3host
@@ -0,0 +1,121 @@
1
+ // Live audio: the plugin plays through the default output device, in real
2
+ // time, with an input of the host's choosing (SDL3's audio).
3
+ //
4
+ // The plugin processes on SDL's audio thread, which only tries the plugin's
5
+ // processing lock: while a request changes the plugin (or an offline process
6
+ // call runs), live audio plays silence instead of waiting. The input plays
7
+ // while the transport does, as in the web runtime; the plugin's main output
8
+ // bus goes to the device in stereo (mono to both sides, the first two
9
+ // channels of anything wider).
10
+ #pragma once
11
+
12
+ #include "EventLoop.h"
13
+ #include "Json.h"
14
+ #include "Plugin.h"
15
+
16
+ #include <atomic>
17
+ #include <functional>
18
+ #include <mutex>
19
+ #include <optional>
20
+ #include <string>
21
+ #include <vector>
22
+
23
+ struct SDL_AudioStream;
24
+
25
+ namespace soundor::vst3host
26
+ {
27
+ enum class AudioInput
28
+ {
29
+ oscillator,
30
+ file,
31
+ microphone,
32
+ none,
33
+ };
34
+
35
+ const char* inputName(AudioInput input);
36
+ std::optional<AudioInput> parseInput(const std::string& name);
37
+
38
+ class LiveAudio
39
+ {
40
+ public:
41
+ LiveAudio(EventLoop& loop, Plugin& plugin);
42
+ ~LiveAudio();
43
+ LiveAudio(const LiveAudio&) = delete;
44
+ LiveAudio& operator=(const LiveAudio&) = delete;
45
+
46
+ /** Opens the default output device; `audio-unavailable` without one. */
47
+ void start();
48
+ void stop();
49
+ [[nodiscard]] bool running() const { return output_ != nullptr; }
50
+
51
+ void setMuted(bool muted) { muted_ = muted; }
52
+ [[nodiscard]] bool muted() const { return muted_; }
53
+ void setVolume(double volume);
54
+ [[nodiscard]] double volume() const { return volume_; }
55
+
56
+ /** Selects the input; a file is a WAV file's path, decoded now. */
57
+ void setInput(AudioInput input, const std::string& file = {});
58
+ [[nodiscard]] AudioInput input() const { return input_; }
59
+ /** The file's name, without its directory; empty without one. */
60
+ [[nodiscard]] std::string fileName() const;
61
+ [[nodiscard]] std::string device() const;
62
+ /** What failed last: the device or processing, and the input. */
63
+ [[nodiscard]] std::string error() const;
64
+ [[nodiscard]] std::string inputError() const { return inputError_; }
65
+
66
+ /** The plugin's sample rate or buses changed. */
67
+ void formatChanged();
68
+ /** The loudest sample per side since the last call, after the volume. */
69
+ float takePeak(int side) { return peaks_[side].exchange(0); }
70
+
71
+ Json status() const;
72
+
73
+ private:
74
+ static void feed(void* self, SDL_AudioStream* stream, int additional, int total);
75
+ void render(int frames);
76
+ void readInput(int frames, bool playing);
77
+ void openMicrophone();
78
+ void closeMicrophone();
79
+ void convertFile();
80
+ void drainOutputChanges();
81
+
82
+ EventLoop& loop_;
83
+ Plugin& plugin_;
84
+ SDL_AudioStream* output_ = nullptr;
85
+ SDL_AudioStream* microphone_ = nullptr;
86
+ double sampleRate_ = 48000;
87
+
88
+ std::atomic<bool> muted_ = false;
89
+ std::atomic<double> volume_ = 0.8;
90
+ AudioInput input_ = AudioInput::oscillator;
91
+ std::string inputError_;
92
+ std::string filePath_;
93
+ // The file as decoded, and as stereo float at the plugin's rate.
94
+ // Changed only with the output stream locked.
95
+ std::vector<unsigned char> fileBytes_;
96
+ int fileFormat_ = 0;
97
+ int fileChannels_ = 0;
98
+ int fileRate_ = 0;
99
+ std::vector<float> file_;
100
+ AudioInput playingInput_ = AudioInput::oscillator;
101
+
102
+ // The audio thread's own.
103
+ ProcessJob job_;
104
+ std::vector<std::vector<float>> inputChannels_;
105
+ std::vector<float> stereoInput_;
106
+ std::vector<float> mix_;
107
+ double phase_ = 0;
108
+ size_t filePosition_ = 0;
109
+ uint32_t noise_ = 1;
110
+ float gain_ = 0;
111
+ std::atomic<float> peaks_[2] = { 0.0F, 0.0F };
112
+
113
+ mutable std::mutex errorMutex_;
114
+ std::string error_;
115
+
116
+ // Output parameter values (meters) on their way to the controller.
117
+ std::mutex changesMutex_;
118
+ std::vector<ParameterChange> changes_;
119
+ std::atomic<bool> drainPosted_ = false;
120
+ };
121
+ } // namespace soundor::vst3host