RAM2118 commited on
Commit
9788b5f
·
verified ·
1 Parent(s): 242769f

fix: call onGuiCreated callback after CARemoteLayerServer setup completes in showGui()

Browse files
Files changed (1) hide show
  1. Source/Helper/HelperPluginHost.mm +47 -121
Source/Helper/HelperPluginHost.mm CHANGED
@@ -30,51 +30,40 @@ HelperPluginHost::~HelperPluginHost()
30
  bool HelperPluginHost::loadPlugin(const juce::String& path, double sampleRate, int blockSize)
31
  {
32
  unloadPlugin();
33
-
34
  currentSampleRate = sampleRate;
35
  currentBlockSize = blockSize;
36
 
37
  juce::File pluginFile(path);
38
- if (!pluginFile.exists())
39
- return false;
40
 
41
  juce::OwnedArray<juce::PluginDescription> typesFound;
42
-
43
  for (int i = 0; i < formatManager.getNumFormats(); ++i)
44
  {
45
  auto* format = formatManager.getFormat(i);
46
  bool isVST3 = pluginFile.getFileExtension() == ".vst3";
47
  bool isAU = pluginFile.getFileExtension() == ".component";
48
-
49
  if (isVST3 && format->getName() != "VST3") continue;
50
  if (isAU && format->getName() != "AudioUnit") continue;
51
-
52
  format->findAllTypesForFile(typesFound, pluginFile.getFullPathName());
53
- if (typesFound.size() > 0)
54
- break;
55
  }
56
 
57
- if (typesFound.isEmpty())
58
- return false;
59
 
60
  juce::String errorMessage;
61
  plugin = formatManager.createPluginInstance(*typesFound[0], sampleRate, blockSize, errorMessage);
62
-
63
  if (plugin == nullptr)
64
  {
65
- DBG("HelperPluginHost: Failed to create plugin: " + errorMessage);
66
  return false;
67
  }
68
 
69
  plugin->enableAllBuses();
70
-
71
  auto layout = juce::AudioProcessor::BusesLayout();
72
  layout.inputBuses.add(juce::AudioChannelSet::stereo());
73
  layout.outputBuses.add(juce::AudioChannelSet::stereo());
74
-
75
  if (plugin->checkBusesLayoutSupported(layout))
76
  plugin->setBusesLayout(layout);
77
-
78
  plugin->prepareToPlay(sampleRate, blockSize);
79
 
80
  DBG("HelperPluginHost: Loaded " + getPluginName() + " (" + juce::String(getParameterCount()) + " params)");
@@ -84,31 +73,16 @@ bool HelperPluginHost::loadPlugin(const juce::String& path, double sampleRate, i
84
  void HelperPluginHost::unloadPlugin()
85
  {
86
  hideGui();
87
- if (plugin)
88
- {
89
- plugin->releaseResources();
90
- plugin.reset();
91
- }
92
  }
93
 
94
- juce::String HelperPluginHost::getPluginName() const
95
- {
96
- return plugin ? plugin->getName() : juce::String();
97
- }
98
-
99
- int HelperPluginHost::getParameterCount() const
100
- {
101
- return plugin ? plugin->getParameters().size() : 0;
102
- }
103
 
104
  // --- Audio ---
105
 
106
  void HelperPluginHost::setSharedAudio(SharedAudioBuffer* shm, SharedSemaphore* inSem, SharedSemaphore* outSem)
107
- {
108
- sharedAudio = shm;
109
- inputSem = inSem;
110
- outputSem = outSem;
111
- }
112
 
113
  void HelperPluginHost::startProcessing()
114
  {
@@ -121,8 +95,7 @@ void HelperPluginHost::stopProcessing()
121
  {
122
  processing.store(false);
123
  if (inputSem) inputSem->signal();
124
- if (audioThread.joinable())
125
- audioThread.join();
126
  }
127
 
128
  void HelperPluginHost::audioProcessingLoop()
@@ -133,9 +106,7 @@ void HelperPluginHost::audioProcessingLoop()
133
 
134
  while (processing.load())
135
  {
136
- if (!inputSem->wait(100))
137
- continue;
138
-
139
  if (!processing.load()) break;
140
  if (!plugin || !sharedAudio) continue;
141
 
@@ -166,34 +137,26 @@ std::vector<HelperPluginHost::ParamInfo> HelperPluginHost::searchParams(const ju
166
  {
167
  std::vector<ParamInfo> results;
168
  if (!plugin) return results;
169
-
170
  auto& params = plugin->getParameters();
171
  auto keywordLower = keyword.toLowerCase();
172
-
173
  for (int i = 0; i < params.size(); ++i)
174
  {
175
  auto* param = params[i];
176
  auto name = param->getName(128);
177
-
178
  if (keyword.isEmpty() || name.toLowerCase().contains(keywordLower))
179
  {
180
  ParamInfo info;
181
- info.index = i;
182
- info.name = name;
183
  info.value = param->getValue();
184
  info.displayText = param->getText(param->getValue(), 64);
185
-
186
  if (auto* hosted = dynamic_cast<juce::AudioPluginInstance::HostedParameter*>(param))
187
  info.id = hosted->getParameterID();
188
  else
189
  info.id = juce::String(i);
190
-
191
  results.push_back(info);
192
- if (results.size() >= PluginBridgeConstants::kMaxParamResults)
193
- break;
194
  }
195
  }
196
-
197
  return results;
198
  }
199
 
@@ -201,20 +164,16 @@ std::vector<std::pair<int, float>> HelperPluginHost::getParams(const std::vector
201
  {
202
  std::vector<std::pair<int, float>> results;
203
  if (!plugin) return results;
204
-
205
  auto& params = plugin->getParameters();
206
  for (int idx : ids)
207
- {
208
  if (idx >= 0 && idx < params.size())
209
  results.push_back({idx, params[idx]->getValue()});
210
- }
211
  return results;
212
  }
213
 
214
  bool HelperPluginHost::setParams(const std::map<int, float>& values)
215
  {
216
  if (!plugin) return false;
217
-
218
  auto& params = plugin->getParameters();
219
  for (auto& [idx, val] : values)
220
  {
@@ -235,7 +194,7 @@ void HelperPluginHost::showGui()
235
  {
236
  #if JUCE_MAC
237
  if (!plugin || !plugin->hasEditor()) return;
238
- if (editor) return; // already showing
239
 
240
  juce::MessageManager::callAsync([this]()
241
  {
@@ -247,18 +206,16 @@ void HelperPluginHost::showGui()
247
  guiWidth = editor->getWidth();
248
  guiHeight = editor->getHeight();
249
 
250
- // Create a hidden NSWindow to host the editor (needed for layer-backed rendering)
251
  NSWindow* window = [[NSWindow alloc]
252
  initWithContentRect:NSMakeRect(0, 0, guiWidth, guiHeight)
253
  styleMask:NSWindowStyleMaskBorderless
254
  backing:NSBackingStoreBuffered
255
  defer:NO];
256
  [window setReleasedWhenClosed:NO];
257
-
258
- // Make the window layer-backed
259
  [[window contentView] setWantsLayer:YES];
260
 
261
- // Add the JUCE editor as a child view
262
  editor->addToDesktop(0, (void*)[window contentView]);
263
  if (auto* peer = editor->getPeer())
264
  {
@@ -267,24 +224,24 @@ void HelperPluginHost::showGui()
267
  [editorView setFrame:[[window contentView] bounds]];
268
  }
269
 
270
- // Set up CARemoteLayerServer
271
  CARemoteLayerServer* server = [CARemoteLayerServer sharedServer];
272
  remoteLayerServer = (__bridge_retained void*)server;
273
 
274
- // Register the server port via NSMachBootstrapServer
275
  mach_port_t port = server.serverPort;
276
  NSMachPort* machPort = [NSMachPort portWithMachPort:port];
277
- NSString* portName = [NSString stringWithUTF8String:
278
- layerPortName.toRawUTF8()];
279
- [[NSMachBootstrapServer sharedInstance] registerPort:machPort name:portName];
280
-
281
- // The window's content layer IS the remote layer content
282
- // CARemoteLayerServer automatically serves the process's layer tree
283
 
284
  guiWindow = (__bridge_retained void*)window;
285
 
286
- DBG("HelperPluginHost: GUI created, layer port: " + layerPortName +
287
  " size: " + juce::String(guiWidth) + "x" + juce::String(guiHeight));
 
 
 
 
288
  });
289
  #endif
290
  }
@@ -305,10 +262,8 @@ void HelperPluginHost::hideGui()
305
 
306
  if (remoteLayerServer)
307
  {
308
- // Unregister port
309
- NSString* portName = [NSString stringWithUTF8String:layerPortName.toRawUTF8()];
310
- [[NSMachBootstrapServer sharedInstance] registerPort:nil name:portName];
311
-
312
  CFRelease(remoteLayerServer);
313
  remoteLayerServer = nullptr;
314
  }
@@ -324,11 +279,9 @@ void HelperPluginHost::injectMouseDown(float x, float y, int button)
324
  if (!guiWindow) return;
325
  NSWindow* window = (__bridge NSWindow*)guiWindow;
326
  NSEvent* event = [NSEvent mouseEventWithType:NSEventTypeLeftMouseDown
327
- location:NSMakePoint(x, guiHeight - y) // flip Y
328
- modifierFlags:0
329
- timestamp:[[NSProcessInfo processInfo] systemUptime]
330
- windowNumber:[window windowNumber]
331
- context:nil eventNumber:0 clickCount:1 pressure:1.0];
332
  [NSApp postEvent:event atStart:NO];
333
  #endif
334
  }
@@ -340,10 +293,8 @@ void HelperPluginHost::injectMouseUp(float x, float y, int button)
340
  NSWindow* window = (__bridge NSWindow*)guiWindow;
341
  NSEvent* event = [NSEvent mouseEventWithType:NSEventTypeLeftMouseUp
342
  location:NSMakePoint(x, guiHeight - y)
343
- modifierFlags:0
344
- timestamp:[[NSProcessInfo processInfo] systemUptime]
345
- windowNumber:[window windowNumber]
346
- context:nil eventNumber:0 clickCount:1 pressure:0.0];
347
  [NSApp postEvent:event atStart:NO];
348
  #endif
349
  }
@@ -355,10 +306,8 @@ void HelperPluginHost::injectMouseMove(float x, float y)
355
  NSWindow* window = (__bridge NSWindow*)guiWindow;
356
  NSEvent* event = [NSEvent mouseEventWithType:NSEventTypeMouseMoved
357
  location:NSMakePoint(x, guiHeight - y)
358
- modifierFlags:0
359
- timestamp:[[NSProcessInfo processInfo] systemUptime]
360
- windowNumber:[window windowNumber]
361
- context:nil eventNumber:0 clickCount:0 pressure:0.0];
362
  [NSApp postEvent:event atStart:NO];
363
  #endif
364
  }
@@ -370,10 +319,8 @@ void HelperPluginHost::injectMouseDrag(float x, float y, int button)
370
  NSWindow* window = (__bridge NSWindow*)guiWindow;
371
  NSEvent* event = [NSEvent mouseEventWithType:NSEventTypeLeftMouseDragged
372
  location:NSMakePoint(x, guiHeight - y)
373
- modifierFlags:0
374
- timestamp:[[NSProcessInfo processInfo] systemUptime]
375
- windowNumber:[window windowNumber]
376
- context:nil eventNumber:0 clickCount:0 pressure:1.0];
377
  [NSApp postEvent:event atStart:NO];
378
  #endif
379
  }
@@ -382,8 +329,7 @@ void HelperPluginHost::injectMouseScroll(float x, float y, float dx, float dy)
382
  {
383
  #if JUCE_MAC
384
  if (!guiWindow) return;
385
- CGEventRef scrollEvent = CGEventCreateScrollWheelEvent(NULL,
386
- kCGScrollEventUnitPixel, 2, (int32_t)dy, (int32_t)dx);
387
  NSEvent* event = [NSEvent eventWithCGEvent:scrollEvent];
388
  CFRelease(scrollEvent);
389
  [NSApp postEvent:event atStart:NO];
@@ -396,16 +342,10 @@ void HelperPluginHost::injectKeyDown(int keyCode, const juce::String& chars, int
396
  if (!guiWindow) return;
397
  NSWindow* window = (__bridge NSWindow*)guiWindow;
398
  NSString* nsChars = [NSString stringWithUTF8String:chars.toRawUTF8()];
399
- NSEvent* event = [NSEvent keyEventWithType:NSEventTypeKeyDown
400
- location:NSZeroPoint
401
- modifierFlags:(NSEventModifierFlags)modifiers
402
- timestamp:[[NSProcessInfo processInfo] systemUptime]
403
- windowNumber:[window windowNumber]
404
- context:nil
405
- characters:nsChars
406
- charactersIgnoringModifiers:nsChars
407
- isARepeat:NO
408
- keyCode:(unsigned short)keyCode];
409
  [NSApp postEvent:event atStart:NO];
410
  #endif
411
  }
@@ -416,16 +356,10 @@ void HelperPluginHost::injectKeyUp(int keyCode, const juce::String& chars, int m
416
  if (!guiWindow) return;
417
  NSWindow* window = (__bridge NSWindow*)guiWindow;
418
  NSString* nsChars = [NSString stringWithUTF8String:chars.toRawUTF8()];
419
- NSEvent* event = [NSEvent keyEventWithType:NSEventTypeKeyUp
420
- location:NSZeroPoint
421
- modifierFlags:(NSEventModifierFlags)modifiers
422
- timestamp:[[NSProcessInfo processInfo] systemUptime]
423
- windowNumber:[window windowNumber]
424
- context:nil
425
- characters:nsChars
426
- charactersIgnoringModifiers:nsChars
427
- isARepeat:NO
428
- keyCode:(unsigned short)keyCode];
429
  [NSApp postEvent:event atStart:NO];
430
  #endif
431
  }
@@ -444,21 +378,13 @@ bool HelperPluginHost::setStateBase64(const juce::String& base64State)
444
  {
445
  if (!plugin) return false;
446
  juce::MemoryBlock stateData;
447
- if (!stateData.fromBase64Encoding(base64State))
448
- return false;
449
  plugin->setStateInformation(stateData.getData(), (int)stateData.getSize());
450
  return true;
451
  }
452
 
453
  void HelperPluginHost::configure(double sampleRate, int blockSize, int channels)
454
  {
455
- currentSampleRate = sampleRate;
456
- currentBlockSize = blockSize;
457
- currentChannels = channels;
458
-
459
- if (plugin)
460
- {
461
- plugin->releaseResources();
462
- plugin->prepareToPlay(sampleRate, blockSize);
463
- }
464
  }
 
30
  bool HelperPluginHost::loadPlugin(const juce::String& path, double sampleRate, int blockSize)
31
  {
32
  unloadPlugin();
 
33
  currentSampleRate = sampleRate;
34
  currentBlockSize = blockSize;
35
 
36
  juce::File pluginFile(path);
37
+ if (!pluginFile.exists()) return false;
 
38
 
39
  juce::OwnedArray<juce::PluginDescription> typesFound;
 
40
  for (int i = 0; i < formatManager.getNumFormats(); ++i)
41
  {
42
  auto* format = formatManager.getFormat(i);
43
  bool isVST3 = pluginFile.getFileExtension() == ".vst3";
44
  bool isAU = pluginFile.getFileExtension() == ".component";
 
45
  if (isVST3 && format->getName() != "VST3") continue;
46
  if (isAU && format->getName() != "AudioUnit") continue;
 
47
  format->findAllTypesForFile(typesFound, pluginFile.getFullPathName());
48
+ if (typesFound.size() > 0) break;
 
49
  }
50
 
51
+ if (typesFound.isEmpty()) return false;
 
52
 
53
  juce::String errorMessage;
54
  plugin = formatManager.createPluginInstance(*typesFound[0], sampleRate, blockSize, errorMessage);
 
55
  if (plugin == nullptr)
56
  {
57
+ DBG("HelperPluginHost: Failed: " + errorMessage);
58
  return false;
59
  }
60
 
61
  plugin->enableAllBuses();
 
62
  auto layout = juce::AudioProcessor::BusesLayout();
63
  layout.inputBuses.add(juce::AudioChannelSet::stereo());
64
  layout.outputBuses.add(juce::AudioChannelSet::stereo());
 
65
  if (plugin->checkBusesLayoutSupported(layout))
66
  plugin->setBusesLayout(layout);
 
67
  plugin->prepareToPlay(sampleRate, blockSize);
68
 
69
  DBG("HelperPluginHost: Loaded " + getPluginName() + " (" + juce::String(getParameterCount()) + " params)");
 
73
  void HelperPluginHost::unloadPlugin()
74
  {
75
  hideGui();
76
+ if (plugin) { plugin->releaseResources(); plugin.reset(); }
 
 
 
 
77
  }
78
 
79
+ juce::String HelperPluginHost::getPluginName() const { return plugin ? plugin->getName() : juce::String(); }
80
+ int HelperPluginHost::getParameterCount() const { return plugin ? plugin->getParameters().size() : 0; }
 
 
 
 
 
 
 
81
 
82
  // --- Audio ---
83
 
84
  void HelperPluginHost::setSharedAudio(SharedAudioBuffer* shm, SharedSemaphore* inSem, SharedSemaphore* outSem)
85
+ { sharedAudio = shm; inputSem = inSem; outputSem = outSem; }
 
 
 
 
86
 
87
  void HelperPluginHost::startProcessing()
88
  {
 
95
  {
96
  processing.store(false);
97
  if (inputSem) inputSem->signal();
98
+ if (audioThread.joinable()) audioThread.join();
 
99
  }
100
 
101
  void HelperPluginHost::audioProcessingLoop()
 
106
 
107
  while (processing.load())
108
  {
109
+ if (!inputSem->wait(100)) continue;
 
 
110
  if (!processing.load()) break;
111
  if (!plugin || !sharedAudio) continue;
112
 
 
137
  {
138
  std::vector<ParamInfo> results;
139
  if (!plugin) return results;
 
140
  auto& params = plugin->getParameters();
141
  auto keywordLower = keyword.toLowerCase();
 
142
  for (int i = 0; i < params.size(); ++i)
143
  {
144
  auto* param = params[i];
145
  auto name = param->getName(128);
 
146
  if (keyword.isEmpty() || name.toLowerCase().contains(keywordLower))
147
  {
148
  ParamInfo info;
149
+ info.index = i; info.name = name;
 
150
  info.value = param->getValue();
151
  info.displayText = param->getText(param->getValue(), 64);
 
152
  if (auto* hosted = dynamic_cast<juce::AudioPluginInstance::HostedParameter*>(param))
153
  info.id = hosted->getParameterID();
154
  else
155
  info.id = juce::String(i);
 
156
  results.push_back(info);
157
+ if (results.size() >= PluginBridgeConstants::kMaxParamResults) break;
 
158
  }
159
  }
 
160
  return results;
161
  }
162
 
 
164
  {
165
  std::vector<std::pair<int, float>> results;
166
  if (!plugin) return results;
 
167
  auto& params = plugin->getParameters();
168
  for (int idx : ids)
 
169
  if (idx >= 0 && idx < params.size())
170
  results.push_back({idx, params[idx]->getValue()});
 
171
  return results;
172
  }
173
 
174
  bool HelperPluginHost::setParams(const std::map<int, float>& values)
175
  {
176
  if (!plugin) return false;
 
177
  auto& params = plugin->getParameters();
178
  for (auto& [idx, val] : values)
179
  {
 
194
  {
195
  #if JUCE_MAC
196
  if (!plugin || !plugin->hasEditor()) return;
197
+ if (editor) return;
198
 
199
  juce::MessageManager::callAsync([this]()
200
  {
 
206
  guiWidth = editor->getWidth();
207
  guiHeight = editor->getHeight();
208
 
209
+ // Hidden NSWindow for layer-backed rendering
210
  NSWindow* window = [[NSWindow alloc]
211
  initWithContentRect:NSMakeRect(0, 0, guiWidth, guiHeight)
212
  styleMask:NSWindowStyleMaskBorderless
213
  backing:NSBackingStoreBuffered
214
  defer:NO];
215
  [window setReleasedWhenClosed:NO];
 
 
216
  [[window contentView] setWantsLayer:YES];
217
 
218
+ // Add JUCE editor view
219
  editor->addToDesktop(0, (void*)[window contentView]);
220
  if (auto* peer = editor->getPeer())
221
  {
 
224
  [editorView setFrame:[[window contentView] bounds]];
225
  }
226
 
227
+ // CARemoteLayerServer
228
  CARemoteLayerServer* server = [CARemoteLayerServer sharedServer];
229
  remoteLayerServer = (__bridge_retained void*)server;
230
 
231
+ // Register port
232
  mach_port_t port = server.serverPort;
233
  NSMachPort* machPort = [NSMachPort portWithMachPort:port];
234
+ NSString* portNameNS = [NSString stringWithUTF8String:layerPortName.toRawUTF8()];
235
+ [[NSMachBootstrapServer sharedInstance] registerPort:machPort name:portNameNS];
 
 
 
 
236
 
237
  guiWindow = (__bridge_retained void*)window;
238
 
239
+ DBG("HelperPluginHost: GUI ready, port: " + layerPortName +
240
  " size: " + juce::String(guiWidth) + "x" + juce::String(guiHeight));
241
+
242
+ // NOTIFY HOST that GUI is ready with port name and dimensions
243
+ if (onGuiCreated)
244
+ onGuiCreated(layerPortName, guiWidth, guiHeight);
245
  });
246
  #endif
247
  }
 
262
 
263
  if (remoteLayerServer)
264
  {
265
+ NSString* portNameNS = [NSString stringWithUTF8String:layerPortName.toRawUTF8()];
266
+ [[NSMachBootstrapServer sharedInstance] registerPort:nil name:portNameNS];
 
 
267
  CFRelease(remoteLayerServer);
268
  remoteLayerServer = nullptr;
269
  }
 
279
  if (!guiWindow) return;
280
  NSWindow* window = (__bridge NSWindow*)guiWindow;
281
  NSEvent* event = [NSEvent mouseEventWithType:NSEventTypeLeftMouseDown
282
+ location:NSMakePoint(x, guiHeight - y)
283
+ modifierFlags:0 timestamp:[[NSProcessInfo processInfo] systemUptime]
284
+ windowNumber:[window windowNumber] context:nil eventNumber:0 clickCount:1 pressure:1.0];
 
 
285
  [NSApp postEvent:event atStart:NO];
286
  #endif
287
  }
 
293
  NSWindow* window = (__bridge NSWindow*)guiWindow;
294
  NSEvent* event = [NSEvent mouseEventWithType:NSEventTypeLeftMouseUp
295
  location:NSMakePoint(x, guiHeight - y)
296
+ modifierFlags:0 timestamp:[[NSProcessInfo processInfo] systemUptime]
297
+ windowNumber:[window windowNumber] context:nil eventNumber:0 clickCount:1 pressure:0.0];
 
 
298
  [NSApp postEvent:event atStart:NO];
299
  #endif
300
  }
 
306
  NSWindow* window = (__bridge NSWindow*)guiWindow;
307
  NSEvent* event = [NSEvent mouseEventWithType:NSEventTypeMouseMoved
308
  location:NSMakePoint(x, guiHeight - y)
309
+ modifierFlags:0 timestamp:[[NSProcessInfo processInfo] systemUptime]
310
+ windowNumber:[window windowNumber] context:nil eventNumber:0 clickCount:0 pressure:0.0];
 
 
311
  [NSApp postEvent:event atStart:NO];
312
  #endif
313
  }
 
319
  NSWindow* window = (__bridge NSWindow*)guiWindow;
320
  NSEvent* event = [NSEvent mouseEventWithType:NSEventTypeLeftMouseDragged
321
  location:NSMakePoint(x, guiHeight - y)
322
+ modifierFlags:0 timestamp:[[NSProcessInfo processInfo] systemUptime]
323
+ windowNumber:[window windowNumber] context:nil eventNumber:0 clickCount:0 pressure:1.0];
 
 
324
  [NSApp postEvent:event atStart:NO];
325
  #endif
326
  }
 
329
  {
330
  #if JUCE_MAC
331
  if (!guiWindow) return;
332
+ CGEventRef scrollEvent = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 2, (int32_t)dy, (int32_t)dx);
 
333
  NSEvent* event = [NSEvent eventWithCGEvent:scrollEvent];
334
  CFRelease(scrollEvent);
335
  [NSApp postEvent:event atStart:NO];
 
342
  if (!guiWindow) return;
343
  NSWindow* window = (__bridge NSWindow*)guiWindow;
344
  NSString* nsChars = [NSString stringWithUTF8String:chars.toRawUTF8()];
345
+ NSEvent* event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSZeroPoint
346
+ modifierFlags:(NSEventModifierFlags)modifiers timestamp:[[NSProcessInfo processInfo] systemUptime]
347
+ windowNumber:[window windowNumber] context:nil characters:nsChars
348
+ charactersIgnoringModifiers:nsChars isARepeat:NO keyCode:(unsigned short)keyCode];
 
 
 
 
 
 
349
  [NSApp postEvent:event atStart:NO];
350
  #endif
351
  }
 
356
  if (!guiWindow) return;
357
  NSWindow* window = (__bridge NSWindow*)guiWindow;
358
  NSString* nsChars = [NSString stringWithUTF8String:chars.toRawUTF8()];
359
+ NSEvent* event = [NSEvent keyEventWithType:NSEventTypeKeyUp location:NSZeroPoint
360
+ modifierFlags:(NSEventModifierFlags)modifiers timestamp:[[NSProcessInfo processInfo] systemUptime]
361
+ windowNumber:[window windowNumber] context:nil characters:nsChars
362
+ charactersIgnoringModifiers:nsChars isARepeat:NO keyCode:(unsigned short)keyCode];
 
 
 
 
 
 
363
  [NSApp postEvent:event atStart:NO];
364
  #endif
365
  }
 
378
  {
379
  if (!plugin) return false;
380
  juce::MemoryBlock stateData;
381
+ if (!stateData.fromBase64Encoding(base64State)) return false;
 
382
  plugin->setStateInformation(stateData.getData(), (int)stateData.getSize());
383
  return true;
384
  }
385
 
386
  void HelperPluginHost::configure(double sampleRate, int blockSize, int channels)
387
  {
388
+ currentSampleRate = sampleRate; currentBlockSize = blockSize; currentChannels = channels;
389
+ if (plugin) { plugin->releaseResources(); plugin->prepareToPlay(sampleRate, blockSize); }
 
 
 
 
 
 
 
390
  }