RAM2118 commited on
Commit
13fcac4
·
verified ·
1 Parent(s): 1af93a1

Out-of-process rebuild: Add Helper/HelperPluginHost.cpp — full implementation

Browse files
Files changed (1) hide show
  1. Source/Helper/HelperPluginHost.cpp +299 -0
Source/Helper/HelperPluginHost.cpp ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include "HelperPluginHost.h"
2
+ #include "../Shared/Constants.h"
3
+ #include <juce_audio_basics/juce_audio_basics.h>
4
+
5
+ HelperPluginHost::HelperPluginHost()
6
+ {
7
+ #if JUCE_PLUGINHOST_VST3
8
+ formatManager.addFormat(new juce::VST3PluginFormat());
9
+ #endif
10
+ #if JUCE_PLUGINHOST_AU
11
+ formatManager.addFormat(new juce::AudioUnitPluginFormat());
12
+ #endif
13
+ }
14
+
15
+ HelperPluginHost::~HelperPluginHost()
16
+ {
17
+ stopProcessing();
18
+ hideGui();
19
+ unloadPlugin();
20
+ }
21
+
22
+ bool HelperPluginHost::loadPlugin(const juce::String& path, double sampleRate, int blockSize)
23
+ {
24
+ unloadPlugin();
25
+
26
+ currentSampleRate = sampleRate;
27
+ currentBlockSize = blockSize;
28
+
29
+ juce::File pluginFile(path);
30
+ if (!pluginFile.exists())
31
+ return false;
32
+
33
+ juce::OwnedArray<juce::PluginDescription> typesFound;
34
+
35
+ for (int i = 0; i < formatManager.getNumFormats(); ++i)
36
+ {
37
+ auto* format = formatManager.getFormat(i);
38
+ bool isVST3 = pluginFile.getFileExtension() == ".vst3";
39
+ bool isAU = pluginFile.getFileExtension() == ".component";
40
+
41
+ if (isVST3 && format->getName() != "VST3") continue;
42
+ if (isAU && format->getName() != "AudioUnit") continue;
43
+
44
+ format->findAllTypesForFile(typesFound, pluginFile.getFullPathName());
45
+ if (typesFound.size() > 0)
46
+ break;
47
+ }
48
+
49
+ if (typesFound.isEmpty())
50
+ return false;
51
+
52
+ juce::String errorMessage;
53
+ plugin = formatManager.createPluginInstance(*typesFound[0], sampleRate, blockSize, errorMessage);
54
+
55
+ if (plugin == nullptr)
56
+ {
57
+ DBG("HelperPluginHost: Failed to create plugin: " + errorMessage);
58
+ return false;
59
+ }
60
+
61
+ plugin->enableAllBuses();
62
+
63
+ auto layout = juce::AudioProcessor::BusesLayout();
64
+ layout.inputBuses.add(juce::AudioChannelSet::stereo());
65
+ layout.outputBuses.add(juce::AudioChannelSet::stereo());
66
+
67
+ if (plugin->checkBusesLayoutSupported(layout))
68
+ plugin->setBusesLayout(layout);
69
+
70
+ plugin->prepareToPlay(sampleRate, blockSize);
71
+
72
+ DBG("HelperPluginHost: Loaded " + getPluginName() + " (" + juce::String(getParameterCount()) + " params)");
73
+ return true;
74
+ }
75
+
76
+ void HelperPluginHost::unloadPlugin()
77
+ {
78
+ hideGui();
79
+ if (plugin)
80
+ {
81
+ plugin->releaseResources();
82
+ plugin.reset();
83
+ }
84
+ }
85
+
86
+ juce::String HelperPluginHost::getPluginName() const
87
+ {
88
+ return plugin ? plugin->getName() : juce::String();
89
+ }
90
+
91
+ int HelperPluginHost::getParameterCount() const
92
+ {
93
+ return plugin ? plugin->getParameters().size() : 0;
94
+ }
95
+
96
+ void HelperPluginHost::setSharedAudio(SharedAudioBuffer* shm, SharedSemaphore* inSem, SharedSemaphore* outSem)
97
+ {
98
+ sharedAudio = shm;
99
+ inputSem = inSem;
100
+ outputSem = outSem;
101
+ }
102
+
103
+ void HelperPluginHost::startProcessing()
104
+ {
105
+ if (processing.load()) return;
106
+ processing.store(true);
107
+
108
+ audioThread = std::thread([this]() { audioProcessingLoop(); });
109
+ }
110
+
111
+ void HelperPluginHost::stopProcessing()
112
+ {
113
+ processing.store(false);
114
+ if (inputSem) inputSem->signal(); // wake thread so it can exit
115
+ if (audioThread.joinable())
116
+ audioThread.join();
117
+ }
118
+
119
+ void HelperPluginHost::audioProcessingLoop()
120
+ {
121
+ // Set thread to realtime priority
122
+ juce::Thread::setCurrentThreadPriority(10);
123
+
124
+ while (processing.load())
125
+ {
126
+ // Wait for parent to signal input is ready
127
+ if (!inputSem->wait(100))
128
+ continue; // timeout — check if still processing
129
+
130
+ if (!processing.load()) break;
131
+ if (!plugin || !sharedAudio) continue;
132
+
133
+ auto* header = sharedAudio->getHeader();
134
+ int numChannels = header->numChannels.load();
135
+ int blockSize = header->blockSize.load();
136
+
137
+ // Read input from shared memory into local buffer
138
+ juce::AudioBuffer<float> buffer(numChannels, blockSize);
139
+ float* channelPtrs[2] = { buffer.getWritePointer(0),
140
+ numChannels > 1 ? buffer.getWritePointer(1) : nullptr };
141
+ sharedAudio->readInput(channelPtrs, numChannels, blockSize);
142
+
143
+ // Process
144
+ juce::MidiBuffer midi;
145
+ plugin->processBlock(buffer, midi);
146
+
147
+ // Write output to shared memory
148
+ const float* constPtrs[2] = { buffer.getReadPointer(0),
149
+ numChannels > 1 ? buffer.getReadPointer(1) : nullptr };
150
+ sharedAudio->writeOutput(constPtrs, numChannels, blockSize);
151
+
152
+ // Signal parent that output is ready
153
+ header->state.store(kStateOutputReady);
154
+ outputSem->signal();
155
+ }
156
+ }
157
+
158
+ // --- Parameters ---
159
+
160
+ std::vector<HelperPluginHost::ParamInfo> HelperPluginHost::searchParams(const juce::String& keyword) const
161
+ {
162
+ std::vector<ParamInfo> results;
163
+ if (!plugin) return results;
164
+
165
+ auto& params = plugin->getParameters();
166
+ auto keywordLower = keyword.toLowerCase();
167
+
168
+ for (int i = 0; i < params.size(); ++i)
169
+ {
170
+ auto* param = params[i];
171
+ auto name = param->getName(128);
172
+
173
+ if (name.toLowerCase().contains(keywordLower))
174
+ {
175
+ ParamInfo info;
176
+ info.index = i;
177
+ info.name = name;
178
+ info.value = param->getValue();
179
+ info.displayText = param->getText(param->getValue(), 64);
180
+
181
+ if (auto* hosted = dynamic_cast<juce::AudioPluginInstance::HostedParameter*>(param))
182
+ info.id = hosted->getParameterID();
183
+ else
184
+ info.id = juce::String(i);
185
+
186
+ results.push_back(info);
187
+ if (results.size() >= PluginBridgeConstants::kMaxParamResults)
188
+ break;
189
+ }
190
+ }
191
+
192
+ return results;
193
+ }
194
+
195
+ std::vector<std::pair<int, float>> HelperPluginHost::getParams(const std::vector<int>& ids) const
196
+ {
197
+ std::vector<std::pair<int, float>> results;
198
+ if (!plugin) return results;
199
+
200
+ auto& params = plugin->getParameters();
201
+ for (int idx : ids)
202
+ {
203
+ if (idx >= 0 && idx < params.size())
204
+ results.push_back({idx, params[idx]->getValue()});
205
+ }
206
+ return results;
207
+ }
208
+
209
+ bool HelperPluginHost::setParams(const std::map<int, float>& values)
210
+ {
211
+ if (!plugin) return false;
212
+
213
+ auto& params = plugin->getParameters();
214
+ for (auto& [idx, val] : values)
215
+ {
216
+ if (idx >= 0 && idx < params.size())
217
+ {
218
+ auto* param = params[idx];
219
+ param->beginChangeGesture();
220
+ param->setValueNotifyingHost(juce::jlimit(0.0f, 1.0f, val));
221
+ param->endChangeGesture();
222
+ }
223
+ }
224
+ return true;
225
+ }
226
+
227
+ // --- GUI ---
228
+
229
+ void HelperPluginHost::showGui()
230
+ {
231
+ if (!plugin || !plugin->hasEditor()) return;
232
+ if (guiWindow) return; // already showing
233
+
234
+ juce::MessageManager::callAsync([this]()
235
+ {
236
+ if (!plugin) return;
237
+
238
+ auto* editor = plugin->createEditor();
239
+ if (!editor) return;
240
+
241
+ guiWindow = std::make_unique<juce::DocumentWindow>(
242
+ plugin->getName(),
243
+ juce::Colour(0xff1a1a2e),
244
+ juce::DocumentWindow::closeButton | juce::DocumentWindow::minimiseButton);
245
+
246
+ guiWindow->setContentOwned(editor, true);
247
+ guiWindow->setResizable(true, true);
248
+ guiWindow->centreWithSize(editor->getWidth(), editor->getHeight());
249
+ guiWindow->setVisible(true);
250
+ guiWindow->setAlwaysOnTop(true);
251
+ });
252
+ }
253
+
254
+ void HelperPluginHost::hideGui()
255
+ {
256
+ if (guiWindow)
257
+ {
258
+ juce::MessageManager::callAsync([this]()
259
+ {
260
+ guiWindow.reset();
261
+ });
262
+ }
263
+ }
264
+
265
+ // --- State ---
266
+
267
+ juce::String HelperPluginHost::getStateBase64() const
268
+ {
269
+ if (!plugin) return {};
270
+
271
+ juce::MemoryBlock stateData;
272
+ plugin->getStateInformation(stateData);
273
+ return stateData.toBase64Encoding();
274
+ }
275
+
276
+ bool HelperPluginHost::setStateBase64(const juce::String& base64State)
277
+ {
278
+ if (!plugin) return false;
279
+
280
+ juce::MemoryBlock stateData;
281
+ if (!stateData.fromBase64Encoding(base64State))
282
+ return false;
283
+
284
+ plugin->setStateInformation(stateData.getData(), (int)stateData.getSize());
285
+ return true;
286
+ }
287
+
288
+ void HelperPluginHost::configure(double sampleRate, int blockSize, int channels)
289
+ {
290
+ currentSampleRate = sampleRate;
291
+ currentBlockSize = blockSize;
292
+ currentChannels = channels;
293
+
294
+ if (plugin)
295
+ {
296
+ plugin->releaseResources();
297
+ plugin->prepareToPlay(sampleRate, blockSize);
298
+ }
299
+ }