Mercurial > hg > pub > prymula > com
comparison DPF-Prymula-audioplugins/dpf/distrho/src/DistrhoUIVST3.cpp @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2:cf2cb71d31dd | 3:84e66ea83026 |
---|---|
1 /* | |
2 * DISTRHO Plugin Framework (DPF) | |
3 * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |
4 * | |
5 * Permission to use, copy, modify, and/or distribute this software for any purpose with | |
6 * or without fee is hereby granted, provided that the above copyright notice and this | |
7 * permission notice appear in all copies. | |
8 * | |
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |
10 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |
11 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |
12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |
13 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 */ | |
16 | |
17 #include "DistrhoUIInternal.hpp" | |
18 | |
19 #include "travesty/base.h" | |
20 #include "travesty/edit_controller.h" | |
21 #include "travesty/host.h" | |
22 #include "travesty/view.h" | |
23 | |
24 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |
25 # if defined(DISTRHO_OS_MAC) | |
26 # include <CoreFoundation/CoreFoundation.h> | |
27 # elif defined(DISTRHO_OS_WINDOWS) | |
28 # include <winuser.h> | |
29 # define DPF_VST3_WIN32_TIMER_ID 1 | |
30 # endif | |
31 #endif | |
32 | |
33 /* TODO items: | |
34 * - mousewheel event | |
35 * - file request? | |
36 */ | |
37 | |
38 #if !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) | |
39 # define DPF_VST3_USING_HOST_RUN_LOOP 1 | |
40 #else | |
41 # define DPF_VST3_USING_HOST_RUN_LOOP 0 | |
42 #endif | |
43 | |
44 #ifndef DPF_VST3_TIMER_INTERVAL | |
45 # define DPF_VST3_TIMER_INTERVAL 16 /* ~60 fps */ | |
46 #endif | |
47 | |
48 START_NAMESPACE_DISTRHO | |
49 | |
50 // -------------------------------------------------------------------------------------------------------------------- | |
51 | |
52 #if ! DISTRHO_PLUGIN_WANT_STATE | |
53 static constexpr const setStateFunc setStateCallback = nullptr; | |
54 #endif | |
55 #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
56 static constexpr const sendNoteFunc sendNoteCallback = nullptr; | |
57 #endif | |
58 | |
59 // -------------------------------------------------------------------------------------------------------------------- | |
60 // Static data, see DistrhoPlugin.cpp | |
61 | |
62 extern const char* d_nextBundlePath; | |
63 | |
64 // -------------------------------------------------------------------------------------------------------------------- | |
65 // Utility functions (defined on plugin side) | |
66 | |
67 const char* tuid2str(const v3_tuid iid); | |
68 | |
69 // -------------------------------------------------------------------------------------------------------------------- | |
70 | |
71 static void applyGeometryConstraints(const uint minimumWidth, | |
72 const uint minimumHeight, | |
73 const bool keepAspectRatio, | |
74 v3_view_rect* const rect) | |
75 { | |
76 d_debug("applyGeometryConstraints %u %u %d {%d,%d,%d,%d} | BEFORE", | |
77 minimumWidth, minimumHeight, keepAspectRatio, rect->top, rect->left, rect->right, rect->bottom); | |
78 const int32_t minWidth = static_cast<int32_t>(minimumWidth); | |
79 const int32_t minHeight = static_cast<int32_t>(minimumHeight); | |
80 | |
81 if (keepAspectRatio) | |
82 { | |
83 if (rect->right < 1) | |
84 rect->right = 1; | |
85 if (rect->bottom < 1) | |
86 rect->bottom = 1; | |
87 | |
88 const double ratio = static_cast<double>(minWidth) / static_cast<double>(minHeight); | |
89 const double reqRatio = static_cast<double>(rect->right) / static_cast<double>(rect->bottom); | |
90 | |
91 if (d_isNotEqual(ratio, reqRatio)) | |
92 { | |
93 // fix width | |
94 if (reqRatio > ratio) | |
95 rect->right = static_cast<int32_t>(rect->bottom * ratio + 0.5); | |
96 // fix height | |
97 else | |
98 rect->bottom = static_cast<int32_t>(static_cast<double>(rect->right) / ratio + 0.5); | |
99 } | |
100 } | |
101 | |
102 if (minWidth > rect->right) | |
103 rect->right = minWidth; | |
104 if (minHeight > rect->bottom) | |
105 rect->bottom = minHeight; | |
106 | |
107 d_debug("applyGeometryConstraints %u %u %d {%d,%d,%d,%d} | AFTER", | |
108 minimumWidth, minimumHeight, keepAspectRatio, rect->top, rect->left, rect->right, rect->bottom); | |
109 } | |
110 | |
111 // -------------------------------------------------------------------------------------------------------------------- | |
112 | |
113 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |
114 static uint translateVST3Modifiers(const int64_t modifiers) noexcept | |
115 { | |
116 using namespace DGL_NAMESPACE; | |
117 | |
118 uint dglmods = 0; | |
119 if (modifiers & (1 << 0)) | |
120 dglmods |= kModifierShift; | |
121 if (modifiers & (1 << 1)) | |
122 dglmods |= kModifierAlt; | |
123 #ifdef DISTRHO_OS_MAC | |
124 if (modifiers & (1 << 2)) | |
125 dglmods |= kModifierSuper; | |
126 if (modifiers & (1 << 3)) | |
127 dglmods |= kModifierControl; | |
128 #else | |
129 if (modifiers & (1 << 2)) | |
130 dglmods |= kModifierControl; | |
131 if (modifiers & (1 << 3)) | |
132 dglmods |= kModifierSuper; | |
133 #endif | |
134 | |
135 return dglmods; | |
136 } | |
137 #endif | |
138 | |
139 // -------------------------------------------------------------------------------------------------------------------- | |
140 | |
141 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !DPF_VST3_USING_HOST_RUN_LOOP | |
142 /** | |
143 * Helper class for getting a native idle timer via native APIs. | |
144 */ | |
145 class NativeIdleHelper | |
146 { | |
147 public: | |
148 NativeIdleHelper(IdleCallback* const callback) | |
149 : fCallback(callback), | |
150 #ifdef DISTRHO_OS_MAC | |
151 fTimerRef(nullptr) | |
152 #else | |
153 fTimerWindow(nullptr), | |
154 fTimerWindowClassName() | |
155 #endif | |
156 { | |
157 } | |
158 | |
159 void registerNativeIdleCallback() | |
160 { | |
161 #ifdef DISTRHO_OS_MAC | |
162 constexpr const CFTimeInterval interval = DPF_VST3_TIMER_INTERVAL * 0.0001; | |
163 | |
164 CFRunLoopTimerContext context = {}; | |
165 context.info = this; | |
166 fTimerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + interval, interval, 0, 0, | |
167 platformIdleTimerCallback, &context); | |
168 DISTRHO_SAFE_ASSERT_RETURN(fTimerRef != nullptr,); | |
169 | |
170 CFRunLoopAddTimer(CFRunLoopGetCurrent(), fTimerRef, kCFRunLoopCommonModes); | |
171 #else | |
172 /* | |
173 * Create an invisible window to handle a timer. | |
174 * There is no need for implementing a window proc because DefWindowProc already calls the | |
175 * callback function when processing WM_TIMER messages. | |
176 */ | |
177 fTimerWindowClassName = ( | |
178 #ifdef DISTRHO_PLUGIN_BRAND | |
179 DISTRHO_PLUGIN_BRAND | |
180 #else | |
181 DISTRHO_MACRO_AS_STRING(DISTRHO_NAMESPACE) | |
182 #endif | |
183 "-" DISTRHO_PLUGIN_NAME "-" | |
184 ); | |
185 | |
186 char suffix[9]; | |
187 std::snprintf(suffix, sizeof(suffix), "%08x", std::rand()); | |
188 suffix[sizeof(suffix)-1] = '\0'; | |
189 fTimerWindowClassName += suffix; | |
190 | |
191 WNDCLASSEX cls; | |
192 ZeroMemory(&cls, sizeof(cls)); | |
193 cls.cbSize = sizeof(WNDCLASSEX); | |
194 cls.cbWndExtra = sizeof(LONG_PTR); | |
195 cls.lpszClassName = fTimerWindowClassName.buffer(); | |
196 cls.lpfnWndProc = DefWindowProc; | |
197 RegisterClassEx(&cls); | |
198 | |
199 fTimerWindow = CreateWindowEx(0, cls.lpszClassName, "DPF Timer Helper", | |
200 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr); | |
201 DISTRHO_SAFE_ASSERT_RETURN(fTimerWindow != nullptr,); | |
202 | |
203 SetWindowLongPtr(fTimerWindow, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(static_cast<void*>(this))); | |
204 SetTimer(fTimerWindow, DPF_VST3_WIN32_TIMER_ID, DPF_VST3_TIMER_INTERVAL, | |
205 static_cast<TIMERPROC>(platformIdleTimerCallback)); | |
206 #endif | |
207 } | |
208 | |
209 void unregisterNativeIdleCallback() | |
210 { | |
211 #ifdef DISTRHO_OS_MAC | |
212 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), fTimerRef, kCFRunLoopCommonModes); | |
213 CFRelease(fTimerRef); | |
214 #else | |
215 DISTRHO_SAFE_ASSERT_RETURN(fTimerWindow != nullptr,); | |
216 KillTimer(fTimerWindow, DPF_VST3_WIN32_TIMER_ID); | |
217 DestroyWindow(fTimerWindow); | |
218 UnregisterClass(fTimerWindowClassName, nullptr); | |
219 #endif | |
220 } | |
221 | |
222 private: | |
223 IdleCallback* const fCallback; | |
224 | |
225 #ifdef DISTRHO_OS_MAC | |
226 CFRunLoopTimerRef fTimerRef; | |
227 | |
228 static void platformIdleTimerCallback(CFRunLoopTimerRef, void* const info) | |
229 { | |
230 static_cast<NativeIdleHelper*>(info)->fCallback->idleCallback(); | |
231 } | |
232 #else | |
233 HWND fTimerWindow; | |
234 String fTimerWindowClassName; | |
235 | |
236 static void WINAPI platformIdleTimerCallback(const HWND hwnd, UINT, UINT_PTR, DWORD) | |
237 { | |
238 reinterpret_cast<NativeIdleHelper*>(GetWindowLongPtr(hwnd, GWLP_USERDATA))->fCallback->idleCallback(); | |
239 } | |
240 #endif | |
241 }; | |
242 #endif | |
243 | |
244 /** | |
245 * Helper class for getting a native idle timer, either through pugl or via native APIs. | |
246 */ | |
247 #if !DPF_VST3_USING_HOST_RUN_LOOP | |
248 class NativeIdleCallback : public IdleCallback | |
249 { | |
250 public: | |
251 NativeIdleCallback(UIExporter& ui) | |
252 : fCallbackRegistered(false), | |
253 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |
254 fIdleHelper(this) | |
255 #else | |
256 fUI(ui) | |
257 #endif | |
258 { | |
259 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |
260 // unused | |
261 (void)ui; | |
262 #endif | |
263 } | |
264 | |
265 void registerNativeIdleCallback() | |
266 { | |
267 DISTRHO_SAFE_ASSERT_RETURN(!fCallbackRegistered,); | |
268 fCallbackRegistered = true; | |
269 | |
270 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |
271 fIdleHelper.registerNativeIdleCallback(); | |
272 #else | |
273 fUI.addIdleCallbackForNativeIdle(this, DPF_VST3_TIMER_INTERVAL); | |
274 #endif | |
275 } | |
276 | |
277 void unregisterNativeIdleCallback() | |
278 { | |
279 DISTRHO_SAFE_ASSERT_RETURN(fCallbackRegistered,); | |
280 fCallbackRegistered = false; | |
281 | |
282 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |
283 fIdleHelper.unregisterNativeIdleCallback(); | |
284 #else | |
285 fUI.removeIdleCallbackForNativeIdle(this); | |
286 #endif | |
287 } | |
288 | |
289 private: | |
290 bool fCallbackRegistered; | |
291 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |
292 NativeIdleHelper fIdleHelper; | |
293 #else | |
294 UIExporter& fUI; | |
295 #endif | |
296 }; | |
297 #endif | |
298 | |
299 // -------------------------------------------------------------------------------------------------------------------- | |
300 | |
301 /** | |
302 * VST3 UI class. | |
303 * | |
304 * All the dynamic things from VST3 get implemented here, free of complex low-level VST3 pointer things. | |
305 * The UI is created during the "attach" view event, and destroyed during "removed". | |
306 * | |
307 * The low-level VST3 stuff comes after. | |
308 */ | |
309 class UIVst3 | |
310 #if !DPF_VST3_USING_HOST_RUN_LOOP | |
311 : public NativeIdleCallback | |
312 #endif | |
313 { | |
314 public: | |
315 UIVst3(v3_plugin_view** const view, | |
316 v3_host_application** const host, | |
317 v3_connection_point** const connection, | |
318 v3_plugin_frame** const frame, | |
319 const intptr_t winId, | |
320 const float scaleFactor, | |
321 const double sampleRate, | |
322 void* const instancePointer, | |
323 const bool willResizeFromHost, | |
324 const bool needsResizeFromPlugin) | |
325 : | |
326 #if !DPF_VST3_USING_HOST_RUN_LOOP | |
327 NativeIdleCallback(fUI), | |
328 #endif | |
329 fView(view), | |
330 fHostApplication(host), | |
331 fConnection(connection), | |
332 fFrame(frame), | |
333 fScaleFactor(scaleFactor), | |
334 fReadyForPluginData(false), | |
335 fIsResizingFromPlugin(false), | |
336 fIsResizingFromHost(willResizeFromHost), | |
337 fNeedsResizeFromPlugin(needsResizeFromPlugin), | |
338 fNextPluginRect(), | |
339 fUI(this, winId, sampleRate, | |
340 editParameterCallback, | |
341 setParameterCallback, | |
342 setStateCallback, | |
343 sendNoteCallback, | |
344 setSizeCallback, | |
345 nullptr, // TODO file request | |
346 d_nextBundlePath, | |
347 instancePointer, | |
348 scaleFactor) | |
349 { | |
350 } | |
351 | |
352 ~UIVst3() | |
353 { | |
354 #if !DPF_VST3_USING_HOST_RUN_LOOP | |
355 unregisterNativeIdleCallback(); | |
356 #endif | |
357 | |
358 if (fConnection != nullptr) | |
359 disconnect(); | |
360 } | |
361 | |
362 void postInit(uint32_t nextWidth, uint32_t nextHeight) | |
363 { | |
364 if (fIsResizingFromHost && nextWidth > 0 && nextHeight > 0) | |
365 { | |
366 #ifdef DISTRHO_OS_MAC | |
367 const double scaleFactor = fUI.getScaleFactor(); | |
368 nextWidth *= scaleFactor; | |
369 nextHeight *= scaleFactor; | |
370 #endif | |
371 | |
372 if (fUI.getWidth() != nextWidth || fUI.getHeight() != nextHeight) | |
373 { | |
374 d_debug("postInit sets new size as %u %u", nextWidth, nextHeight); | |
375 fUI.setWindowSizeFromHost(nextWidth, nextHeight); | |
376 } | |
377 } | |
378 else if (fNeedsResizeFromPlugin) | |
379 { | |
380 d_debug("postInit forcely sets size from plugin as %u %u", fUI.getWidth(), fUI.getHeight()); | |
381 setSize(fUI.getWidth(), fUI.getHeight()); | |
382 } | |
383 | |
384 if (fConnection != nullptr) | |
385 connect(fConnection); | |
386 | |
387 #if !DPF_VST3_USING_HOST_RUN_LOOP | |
388 registerNativeIdleCallback(); | |
389 #endif | |
390 } | |
391 | |
392 // ---------------------------------------------------------------------------------------------------------------- | |
393 // v3_plugin_view interface calls | |
394 | |
395 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |
396 v3_result onWheel(float /*distance*/) | |
397 { | |
398 // TODO | |
399 return V3_NOT_IMPLEMENTED; | |
400 } | |
401 | |
402 v3_result onKeyDown(const int16_t keychar, const int16_t keycode, const int16_t modifiers) | |
403 { | |
404 DISTRHO_SAFE_ASSERT_INT_RETURN(keychar >= 0 && keychar < 0x7f, keychar, V3_FALSE); | |
405 | |
406 bool special; | |
407 const uint key = translateVstKeyCode(special, keychar, keycode); | |
408 d_debug("onKeyDown %d %d %x -> %d %d", keychar, keycode, modifiers, special, key); | |
409 | |
410 return fUI.handlePluginKeyboardVST(true, special, key, | |
411 keycode >= 0 ? static_cast<uint>(keycode) : 0, | |
412 translateVST3Modifiers(modifiers)) ? V3_TRUE : V3_FALSE; | |
413 } | |
414 | |
415 v3_result onKeyUp(const int16_t keychar, const int16_t keycode, const int16_t modifiers) | |
416 { | |
417 DISTRHO_SAFE_ASSERT_INT_RETURN(keychar >= 0 && keychar < 0x7f, keychar, V3_FALSE); | |
418 | |
419 bool special; | |
420 const uint key = translateVstKeyCode(special, keychar, keycode); | |
421 d_debug("onKeyUp %d %d %x -> %d %d", keychar, keycode, modifiers, special, key); | |
422 | |
423 return fUI.handlePluginKeyboardVST(false, special, key, | |
424 keycode >= 0 ? static_cast<uint>(keycode) : 0, | |
425 translateVST3Modifiers(modifiers)) ? V3_TRUE : V3_FALSE; | |
426 } | |
427 | |
428 v3_result onFocus(const bool state) | |
429 { | |
430 if (state) | |
431 fUI.focus(); | |
432 fUI.notifyFocusChanged(state); | |
433 return V3_OK; | |
434 } | |
435 #endif | |
436 | |
437 v3_result getSize(v3_view_rect* const rect) const noexcept | |
438 { | |
439 if (fIsResizingFromPlugin) | |
440 { | |
441 *rect = fNextPluginRect; | |
442 } | |
443 else | |
444 { | |
445 rect->left = rect->top = 0; | |
446 rect->right = fUI.getWidth(); | |
447 rect->bottom = fUI.getHeight(); | |
448 #ifdef DISTRHO_OS_MAC | |
449 const double scaleFactor = fUI.getScaleFactor(); | |
450 rect->right /= scaleFactor; | |
451 rect->bottom /= scaleFactor; | |
452 #endif | |
453 } | |
454 | |
455 d_debug("getSize request returning %i %i", rect->right, rect->bottom); | |
456 return V3_OK; | |
457 } | |
458 | |
459 v3_result onSize(v3_view_rect* const orect) | |
460 { | |
461 v3_view_rect rect = *orect; | |
462 | |
463 #ifdef DISTRHO_OS_MAC | |
464 const double scaleFactor = fUI.getScaleFactor(); | |
465 rect.top *= scaleFactor; | |
466 rect.left *= scaleFactor; | |
467 rect.right *= scaleFactor; | |
468 rect.bottom *= scaleFactor; | |
469 #endif | |
470 | |
471 if (fIsResizingFromPlugin) | |
472 { | |
473 d_debug("host->plugin onSize request %i %i (plugin resize was active, unsetting now)", | |
474 rect.right - rect.left, rect.bottom - rect.top); | |
475 fIsResizingFromPlugin = false; | |
476 } | |
477 else | |
478 { | |
479 d_debug("host->plugin onSize request %i %i (OK)", rect.right - rect.left, rect.bottom - rect.top); | |
480 } | |
481 | |
482 fIsResizingFromHost = true; | |
483 fUI.setWindowSizeFromHost(rect.right - rect.left, rect.bottom - rect.top); | |
484 return V3_OK; | |
485 } | |
486 | |
487 v3_result setFrame(v3_plugin_frame** const frame) noexcept | |
488 { | |
489 fFrame = frame; | |
490 return V3_OK; | |
491 } | |
492 | |
493 v3_result canResize() noexcept | |
494 { | |
495 return fUI.isResizable() ? V3_TRUE : V3_FALSE; | |
496 } | |
497 | |
498 v3_result checkSizeConstraint(v3_view_rect* const rect) | |
499 { | |
500 uint minimumWidth, minimumHeight; | |
501 bool keepAspectRatio; | |
502 fUI.getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio); | |
503 | |
504 #ifdef DISTRHO_OS_MAC | |
505 const double scaleFactor = fUI.getScaleFactor(); | |
506 minimumWidth /= scaleFactor; | |
507 minimumHeight /= scaleFactor; | |
508 #endif | |
509 | |
510 applyGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, rect); | |
511 return V3_TRUE; | |
512 } | |
513 | |
514 // ---------------------------------------------------------------------------------------------------------------- | |
515 // v3_connection_point interface calls | |
516 | |
517 void connect(v3_connection_point** const point) noexcept | |
518 { | |
519 DISTRHO_SAFE_ASSERT_RETURN(point != nullptr,); | |
520 | |
521 fConnection = point; | |
522 | |
523 d_debug("requesting current plugin state"); | |
524 | |
525 v3_message** const message = createMessage("init"); | |
526 DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); | |
527 | |
528 v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); | |
529 DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); | |
530 | |
531 v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); | |
532 v3_cpp_obj(fConnection)->notify(fConnection, message); | |
533 | |
534 v3_cpp_obj_unref(message); | |
535 } | |
536 | |
537 void disconnect() noexcept | |
538 { | |
539 DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); | |
540 | |
541 d_debug("reporting UI closed"); | |
542 fReadyForPluginData = false; | |
543 | |
544 v3_message** const message = createMessage("close"); | |
545 DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); | |
546 | |
547 v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); | |
548 DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); | |
549 | |
550 v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); | |
551 v3_cpp_obj(fConnection)->notify(fConnection, message); | |
552 | |
553 v3_cpp_obj_unref(message); | |
554 | |
555 fConnection = nullptr; | |
556 } | |
557 | |
558 v3_result notify(v3_message** const message) | |
559 { | |
560 const char* const msgid = v3_cpp_obj(message)->get_message_id(message); | |
561 DISTRHO_SAFE_ASSERT_RETURN(msgid != nullptr, V3_INVALID_ARG); | |
562 | |
563 v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message); | |
564 DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr, V3_INVALID_ARG); | |
565 | |
566 if (std::strcmp(msgid, "ready") == 0) | |
567 { | |
568 DISTRHO_SAFE_ASSERT_RETURN(! fReadyForPluginData, V3_INTERNAL_ERR); | |
569 fReadyForPluginData = true; | |
570 return V3_OK; | |
571 } | |
572 | |
573 if (std::strcmp(msgid, "parameter-set") == 0) | |
574 { | |
575 int64_t rindex; | |
576 double value; | |
577 v3_result res; | |
578 | |
579 res = v3_cpp_obj(attrs)->get_int(attrs, "rindex", &rindex); | |
580 DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); | |
581 | |
582 res = v3_cpp_obj(attrs)->get_float(attrs, "value", &value); | |
583 DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); | |
584 | |
585 if (rindex < kVst3InternalParameterBaseCount) | |
586 { | |
587 switch (rindex) | |
588 { | |
589 #if DPF_VST3_USES_SEPARATE_CONTROLLER | |
590 case kVst3InternalParameterSampleRate: | |
591 DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0, V3_INVALID_ARG); | |
592 fUI.setSampleRate(value, true); | |
593 break; | |
594 #endif | |
595 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
596 case kVst3InternalParameterProgram: | |
597 DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0, V3_INVALID_ARG); | |
598 fUI.programLoaded(static_cast<uint32_t>(value + 0.5)); | |
599 break; | |
600 #endif | |
601 } | |
602 | |
603 // others like latency and buffer-size do not matter on UI side | |
604 return V3_OK; | |
605 } | |
606 | |
607 DISTRHO_SAFE_ASSERT_UINT2_RETURN(rindex >= kVst3InternalParameterCount, rindex, kVst3InternalParameterCount, V3_INVALID_ARG); | |
608 const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterCount); | |
609 | |
610 fUI.parameterChanged(index, value); | |
611 return V3_OK; | |
612 } | |
613 | |
614 #if DISTRHO_PLUGIN_WANT_STATE | |
615 if (std::strcmp(msgid, "state-set") == 0) | |
616 { | |
617 int64_t keyLength = -1; | |
618 int64_t valueLength = -1; | |
619 v3_result res; | |
620 | |
621 res = v3_cpp_obj(attrs)->get_int(attrs, "key:length", &keyLength); | |
622 DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); | |
623 DISTRHO_SAFE_ASSERT_INT_RETURN(keyLength >= 0, keyLength, V3_INTERNAL_ERR); | |
624 | |
625 res = v3_cpp_obj(attrs)->get_int(attrs, "value:length", &valueLength); | |
626 DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); | |
627 DISTRHO_SAFE_ASSERT_INT_RETURN(valueLength >= 0, valueLength, V3_INTERNAL_ERR); | |
628 | |
629 int16_t* const key16 = (int16_t*)std::malloc(sizeof(int16_t)*(keyLength + 1)); | |
630 DISTRHO_SAFE_ASSERT_RETURN(key16 != nullptr, V3_NOMEM); | |
631 | |
632 int16_t* const value16 = (int16_t*)std::malloc(sizeof(int16_t)*(valueLength + 1)); | |
633 DISTRHO_SAFE_ASSERT_RETURN(value16 != nullptr, V3_NOMEM); | |
634 | |
635 res = v3_cpp_obj(attrs)->get_string(attrs, "key", key16, sizeof(int16_t)*(keyLength+1)); | |
636 DISTRHO_SAFE_ASSERT_INT2_RETURN(res == V3_OK, res, keyLength, res); | |
637 | |
638 if (valueLength != 0) | |
639 { | |
640 res = v3_cpp_obj(attrs)->get_string(attrs, "value", value16, sizeof(int16_t)*(valueLength+1)); | |
641 DISTRHO_SAFE_ASSERT_INT2_RETURN(res == V3_OK, res, valueLength, res); | |
642 } | |
643 | |
644 // do cheap inline conversion | |
645 char* const key = (char*)key16; | |
646 char* const value = (char*)value16; | |
647 | |
648 for (int64_t i=0; i<keyLength; ++i) | |
649 key[i] = key16[i]; | |
650 for (int64_t i=0; i<valueLength; ++i) | |
651 value[i] = value16[i]; | |
652 | |
653 key[keyLength] = '\0'; | |
654 value[valueLength] = '\0'; | |
655 | |
656 fUI.stateChanged(key, value); | |
657 | |
658 std::free(key16); | |
659 std::free(value16); | |
660 return V3_OK; | |
661 } | |
662 #endif | |
663 | |
664 d_stderr("UIVst3 received unknown msg '%s'", msgid); | |
665 | |
666 return V3_NOT_IMPLEMENTED; | |
667 } | |
668 | |
669 // ---------------------------------------------------------------------------------------------------------------- | |
670 // v3_plugin_view_content_scale_steinberg interface calls | |
671 | |
672 v3_result setContentScaleFactor(const float factor) | |
673 { | |
674 if (d_isEqual(fScaleFactor, factor)) | |
675 return V3_OK; | |
676 | |
677 fScaleFactor = factor; | |
678 fUI.notifyScaleFactorChanged(factor); | |
679 return V3_OK; | |
680 } | |
681 | |
682 #if DPF_VST3_USING_HOST_RUN_LOOP | |
683 // ---------------------------------------------------------------------------------------------------------------- | |
684 // v3_timer_handler interface calls | |
685 | |
686 void onTimer() | |
687 { | |
688 fUI.plugin_idle(); | |
689 doIdleStuff(); | |
690 } | |
691 #else | |
692 // ---------------------------------------------------------------------------------------------------------------- | |
693 // special idle callback without v3_timer_handler | |
694 | |
695 void idleCallback() override | |
696 { | |
697 fUI.idleFromNativeIdle(); | |
698 doIdleStuff(); | |
699 } | |
700 #endif | |
701 | |
702 void doIdleStuff() | |
703 { | |
704 if (fReadyForPluginData) | |
705 { | |
706 fReadyForPluginData = false; | |
707 requestMorePluginData(); | |
708 } | |
709 | |
710 if (fNeedsResizeFromPlugin) | |
711 { | |
712 fNeedsResizeFromPlugin = false; | |
713 d_debug("first resize forced behaviour is now stopped"); | |
714 } | |
715 | |
716 if (fIsResizingFromHost) | |
717 { | |
718 fIsResizingFromHost = false; | |
719 d_debug("was resizing from host, now stopped"); | |
720 } | |
721 | |
722 if (fIsResizingFromPlugin) | |
723 { | |
724 fIsResizingFromPlugin = false; | |
725 d_debug("was resizing from plugin, now stopped"); | |
726 } | |
727 } | |
728 | |
729 // ---------------------------------------------------------------------------------------------------------------- | |
730 | |
731 private: | |
732 // VST3 stuff | |
733 v3_plugin_view** const fView; | |
734 v3_host_application** const fHostApplication; | |
735 v3_connection_point** fConnection; | |
736 v3_plugin_frame** fFrame; | |
737 | |
738 // Temporary data | |
739 float fScaleFactor; | |
740 bool fReadyForPluginData; | |
741 bool fIsResizingFromPlugin; | |
742 bool fIsResizingFromHost; | |
743 bool fNeedsResizeFromPlugin; | |
744 v3_view_rect fNextPluginRect; // for when plugin requests a new size | |
745 | |
746 // Plugin UI (after VST3 stuff so the UI can call into us during its constructor) | |
747 UIExporter fUI; | |
748 | |
749 // ---------------------------------------------------------------------------------------------------------------- | |
750 // helper functions called during message passing | |
751 | |
752 v3_message** createMessage(const char* const id) const | |
753 { | |
754 DISTRHO_SAFE_ASSERT_RETURN(fHostApplication != nullptr, nullptr); | |
755 | |
756 v3_tuid iid; | |
757 std::memcpy(iid, v3_message_iid, sizeof(v3_tuid)); | |
758 v3_message** msg = nullptr; | |
759 const v3_result res = v3_cpp_obj(fHostApplication)->create_instance(fHostApplication, iid, iid, (void**)&msg); | |
760 DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_TRUE, res, nullptr); | |
761 DISTRHO_SAFE_ASSERT_RETURN(msg != nullptr, nullptr); | |
762 | |
763 v3_cpp_obj(msg)->set_message_id(msg, id); | |
764 return msg; | |
765 } | |
766 | |
767 void requestMorePluginData() const | |
768 { | |
769 DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); | |
770 | |
771 v3_message** const message = createMessage("idle"); | |
772 DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); | |
773 | |
774 v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); | |
775 DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); | |
776 | |
777 v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); | |
778 v3_cpp_obj(fConnection)->notify(fConnection, message); | |
779 | |
780 v3_cpp_obj_unref(message); | |
781 } | |
782 | |
783 // ---------------------------------------------------------------------------------------------------------------- | |
784 // DPF callbacks | |
785 | |
786 void editParameter(const uint32_t rindex, const bool started) const | |
787 { | |
788 DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); | |
789 | |
790 v3_message** const message = createMessage("parameter-edit"); | |
791 DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); | |
792 | |
793 v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); | |
794 DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); | |
795 | |
796 v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); | |
797 v3_cpp_obj(attrlist)->set_int(attrlist, "rindex", rindex); | |
798 v3_cpp_obj(attrlist)->set_int(attrlist, "started", started ? 1 : 0); | |
799 v3_cpp_obj(fConnection)->notify(fConnection, message); | |
800 | |
801 v3_cpp_obj_unref(message); | |
802 } | |
803 | |
804 static void editParameterCallback(void* const ptr, const uint32_t rindex, const bool started) | |
805 { | |
806 static_cast<UIVst3*>(ptr)->editParameter(rindex, started); | |
807 } | |
808 | |
809 void setParameterValue(const uint32_t rindex, const float realValue) | |
810 { | |
811 DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); | |
812 | |
813 v3_message** const message = createMessage("parameter-set"); | |
814 DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); | |
815 | |
816 v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); | |
817 DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); | |
818 | |
819 v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); | |
820 v3_cpp_obj(attrlist)->set_int(attrlist, "rindex", rindex); | |
821 v3_cpp_obj(attrlist)->set_float(attrlist, "value", realValue); | |
822 v3_cpp_obj(fConnection)->notify(fConnection, message); | |
823 | |
824 v3_cpp_obj_unref(message); | |
825 } | |
826 | |
827 static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value) | |
828 { | |
829 static_cast<UIVst3*>(ptr)->setParameterValue(rindex, value); | |
830 } | |
831 | |
832 #if DISTRHO_PLUGIN_WANT_STATE | |
833 void setState(const char* const key, const char* const value) | |
834 { | |
835 DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); | |
836 | |
837 v3_message** const message = createMessage("state-set"); | |
838 DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); | |
839 | |
840 v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); | |
841 DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); | |
842 | |
843 v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); | |
844 v3_cpp_obj(attrlist)->set_int(attrlist, "key:length", std::strlen(key)); | |
845 v3_cpp_obj(attrlist)->set_int(attrlist, "value:length", std::strlen(value)); | |
846 v3_cpp_obj(attrlist)->set_string(attrlist, "key", ScopedUTF16String(key)); | |
847 v3_cpp_obj(attrlist)->set_string(attrlist, "value", ScopedUTF16String(value)); | |
848 v3_cpp_obj(fConnection)->notify(fConnection, message); | |
849 | |
850 v3_cpp_obj_unref(message); | |
851 } | |
852 | |
853 static void setStateCallback(void* const ptr, const char* const key, const char* const value) | |
854 { | |
855 static_cast<UIVst3*>(ptr)->setState(key, value); | |
856 } | |
857 #endif | |
858 | |
859 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
860 void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) | |
861 { | |
862 DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); | |
863 | |
864 v3_message** const message = createMessage("midi"); | |
865 DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); | |
866 | |
867 v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message); | |
868 DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,); | |
869 | |
870 uint8_t midiData[3]; | |
871 midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel; | |
872 midiData[1] = note; | |
873 midiData[2] = velocity; | |
874 | |
875 v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1); | |
876 v3_cpp_obj(attrlist)->set_binary(attrlist, "data", midiData, sizeof(midiData)); | |
877 v3_cpp_obj(fConnection)->notify(fConnection, message); | |
878 | |
879 v3_cpp_obj_unref(message); | |
880 } | |
881 | |
882 static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity) | |
883 { | |
884 static_cast<UIVst3*>(ptr)->sendNote(channel, note, velocity); | |
885 } | |
886 #endif | |
887 | |
888 void setSize(uint width, uint height) | |
889 { | |
890 DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,); | |
891 DISTRHO_SAFE_ASSERT_RETURN(fFrame != nullptr,); | |
892 | |
893 #ifdef DISTRHO_OS_MAC | |
894 const double scaleFactor = fUI.getScaleFactor(); | |
895 width /= scaleFactor; | |
896 height /= scaleFactor; | |
897 #endif | |
898 | |
899 if (fIsResizingFromHost) | |
900 { | |
901 if (fNeedsResizeFromPlugin) | |
902 { | |
903 d_debug("plugin->host setSize %u %u (FORCED, exception for first resize)", width, height); | |
904 } | |
905 else | |
906 { | |
907 d_debug("plugin->host setSize %u %u (IGNORED, host resize active)", width, height); | |
908 return; | |
909 } | |
910 } | |
911 else | |
912 { | |
913 d_debug("plugin->host setSize %u %u (OK)", width, height); | |
914 } | |
915 | |
916 fIsResizingFromPlugin = true; | |
917 | |
918 v3_view_rect rect; | |
919 rect.left = rect.top = 0; | |
920 rect.right = width; | |
921 rect.bottom = height; | |
922 fNextPluginRect = rect; | |
923 v3_cpp_obj(fFrame)->resize_view(fFrame, fView, &rect); | |
924 } | |
925 | |
926 static void setSizeCallback(void* const ptr, const uint width, const uint height) | |
927 { | |
928 static_cast<UIVst3*>(ptr)->setSize(width, height); | |
929 } | |
930 }; | |
931 | |
932 // -------------------------------------------------------------------------------------------------------------------- | |
933 | |
934 /** | |
935 * VST3 low-level pointer thingies follow, proceed with care. | |
936 */ | |
937 | |
938 // -------------------------------------------------------------------------------------------------------------------- | |
939 // v3_funknown for classes with a single instance | |
940 | |
941 template<class T> | |
942 static uint32_t V3_API dpf_single_instance_ref(void* const self) | |
943 { | |
944 return ++(*static_cast<T**>(self))->refcounter; | |
945 } | |
946 | |
947 template<class T> | |
948 static uint32_t V3_API dpf_single_instance_unref(void* const self) | |
949 { | |
950 return --(*static_cast<T**>(self))->refcounter; | |
951 } | |
952 | |
953 // -------------------------------------------------------------------------------------------------------------------- | |
954 // dpf_ui_connection_point | |
955 | |
956 struct dpf_ui_connection_point : v3_connection_point_cpp { | |
957 std::atomic_int refcounter; | |
958 ScopedPointer<UIVst3>& uivst3; | |
959 v3_connection_point** other; | |
960 | |
961 dpf_ui_connection_point(ScopedPointer<UIVst3>& v) | |
962 : refcounter(1), | |
963 uivst3(v), | |
964 other(nullptr) | |
965 { | |
966 // v3_funknown, single instance | |
967 query_interface = query_interface_connection_point; | |
968 ref = dpf_single_instance_ref<dpf_ui_connection_point>; | |
969 unref = dpf_single_instance_unref<dpf_ui_connection_point>; | |
970 | |
971 // v3_connection_point | |
972 point.connect = connect; | |
973 point.disconnect = disconnect; | |
974 point.notify = notify; | |
975 } | |
976 | |
977 // ---------------------------------------------------------------------------------------------------------------- | |
978 // v3_funknown | |
979 | |
980 static v3_result V3_API query_interface_connection_point(void* const self, const v3_tuid iid, void** const iface) | |
981 { | |
982 dpf_ui_connection_point* const point = *static_cast<dpf_ui_connection_point**>(self); | |
983 | |
984 if (v3_tuid_match(iid, v3_funknown_iid) || | |
985 v3_tuid_match(iid, v3_connection_point_iid)) | |
986 { | |
987 d_debug("UI|query_interface_connection_point => %p %s %p | OK", self, tuid2str(iid), iface); | |
988 ++point->refcounter; | |
989 *iface = self; | |
990 return V3_OK; | |
991 } | |
992 | |
993 d_debug("DSP|query_interface_connection_point => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); | |
994 | |
995 *iface = NULL; | |
996 return V3_NO_INTERFACE; | |
997 } | |
998 | |
999 // ---------------------------------------------------------------------------------------------------------------- | |
1000 // v3_connection_point | |
1001 | |
1002 static v3_result V3_API connect(void* const self, v3_connection_point** const other) | |
1003 { | |
1004 dpf_ui_connection_point* const point = *static_cast<dpf_ui_connection_point**>(self); | |
1005 d_debug("UI|dpf_ui_connection_point::connect => %p %p", self, other); | |
1006 | |
1007 DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); | |
1008 | |
1009 point->other = other; | |
1010 | |
1011 if (UIVst3* const uivst3 = point->uivst3) | |
1012 uivst3->connect(other); | |
1013 | |
1014 return V3_OK; | |
1015 }; | |
1016 | |
1017 static v3_result V3_API disconnect(void* const self, v3_connection_point** const other) | |
1018 { | |
1019 d_debug("UI|dpf_ui_connection_point::disconnect => %p %p", self, other); | |
1020 dpf_ui_connection_point* const point = *static_cast<dpf_ui_connection_point**>(self); | |
1021 | |
1022 DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); | |
1023 DISTRHO_SAFE_ASSERT(point->other == other); | |
1024 | |
1025 point->other = nullptr; | |
1026 | |
1027 if (UIVst3* const uivst3 = point->uivst3) | |
1028 uivst3->disconnect(); | |
1029 | |
1030 return V3_OK; | |
1031 }; | |
1032 | |
1033 static v3_result V3_API notify(void* const self, v3_message** const message) | |
1034 { | |
1035 dpf_ui_connection_point* const point = *static_cast<dpf_ui_connection_point**>(self); | |
1036 | |
1037 UIVst3* const uivst3 = point->uivst3; | |
1038 DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); | |
1039 | |
1040 return uivst3->notify(message); | |
1041 } | |
1042 }; | |
1043 | |
1044 // -------------------------------------------------------------------------------------------------------------------- | |
1045 // dpf_plugin_view_content_scale | |
1046 | |
1047 struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { | |
1048 std::atomic_int refcounter; | |
1049 ScopedPointer<UIVst3>& uivst3; | |
1050 // cached values | |
1051 float scaleFactor; | |
1052 | |
1053 dpf_plugin_view_content_scale(ScopedPointer<UIVst3>& v) | |
1054 : refcounter(1), | |
1055 uivst3(v), | |
1056 scaleFactor(0.0f) | |
1057 { | |
1058 // v3_funknown, single instance | |
1059 query_interface = query_interface_view_content_scale; | |
1060 ref = dpf_single_instance_ref<dpf_plugin_view_content_scale>; | |
1061 unref = dpf_single_instance_unref<dpf_plugin_view_content_scale>; | |
1062 | |
1063 // v3_plugin_view_content_scale | |
1064 scale.set_content_scale_factor = set_content_scale_factor; | |
1065 } | |
1066 | |
1067 // ---------------------------------------------------------------------------------------------------------------- | |
1068 // v3_funknown | |
1069 | |
1070 static v3_result V3_API query_interface_view_content_scale(void* const self, const v3_tuid iid, void** const iface) | |
1071 { | |
1072 dpf_plugin_view_content_scale* const scale = *static_cast<dpf_plugin_view_content_scale**>(self); | |
1073 | |
1074 if (v3_tuid_match(iid, v3_funknown_iid) || | |
1075 v3_tuid_match(iid, v3_plugin_view_content_scale_iid)) | |
1076 { | |
1077 d_debug("query_interface_view_content_scale => %p %s %p | OK", self, tuid2str(iid), iface); | |
1078 ++scale->refcounter; | |
1079 *iface = self; | |
1080 return V3_OK; | |
1081 } | |
1082 | |
1083 d_debug("query_interface_view_content_scale => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); | |
1084 | |
1085 *iface = NULL; | |
1086 return V3_NO_INTERFACE; | |
1087 } | |
1088 | |
1089 // ---------------------------------------------------------------------------------------------------------------- | |
1090 // v3_plugin_view_content_scale | |
1091 | |
1092 static v3_result V3_API set_content_scale_factor(void* const self, const float factor) | |
1093 { | |
1094 dpf_plugin_view_content_scale* const scale = *static_cast<dpf_plugin_view_content_scale**>(self); | |
1095 d_debug("dpf_plugin_view::set_content_scale_factor => %p %f", self, factor); | |
1096 | |
1097 scale->scaleFactor = factor; | |
1098 | |
1099 if (UIVst3* const uivst3 = scale->uivst3) | |
1100 return uivst3->setContentScaleFactor(factor); | |
1101 | |
1102 return V3_NOT_INITIALIZED; | |
1103 } | |
1104 }; | |
1105 | |
1106 #if DPF_VST3_USING_HOST_RUN_LOOP | |
1107 // -------------------------------------------------------------------------------------------------------------------- | |
1108 // dpf_timer_handler | |
1109 | |
1110 struct dpf_timer_handler : v3_timer_handler_cpp { | |
1111 std::atomic_int refcounter; | |
1112 ScopedPointer<UIVst3>& uivst3; | |
1113 bool valid; | |
1114 | |
1115 dpf_timer_handler(ScopedPointer<UIVst3>& v) | |
1116 : refcounter(1), | |
1117 uivst3(v), | |
1118 valid(true) | |
1119 { | |
1120 // v3_funknown, single instance | |
1121 query_interface = query_interface_timer_handler; | |
1122 ref = dpf_single_instance_ref<dpf_timer_handler>; | |
1123 unref = dpf_single_instance_unref<dpf_timer_handler>; | |
1124 | |
1125 // v3_timer_handler | |
1126 timer.on_timer = on_timer; | |
1127 } | |
1128 | |
1129 // ---------------------------------------------------------------------------------------------------------------- | |
1130 // v3_funknown | |
1131 | |
1132 static v3_result V3_API query_interface_timer_handler(void* self, const v3_tuid iid, void** iface) | |
1133 { | |
1134 dpf_timer_handler* const timer = *static_cast<dpf_timer_handler**>(self); | |
1135 | |
1136 if (v3_tuid_match(iid, v3_funknown_iid) || | |
1137 v3_tuid_match(iid, v3_timer_handler_iid)) | |
1138 { | |
1139 d_debug("query_interface_timer_handler => %p %s %p | OK", self, tuid2str(iid), iface); | |
1140 ++timer->refcounter; | |
1141 *iface = self; | |
1142 return V3_OK; | |
1143 } | |
1144 | |
1145 d_debug("query_interface_timer_handler => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); | |
1146 | |
1147 *iface = NULL; | |
1148 return V3_NO_INTERFACE; | |
1149 } | |
1150 | |
1151 // ---------------------------------------------------------------------------------------------------------------- | |
1152 // v3_timer_handler | |
1153 | |
1154 static void V3_API on_timer(void* self) | |
1155 { | |
1156 dpf_timer_handler* const timer = *static_cast<dpf_timer_handler**>(self); | |
1157 | |
1158 DISTRHO_SAFE_ASSERT_RETURN(timer->valid,); | |
1159 | |
1160 timer->uivst3->onTimer(); | |
1161 } | |
1162 }; | |
1163 #endif | |
1164 | |
1165 // -------------------------------------------------------------------------------------------------------------------- | |
1166 // dpf_plugin_view | |
1167 | |
1168 static const char* const kSupportedPlatforms[] = { | |
1169 #if defined(DISTRHO_OS_WINDOWS) | |
1170 V3_VIEW_PLATFORM_TYPE_HWND, | |
1171 #elif defined(DISTRHO_OS_MAC) | |
1172 V3_VIEW_PLATFORM_TYPE_NSVIEW, | |
1173 #else | |
1174 V3_VIEW_PLATFORM_TYPE_X11, | |
1175 #endif | |
1176 }; | |
1177 | |
1178 struct dpf_plugin_view : v3_plugin_view_cpp { | |
1179 std::atomic_int refcounter; | |
1180 ScopedPointer<dpf_ui_connection_point> connection; | |
1181 ScopedPointer<dpf_plugin_view_content_scale> scale; | |
1182 #if DPF_VST3_USING_HOST_RUN_LOOP | |
1183 ScopedPointer<dpf_timer_handler> timer; | |
1184 #endif | |
1185 ScopedPointer<UIVst3> uivst3; | |
1186 // cached values | |
1187 v3_host_application** const hostApplication; | |
1188 void* const instancePointer; | |
1189 double sampleRate; | |
1190 v3_plugin_frame** frame; | |
1191 v3_run_loop** runloop; | |
1192 uint32_t nextWidth, nextHeight; | |
1193 bool sizeRequestedBeforeBeingAttached; | |
1194 | |
1195 dpf_plugin_view(v3_host_application** const host, void* const instance, const double sr) | |
1196 : refcounter(1), | |
1197 hostApplication(host), | |
1198 instancePointer(instance), | |
1199 sampleRate(sr), | |
1200 frame(nullptr), | |
1201 runloop(nullptr), | |
1202 nextWidth(0), | |
1203 nextHeight(0), | |
1204 sizeRequestedBeforeBeingAttached(false) | |
1205 { | |
1206 d_debug("dpf_plugin_view() with hostApplication %p", hostApplication); | |
1207 | |
1208 // make sure host application is valid through out this view lifetime | |
1209 if (hostApplication != nullptr) | |
1210 v3_cpp_obj_ref(hostApplication); | |
1211 | |
1212 // v3_funknown, everything custom | |
1213 query_interface = query_interface_view; | |
1214 ref = ref_view; | |
1215 unref = unref_view; | |
1216 | |
1217 // v3_plugin_view | |
1218 view.is_platform_type_supported = is_platform_type_supported; | |
1219 view.attached = attached; | |
1220 view.removed = removed; | |
1221 view.on_wheel = on_wheel; | |
1222 view.on_key_down = on_key_down; | |
1223 view.on_key_up = on_key_up; | |
1224 view.get_size = get_size; | |
1225 view.on_size = on_size; | |
1226 view.on_focus = on_focus; | |
1227 view.set_frame = set_frame; | |
1228 view.can_resize = can_resize; | |
1229 view.check_size_constraint = check_size_constraint; | |
1230 } | |
1231 | |
1232 ~dpf_plugin_view() | |
1233 { | |
1234 d_debug("~dpf_plugin_view()"); | |
1235 | |
1236 connection = nullptr; | |
1237 scale = nullptr; | |
1238 #if DPF_VST3_USING_HOST_RUN_LOOP | |
1239 timer = nullptr; | |
1240 #endif | |
1241 uivst3 = nullptr; | |
1242 | |
1243 if (hostApplication != nullptr) | |
1244 v3_cpp_obj_unref(hostApplication); | |
1245 } | |
1246 | |
1247 // ---------------------------------------------------------------------------------------------------------------- | |
1248 // v3_funknown | |
1249 | |
1250 static v3_result V3_API query_interface_view(void* self, const v3_tuid iid, void** iface) | |
1251 { | |
1252 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |
1253 | |
1254 if (v3_tuid_match(iid, v3_funknown_iid) || | |
1255 v3_tuid_match(iid, v3_plugin_view_iid)) | |
1256 { | |
1257 d_debug("query_interface_view => %p %s %p | OK", self, tuid2str(iid), iface); | |
1258 ++view->refcounter; | |
1259 *iface = self; | |
1260 return V3_OK; | |
1261 } | |
1262 | |
1263 if (v3_tuid_match(v3_connection_point_iid, iid)) | |
1264 { | |
1265 d_debug("query_interface_view => %p %s %p | OK convert %p", | |
1266 self, tuid2str(iid), iface, view->connection.get()); | |
1267 | |
1268 if (view->connection == nullptr) | |
1269 view->connection = new dpf_ui_connection_point(view->uivst3); | |
1270 else | |
1271 ++view->connection->refcounter; | |
1272 *iface = &view->connection; | |
1273 return V3_OK; | |
1274 } | |
1275 | |
1276 #ifndef DISTRHO_OS_MAC | |
1277 if (v3_tuid_match(v3_plugin_view_content_scale_iid, iid)) | |
1278 { | |
1279 d_debug("query_interface_view => %p %s %p | OK convert %p", | |
1280 self, tuid2str(iid), iface, view->scale.get()); | |
1281 | |
1282 if (view->scale == nullptr) | |
1283 view->scale = new dpf_plugin_view_content_scale(view->uivst3); | |
1284 else | |
1285 ++view->scale->refcounter; | |
1286 *iface = &view->scale; | |
1287 return V3_OK; | |
1288 } | |
1289 #endif | |
1290 | |
1291 d_debug("query_interface_view => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); | |
1292 | |
1293 *iface = nullptr; | |
1294 return V3_NO_INTERFACE; | |
1295 } | |
1296 | |
1297 static uint32_t V3_API ref_view(void* self) | |
1298 { | |
1299 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |
1300 const int refcount = ++view->refcounter; | |
1301 d_debug("dpf_plugin_view::ref => %p | refcount %i", self, refcount); | |
1302 return refcount; | |
1303 } | |
1304 | |
1305 static uint32_t V3_API unref_view(void* self) | |
1306 { | |
1307 dpf_plugin_view** const viewptr = static_cast<dpf_plugin_view**>(self); | |
1308 dpf_plugin_view* const view = *viewptr; | |
1309 | |
1310 if (const int refcount = --view->refcounter) | |
1311 { | |
1312 d_debug("dpf_plugin_view::unref => %p | refcount %i", self, refcount); | |
1313 return refcount; | |
1314 } | |
1315 | |
1316 if (view->connection != nullptr && view->connection->other) | |
1317 v3_cpp_obj(view->connection->other)->disconnect(view->connection->other, | |
1318 (v3_connection_point**)&view->connection); | |
1319 | |
1320 /** | |
1321 * Some hosts will have unclean instances of a few of the view child classes at this point. | |
1322 * We check for those here, going through the whole possible chain to see if it is safe to delete. | |
1323 * TODO cleanup. | |
1324 */ | |
1325 | |
1326 bool unclean = false; | |
1327 | |
1328 if (dpf_ui_connection_point* const conn = view->connection) | |
1329 { | |
1330 if (const int refcount = conn->refcounter) | |
1331 { | |
1332 unclean = true; | |
1333 d_stderr("DPF warning: asked to delete view while connection point still active (refcount %d)", refcount); | |
1334 } | |
1335 } | |
1336 | |
1337 #ifndef DISTRHO_OS_MAC | |
1338 if (dpf_plugin_view_content_scale* const scale = view->scale) | |
1339 { | |
1340 if (const int refcount = scale->refcounter) | |
1341 { | |
1342 unclean = true; | |
1343 d_stderr("DPF warning: asked to delete view while content scale still active (refcount %d)", refcount); | |
1344 } | |
1345 } | |
1346 #endif | |
1347 | |
1348 if (unclean) | |
1349 return 0; | |
1350 | |
1351 d_debug("dpf_plugin_view::unref => %p | refcount is zero, deleting everything now!", self); | |
1352 | |
1353 delete view; | |
1354 delete viewptr; | |
1355 return 0; | |
1356 } | |
1357 | |
1358 // ---------------------------------------------------------------------------------------------------------------- | |
1359 // v3_plugin_view | |
1360 | |
1361 static v3_result V3_API is_platform_type_supported(void* const self, const char* const platform_type) | |
1362 { | |
1363 d_debug("dpf_plugin_view::is_platform_type_supported => %p %s", self, platform_type); | |
1364 | |
1365 for (size_t i=0; i<ARRAY_SIZE(kSupportedPlatforms); ++i) | |
1366 { | |
1367 if (std::strcmp(kSupportedPlatforms[i], platform_type) == 0) | |
1368 return V3_OK; | |
1369 } | |
1370 | |
1371 return V3_NOT_IMPLEMENTED; | |
1372 | |
1373 // unused unless debug | |
1374 (void)self; | |
1375 } | |
1376 | |
1377 static v3_result V3_API attached(void* const self, void* const parent, const char* const platform_type) | |
1378 { | |
1379 d_debug("dpf_plugin_view::attached => %p %p %s", self, parent, platform_type); | |
1380 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |
1381 DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG); | |
1382 | |
1383 for (size_t i=0; i<ARRAY_SIZE(kSupportedPlatforms); ++i) | |
1384 { | |
1385 if (std::strcmp(kSupportedPlatforms[i], platform_type) == 0) | |
1386 { | |
1387 #if DPF_VST3_USING_HOST_RUN_LOOP | |
1388 // find host run loop to plug ourselves into (required on some systems) | |
1389 DISTRHO_SAFE_ASSERT_RETURN(view->frame != nullptr, V3_INVALID_ARG); | |
1390 | |
1391 v3_run_loop** runloop = nullptr; | |
1392 v3_cpp_obj_query_interface(view->frame, v3_run_loop_iid, &runloop); | |
1393 DISTRHO_SAFE_ASSERT_RETURN(runloop != nullptr, V3_INVALID_ARG); | |
1394 | |
1395 view->runloop = runloop; | |
1396 #endif | |
1397 | |
1398 const float lastScaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; | |
1399 view->uivst3 = new UIVst3((v3_plugin_view**)self, | |
1400 view->hostApplication, | |
1401 view->connection != nullptr ? view->connection->other : nullptr, | |
1402 view->frame, | |
1403 (uintptr_t)parent, | |
1404 lastScaleFactor, | |
1405 view->sampleRate, | |
1406 view->instancePointer, | |
1407 view->nextWidth > 0 && view->nextHeight > 0, | |
1408 view->sizeRequestedBeforeBeingAttached); | |
1409 | |
1410 view->uivst3->postInit(view->nextWidth, view->nextHeight); | |
1411 view->nextWidth = 0; | |
1412 view->nextHeight = 0; | |
1413 view->sizeRequestedBeforeBeingAttached = false; | |
1414 | |
1415 #if DPF_VST3_USING_HOST_RUN_LOOP | |
1416 // register a timer host run loop stuff | |
1417 view->timer = new dpf_timer_handler(view->uivst3); | |
1418 v3_cpp_obj(runloop)->register_timer(runloop, | |
1419 (v3_timer_handler**)&view->timer, | |
1420 DPF_VST3_TIMER_INTERVAL); | |
1421 #endif | |
1422 | |
1423 return V3_OK; | |
1424 } | |
1425 } | |
1426 | |
1427 return V3_NOT_IMPLEMENTED; | |
1428 } | |
1429 | |
1430 static v3_result V3_API removed(void* const self) | |
1431 { | |
1432 d_debug("dpf_plugin_view::removed => %p", self); | |
1433 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |
1434 DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG); | |
1435 | |
1436 #if DPF_VST3_USING_HOST_RUN_LOOP | |
1437 // unregister our timer as needed | |
1438 if (v3_run_loop** const runloop = view->runloop) | |
1439 { | |
1440 if (view->timer != nullptr && view->timer->valid) | |
1441 { | |
1442 v3_cpp_obj(runloop)->unregister_timer(runloop, (v3_timer_handler**)&view->timer); | |
1443 | |
1444 if (const int refcount = --view->timer->refcounter) | |
1445 { | |
1446 view->timer->valid = false; | |
1447 d_stderr("VST3 warning: Host run loop did not give away timer (refcount %d)", refcount); | |
1448 } | |
1449 else | |
1450 { | |
1451 view->timer = nullptr; | |
1452 } | |
1453 } | |
1454 | |
1455 v3_cpp_obj_unref(runloop); | |
1456 view->runloop = nullptr; | |
1457 } | |
1458 #endif | |
1459 | |
1460 view->uivst3 = nullptr; | |
1461 return V3_OK; | |
1462 } | |
1463 | |
1464 static v3_result V3_API on_wheel(void* const self, const float distance) | |
1465 { | |
1466 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |
1467 d_debug("dpf_plugin_view::on_wheel => %p %f", self, distance); | |
1468 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |
1469 | |
1470 UIVst3* const uivst3 = view->uivst3; | |
1471 DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); | |
1472 | |
1473 return uivst3->onWheel(distance); | |
1474 #else | |
1475 return V3_NOT_IMPLEMENTED; | |
1476 // unused | |
1477 (void)self; (void)distance; | |
1478 #endif | |
1479 } | |
1480 | |
1481 static v3_result V3_API on_key_down(void* const self, const int16_t key_char, const int16_t key_code, const int16_t modifiers) | |
1482 { | |
1483 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |
1484 d_debug("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers); | |
1485 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |
1486 | |
1487 UIVst3* const uivst3 = view->uivst3; | |
1488 DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); | |
1489 | |
1490 return uivst3->onKeyDown(key_char, key_code, modifiers); | |
1491 #else | |
1492 return V3_NOT_IMPLEMENTED; | |
1493 // unused | |
1494 (void)self; (void)key_char; (void)key_code; (void)modifiers; | |
1495 #endif | |
1496 } | |
1497 | |
1498 static v3_result V3_API on_key_up(void* const self, const int16_t key_char, const int16_t key_code, const int16_t modifiers) | |
1499 { | |
1500 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |
1501 d_debug("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers); | |
1502 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |
1503 | |
1504 UIVst3* const uivst3 = view->uivst3; | |
1505 DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); | |
1506 | |
1507 return uivst3->onKeyUp(key_char, key_code, modifiers); | |
1508 #else | |
1509 return V3_NOT_IMPLEMENTED; | |
1510 // unused | |
1511 (void)self; (void)key_char; (void)key_code; (void)modifiers; | |
1512 #endif | |
1513 } | |
1514 | |
1515 static v3_result V3_API get_size(void* const self, v3_view_rect* const rect) | |
1516 { | |
1517 d_debug("dpf_plugin_view::get_size => %p", self); | |
1518 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |
1519 | |
1520 if (UIVst3* const uivst3 = view->uivst3) | |
1521 return uivst3->getSize(rect); | |
1522 | |
1523 d_debug("dpf_plugin_view::get_size => %p | NOTE: size request before attach", self); | |
1524 | |
1525 view->sizeRequestedBeforeBeingAttached = true; | |
1526 | |
1527 double scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0; | |
1528 #if defined(DISTRHO_UI_DEFAULT_WIDTH) && defined(DISTRHO_UI_DEFAULT_HEIGHT) | |
1529 rect->right = DISTRHO_UI_DEFAULT_WIDTH; | |
1530 rect->bottom = DISTRHO_UI_DEFAULT_HEIGHT; | |
1531 if (d_isZero(scaleFactor)) | |
1532 scaleFactor = 1.0; | |
1533 #else | |
1534 UIExporter tmpUI(nullptr, 0, view->sampleRate, | |
1535 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath, | |
1536 view->instancePointer, scaleFactor); | |
1537 rect->right = tmpUI.getWidth(); | |
1538 rect->bottom = tmpUI.getHeight(); | |
1539 scaleFactor = tmpUI.getScaleFactor(); | |
1540 tmpUI.quit(); | |
1541 #endif | |
1542 rect->left = rect->top = 0; | |
1543 #ifdef DISTRHO_OS_MAC | |
1544 rect->right /= scaleFactor; | |
1545 rect->bottom /= scaleFactor; | |
1546 #endif | |
1547 | |
1548 return V3_OK; | |
1549 } | |
1550 | |
1551 static v3_result V3_API on_size(void* const self, v3_view_rect* const rect) | |
1552 { | |
1553 d_debug("dpf_plugin_view::on_size => %p {%d,%d,%d,%d}", | |
1554 self, rect->top, rect->left, rect->right, rect->bottom); | |
1555 DISTRHO_SAFE_ASSERT_INT2_RETURN(rect->right > rect->left, rect->right, rect->left, V3_INVALID_ARG); | |
1556 DISTRHO_SAFE_ASSERT_INT2_RETURN(rect->bottom > rect->top, rect->bottom, rect->top, V3_INVALID_ARG); | |
1557 | |
1558 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |
1559 | |
1560 if (UIVst3* const uivst3 = view->uivst3) | |
1561 return uivst3->onSize(rect); | |
1562 | |
1563 view->nextWidth = static_cast<uint32_t>(rect->right - rect->left); | |
1564 view->nextHeight = static_cast<uint32_t>(rect->bottom - rect->top); | |
1565 return V3_OK; | |
1566 } | |
1567 | |
1568 static v3_result V3_API on_focus(void* const self, const v3_bool state) | |
1569 { | |
1570 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |
1571 d_debug("dpf_plugin_view::on_focus => %p %u", self, state); | |
1572 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |
1573 | |
1574 UIVst3* const uivst3 = view->uivst3; | |
1575 DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); | |
1576 | |
1577 return uivst3->onFocus(state); | |
1578 #else | |
1579 return V3_NOT_IMPLEMENTED; | |
1580 // unused | |
1581 (void)self; (void)state; | |
1582 #endif | |
1583 } | |
1584 | |
1585 static v3_result V3_API set_frame(void* const self, v3_plugin_frame** const frame) | |
1586 { | |
1587 d_debug("dpf_plugin_view::set_frame => %p %p", self, frame); | |
1588 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |
1589 | |
1590 view->frame = frame; | |
1591 | |
1592 if (UIVst3* const uivst3 = view->uivst3) | |
1593 return uivst3->setFrame(frame); | |
1594 | |
1595 return V3_OK; | |
1596 } | |
1597 | |
1598 static v3_result V3_API can_resize(void* const self) | |
1599 { | |
1600 #if DISTRHO_UI_USER_RESIZABLE | |
1601 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |
1602 | |
1603 if (UIVst3* const uivst3 = view->uivst3) | |
1604 return uivst3->canResize(); | |
1605 | |
1606 return V3_TRUE; | |
1607 #else | |
1608 return V3_FALSE; | |
1609 | |
1610 // unused | |
1611 (void)self; | |
1612 #endif | |
1613 } | |
1614 | |
1615 static v3_result V3_API check_size_constraint(void* const self, v3_view_rect* const rect) | |
1616 { | |
1617 d_debug("dpf_plugin_view::check_size_constraint => %p {%d,%d,%d,%d}", | |
1618 self, rect->top, rect->left, rect->right, rect->bottom); | |
1619 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); | |
1620 | |
1621 if (UIVst3* const uivst3 = view->uivst3) | |
1622 return uivst3->checkSizeConstraint(rect); | |
1623 | |
1624 return V3_NOT_INITIALIZED; | |
1625 } | |
1626 }; | |
1627 | |
1628 // -------------------------------------------------------------------------------------------------------------------- | |
1629 // dpf_plugin_view_create (called from plugin side) | |
1630 | |
1631 v3_plugin_view** dpf_plugin_view_create(v3_host_application** host, void* instancePointer, double sampleRate); | |
1632 | |
1633 v3_plugin_view** dpf_plugin_view_create(v3_host_application** const host, | |
1634 void* const instancePointer, | |
1635 const double sampleRate) | |
1636 { | |
1637 dpf_plugin_view** const viewptr = new dpf_plugin_view*; | |
1638 *viewptr = new dpf_plugin_view(host, instancePointer, sampleRate); | |
1639 return static_cast<v3_plugin_view**>(static_cast<void*>(viewptr)); | |
1640 } | |
1641 | |
1642 // -------------------------------------------------------------------------------------------------------------------- | |
1643 | |
1644 END_NAMESPACE_DISTRHO |