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 = &rect;
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