1 /*
2 Foo-YC20 VSTi plugin
3 Copyright (C) 2010 Sampo Savolainen <v2@iki.fi>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 VST2 SDK Interfaces by (c)1996-1999 Steinberg Soft und Hardware GmbH, All Rights Reserved
19
20 */
21
22 #include <iostream>
23 #include <map>
24
25 #include <audioeffect.cpp>
26 #include <audioeffectx.h>
27 #include <audioeffectx.cpp>
28
29 #include <foo-yc20.h>
30 #include <yc20-base-ui.h>
31
32 #ifdef __WIN32__
33 #include <cairo-win32.h>
34 #else /* __APPLE__*/
35 #include <Carbon/Carbon.h>
36 #include <cairo-quartz.h>
37 #endif
38
39 // Note: This is not a dependency to jack! We just use the ringbuffer implementation for VST -> UI communication
40 #include <jack/ringbuffer.h>
41
42 #define NUM_PARAMS 23
43
44 #define PARAM_PITCH 0
45
46 class FooYC20VSTi : public AudioEffectX
47 {
48 public:
49 FooYC20VSTi (audioMasterCallback, VstInt32, VstInt32);
50 ~FooYC20VSTi ();
51
52 bool getProductString (char *);
53 bool getVendorString (char *);
54 bool getEffectName (char *);
55
56 // Vendor version in hex: 0xaabbcc where aa = major, bb = minor, cc = micro
getVendorVersion()57 VstInt32 getVendorVersion () { return 0x010300; };
58
59 void setProgramName (char *);
60 void getProgramName (char *);
61 bool getProgramNameIndexed(VstInt32, VstInt32, char*);
62
63
64 bool getOutputProperties(VstInt32, VstPinProperties*);
65 void setSampleRate (float sampleRate);
66
67 void process (float **, float **, VstInt32);
68 void processReplacing (float **, float **, VstInt32);
69 VstInt32 processEvents (VstEvents*);
70
71 void setParameter (VstInt32, float);
72 float getParameter (VstInt32);
73 void getParameterName (VstInt32, char *);
74 void getParameterDisplay(VstInt32, char *);
75
76
getNumMidiInputChannels()77 VstInt32 getNumMidiInputChannels () { return 1; };
78
79 YC20Processor *yc20;
80 std::string label_for_parameter[NUM_PARAMS];
81
82 private:
83 char programName[kVstMaxNameLen+1];
84 };
85
86
87 #ifdef __WIN32__ /*{{{*/
88 LRESULT CALLBACK yc20WndProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
89
90 extern HINSTANCE cairoResourceInstance;
91 extern HINSTANCE hInstance;
92
93 const char yc20WindowClassName[] = "FooYC20WindowClass";
94
95
96 class YC20AEffEditor : public AEffEditor, public YC20BaseUI
97 {
98 public:
YC20AEffEditor(AudioEffect * fx)99 YC20AEffEditor(AudioEffect* fx)
100 : AEffEditor(fx)
101 , YC20BaseUI()
102 , hdc(0)
103 , wm_paint(false)
104 {
105 rect.left = 0;
106 rect.top = 0;
107 rect.right = 1024;
108 rect.bottom = 160;
109 set_scale(0.8);
110
111 exposeRingbuffer = jack_ringbuffer_create(sizeof(Wdgt::Draggable *)*1000);
112 if (exposeRingbuffer == NULL) {
113 throw "Could not create ringbuffer";
114 }
115
116
117 FooYC20VSTi *eff = (FooYC20VSTi *)fx;
118
119 for (int i = 0; i < NUM_PARAMS; i++) {
120 draggableForIndex[i] = wdgtPerLabel[eff->label_for_parameter[i]];
121 draggableForIndex[i]->setPortIndex(i);
122 }
123
124 WNDCLASSEX wc;
125 wc.cbSize = sizeof(WNDCLASSEX);
126 wc.style = CS_HREDRAW | CS_VREDRAW;
127 wc.lpfnWndProc = yc20WndProcedure;
128 wc.cbClsExtra = 0;
129 wc.cbWndExtra = 0;
130 wc.hInstance = hInstance;
131 wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
132 wc.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);
133 wc.hCursor = LoadCursor(hInstance, IDC_ARROW);
134 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
135 wc.lpszMenuName = NULL;
136 wc.lpszClassName = yc20WindowClassName;
137
138 if(!RegisterClassEx(&wc)) {
139 std::cerr << "Could not register class '" << yc20WindowClassName << "'" << std::endl;
140 }
141
142
143 };
144
~YC20AEffEditor()145 ~YC20AEffEditor()
146 {
147 jack_ringbuffer_free(exposeRingbuffer);
148 }
149
150
151
getRect(ERect ** r)152 virtual bool getRect(ERect **r)
153 {
154 *r = ▭
155 return true;
156 };
157
open(void * ptr)158 virtual bool open(void *ptr)
159 {
160 #ifdef VERBOSE
161 std::cerr << "########## open()" << std::endl;
162 #endif
163 AEffEditor::open(ptr);
164
165 // TODO: Reaper doesn't like us touching its' window. Maybe we need to put in a new window in the window?
166
167 uiWnd = CreateWindow(yc20WindowClassName, "A title", WS_CHILD,
168 CW_USEDEFAULT, CW_USEDEFAULT, rect.right, rect.bottom, (HWND)systemWindow, NULL, hInstance, NULL);
169
170 if (!uiWnd) {
171 std::cerr << "CreateWindow() returned 0" << std::endl;
172 }
173
174 SetWindowLongPtr(uiWnd, GWLP_USERDATA, (LONG_PTR)this);
175
176 ShowWindow (uiWnd, SW_SHOW);
177
178
179
180 // Set UI controls to what they are in the processor
181 FooYC20VSTi *eff = (FooYC20VSTi *)effect;
182 for (int i = 0; i < NUM_PARAMS; i++) {
183 Control *c = eff->yc20->getControl(eff->label_for_parameter[i]);
184 draggableForIndex[i]->setValue(*c->getZone());
185 }
186
187 #ifdef VERBOSE
188 std::cerr << " .. exit open()" << std::endl;
189 #endif
190
191 return true;
192 };
193
close()194 virtual void close()
195 {
196 #ifdef VERBOSE
197 std::cerr << "########## close()" << std::endl;
198 #endif
199 AEffEditor::close();
200 };
201
idle()202 virtual void idle()
203 {
204 Wdgt::Draggable *obj;
205 //std::cerr << "idle()" << std::endl;
206
207 while ( jack_ringbuffer_read(exposeRingbuffer,
208 (char *)&obj,
209 sizeof(Wdgt::Draggable *)) == sizeof(Wdgt::Draggable *)) {
210 draw_wdgt(obj);
211 }
212 //std::cerr << " .. exit idle()" << std::endl;
213
214 AEffEditor::idle();
215 };
216
get_cairo_surface()217 virtual cairo_t *get_cairo_surface()
218 {
219 #ifdef VERBOSE
220 std::cerr << "get_cairo_surface()" << std::endl;
221 #endif
222 if (!wm_paint) {
223 hdc = GetDC(uiWnd );
224 }
225
226 surface = cairo_win32_surface_create(hdc);
227 cairo_t *ret = cairo_create(surface);
228 #ifdef VERBOSE
229 std::cerr << " .. exit get_cairo_surface()" << std::endl;
230 #endif
231 return ret;
232 }
233
return_cairo_surface(cairo_t * cr)234 virtual void return_cairo_surface(cairo_t *cr)
235 {
236 #ifdef VERBOSE
237 std::cerr << "return_cairo_surface()" << std::endl;
238 #endif
239 cairo_surface_finish(surface);
240 YC20BaseUI::return_cairo_surface(cr);
241 cairo_surface_destroy(surface);
242
243 if (!wm_paint) {
244 ReleaseDC(uiWnd, hdc);
245 hdc = 0;
246 }
247 #ifdef VERBOSE
248 std::cerr << " .. exit return_cairo_surface()" << std::endl;
249 #endif
250 };
251
252 HDC hdc;
253 PAINTSTRUCT ps;
254
255 bool wm_paint;
256
257 jack_ringbuffer_t *exposeRingbuffer;
258
259 // My stuff
value_changed(Wdgt::Draggable * draggable)260 void value_changed (Wdgt::Draggable *draggable)
261 {
262 #ifdef VERBOSE
263 std::cerr << "value_changed()" << std::endl;
264 #endif
265 float value = draggable->getValue();
266 if (draggable->getPortIndex() == PARAM_PITCH) {
267 value /= 2.0;
268 value += 0.5;
269 }
270
271 VstInt32 idx = draggable->getPortIndex();
272 ((AudioEffectX*)effect)->beginEdit(idx);
273 effect->setParameterAutomated(idx, value);
274 ((AudioEffectX*)effect)->endEdit(idx);
275 #ifdef VERBOSE
276 std::cerr << " .. exit value_changed()" << std::endl;
277 #endif
278 };
279
queueChange(VstInt32 idx,float value)280 void queueChange(VstInt32 idx, float value)
281 {
282 #ifdef VERBOSE
283 std::cerr << "queueChange(" << idx << ", " << value << ")" << std::endl;
284 #endif
285 Wdgt::Draggable *obj = draggableForIndex[idx];
286
287 if (!obj->setValue(value)) {
288 // Don't queue a change if the value did not change
289 return;
290 }
291
292 int i = jack_ringbuffer_write(exposeRingbuffer, (char *)&obj, sizeof(Wdgt::Draggable *));
293 if (i != sizeof(Wdgt::Draggable *)) {
294 std::cerr << "Ringbuffer full!" << std::endl;
295 }
296 #ifdef VERBOSE
297 std::cerr << " .. exit queueChange()" << std::endl;
298 #endif
299 }
300
301 HWND uiWnd;
302
303 private:
304 cairo_surface_t *surface;
305
306 ERect rect;
307 Wdgt::Draggable *draggableForIndex[NUM_PARAMS];
308
309 };
310
311 //#include <gdiplus.h>
312
yc20WndProcedure(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)313 LRESULT CALLBACK yc20WndProcedure(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
314 {
315 YC20AEffEditor *ui = (YC20AEffEditor *)GetWindowLongPtr( hWnd, GWLP_USERDATA);
316
317 #ifdef VERBOSE
318 if (Msg != 275) {
319 std::cerr << "yc20WndProcedure(xx, " << Msg << ", .., .. ) (userdata = " << ui << ")" << std::endl;
320 }
321 #endif
322
323 double x,y;
324 RECT clipRect;
325 RECT rect;
326
327
328 switch(Msg)
329 {
330 case WM_MOUSEMOVE:
331 x = ((int)(short)LOWORD(lParam)); // GET_X_LPARAM
332 y = ((int)(short)HIWORD(lParam)); // GET_Y_LPARAM
333
334 // Catch if dragged outside the window, then released the button
335 if ((wParam & MK_LBUTTON) == 0 && ui->is_dragging()) {
336 ui->button_released(x,y);
337 }
338 ui->mouse_movement(x,y);
339 return 0;
340
341 case WM_LBUTTONDOWN:
342 x = ((int)(short)LOWORD(lParam)); // GET_X_LPARAM
343 y = ((int)(short)HIWORD(lParam)); // GET_Y_LPARAM
344 ui->button_pressed(x,y);
345 return 0;
346
347 // TODO: figure out how to detect dragging outside the box..
348 case WM_LBUTTONUP:
349 x = ((int)(short)LOWORD(lParam)); // GET_X_LPARAM
350 y = ((int)(short)HIWORD(lParam)); // GET_Y_LPARAM
351 ui->button_released(x,y);
352 return 0;
353
354 case WM_PAINT:
355 if (ui->hdc) {
356 std::cerr << "!! Painting while painting? bad!" << std::endl;
357 return 0;
358 }
359
360 ui->wm_paint = true;
361 {
362 GetUpdateRect(hWnd, &clipRect, true);
363 HDC tmpHdc = BeginPaint(ui->uiWnd, &ui->ps);
364 GetClientRect(ui->uiWnd, &rect);
365
366 int x = rect.left;
367 int y = rect.top;
368 int width = rect.right - rect.left;
369 int height = rect.bottom - rect.top;
370
371 // Setup double-buffered HDC for the ui->draw() to draw on
372 ui->hdc = CreateCompatibleDC(tmpHdc) ;
373 HBITMAP hBitmap = CreateCompatibleBitmap(tmpHdc,width,height);
374 HGDIOBJ hOldBitmap = SelectObject(ui->hdc, hBitmap );
375
376 // Draw using the update rect
377 ui->draw(clipRect.left, clipRect.top,
378 clipRect.right - clipRect.left,
379 clipRect.bottom - clipRect.top, true);
380
381 // Flush double-buffering
382 BitBlt(tmpHdc, x, y, width, height, ui->hdc, 0, 0, SRCCOPY) ;
383 SelectObject( ui->hdc, hOldBitmap );
384 DeleteObject( hBitmap );
385 DeleteDC( ui->hdc );
386 EndPaint(ui->uiWnd, &ui->ps);
387 ui->hdc = 0;
388
389 }
390
391 ui->wm_paint = false;
392
393 return 0;
394 }
395 return DefWindowProc(hWnd, Msg, wParam, lParam);
396 }
397 #endif /* __WIN32__ */ /*}}}*/
398
399 #ifdef __APPLE__ /*{{{*/
400
401 /* prototypes */
402 static OSStatus WindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData);
403 static OSStatus MouseEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData);
404 static OSStatus carbonEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData);
405 static int yc20open = 0;
406 static bool yc20hic = false;
407 #define defControlStringMask CFSTR ("com.studionumbersix.foo.yc20.%d")
408 #define OSXMENUBARHEIGHT (22)
409
410 class YC20AEffEditor : public AEffEditor, public YC20BaseUI
411 {
412 public:
YC20AEffEditor(AudioEffect * fx)413 YC20AEffEditor(AudioEffect* fx)
414 : AEffEditor(fx)
415 , YC20BaseUI()
416 {
417 _rect.left = 0;
418 _rect.top = 0;
419 _rect.right = 1024;
420 _rect.bottom = 160;
421 set_scale(0.8);
422
423 exposeRingbuffer = jack_ringbuffer_create(sizeof(Wdgt::Draggable *)*1000);
424 if (exposeRingbuffer == NULL) {
425 throw "Could not create ringbuffer";
426 }
427
428 FooYC20VSTi *eff = (FooYC20VSTi *)fx;
429
430 for (int i = 0; i < NUM_PARAMS; i++) {
431 draggableForIndex[i] = wdgtPerLabel[eff->label_for_parameter[i]];
432 draggableForIndex[i]->setPortIndex(i);
433 }
434 };
435
~YC20AEffEditor()436 ~YC20AEffEditor()
437 {
438 #ifdef VERBOSE
439 std::cerr << "########## destruct()" << std::endl;
440 #endif
441 if (yc20open) close();
442 jack_ringbuffer_free(exposeRingbuffer);
443 }
444
445
getRect(ERect ** rect)446 virtual bool getRect(ERect **rect)
447 {
448 *rect = &_rect;
449 return true;
450 };
451
open(void * ptr)452 virtual bool open(void *ptr)
453 {
454 #ifdef VERBOSE
455 std::cout << "########## open()" << std::endl;
456 #endif
457 AEffEditor::open(ptr);
458 yc20open=1;
459 OSStatus status;
460
461 WindowAttributes attributes;
462 GetWindowAttributes ((WindowRef)systemWindow, &attributes);
463 if (attributes & kWindowCompositingAttribute) {
464 yc20hic = true;
465 CFStringRef defControlString = CFStringCreateWithFormat (NULL, NULL, defControlStringMask, this);
466
467 controlSpec.defType = kControlDefObjectClass;
468 controlSpec.u.classRef = NULL;
469 EventTypeSpec eventTypes[] = {
470 {kEventClassControl, kEventControlDraw},
471 {kEventClassControl, kEventControlHitTest},
472 {kEventClassControl, kEventControlClick},
473 };
474
475 ToolboxObjectClassRef controlClass = NULL;
476
477 status = RegisterToolboxObjectClass ( defControlString,
478 NULL,
479 GetEventTypeCount (eventTypes),
480 eventTypes,
481 NewEventHandlerUPP(carbonEventHandler),
482 this,
483 &controlClass);
484
485 CFRelease (defControlString);
486
487 if (status == noErr) controlSpec.u.classRef = controlClass;
488 if (controlSpec.u.classRef == NULL) return false;
489
490 Rect r = {(short)_rect.top, (short)_rect.left, (short)_rect.bottom, (short)_rect.right};
491 status = CreateCustomControl (NULL, &r, &controlSpec, NULL, &controlRef);
492 } else {
493 yc20hic = false;
494 controlRef=NULL;
495 controlSpec.u.classRef=NULL;
496 }
497
498
499 if (yc20hic)
500 {
501 HIViewRef contentView;
502 HIViewRef rootView = HIViewGetRoot ((WindowRef)systemWindow);
503 if (HIViewFindByID (rootView, kHIViewWindowContentID, &contentView) != noErr)
504 contentView = rootView;
505 HIViewAddSubview (contentView, controlRef);
506 #ifdef VERBOSE
507 std::cout << "OSX HI Compositing" << std::endl;
508 #endif
509 }
510 else
511 {
512 ControlRef rootControl;
513 GetRootControl ((WindowRef)systemWindow, &rootControl);
514 if (rootControl == NULL)
515 CreateRootControl ((WindowRef)systemWindow, &rootControl);
516 EmbedControl(controlRef, rootControl);
517 #ifdef VERBOSE
518 std::cout << "OSX Quartz native" << std::endl;
519 #endif
520 }
521
522 QDBeginCGContext(GetWindowPort((WindowRef)systemWindow), &context);
523
524 const EventTypeSpec win_events[] = {
525 { kEventClassWindow, kEventWindowClosed },
526 { kEventClassWindow, kEventWindowDrawContent },
527 { kEventClassWindow, kEventWindowGetMinimumSize },
528 // { kEventClassWindow, kEventWindowGetMaximumSize },
529 { kEventClassWindow, kEventWindowBoundsChanged }
530 };
531
532 InstallWindowEventHandler((WindowRef)systemWindow, NewEventHandlerUPP(WindowEventHandler),
533 GetEventTypeCount(win_events), win_events, this, &windowEventHandler);
534
535 #if 1
536 const EventTypeSpec mouse_events[] = {
537 { kEventClassMouse, kEventMouseMoved },
538 //{ kEventClassMouse, kEventMouseWheelMoved },
539 { kEventClassMouse, kEventMouseDown },
540 { kEventClassMouse, kEventMouseUp },
541 { kEventClassMouse, kEventMouseDragged }
542 };
543 InstallApplicationEventHandler(NewEventHandlerUPP(MouseEventHandler),
544 GetEventTypeCount(mouse_events), mouse_events, this, &mouseEventHandler);
545 #endif
546
547 // Set UI controls to what they are in the processor
548 FooYC20VSTi *eff = (FooYC20VSTi *)effect;
549 for (int i = 0; i < NUM_PARAMS; i++) {
550 Control *c = eff->yc20->getControl(eff->label_for_parameter[i]);
551 draggableForIndex[i]->setValue(*c->getZone());
552 }
553
554 /* flip canvas coordinates
555 * http://macdevcenter.com/pub/a/mac/2004/11/02/quartz.html */
556 Rect rectPort;
557 GetWindowPortBounds((WindowRef)systemWindow,&rectPort);
558 CGContextTranslateCTM(context, 0, rectPort.bottom);
559 CGContextScaleCTM(context, 1.0, -1.0);
560
561 #ifdef VERBOSE
562 std::cout << " .. exit open()" << std::endl;
563 #endif
564 return true;
565 };
566
close()567 virtual void close()
568 {
569 #ifdef VERBOSE
570 std::cout << "########## close()" << std::endl;
571 #endif
572 yc20open=0;
573
574 RemoveEventHandler(windowEventHandler);
575 RemoveEventHandler(mouseEventHandler);
576
577 if (controlRef) DisposeControl (controlRef);
578 if (controlSpec.u.classRef) {
579 OSStatus status = UnregisterToolboxObjectClass ((ToolboxObjectClassRef)controlSpec.u.classRef);
580 if (status != noErr)
581 std::cout << "UnregisterToolboxObjectClass failed: " << status << std::endl;
582 }
583
584 CGContextSynchronize(context);
585 QDEndCGContext(GetWindowPort((WindowRef)systemWindow), &context);
586
587 AEffEditor::close();
588 };
589
590
idle()591 virtual void idle()
592 {
593 Wdgt::Draggable *obj;
594 //std::cout << "idle()" << std::endl;
595 while ( jack_ringbuffer_read(exposeRingbuffer,
596 (char *)&obj,
597 sizeof(Wdgt::Draggable *)) == sizeof(Wdgt::Draggable *)) {
598 draw_wdgt(obj);
599 }
600 //std::cout << " .. exit idle()" << std::endl;
601
602 #if 1
603 if (yc20open == 1) {
604 yc20open=2;
605 Rect rectPort;
606 GetWindowPortBounds((WindowRef)systemWindow,&rectPort);
607 YC20BaseUI::draw(rectPort.top, rectPort.left, rectPort.right, rectPort.bottom, true);
608 InvalWindowRect((WindowRef)systemWindow,&rectPort);
609 }
610 #endif
611 #if 0
612 EventRef theEvent;
613 EventTargetRef theTarget;
614 OSStatus theErr;
615
616 theTarget = GetEventDispatcherTarget();
617 theErr = ReceiveNextEvent(0, 0, kEventDurationNoWait,true, &theEvent);
618 if(theErr == noErr && theEvent != NULL) {
619 SendEventToEventTarget (theEvent, theTarget);
620 ReleaseEvent(theEvent);
621 }
622
623 #endif
624 AEffEditor::idle();
625 };
626
get_cairo_surface()627 virtual cairo_t *get_cairo_surface()
628 {
629 #ifdef VERBOSE
630 std::cout << "get_cairo_surface()" << std::endl;
631 #endif
632 CGContextSaveGState(context);
633 surface = cairo_quartz_surface_create_for_cg_context(context, _rect.right, _rect.bottom);
634 return cairo_create(surface);
635 }
636
return_cairo_surface(cairo_t * cr)637 virtual void return_cairo_surface(cairo_t *cr)
638 {
639 #ifdef VERBOSE
640 std::cout << "return_cairo_surface()" << std::endl;
641 #endif
642 YC20BaseUI::return_cairo_surface(cr);
643
644 #if 1 /* draw resize bars */
645 if (!yc20hic) {
646 Rect winRect;
647 GetWindowPortBounds((WindowRef)systemWindow,&winRect);
648
649 CGContextBeginPath(context);
650 CGContextSetAllowsAntialiasing(context, false);
651
652 //line white
653 CGContextSetRGBStrokeColor(context, 0.2, 0.2, 0.2, 0.5);
654 CGContextMoveToPoint(context, winRect.right-1, winRect.bottom-1);
655 CGContextAddLineToPoint(context, winRect.right-1, winRect.bottom-1);
656 CGContextMoveToPoint(context, winRect.right-1, winRect.bottom-5);
657 CGContextAddLineToPoint(context, winRect.right-5, winRect.bottom-1);
658 CGContextMoveToPoint(context, winRect.right-1, winRect.bottom-9);
659 CGContextAddLineToPoint(context, winRect.right-9, winRect.bottom-1);
660 CGContextStrokePath(context);
661
662 //line gray
663 CGContextSetRGBStrokeColor(context, 0.4, 0.4, 0.4, 0.5);
664 CGContextMoveToPoint(context, winRect.right-1, winRect.bottom-2);
665 CGContextAddLineToPoint( context, winRect.right-2, winRect.bottom-1);
666 CGContextMoveToPoint(context, winRect.right-1, winRect.bottom-6);
667 CGContextAddLineToPoint( context, winRect.right-6, winRect.bottom-1);
668 CGContextMoveToPoint(context, winRect.right-1, winRect.bottom-10);
669 CGContextAddLineToPoint( context, winRect.right-10, winRect.bottom-1);
670 CGContextStrokePath(context);
671
672 //line black
673 CGContextSetRGBStrokeColor(context, 0.6, 0.6, 0.6, 0.5);
674 CGContextMoveToPoint(context, winRect.right-1, winRect.bottom-3);
675 CGContextAddLineToPoint( context, winRect.right-3, winRect.bottom-1);
676 CGContextMoveToPoint(context, winRect.right-1, winRect.bottom-7);
677 CGContextAddLineToPoint( context, winRect.right-7, winRect.bottom-1);
678 CGContextStrokePath(context);
679 }
680 #endif
681 CGContextRestoreGState(context);
682 CGContextFlush(context);
683 #ifdef VERBOSE
684 std::cout << " .. exit return_cairo_surface()" << std::endl;
685 #endif
686 };
687
value_changed(Wdgt::Draggable * draggable)688 void value_changed (Wdgt::Draggable *draggable)
689 {
690 #ifdef VERBOSE
691 std::cout << "value_changed()" << std::endl;
692 #endif
693 float value = draggable->getValue();
694 if (draggable->getPortIndex() == PARAM_PITCH) {
695 value /= 2.0;
696 value += 0.5;
697 }
698
699 VstInt32 idx = draggable->getPortIndex();
700 ((AudioEffectX*)effect)->beginEdit(idx);
701 effect->setParameterAutomated(idx, value);
702 ((AudioEffectX*)effect)->endEdit(idx);
703 #ifdef VERBOSE
704 std::cout << " .. exit value_changed()" << std::endl;
705 #endif
706 };
707
queueChange(VstInt32 idx,float value)708 void queueChange(VstInt32 idx, float value)
709 {
710 #ifdef VERBOSE
711 std::cout << "queueChange(" << idx << ", " << value << ")" << std::endl;
712 #endif
713
714 Wdgt::Draggable *obj = draggableForIndex[idx];
715
716 if (!obj->setValue(value)) {
717 // Don't queue a change if the value did not change
718 return;
719 }
720
721 int i = jack_ringbuffer_write(exposeRingbuffer, (char *)&obj, sizeof(Wdgt::Draggable *));
722 if (i != sizeof(Wdgt::Draggable *)) {
723 std::cout << "Ringbuffer full!" << std::endl;
724 }
725
726 #ifdef VERBOSE
727 std::cout << " .. exit queueChange()" << std::endl;
728 #endif
729 }
730
sameWindow(WindowRef cmp)731 bool sameWindow(WindowRef cmp)
732 {
733 #ifdef DEBUG
734 std::cout << " same Window ? " << cmp << " ~ " << (WindowRef)systemWindow << std::endl;
735 #endif
736 if (cmp == (WindowRef)systemWindow) return true;
737 return false;
738 }
739
aspectWindow(Rect rectNew)740 void aspectWindow(Rect rectNew)
741 {
742 Rect rectPort;
743
744 _rect.right = rectNew.right - rectNew.left;
745 if (_rect.right < 640) _rect.right=640;
746 double scale = (double)_rect.right/1280.0;
747 set_scale(scale);
748 _rect.bottom = (unsigned int) floor(200.0 * scale);
749
750 SizeWindow((WindowRef)systemWindow, _rect.right, _rect.bottom, 1);
751
752 QDEndCGContext(GetWindowPort((WindowRef)systemWindow), &context);
753 QDBeginCGContext(GetWindowPort((WindowRef)systemWindow), &context);
754
755 GetWindowPortBounds((WindowRef)systemWindow,&rectPort);
756 CGContextTranslateCTM(context, 0, rectPort.bottom);
757 CGContextScaleCTM(context, 1.0, -1.0);
758 }
759
resizeWindow(void)760 void resizeWindow(void)
761 {
762 Rect rectPort;
763 GetWindowPortBounds((WindowRef)systemWindow,&rectPort);
764 aspectWindow(rectPort);
765
766 YC20BaseUI::draw(rectPort.top, rectPort.left, rectPort.right, rectPort.bottom, true);
767 InvalWindowRect((WindowRef)systemWindow,&rectPort);
768 #if 0
769 std::cout << " NEW OSX SIZE: "
770 << (short) rectPort.left << "x" << (short) rectPort.top << "|"
771 << (short) rectPort.right << "x" << (short) rectPort.bottom << "|"
772 << std::endl;
773 #endif
774 }
775
resizeWindow(Point winMousePos,Point mousePos)776 void resizeWindow(Point winMousePos, Point mousePos)
777 {
778 Rect rectPort, rectNew, rectConstrain;
779 GetWindowPortBounds((WindowRef)systemWindow,&rectPort);
780 if( (winMousePos.h <= (rectPort.right - 15)) || (winMousePos.v <= (rectPort.bottom)) )
781 return;
782
783 rectConstrain.top=100; // min Height
784 rectConstrain.bottom=200; // max Height
785 rectConstrain.left=640; // min Width
786 rectConstrain.right=1280; // max Width
787
788 ResizeWindow((WindowRef)systemWindow, mousePos, &rectConstrain, &rectNew);
789 aspectWindow(rectNew);
790
791 #ifdef DEBUG
792 std::cout << "OSX resize event:"
793 << rectNew.left << "x" << rectNew.top << "|"
794 << rectNew.right << "x" << rectNew.bottom << "|"
795 << std::endl;
796 #endif
797 }
798
799
800 private:
801 cairo_surface_t *surface;
802 ERect _rect;
803 Wdgt::Draggable *draggableForIndex[NUM_PARAMS];
804
805 jack_ringbuffer_t *exposeRingbuffer;
806 ControlDefSpec controlSpec;
807 ControlRef controlRef;
808 EventHandlerRef windowEventHandler;
809 EventHandlerRef mouseEventHandler;
810 CGContextRef context;
811 };
812
813 /*
814 http://serenity.uncc.edu/web/ADC/2005/Developer_DVD_Series/April/ADC%20Reference%20Library/documentation/Carbon/Reference/Window_Manager/index.html
815 http://serenity.uncc.edu/web/ADC/2005/Developer_DVD_Series/April/ADC%20Reference%20Library/documentation/Carbon/Reference/Carbon_Event_Manager_Ref/index.html
816 */
817
carbonEventHandler(EventHandlerCallRef nextHandler,EventRef event,void * userData)818 static OSStatus carbonEventHandler (EventHandlerCallRef nextHandler, EventRef event, void *userData)
819 {
820 UInt32 eclass = GetEventClass(event);
821 UInt32 kind = GetEventKind (event);
822 YC20AEffEditor *ui = (YC20AEffEditor *)userData;
823
824 if (eclass != kEventClassControl) return CallNextEventHandler(nextHandler, event);
825 switch (kind) {
826 case kEventControlDraw:
827 #ifdef DEBUG
828 std::cout << " OSX event: kEventControlDraw" << std::endl;
829 #endif
830 ui->resizeWindow();
831 break;
832 case kEventControlClick:
833 #if 0 /* done via 'kEventMouseDragged' and mymousedown */
834 if (yc20hic)
835 {
836 double x,y;
837 Point mousePos;
838 Point winMousePos;
839 GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, 0, sizeof(Point), 0, &mousePos);
840 std::cout << "OSX CTRL click: "
841 << mousePos.h << "x" << mousePos.v
842 << std::endl;
843 x = (double) winMousePos.h;
844 y = (double) winMousePos.v;
845 if (x>=0 && y>=0) {
846 if (y>=OSXMENUBARHEIGHT) ui->button_pressed(x,y);
847 }
848 }
849 #endif
850 break;
851 case kEventControlHitTest:
852 #if 0 /* done via kEventMouseMoved: */
853 if (yc20hic)
854 {
855 double x,y;
856 Point mousePos;
857 Point winMousePos;
858 GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, 0, sizeof(Point), 0, &mousePos);
859 std::cout << "OSX CTRL mouse: "
860 << mousePos.h << "x" << mousePos.v
861 << std::endl;
862 x = (double) mousePos.h;
863 y = (double) mousePos.v;
864 if (x>=0 && y>=OSXMENUBARHEIGHT)
865 ui->mouse_movement(x,y-OSXMENUBARHEIGHT);
866 }
867 #endif
868 break;
869 default:
870 #ifdef VERBOSE
871 std::cout << " NEW OSX event: Other" << kind << std::endl;
872 #endif
873 break;
874 }
875 return CallNextEventHandler(nextHandler, event);
876 }
877
WindowEventHandler(EventHandlerCallRef nextHandler,EventRef event,void * userData)878 static OSStatus WindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData)
879 {
880 #if 0
881 std::cout << "OSX window event" << std::endl;
882 #endif
883 UInt32 eclass = GetEventClass(event);
884 UInt32 kind = GetEventKind (event);
885 Point minmaxHeightAndWidth;
886 YC20AEffEditor *ui = (YC20AEffEditor *)userData;
887
888 if(eclass != kEventClassWindow) return CallNextEventHandler(nextHandler, event);
889
890 WindowRef window;
891 Rect rectPort;
892 GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &window);
893
894 #ifdef DEBUG
895 std::cout << "OSX kEventClassWindow: " << kind << std::endl;
896 #endif
897 switch (kind) {
898 case kEventWindowDrawContent:
899 case kEventWindowZoomed:
900 GetWindowPortBounds(window,&rectPort);
901 ui->YC20BaseUI::draw(rectPort.top, rectPort.left, rectPort.right, rectPort.bottom, true);
902 #ifdef DEBUG
903 std::cout << " Q Draw : "
904 << (short) rectPort.left << "x" << (short) rectPort.top << "|"
905 << (short) rectPort.right << "x" << (short) rectPort.bottom << "|"
906 << std::endl;
907 #endif
908 break;
909 case kEventWindowBoundsChanged:
910 // get's called in on resize and move
911 //GetWindowPortBounds(window,&rectPort);
912 //InvalWindowRect(window,&rectPort);
913 #ifdef DEBUG
914 std::cout << " Bounds : "
915 << (short) rectPort.left << "x" << (short) rectPort.top << "|"
916 << (short) rectPort.right << "x" << (short) rectPort.bottom << "|"
917 << std::endl;
918 #endif
919 break;
920 case kEventWindowGetMinimumSize:
921 minmaxHeightAndWidth.v = 640;
922 minmaxHeightAndWidth.h = 100;
923 SetEventParameter(event,kEventParamDimensions,typeQDPoint,
924 sizeof(minmaxHeightAndWidth),&minmaxHeightAndWidth);
925 break;
926 case kEventWindowGetMaximumSize:
927 minmaxHeightAndWidth.v = 1280;
928 minmaxHeightAndWidth.h = 200;
929 SetEventParameter(event,kEventParamDimensions,typeQDPoint,
930 sizeof(minmaxHeightAndWidth),&minmaxHeightAndWidth);
931 break;
932 default:
933 break;
934 }
935 return CallNextEventHandler(nextHandler, event);
936 }
937
MouseEventHandler(EventHandlerCallRef nextHandler,EventRef event,void * userData)938 static OSStatus MouseEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData)
939 {
940 /* NOTE: with "VSTi Host Demo" this event handler causes a crash
941 * when unloading the plugin while the window is still visible.
942 *
943 * calling those in close() does not help either:
944 * RemoveEventHandler(mouseEventHandler);
945 * FlushEventQueue(GetMainEventQueue());
946 * FlushEventQueue(GetCurrentEventQueue());
947 *
948 * however calling
949 * RemoveEventHandler(mouseEventHandler);
950 * anytime before close() resolves the problem.
951 *
952 * valgrind! valgrind! my kingdom for valgrind!
953 */
954 #if 0
955 std::cout << "OSX mouse event" << std::endl;
956 #endif
957 OSStatus result = CallNextEventHandler(nextHandler, event);
958 if (result != noErr && result != eventNotHandledErr) return result;
959 if (!yc20open) return noErr;
960
961 UInt32 eclass = GetEventClass (event);
962 UInt32 kind = GetEventKind (event);
963 YC20AEffEditor *ui = (YC20AEffEditor *)userData;
964
965 if(eclass == kEventClassMouse) {
966 double x,y;
967 Point mousePos;
968 Point winMousePos;
969 EventMouseButton button;
970 GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, 0, sizeof(Point), 0, &mousePos);
971 GetEventParameter(event, kEventParamWindowMouseLocation, typeQDPoint, 0, sizeof(Point), 0, &winMousePos);
972 GetEventParameter(event, kEventParamMouseButton, typeMouseButton, 0, sizeof(EventMouseButton), 0, &button);
973
974 if (!yc20hic) {
975 WindowRef tmpWin;
976 short part = FindWindow(mousePos,&tmpWin);
977 if (part != inContent) return noErr;
978 if (!ui->sameWindow(tmpWin)) return noErr;
979 }
980
981 #ifdef DEBUG
982 std::cout << "OSX mouse event: "
983 << "type: " << kind << "pos: "
984 << winMousePos.h << "x" << winMousePos.v << "|"
985 << mousePos.h << "x" << mousePos.v << "|"
986 << " button:" << (button == kEventMouseButtonPrimary ? "1":"0")
987 << std::endl;
988 #endif
989 static bool mymousedown = false;
990 switch (kind) {
991 case kEventMouseDragged:
992 //std::cout << "OSX Mouse Draged." << std::endl;
993 if (!mymousedown && yc20hic) {
994 mymousedown=true;
995 x = (double) winMousePos.h;
996 y = (double) winMousePos.v;
997 if (x>=0 && y>=0 && button==kEventMouseButtonPrimary) {
998 if (y>=OSXMENUBARHEIGHT) ui->button_pressed(x,y-OSXMENUBARHEIGHT);
999 if (!yc20hic) ui->resizeWindow(winMousePos, mousePos);
1000 }
1001 }
1002 case kEventMouseMoved:
1003 //std::cout << "OSX Mouse Moved." << std::endl;
1004 x = (double) winMousePos.h;
1005 y = (double) winMousePos.v;
1006 if (x>=0 && y>=OSXMENUBARHEIGHT)
1007 ui->mouse_movement(x,y-OSXMENUBARHEIGHT);
1008 break;
1009 case kEventMouseDown:
1010 //std::cout << "OSX Mouse Down." << std::endl;
1011 /* Note: this event won't fire with kWindowCompositingAttribute */
1012 x = (double) winMousePos.h;
1013 y = (double) winMousePos.v;
1014 if (x>=0 && y>=0 && button==kEventMouseButtonPrimary) {
1015 if (y>=OSXMENUBARHEIGHT) ui->button_pressed(x,y-OSXMENUBARHEIGHT);
1016 if (!yc20hic) ui->resizeWindow(winMousePos, mousePos);
1017 }
1018 break;
1019 case kEventMouseUp:
1020 //std::cout << "OSX Mouse Up." << std::endl;
1021 x = (double) mousePos.h;
1022 y = (double) mousePos.v;
1023 if (x>=0 && y>=0 && button==kEventMouseButtonPrimary) {
1024 ui->button_released(x,y);
1025 ui->resizeWindow(winMousePos, mousePos);
1026 }
1027 mymousedown=false;
1028 break;
1029 case kEventMouseWheelMoved:
1030 break;
1031 default:
1032 break;
1033 }
1034 }
1035 return noErr;
1036 }
1037
1038
1039 #endif /* __APPLE__*/ /*}}}*/
1040
createEffectInstance(audioMasterCallback audioMaster)1041 AudioEffect *createEffectInstance(audioMasterCallback audioMaster)
1042 {
1043 #ifdef __WIN32__
1044 // Propagate the HINSTANCE set by vstgui to th HINSTANCE used in yc20-base-ui.cpp
1045 cairoResourceInstance = hInstance;
1046 #endif
1047 return new FooYC20VSTi (audioMaster, 1, NUM_PARAMS);
1048 }
1049
1050
1051 bool
getProductString(char * text)1052 FooYC20VSTi::getProductString(char* text)
1053 {
1054 vst_strncpy(text, "Foo YC20", kVstMaxProductStrLen);
1055 return true;
1056 }
1057
1058 bool
getVendorString(char * text)1059 FooYC20VSTi::getVendorString(char* text)
1060 {
1061 vst_strncpy(text, "Sampo Savolainen", kVstMaxVendorStrLen);
1062 return true;
1063 }
1064
1065 bool
getEffectName(char * name)1066 FooYC20VSTi::getEffectName(char* name)
1067 {
1068 vst_strncpy(name, "YC20 emulation", kVstMaxEffectNameLen);
1069 return true;
1070 }
1071
1072 void
setProgramName(char * name)1073 FooYC20VSTi::setProgramName(char *name)
1074 {
1075 vst_strncpy(programName, name, kVstMaxNameLen);
1076 }
1077
1078 void
getProgramName(char * name)1079 FooYC20VSTi::getProgramName(char *name)
1080 {
1081 vst_strncpy(name, programName, kVstMaxNameLen);
1082 }
1083
1084 bool
getProgramNameIndexed(VstInt32 category,VstInt32 index,char * name)1085 FooYC20VSTi::getProgramNameIndexed (VstInt32 category, VstInt32 index, char* name)
1086 {
1087 if (index == 0) {
1088 vst_strncpy(name, programName, kVstMaxNameLen);
1089 return true;
1090 }
1091 *name = 0;
1092
1093 return false;
1094 }
1095
FooYC20VSTi(audioMasterCallback callback,VstInt32 programs,VstInt32 params)1096 FooYC20VSTi::FooYC20VSTi (audioMasterCallback callback, VstInt32 programs, VstInt32 params)
1097 : AudioEffectX(callback, programs, params)
1098 {
1099
1100 setUniqueID ((int)'YC20');
1101
1102 setNumInputs(0);
1103 setNumOutputs(2);
1104 canProcessReplacing(true);
1105 isSynth(true);
1106
1107 vst_strncpy(programName, "Foo YC20 Organ", kVstMaxNameLen);
1108
1109 setProgram(0);
1110
1111 label_for_parameter[PARAM_PITCH] = "pitch";
1112 label_for_parameter[1] = "volume";
1113 label_for_parameter[2] = "bass volume";
1114
1115 label_for_parameter[3] = "realism";
1116 label_for_parameter[4] = "depth";
1117 label_for_parameter[5] = "speed";
1118
1119 label_for_parameter[6] = "16' b";
1120 label_for_parameter[7] = "8' b";
1121 label_for_parameter[8] = "bass manual";
1122
1123 label_for_parameter[9] = "16' i";
1124 label_for_parameter[10] = "8' i";
1125 label_for_parameter[11] = "4' i";
1126 label_for_parameter[12] = "2 2/3' i";
1127 label_for_parameter[13] = "2' i";
1128 label_for_parameter[14] = "1 3/5' i";
1129 label_for_parameter[15] = "1' i";
1130
1131 label_for_parameter[16] = "balance";
1132 label_for_parameter[17] = "bright";
1133
1134 label_for_parameter[18] = "16' ii";
1135 label_for_parameter[19] = "8' ii";
1136 label_for_parameter[20] = "4' ii";
1137 label_for_parameter[21] = "2' ii";
1138
1139 label_for_parameter[22] = "percussive";
1140
1141 yc20 = new YC20Processor();
1142
1143 dsp *tmp = createDSP();
1144 tmp->init(getSampleRate());
1145
1146 yc20->setDSP(tmp);
1147
1148 #ifdef VERBOSE
1149 std::cerr << "Creating the editor..." << std::endl;
1150 #endif
1151 setEditor(new YC20AEffEditor(this));
1152 #ifdef VERBOSE
1153 std::cout << "...done: " << editor << std::endl;
1154 #endif
1155 }
1156
1157 void
setSampleRate(float sampleRate)1158 FooYC20VSTi::setSampleRate (float sampleRate)
1159 {
1160 AudioEffectX::setSampleRate(sampleRate);
1161
1162 dsp *tmp = yc20->getDSP();
1163 delete tmp;
1164
1165
1166 tmp = createDSP();
1167 tmp->init(sampleRate);
1168
1169 yc20->setDSP(tmp);
1170 }
1171
getOutputProperties(VstInt32 index,VstPinProperties * properties)1172 bool FooYC20VSTi::getOutputProperties(VstInt32 index, VstPinProperties* properties)
1173 {
1174 if(index<2)
1175 {
1176 sprintf(properties->label, "YC20");
1177 properties->flags = kVstPinIsActive | kVstPinIsStereo;
1178 return true;
1179 }
1180 return false;
1181 }
1182
~FooYC20VSTi()1183 FooYC20VSTi::~FooYC20VSTi()
1184 {
1185 if (editor) {
1186 delete editor;
1187 editor = 0;
1188 }
1189 delete yc20;
1190 yc20 = 0;
1191 }
1192
1193 void
process(float ** input,float ** output,VstInt32 nframes)1194 FooYC20VSTi::process (float **input, float **output, VstInt32 nframes)
1195 {
1196 processReplacing(input, output, nframes);
1197 }
1198
1199 void
processReplacing(float ** input,float ** output,VstInt32 nframes)1200 FooYC20VSTi::processReplacing (float **input, float **output, VstInt32 nframes)
1201 {
1202 TURNOFFDENORMALS;
1203 if (yc20 && yc20->getDSP()) {
1204 yc20->getDSP()->compute(nframes, 0, output);
1205 }
1206 }
1207
1208 VstInt32
processEvents(VstEvents * events)1209 FooYC20VSTi::processEvents(VstEvents *events)
1210 {
1211 for (VstInt32 i=0; i<events->numEvents; i++) {
1212
1213 if((events->events[i])->type != kVstMidiType) continue;
1214 VstMidiEvent* event = (VstMidiEvent*)events->events[i];
1215 uint8_t* data = (uint8_t *)event->midiData;
1216
1217 float value = 0;
1218 int key = -1;
1219
1220 switch ( (data[0]) & 0xf0 ) {
1221 case 0x90:
1222 if (data[2] == 0) {
1223 value = 0.0;
1224 } else {
1225 value = 1;
1226 }
1227 key = data[1] - 36;
1228 break;
1229 case 0x80:
1230 value = 0.0;
1231 key = data[1] - 36;
1232 break;
1233 }
1234 if (key >= 0 && key < 61) {
1235 yc20->setKey(key, value);
1236 }
1237
1238 }
1239
1240 return 1;
1241 }
1242
1243 void
setParameter(VstInt32 index,float value)1244 FooYC20VSTi::setParameter (VstInt32 index, float value)
1245 {
1246 if (index < 0 || index >= NUM_PARAMS) return;
1247
1248 Control *c = yc20->getControl(label_for_parameter[index]);
1249 if (c == 0) {
1250 std::cerr << "no parameter for index " << index << std::endl;
1251 return;
1252 }
1253
1254 if (index == PARAM_PITCH) {
1255 value -= 0.5;
1256 value *= 2.0;
1257 }
1258
1259 bool didChange = (*c->getZone() != value);
1260
1261 *c->getZone() = value;
1262
1263 if (editor && didChange) {
1264 ((YC20AEffEditor *)editor)->queueChange(index, value);
1265 }
1266 }
1267
1268 float
getParameter(VstInt32 index)1269 FooYC20VSTi::getParameter (VstInt32 index)
1270 {
1271 if (index < 0 || index >= NUM_PARAMS) return 0.0;
1272
1273 Control *c = yc20->getControl(label_for_parameter[index]);
1274 if (c == 0) {
1275 std::cerr << "no parameter for index " << index << std::endl;
1276 return 0.0;
1277 }
1278
1279 float value = *c->getZone();
1280 if (index == PARAM_PITCH) {
1281 value /= 2.0;
1282 value += 0.5;
1283 }
1284 return value;
1285 }
1286
1287 void
getParameterName(VstInt32 index,char * ptr)1288 FooYC20VSTi::getParameterName (VstInt32 index, char *ptr)
1289 {
1290 if (index < 0 || index >= NUM_PARAMS) return;
1291
1292 vst_strncpy(ptr, label_for_parameter[index].c_str(), kVstMaxParamStrLen);
1293 }
1294
1295 void
getParameterDisplay(VstInt32 index,char * ptr)1296 FooYC20VSTi::getParameterDisplay(VstInt32 index, char *ptr)
1297 {
1298 if (index < 0 || index >= NUM_PARAMS) return;
1299
1300 Control *c = yc20->getControl(label_for_parameter[index]);
1301 if (c == 0) {
1302 std::cerr << "no parameter for index " << index << std::endl;
1303 return;
1304 }
1305 float value = *c->getZone();
1306 // No need to convert pitch value from zone,
1307
1308 snprintf(ptr, kVstMaxParamStrLen, "%.2f", value);
1309 }
1310
1311 /* vim: set ts=8 sw=8 foldmethod=marker: */
1312