1 /* Mixer+Trigger
2  *
3  * Copyright (C) 2013 Robin Gareus <robin@gareus.org>
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 2, or (at your option)
8  * 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 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <math.h>
22 
23 #include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
24 #include "src/mixtri.h"
25 
26 #define MIX_WIDTH  80
27 #define MIX_HEIGHT 40
28 #define MIX_RADIUS 10
29 #define MIX_CX 39.5
30 #define MIX_CY 16.5
31 
32 typedef struct {
33 	LV2UI_Write_Function write;
34 	LV2UI_Controller controller;
35 
36 	RobWidget *hbox, *ctable;
37 
38 	RobTkLbl  *lbl_in[4];
39 	RobTkLbl  *lbl_out[3];
40 	RobTkLbl  *label[9];
41 	RobTkDial *dial_in[4];
42 	RobTkDial *dial_mix[12];
43 	RobTkSpin *spb_delay_in[4];
44 	RobTkSpin *spb_delay_out[3];
45 	RobTkCBtn *btn_hpfilt_in[4];
46 	RobTkCBtn *btn_mute_in[4];
47 	RobTkRBtn *btn_trig_src[4];
48 	RobTkSelect *sel_trig_mode;
49 
50 	RobTkLbl  *lbl_trig[4];
51 	RobTkSelect *sel_trig_edge;
52 	RobTkSpin *spb_trigger_tme[2];
53 	RobTkSpin *spb_trigger_lvl[2];
54 	RobTkDarea *drawing_area;
55 	RobTkCBtn *btn_show_doc;
56 
57 	bool disable_signals;
58 
59 	PangoFontDescription *font[2];
60 
61 	cairo_surface_t* routeT;
62 	cairo_surface_t* routeC;
63 	cairo_surface_t* routeM;
64 	cairo_surface_t* routeE;
65 	cairo_surface_t* routeI;
66 	cairo_surface_t* delayI;
67 	cairo_surface_t* delayO;
68 
69 } MixTriUI;
70 
create_faceplate(MixTriUI * ui)71 static void create_faceplate(MixTriUI *ui) {
72 	cairo_t* cr;
73 	float xlp, ylp;
74 	PangoFontDescription *font = pango_font_description_from_string("Sans 8px");
75 
76 #define AMPLABEL(V, O, T, X) \
77 	{ \
78 		float ang = (-.75 * M_PI) + (1.5 * M_PI) * ((V) + (O)) / (T); \
79 		xlp = X + .5 + sinf (ang) * (MIX_RADIUS + 3.0); \
80 		ylp = MIX_CY + .5 - cosf (ang) * (MIX_RADIUS + 3.0); \
81 		cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); \
82 		CairoSetSouerceRGBA(c_wht); \
83 		cairo_set_line_width(cr, 1.5); \
84 		cairo_move_to(cr, rint(xlp)-.5, rint(ylp)-.5); \
85 		cairo_close_path(cr); \
86 		cairo_stroke(cr); \
87 		xlp = X + .5 + sinf (ang) * (MIX_RADIUS + 9.5); \
88 		ylp = MIX_CY + .5 - cosf (ang) * (MIX_RADIUS + 9.5); \
89 	}
90 
91 #define COMMONRROUTE(SF, TOP, RIGHT) \
92 	SF = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, MIX_WIDTH, MIX_HEIGHT); \
93 	cr = cairo_create (SF); \
94 	cairo_set_source_rgba (cr, .3, .3, .4, 1.0); \
95 	cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); \
96 	cairo_rectangle (cr, 0, 0, MIX_WIDTH, MIX_HEIGHT); \
97 	cairo_fill (cr); \
98 	cairo_set_operator (cr, CAIRO_OPERATOR_OVER); \
99 	CairoSetSouerceRGBA(c_g60); \
100 	cairo_set_line_width(cr, 1.0); \
101 	cairo_move_to(cr, 0, MIX_CY); \
102 	cairo_line_to(cr, RIGHT, MIX_CY); \
103 	cairo_stroke(cr); \
104 	CairoSetSouerceRGBA(c_blk); \
105 	cairo_move_to(cr, MIX_CX, TOP); \
106 	cairo_line_to(cr, MIX_CX, MIX_HEIGHT); \
107 	cairo_stroke(cr); \
108 	cairo_move_to(cr, MIX_CX-3, MIX_HEIGHT-6.5); \
109 	cairo_line_to(cr, MIX_CX+3, MIX_HEIGHT-6.5); \
110 	cairo_line_to(cr, MIX_CX, MIX_HEIGHT-0.5); \
111 	cairo_close_path(cr); \
112 	cairo_fill(cr); \
113 	AMPLABEL(-20, 20., 40., MIX_CX); write_text_full(cr, "-20", font, xlp, ylp, 0, 2, c_wht); \
114 	AMPLABEL( 20, 20., 40., MIX_CX); write_text_full(cr, "+20", font, xlp, ylp, 0, 2, c_wht); \
115 	AMPLABEL(  0, 20., 40., MIX_CX); \
116 	AMPLABEL(-12, 20., 40., MIX_CX); \
117 	AMPLABEL( -6, 20., 40., MIX_CX); \
118 	AMPLABEL(  0, 20., 40., MIX_CX); \
119 	AMPLABEL(  6, 20., 40., MIX_CX); \
120 	AMPLABEL( 12, 20., 40., MIX_CX); \
121 	cairo_destroy (cr);
122 
123 	COMMONRROUTE(ui->routeM, 0, MIX_WIDTH);
124 	COMMONRROUTE(ui->routeE, 0, MIX_CX);
125 	COMMONRROUTE(ui->routeT, MIX_CY, MIX_WIDTH);
126 	COMMONRROUTE(ui->routeC, MIX_CY, MIX_CX);
127 
128 
129 	const double dashed[] = {2.5};
130 #define DASHEDROUTE(SF) \
131 	cr = cairo_create (SF); \
132 	CairoSetSouerceRGBA(c_g60); \
133 	cairo_set_line_width(cr, 1.0); \
134 	cairo_set_dash(cr, dashed, 1, 0); \
135 	cairo_move_to(cr, MIX_CX, MIX_CY); \
136 	cairo_line_to(cr, MIX_WIDTH, MIX_CY); \
137 	cairo_stroke(cr); \
138 	cairo_move_to(cr, MIX_WIDTH-15.5, MIX_CY-3.5); \
139 	cairo_line_to(cr, MIX_WIDTH-15.5, MIX_CY+3.5); \
140 	cairo_line_to(cr, MIX_WIDTH-9.5, MIX_CY); \
141 	cairo_close_path(cr); \
142 	cairo_fill(cr); \
143 	cairo_destroy (cr);
144 
145 	DASHEDROUTE(ui->routeE)
146 	DASHEDROUTE(ui->routeC)
147 
148 	ui->routeI = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 60, MIX_HEIGHT);
149 	cr = cairo_create (ui->routeI);
150 	cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
151 	cairo_rectangle (cr, 0, 0, 2, MIX_HEIGHT);
152 	cairo_set_source_rgba (cr, .0, .0, .0, .0);
153 	cairo_fill (cr);
154 	cairo_rectangle (cr, 2, 0, MIX_WIDTH-2, MIX_HEIGHT);
155 	cairo_set_source_rgba (cr, .3, .4, .3, 1.0);
156 	cairo_fill (cr);
157 	CairoSetSouerceRGBA(c_g60);
158 	cairo_set_line_width(cr, 1.0);
159 	cairo_move_to(cr, 0, MIX_CY);
160 	cairo_line_to(cr, 60, MIX_CY);
161 	cairo_stroke(cr);
162 
163 	cairo_move_to(cr, 6.5, MIX_CY-3.5);
164 	cairo_line_to(cr, 6.5, MIX_CY+3.5);
165 	cairo_line_to(cr, 12.5, MIX_CY);
166 	cairo_close_path(cr);
167 	cairo_move_to(cr, 60-7.5, MIX_CY-3.5);
168 	cairo_line_to(cr, 60-7.5, MIX_CY+3.5);
169 	cairo_line_to(cr, 60-1.5, MIX_CY);
170 	cairo_close_path(cr);
171 	cairo_fill(cr);
172 
173 	AMPLABEL(  0, 60., 80., 30.5); write_text_full(cr, " 0dB", font, xlp, ylp, 0, 2, c_wht);
174 	AMPLABEL( 20, 60., 80., 30.5); write_text_full(cr, "+20", font, xlp, ylp, 0, 2, c_wht);
175 	AMPLABEL(-60, 60., 80., 30.5); write_text_full(cr, "-60", font, xlp, ylp, 0, 2, c_wht);
176 	AMPLABEL(-50, 60., 80., 30.5);
177 	AMPLABEL(-40, 60., 80., 30.5);
178 	AMPLABEL(-30, 60., 80., 30.5);
179 	AMPLABEL(-20, 60., 80., 30.5);
180 	AMPLABEL(-10, 60., 80., 30.5);
181 	AMPLABEL( 10, 60., 80., 30.5);
182 	AMPLABEL( 20, 60., 80., 30.5);
183 	pango_font_description_free(font);
184 
185 	ui->delayO = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, MIX_WIDTH, GSP_HEIGHT);
186 	cr = cairo_create (ui->delayO);
187 	cairo_set_source_rgba (cr, .3, .2, .4, 1.0);
188 	cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
189 	cairo_rectangle (cr, 0, 0, MIX_WIDTH, GSP_HEIGHT);
190 	cairo_fill (cr);
191 	cairo_set_line_width(cr, 1.0);
192 	CairoSetSouerceRGBA(c_blk);
193 	cairo_move_to(cr, MIX_CX, 0);
194 	cairo_line_to(cr, MIX_CX, GSP_HEIGHT);
195 	cairo_stroke(cr);
196 	cairo_move_to(cr, MIX_CX-3, GSP_HEIGHT-6.5);
197 	cairo_line_to(cr, MIX_CX+3, GSP_HEIGHT-6.5);
198 	cairo_line_to(cr, MIX_CX, GSP_HEIGHT-0.5);
199 	cairo_close_path(cr);
200 	cairo_fill(cr);
201 	cairo_destroy (cr);
202 
203 	ui->delayI = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, MIX_WIDTH, GSP_HEIGHT);
204 	cr = cairo_create (ui->delayI);
205 	cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
206 	cairo_set_source_rgba (cr, .0, .0, .0, .0);
207 	cairo_rectangle (cr, 0, 0, MIX_WIDTH, GSP_HEIGHT);
208 	cairo_fill (cr);
209 	cairo_set_source_rgba (cr, .4, .3, .3, 1.0);
210 	cairo_rectangle (cr, 0, 0, MIX_WIDTH-4, GSP_HEIGHT);
211 	cairo_fill (cr);
212 	cairo_set_line_width(cr, 1.0);
213 	CairoSetSouerceRGBA(c_g60);
214 	cairo_set_line_width(cr, 1.0);
215 	cairo_move_to(cr, 0, GSP_CY);
216 	cairo_line_to(cr, MIX_WIDTH-3, GSP_CY);
217 	cairo_stroke(cr);
218 	cairo_destroy (cr);
219 
220 }
221 
annotation_txt(MixTriUI * ui,RobTkDial * d,cairo_t * cr,const char * txt)222 static void annotation_txt(MixTriUI *ui, RobTkDial * d, cairo_t *cr, const char *txt) {
223 	int tw, th;
224 	cairo_save(cr);
225 	PangoLayout * pl = pango_cairo_create_layout(cr);
226 	pango_layout_set_font_description(pl, ui->font[0]);
227 	pango_layout_set_text(pl, txt, -1);
228 	pango_layout_get_pixel_size(pl, &tw, &th);
229 	cairo_translate (cr, d->w_cx, d->w_height);
230 	cairo_translate (cr, -tw/2.0 - 0.5, -th);
231 	cairo_set_source_rgba (cr, .0, .0, .0, .7);
232 	rounded_rectangle(cr, -1, -1, tw+3, th+1, 3);
233 	cairo_fill(cr);
234 	CairoSetSouerceRGBA(c_wht);
235 	pango_cairo_show_layout(cr, pl);
236 	g_object_unref(pl);
237 	cairo_restore(cr);
238 	cairo_new_path(cr);
239 }
240 
dial_annotation_val(RobTkDial * d,cairo_t * cr,void * data)241 static void dial_annotation_val(RobTkDial * d, cairo_t *cr, void *data) {
242 	MixTriUI* ui = (MixTriUI*) (data);
243 	char tmp[16];
244 	switch(d->click_state) {
245 		case 1:
246 			snprintf(tmp, 16, "-\u221EdB");
247 			break;
248 		case 2:
249 			snprintf(tmp, 16, "\u00D8%+4.1fdB", d->cur);
250 			break;
251 		default:
252 			snprintf(tmp, 16, "%+4.1fdB", d->cur);
253 			break;
254 	}
255 	annotation_txt(ui, d, cr, tmp);
256 }
257 
dial_annotation_db(RobTkDial * d,cairo_t * cr,void * data)258 static void dial_annotation_db(RobTkDial * d, cairo_t *cr, void *data) {
259 	MixTriUI* ui = (MixTriUI*) (data);
260 	char tmp[16];
261 	snprintf(tmp, 16, "%+4.1fdB", d->cur);
262 	annotation_txt(ui, d, cr, tmp);
263 }
264 
box_expose_event(RobWidget * rw,cairo_t * cr,cairo_rectangle_t * ev)265 static bool box_expose_event(RobWidget* rw, cairo_t* cr, cairo_rectangle_t *ev) {
266 	if (rw->resized) {
267 		cairo_rectangle_t event;
268 		event.x = MAX(0, ev->x - rw->area.x);
269 		event.y = MAX(0, ev->y - rw->area.y);
270 		event.width  = MIN(rw->area.x + rw->area.width , ev->x + ev->width)   - MAX(ev->x, rw->area.x);
271 		event.height = MIN(rw->area.y + rw->area.height, ev->y + ev->height) - MAX(ev->y, rw->area.y);
272 		cairo_save(cr);
273 		rcontainer_clear_bg(rw, cr, &event);
274 
275 		const float ytop = ((struct rob_table*)rw->self)->rows[0].acq_h;
276 		float tx0 = 0, tc0 = 0, tc1 = 0;
277 		for (uint32_t i = 0; i < 8; ++i) {
278 			tx0 += ((struct rob_table*)rw->self)->cols[i].acq_w;
279 			if (i == 0) tc0 = tx0;
280 			if (i == 3) tc1 = tx0;
281 		}
282 		const float yof = ytop + MIX_CY;
283 		const float twi = ((struct rob_table*)rw->self)->cols[8].acq_w;
284 		const float tx1 = tx0 + twi / 2;
285 
286 		/* fixup in-delayline bg  & channel mods */
287 		cairo_set_source_rgba (cr, .4, .3, .3, 1.0);
288 		cairo_rectangle (cr, tc0, ytop, tc1-tc0, 160);
289 		cairo_fill(cr);
290 
291 		/* right end trigger background */
292 		cairo_set_source_rgba (cr, .2, .3, .35, 1.0);
293 		cairo_rectangle (cr, tx0, ytop, twi, 160+30);
294 		cairo_fill(cr);
295 
296 		cairo_set_line_width(cr, 1.0);
297 		CairoSetSouerceRGBA(c_g60);
298 
299 		for (uint32_t i = 0; i < 4; ++i) {
300 			const float y0 = yof + i * MIX_HEIGHT;
301 			cairo_move_to(cr, tc0, y0);
302 			cairo_line_to(cr, tc1, y0);
303 			cairo_stroke(cr);
304 		}
305 
306 		const double dashed[] = {2.5};
307 		cairo_set_dash(cr, dashed, 1, 4);
308 		for (uint32_t i = 0; i < 4; ++i) {
309 			const float y0 = yof + i * MIX_HEIGHT;
310 			cairo_move_to(cr, tx0-2, y0);
311 			cairo_line_to(cr, tx1, y0);
312 			cairo_stroke(cr);
313 		}
314 		cairo_set_dash(cr, NULL, 0, 0);
315 		CairoSetSouerceRGBA(c_blk);
316 		for (uint32_t i = 0; i < 5; ++i) {
317 			float y0 = yof + i * MIX_HEIGHT;
318 			cairo_move_to(cr, tx1+.5, y0);
319 			cairo_line_to(cr, tx1+.5, y0 + MIX_HEIGHT);
320 			cairo_stroke(cr);
321 
322 			if (i == 4) y0 -= 10; // MIX_HEIGHT - GED_HEIGHT
323 
324 			cairo_move_to(cr, tx1+.5-3, y0+23-6.5);
325 			cairo_line_to(cr, tx1+.5+3, y0+23-6.5);
326 			cairo_line_to(cr, tx1+.5,   y0+23-0.5);
327 			cairo_close_path(cr);
328 			cairo_fill(cr);
329 		}
330 		cairo_restore(cr);
331 	}
332 	return rcontainer_expose_event_no_clear(rw, cr, ev);
333 }
334 
draw_arrow(cairo_t * cr,float x,float y,bool down)335 static void draw_arrow (cairo_t *cr, float x, float y, bool down) {
336 	cairo_save(cr);
337 	cairo_set_source_rgba (cr, .95, 1.0, .95, .8);
338 	cairo_set_line_width(cr, 1.0);
339 	cairo_move_to(cr, x+.5, y+.5);
340 	if (down) {
341 		cairo_line_to(cr, x+.5, y+12.5);
342 		cairo_stroke(cr);
343 		cairo_move_to(cr, x+.5, y+12.5);
344 		cairo_line_to(cr, x+3.5,  y+7.5);
345 		cairo_line_to(cr, x-2.5,  y+7.5);
346 		cairo_close_path(cr);
347 		cairo_fill(cr);
348 	} else {
349 		cairo_line_to(cr, x+.5, y-11.5);
350 		cairo_stroke(cr);
351 		cairo_move_to(cr, x+.5, y-11.5);
352 		cairo_line_to(cr, x+3.5,  y-6.5);
353 		cairo_line_to(cr, x-2.5,  y-6.5);
354 		cairo_close_path(cr);
355 		cairo_fill(cr);
356 	}
357 	cairo_restore(cr);
358 }
359 
draw_cross(cairo_t * cr,float x,float y)360 static void draw_cross (cairo_t *cr, float x, float y) {
361 	cairo_save(cr);
362 	cairo_set_source_rgba (cr, .95, 1.0, .95, .8);
363 	cairo_set_line_width(cr, 1.0);
364 
365 	cairo_move_to(cr, x-2.5, y-2.5);
366 	cairo_line_to(cr, x+3.5, y+3.5);
367 	cairo_stroke(cr);
368 	cairo_move_to(cr, x+3.5, y-2.5);
369 	cairo_line_to(cr, x-2.5, y+3.5);
370 	cairo_stroke(cr);
371 	cairo_restore(cr);
372 }
373 
draw_timedelta(cairo_t * cr,float x,float y,float w,float dw)374 static void draw_timedelta (cairo_t *cr, float x, float y, float w, float dw) {
375 	cairo_save(cr);
376 	cairo_set_line_width(cr, 1.0);
377 
378 	if (dw > 0) {
379 		cairo_set_source_rgba (cr, .95, 1.0, .95, .6);
380 		cairo_rectangle(cr, x+w-dw+.5, y-2.5, dw*2, 6.0);
381 		cairo_fill(cr);
382 	}
383 
384 	cairo_set_source_rgba (cr, .95, 1.0, .95, .8);
385 	cairo_move_to(cr, x+.5, y-2.5);
386 	cairo_line_to(cr, x+.5, y+3.5);
387 	cairo_stroke(cr);
388 
389 	cairo_move_to(cr, x+.5, y+.5);
390 	cairo_line_to(cr, x+w+.5, y+.5);
391 	cairo_stroke(cr);
392 
393 	cairo_move_to(cr, x+w+.5, y-1.5);
394 	cairo_line_to(cr, x+w+.5, y+2.5);
395 	cairo_stroke(cr);
396 
397 	cairo_restore(cr);
398 }
399 
400 #define ANN_TEXT(txt) \
401 	write_text_full(cr, txt, ui->font[1], 0, doc_h, 0, 6, c_wht);
402 
draw_trigger_doc(cairo_t * cr,void * d)403 static void draw_trigger_doc (cairo_t *cr, void *d) {
404 	MixTriUI* ui = (MixTriUI*) (d);
405 	int mode = robtk_select_get_value(ui->sel_trig_mode);
406 	int edge = robtk_select_get_value(ui->sel_trig_edge);
407 
408 	float c_bg[4];
409 	get_color_from_theme(1, c_bg);
410 
411 	cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
412 	CairoSetSouerceRGBA(c_bg);
413 	cairo_rectangle (cr, 0, 0, 150, 180);
414 	cairo_fill(cr);
415 
416 	cairo_set_source_rgba (cr, .1, .1, .1, 1.0);
417 	cairo_rectangle (cr, 5, 0, 140, 180);
418 	cairo_fill(cr);
419 
420 	cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
421 
422 	cairo_save(cr);
423 	cairo_translate(cr, 10, 5);
424 	const float doc_w = 130;
425 	const float doc_h = 175;
426 	const float doc_b = 100;
427 	cairo_rectangle(cr, 0, 0, doc_w, doc_h);
428 	cairo_clip(cr);
429 
430 	/* grid */
431 
432 	cairo_set_line_width(cr, 1.5);
433 	cairo_set_source_rgba (cr, .4, .4, .4, 1.0);
434 
435 	const double dash[] = {1.5};
436 	cairo_set_line_width(cr, 1.0);
437 	cairo_set_dash(cr, dash, 1, 0);
438 
439 	switch (mode) {
440 		case TRG_PASSTRHU :
441 			break;
442 		default:
443 			for (uint32_t i = 5; i < doc_w; i+=10) {
444 				cairo_move_to(cr, i+.5, 0);
445 				cairo_line_to(cr, i+.5, doc_b);
446 				cairo_stroke(cr);
447 			}
448 			for (uint32_t i = 5; i < doc_b; i+=10) {
449 				cairo_move_to(cr, 0, i+.5);
450 				cairo_line_to(cr, doc_w, i+.5);
451 				cairo_stroke(cr);
452 			}
453 			break;
454 	}
455 
456 	cairo_set_dash(cr, NULL, 0, 0);
457 
458 	/* waveform */
459 
460 	cairo_set_source_rgba (cr, 1.0, .0, .0, 1.0);
461 
462 	switch (mode) {
463 		case TRG_EDGE :
464 		case TRG_WINDOW_ENTER:
465 		case TRG_WINDOW_LEAVE:
466 			cairo_move_to(cr, 5, 60);
467 			cairo_curve_to(cr, 15, 50, 20, 75, 25, 80);
468 			cairo_curve_to(cr, 25, 80, 38, 90, 45, 70);
469 			cairo_curve_to(cr, 45, 70, 60, 0, 87, 40);
470 			cairo_curve_to(cr, 88, 40, 100, 75, 120, 25);
471 			cairo_line_to(cr, 125, 8);
472 			cairo_stroke(cr);
473 			break;
474 		case TRG_DROPIN:
475 		case TRG_HYSTERESIS:
476 			cairo_move_to(cr, 5, 20);
477 			cairo_curve_to(cr, 18, 40, 22, 75, 30, 80);
478 			cairo_curve_to(cr, 30, 80, 38, 90, 45, 70);
479 			cairo_curve_to(cr, 45, 70, 60, 0, 87, 40);
480 			cairo_curve_to(cr, 88, 40, 100, 75, 120, 25);
481 			cairo_line_to(cr, 125, 8);
482 			cairo_stroke(cr);
483 			break;
484 		case TRG_DROPOUT:
485 			cairo_move_to(cr,  5.5, 75.5);
486 			cairo_line_to(cr, 25.5, 75.5);
487 			cairo_line_to(cr, 26.5, 25.5);
488 			cairo_line_to(cr, 55.5, 25.5);
489 			cairo_line_to(cr, 56.5, 75.5);
490 			cairo_line_to(cr,125.5, 75.5);
491 			cairo_stroke(cr);
492 			break;
493 		case TRG_RUNT:
494 			cairo_move_to(cr,  5.5, 75.5);
495 			cairo_line_to(cr, 15.5, 75.5);
496 			cairo_line_to(cr, 16.5, 25.5);
497 			cairo_line_to(cr, 35.5, 25.5);
498 			cairo_line_to(cr, 36.5, 75.5);
499 			cairo_line_to(cr, 55.5, 75.5);
500 			cairo_line_to(cr, 56.5, 45.5);
501 			cairo_line_to(cr, 75.5, 45.5);
502 			cairo_line_to(cr, 76.5, 75.5);
503 			cairo_line_to(cr, 95.5, 75.5);
504 			cairo_line_to(cr, 96.5, 25.5);
505 			cairo_line_to(cr,105.5, 25.5);
506 			cairo_line_to(cr,106.5, 55.5);
507 			cairo_line_to(cr,115.5, 55.5);
508 			cairo_line_to(cr,116.5, 25.5);
509 			cairo_line_to(cr,125.5, 25.5);
510 			cairo_stroke(cr);
511 			break;
512 		case TRG_PULSEWIDTH:
513 		case TRG_PULSETRAIN:
514 			cairo_move_to(cr,  5.5, 25.5);
515 			cairo_line_to(cr,  6.5, 75.5);
516 			cairo_line_to(cr, 25.5, 75.5);
517 			cairo_line_to(cr, 26.5, 25.5);
518 			cairo_line_to(cr, 45.5, 25.5);
519 			cairo_line_to(cr, 46.5, 75.5);
520 			cairo_line_to(cr, 75.5, 75.5);
521 			cairo_line_to(cr, 76.5, 25.5);
522 			cairo_line_to(cr, 85.5, 25.5);
523 			cairo_line_to(cr, 86.5, 75.5);
524 			cairo_line_to(cr, 95.5, 75.5);
525 			cairo_line_to(cr, 96.5, 25.5);
526 			cairo_line_to(cr,115.5, 25.5);
527 			cairo_line_to(cr,116.5, 75.5);
528 			cairo_line_to(cr,125.5, 75.5);
529 			cairo_stroke(cr);
530 			break;
531 		case TRG_LTC:
532 			cairo_move_to(cr,  5.5, 75.5);
533 			for (int i = 0; i < 70; i+=5) {
534 				cairo_line_to(cr, i + 10.5, (i%10 != 0)? 25.5 : 75.5);
535 				cairo_line_to(cr, i + 11.5, (i%10 == 0)? 25.5 : 75.5);
536 			}
537 			cairo_line_to(cr,85.5, 75.5);
538 			cairo_line_to(cr,86.5, 25.5);
539 			cairo_line_to(cr,90.5, 25.5);
540 			cairo_line_to(cr,91.5, 75.5);
541 			cairo_line_to(cr,95.5, 75.5);
542 			cairo_line_to(cr,96.5, 25.5);
543 			cairo_line_to(cr,105.5, 25.5);
544 			cairo_line_to(cr,106.5, 75.5);
545 			cairo_line_to(cr,115.5, 75.5);
546 			cairo_line_to(cr,116.5, 25.5);
547 			cairo_line_to(cr,120.5, 25.5);
548 			cairo_line_to(cr,121.5, 75.5);
549 			cairo_line_to(cr,125.5, 75.5);
550 			cairo_line_to(cr,126.5, 25.5);
551 			cairo_stroke(cr);
552 			break;
553 		case TRG_LPF:
554 			cairo_move_to(cr,  5.5, 50);
555 			{
556 				float phase = 0;
557 				for (uint32_t i = 1; i < 120; ++i) {
558 					phase+=.05 + .4 * i / 120.0;
559 					cairo_line_to(cr, i + 5.5, 50 + 40.0 * sinf(phase));
560 				}
561 			}
562 			cairo_stroke(cr);
563 			break;
564 		case TRG_RMS:
565 			cairo_move_to(cr,  5.5, 50);
566 			for (uint32_t i = 1; i < 120; ++i) {
567 				cairo_line_to(cr, i + 5.5, 50 + 40.0 * sinf(i * .2));
568 			}
569 			cairo_stroke(cr);
570 			break;
571 		case TRG_PASSTRHU:
572 		default:
573 			break;
574 	}
575 
576 	/* settings & annotation */
577 	switch (mode) {
578 		case TRG_EDGE :
579 			cairo_set_source_rgba (cr, .0, 1.0, .0, .8);
580 			cairo_move_to(cr, 0, 40.5);
581 			cairo_line_to(cr, doc_w, 40.5);
582 			cairo_stroke(cr);
583 			if (edge&1) {
584 				draw_arrow(cr, 55, 70, false);
585 				draw_arrow(cr, 112, 70, false);
586 			}
587 			if (edge&2) {
588 				draw_arrow(cr, 85, 10, true);
589 			}
590 			ANN_TEXT("Signal Edge\n Signal passes 'Level 1'.");
591 			break;
592 		case TRG_PULSEWIDTH:
593 			cairo_set_source_rgba (cr, .0, 1.0, .0, .8);
594 			cairo_move_to(cr, 0, 40.5);
595 			cairo_line_to(cr, doc_w, 40.5);
596 			cairo_stroke(cr);
597 
598 			if (edge & 1) {
599 				draw_cross(cr, 25, 40);
600 				draw_cross(cr, 75, 40);
601 				draw_cross(cr, 95, 40);
602 			}
603 
604 			if (edge & 2) {
605 				draw_cross(cr,  5, 40);
606 				draw_cross(cr, 45, 40);
607 				draw_cross(cr, 85, 40);
608 				draw_cross(cr,115, 40);
609 			}
610 
611 			switch (edge) {
612 				case 1:
613 					draw_timedelta(cr, 25, 90, 20, 5);
614 					draw_timedelta(cr, 75, 90, 20, 5);
615 					draw_arrow(cr,95, 20, false);
616 					break;
617 				case 2:
618 					draw_timedelta(cr,  5, 80, 40, 5);
619 					draw_timedelta(cr, 45, 90, 40, 5);
620 					draw_arrow(cr, 45, 10, true);
621 					draw_arrow(cr, 85, 10, true);
622 					break;
623 				case 3:
624 					draw_timedelta(cr,  5, 80, 20, 5);
625 					draw_timedelta(cr, 25, 90, 20, 5);
626 					draw_timedelta(cr, 45, 80, 20, 5);
627 					draw_timedelta(cr, 75, 90, 20, 5);
628 					draw_timedelta(cr, 85, 90, 20, 5);
629 					draw_timedelta(cr, 95, 80, 20, 5);
630 
631 					draw_arrow(cr, 25, 20, false);
632 					draw_arrow(cr, 45, 10, true);
633 					draw_arrow(cr,115, 10, true);
634 					break;
635 			}
636 
637 			ANN_TEXT("Pulse Width\n Last edge-trigger\n occurred between min\n and max (Time 1, 2) ago.");
638 			break;
639 		case TRG_PULSETRAIN:
640 			cairo_set_source_rgba (cr, .0, 1.0, .0, .8);
641 			cairo_move_to(cr, 0, 40.5);
642 			cairo_line_to(cr, doc_w, 40.5);
643 			cairo_stroke(cr);
644 
645 			if (edge & 1) {
646 				draw_cross(cr, 25, 40);
647 				draw_cross(cr, 75, 40);
648 				draw_cross(cr, 95, 40);
649 			}
650 			if (edge & 2) {
651 				draw_cross(cr,  5, 40);
652 				draw_cross(cr, 45, 40);
653 				draw_cross(cr, 85, 40);
654 				draw_cross(cr,115, 40);
655 			}
656 
657 			switch (edge) {
658 				case 1:
659 					draw_timedelta(cr, 25, 90, 20, 5);
660 					draw_timedelta(cr, 75, 90, 20, 5);
661 					draw_timedelta(cr, 95, 80, 20, 5);
662 					draw_arrow(cr, 50, 20, false);
663 					draw_arrow(cr,120, 20, false);
664 					break;
665 				case 2:
666 					draw_timedelta(cr,  5, 80, 40, 5);
667 					draw_timedelta(cr, 45, 90, 40, 5);
668 					draw_timedelta(cr, 85, 80, 40, 5);
669 					draw_arrow(cr,115, 10, true);
670 					break;
671 				case 3:
672 					draw_timedelta(cr,  5, 80, 20, 5);
673 					draw_timedelta(cr, 25, 90, 20, 5);
674 					draw_timedelta(cr, 45, 80, 20, 5);
675 					draw_timedelta(cr, 75, 90, 20, 5);
676 
677 					draw_arrow(cr, 70, 10, true);
678 					draw_arrow(cr, 85, 20, false);
679 					break;
680 			}
681 
682 			ANN_TEXT("Pulse Train\n No edge-trigger for a\n given time (max, Time 2),\n or more than one trigger\n since a given time (min,\n Time 1).");
683 			break;
684 		case TRG_WINDOW_ENTER:
685 			cairo_set_source_rgba (cr, .0, 1.0, .0, .8);
686 			cairo_move_to(cr, 0, 35.5);
687 			cairo_line_to(cr, doc_w, 35.5);
688 			cairo_stroke(cr);
689 			cairo_move_to(cr, 0, 45.5);
690 			cairo_line_to(cr, doc_w, 45.5);
691 			cairo_stroke(cr);
692 
693 
694 			if (edge&1) {
695 				cairo_set_source_rgba (cr, .0, 1.0, .0, .4);
696 				cairo_rectangle(cr,  53, 35.5, 5, 10); cairo_fill(cr);
697 				cairo_rectangle(cr, 108, 35.5, 7, 10); cairo_fill(cr);
698 				draw_arrow(cr, 53, 70, false);
699 				draw_arrow(cr,108, 70, false);
700 			}
701 			if (edge&2) {
702 				cairo_set_source_rgba (cr, .0, 1.0, .0, .4);
703 				cairo_rectangle(cr,  84, 35.5, 6, 10); cairo_fill(cr);
704 				draw_arrow(cr, 84, 10, true);
705 			}
706 			ANN_TEXT("Enter Window\n Signal enters a given\n range (Level 1, 2).");
707 			break;
708 		case TRG_WINDOW_LEAVE:
709 			cairo_set_source_rgba (cr, .0, 1.0, .0, .8);
710 			cairo_move_to(cr, 0, 35.5);
711 			cairo_line_to(cr, doc_w, 35.5);
712 			cairo_stroke(cr);
713 			cairo_move_to(cr, 0, 45.5);
714 			cairo_line_to(cr, doc_w, 45.5);
715 			cairo_stroke(cr);
716 
717 			cairo_set_source_rgba (cr, .0, 1.0, .0, .4);
718 			if (edge&1) {
719 				cairo_rectangle(cr,  53, 35.5, 5, 10); cairo_fill(cr);
720 				cairo_rectangle(cr, 108, 35.5, 7, 10); cairo_fill(cr);
721 				draw_arrow(cr, 58, 70, false);
722 				draw_arrow(cr,115, 70, false);
723 			}
724 			if (edge&2) {
725 				cairo_rectangle(cr,  84, 35.5, 6, 10); cairo_fill(cr);
726 				draw_arrow(cr, 90, 10, true);
727 			}
728 
729 			ANN_TEXT("Leave Window\n Signal leaves a given\n range (Level 1, 2).");
730 			break;
731 		case TRG_HYSTERESIS:
732 			cairo_set_source_rgba (cr, .0, 1.0, .0, .8);
733 			cairo_move_to(cr, 0, 35.5);
734 			cairo_line_to(cr, doc_w, 35.5);
735 			cairo_stroke(cr);
736 			cairo_move_to(cr, 0, 65.5);
737 			cairo_line_to(cr, doc_w, 65.5);
738 			cairo_stroke(cr);
739 			if (edge&1) {
740 				draw_arrow(cr, 58, 80, false);
741 			}
742 			if (edge&2) {
743 				draw_arrow(cr, 23, 10, true);
744 			}
745 			ANN_TEXT("Hysteresis\n Signal crosses both min\n and max (Level 1, 2) in\n the same direction\n without interruption.");
746 			break;
747 		case TRG_RUNT:
748 			cairo_set_source_rgba (cr, .0, 1.0, .0, .8);
749 			cairo_move_to(cr, 0, 40.5);
750 			cairo_line_to(cr, doc_w, 40.5);
751 			cairo_stroke(cr);
752 			cairo_move_to(cr, 0, 60.5);
753 			cairo_line_to(cr, doc_w, 60.5);
754 			cairo_stroke(cr);
755 			if (edge & 1) {
756 				draw_arrow(cr, 75, 90, false);
757 			}
758 			if (edge & 2) {
759 				draw_arrow(cr, 115, 10, true);
760 			}
761 			ANN_TEXT("Runt\n Fire if signal crosses 1st,\n but not 2nd threshold.");
762 			break;
763 		case TRG_DROPOUT:
764 			cairo_set_source_rgba (cr, .0, 1.0, .0, .8);
765 			cairo_move_to(cr, 0, 40.5);
766 			cairo_line_to(cr, doc_w, 40.5);
767 			cairo_stroke(cr);
768 			cairo_move_to(cr, 0, 60.5);
769 			cairo_line_to(cr, doc_w, 60.5);
770 			cairo_stroke(cr);
771 
772 			cairo_set_source_rgba (cr, .0, 1.0, .0, .2);
773 			cairo_set_line_width(cr, 4.0);
774 			cairo_move_to(cr, 25.5,     40.5);
775 			cairo_line_to(cr, 25.5, 60.5);
776 			cairo_stroke(cr);
777 			cairo_move_to(cr, 55.5,     40.5);
778 			cairo_line_to(cr, 55.5, 60.5);
779 			cairo_stroke(cr);
780 
781 			if (edge & 1) {
782 				draw_cross(cr,  25, 50);
783 				draw_timedelta(cr, 25, 80, 40, 0);
784 			}
785 			if (edge & 2) {
786 				draw_cross(cr,  55, 50);
787 				draw_timedelta(cr, 55, 90, 40, 0);
788 			}
789 			if (edge == 1) {
790 				draw_arrow(cr, 65, 100, false);
791 			} else {
792 				draw_arrow(cr, 95, 10, true);
793 			}
794 
795 			ANN_TEXT("Dropout\n Signal does not pass\n though a given range\n for at least 'Time 1'.");
796 			break;
797 		case TRG_DROPIN:
798 			cairo_set_source_rgba (cr, .0, 1.0, .0, .8);
799 			cairo_move_to(cr, 0, 65.5);
800 			cairo_line_to(cr, doc_w, 65.5);
801 			cairo_stroke(cr);
802 			cairo_move_to(cr, 0, 25.5);
803 			cairo_line_to(cr, doc_w, 25.5);
804 			cairo_stroke(cr);
805 
806 			cairo_set_source_rgba (cr, .0, 1.0, .0, .2);
807 			switch(edge) {
808 				case 1:
809 					cairo_rectangle(cr, 48, 25.5, 70, 40);
810 					cairo_fill(cr);
811 					draw_timedelta(cr, 47, 65, 40, 0);
812 					draw_arrow(cr, 87, 90, false);
813 					break;
814 				case 2:
815 					cairo_rectangle(cr, 8, 25.5, 15, 40);
816 					cairo_fill(cr);
817 					draw_timedelta(cr, 8, 65, 10, 0);
818 					draw_arrow(cr, 18, 10, true);
819 					break;
820 				case 3:
821 					cairo_rectangle(cr, 8, 25.5, 15, 40);
822 					cairo_fill(cr);
823 					cairo_rectangle(cr, 48, 25.5, 70, 40);
824 					cairo_fill(cr);
825 					draw_timedelta(cr, 47, 65, 10, 0);
826 					draw_timedelta(cr, 8, 65, 10, 0);
827 					draw_arrow(cr, 18, 10, true);
828 					draw_arrow(cr, 57, 90, false);
829 					break;
830 			}
831 
832 			ANN_TEXT("Constrained\n Signal remains within a\n given range for at least\n 'Time 1'.");
833 			break;
834 		case TRG_RMS:
835 			cairo_set_source_rgba (cr, .0, 0.5, 1.0, .8);
836 			cairo_move_to(cr,  5.5, 50);
837 			{
838 				float ts_prev = 0;
839 				for (uint32_t i = 1; i < 120; ++i) {
840 					const float y0 = sinf(i * .2);
841 					ts_prev += .02 * (y0 * y0 - ts_prev);
842 					cairo_line_to(cr, i + 5.5, 50 - 40.0 * sqrtf(ts_prev));
843 				}
844 			}
845 			cairo_stroke(cr);
846 			ANN_TEXT("Calculate RMS\n time constant\n 'Time 1'");
847 			break;
848 		case TRG_LPF:
849 			cairo_set_source_rgba (cr, .0, 0.5, 1.0, .8);
850 			cairo_move_to(cr,  5.5, 50);
851 			{
852 				float ts_prev = 0;
853 				float phase = 0;
854 				for (uint32_t i = 1; i < 120; ++i) {
855 					phase+=.05 + .4 * i / 120.0;
856 					const float y0 = sinf(phase);
857 					ts_prev += .1 * (y0 - ts_prev);
858 					cairo_line_to(cr, i + 5.5, 50 + 50.0 * ts_prev);
859 				}
860 			}
861 			cairo_stroke(cr);
862 			ANN_TEXT("Low Pass Filter\n 1.0 / 'Time 1' Hz");
863 			break;
864 		case TRG_PASSTRHU:
865 			ANN_TEXT("Signal Passthough");
866 			break;
867 		case TRG_LTC:
868 			draw_arrow(cr, 95, 90, false);
869 			ANN_TEXT("Linear Time Code\n LTC sync word.");
870 			break;
871 		default:
872 			break;
873 	}
874 
875 	cairo_restore(cr);
876 }
877 
cb_show_doc(RobWidget * handle,void * d)878 static bool cb_show_doc (RobWidget* handle, void *d) {
879 	MixTriUI* ui = (MixTriUI*) (d);
880 	if (robtk_cbtn_get_active(ui->btn_show_doc)) {
881 		robwidget_show(ui->drawing_area->rw, true);
882 	} else {
883 		robwidget_hide(ui->drawing_area->rw, true);
884 	}
885 	return TRUE;
886 }
887 
888 /******************************************************************************
889  * UI callbacks
890  */
891 
892 #define TRIGGERSENS(ED,L0,L1,T0,T1) \
893 	robtk_select_set_sensitive(ui->sel_trig_edge,    (ED)?true:false); \
894 	robtk_spin_set_sensitive(ui->spb_trigger_lvl[0], (L0)?true:false); \
895 	robtk_spin_set_sensitive(ui->spb_trigger_lvl[1], (L1)?true:false); \
896 	robtk_spin_set_sensitive(ui->spb_trigger_tme[0], (T0)?true:false); \
897 	robtk_spin_set_sensitive(ui->spb_trigger_tme[1], (T1)?true:false);
898 
cb_set_trig_mode(RobWidget * handle,void * data)899 static bool cb_set_trig_mode (RobWidget* handle, void *data) {
900 	MixTriUI* ui = (MixTriUI*) (data);
901 	float mode = robtk_select_get_value(ui->sel_trig_mode);
902 	switch ((int)mode) {
903 		case TRG_PASSTRHU:
904 		case TRG_LTC:
905 			TRIGGERSENS(0,0,0,0,0);
906 			break;
907 		case TRG_PULSEWIDTH:
908 		case TRG_PULSETRAIN:
909 			TRIGGERSENS(1,1,0,1,1);
910 			break;
911 		case TRG_WINDOW_ENTER:
912 		case TRG_WINDOW_LEAVE:
913 		case TRG_RUNT:
914 			TRIGGERSENS(1,1,1,0,0);
915 			break;
916 		case TRG_HYSTERESIS:
917 			TRIGGERSENS(1,1,1,0,0);
918 			break;
919 		case TRG_DROPIN:
920 		case TRG_DROPOUT:
921 			TRIGGERSENS(1,1,1,1,0);
922 			break;
923 		case TRG_RMS:
924 		case TRG_LPF:
925 			TRIGGERSENS(0,0,0,1,0);
926 			break;
927 		case TRG_EDGE :
928 			TRIGGERSENS(1,1,0,0,0);
929 			break;
930 	}
931 	if (robtk_cbtn_get_active(ui->btn_show_doc)) {
932 		robtk_darea_redraw(ui->drawing_area);
933 	}
934 	if (ui->disable_signals) return TRUE;
935 	ui->write(ui->controller, MIXTRI_TRIG_MODE, sizeof(float), 0, (const void*) &mode);
936 	return TRUE;
937 }
938 
cb_set_trig_edge(RobWidget * handle,void * data)939 static bool cb_set_trig_edge (RobWidget* handle, void *data) {
940 	MixTriUI* ui = (MixTriUI*) (data);
941 	if (robtk_cbtn_get_active(ui->btn_show_doc)) {
942 		robtk_darea_redraw(ui->drawing_area);
943 	}
944 	if (ui->disable_signals) return TRUE;
945 	float mode = robtk_select_get_value(ui->sel_trig_edge);
946 	ui->write(ui->controller, MIXTRI_TRIG_EDGE, sizeof(float), 0, (const void*) &mode);
947 	return TRUE;
948 }
949 
cb_set_trig_chn(RobWidget * handle,void * data)950 static bool cb_set_trig_chn (RobWidget* handle, void *data) {
951 	MixTriUI* ui = (MixTriUI*) (data);
952 	float chn = 0;
953 	if (ui->disable_signals) return TRUE;
954 	for (uint32_t i = 0; i < 4; ++i) {
955 		if (robtk_rbtn_get_active(ui->btn_trig_src[i])) {
956 			chn = i;
957 			break;
958 		}
959 	}
960 	ui->write(ui->controller, MIXTRI_TRIG_CHN, sizeof(float), 0, (const void*) &chn);
961 	return TRUE;
962 }
963 
cb_set_trig_values(RobWidget * handle,void * data)964 static bool cb_set_trig_values (RobWidget* handle, void *data) {
965 	MixTriUI* ui = (MixTriUI*) (data);
966 	float val0, val1;
967 	if (ui->disable_signals) return TRUE;
968 
969 	val0 = robtk_spin_get_value(ui->spb_trigger_lvl[0]);
970 	val1 = robtk_spin_get_value(ui->spb_trigger_lvl[1]);
971 	if (val1 < val0) {
972 		ui->disable_signals = true;
973 		robtk_spin_set_value(ui->spb_trigger_lvl[1], val0);
974 		val1 = val0;
975 	}
976 	ui->write(ui->controller, MIXTRI_TRIG_LVL1, sizeof(float), 0, (const void*) &val1);
977 	ui->write(ui->controller, MIXTRI_TRIG_LVL0, sizeof(float), 0, (const void*) &val0);
978 	val0 = robtk_spin_get_value(ui->spb_trigger_tme[0]);
979 	val1 = robtk_spin_get_value(ui->spb_trigger_tme[1]);
980 	if (val1 < val0) {
981 		ui->disable_signals = true;
982 		robtk_spin_set_value(ui->spb_trigger_tme[1], val0);
983 		val1 = val0;
984 	}
985 	ui->write(ui->controller, MIXTRI_TRIG_TME1, sizeof(float), 0, (const void*) &val1);
986 	ui->write(ui->controller, MIXTRI_TRIG_TME0, sizeof(float), 0, (const void*) &val0);
987 	ui->disable_signals = false; // XXX
988 	return TRUE;
989 }
990 
cb_set_fm(RobWidget * handle,void * data)991 static bool cb_set_fm (RobWidget* handle, void *data) {
992 	MixTriUI* ui = (MixTriUI*) (data);
993 	if (ui->disable_signals) return TRUE;
994 	for (uint32_t i = 0; i < 4; ++i) {
995 		int v = 0; float val;
996 		if (robtk_cbtn_get_active(ui->btn_mute_in[i])) v|=1;
997 		if (robtk_cbtn_get_active(ui->btn_hpfilt_in[i])) v|=2;
998 		val = v;
999 		ui->write(ui->controller, MIXTRI_MOD_I_0 + i, sizeof(float), 0, (const void*) &val);
1000 	}
1001 	return TRUE;
1002 }
1003 
cb_set_in(RobWidget * handle,void * data)1004 static bool cb_set_in (RobWidget* handle, void *data) {
1005 	MixTriUI* ui = (MixTriUI*) (data);
1006 	if (ui->disable_signals) return TRUE;
1007 	for (uint32_t i = 0; i < 4; ++i) {
1008 		float val = robtk_dial_get_value(ui->dial_in[i]);
1009 		ui->write(ui->controller, MIXTRI_GAIN_I_0 + i, sizeof(float), 0, (const void*) &val);
1010 	}
1011 	return TRUE;
1012 }
1013 
cb_set_mix(RobWidget * handle,void * data)1014 static bool cb_set_mix (RobWidget* handle, void *data) {
1015 	MixTriUI* ui = (MixTriUI*) (data);
1016 	if (ui->disable_signals) return TRUE;
1017 	for (uint32_t i = 0; i < 12; ++i) {
1018 		float val = pow(10, .05 * robtk_dial_get_value(ui->dial_mix[i]));
1019 		switch(robtk_dial_get_state(ui->dial_mix[i])) {
1020 			case 1:
1021 				val = 0;
1022 				break;
1023 			case 2:
1024 				val *= -1;
1025 				break;
1026 			default:
1027 				break;
1028 		}
1029 		ui->write(ui->controller, MIXTRI_MIX_0_0 + i, sizeof(float), 0, (const void*) &val);
1030 	}
1031 	return TRUE;
1032 }
1033 
cb_set_delay(RobWidget * handle,void * data)1034 static bool cb_set_delay (RobWidget* handle, void *data) {
1035 	MixTriUI* ui = (MixTriUI*) (data);
1036 	if (ui->disable_signals) return TRUE;
1037 	for (uint32_t i = 0; i < 4; ++i) {
1038 		float val = robtk_spin_get_value(ui->spb_delay_in[i]);
1039 		ui->write(ui->controller, MIXTRI_DLY_I_0 + i, sizeof(float), 0, (const void*) &val);
1040 	}
1041 	for (uint32_t i = 0; i < 3; ++i) {
1042 		float val = robtk_spin_get_value(ui->spb_delay_out[i]);
1043 		ui->write(ui->controller, MIXTRI_DLY_O_0 + i, sizeof(float), 0, (const void*) &val);
1044 	}
1045 	return TRUE;
1046 }
1047 
1048 /******************************************************************************
1049  * RobWidget
1050  */
1051 
toplevel_mixtri(MixTriUI * ui)1052 static RobWidget *toplevel_mixtri(MixTriUI* ui)
1053 {
1054 	ui->hbox = rob_hbox_new(FALSE, 2);
1055 
1056 	ui->font[0] = pango_font_description_from_string("Mono 10px");
1057 	ui->font[1] = pango_font_description_from_string("Sans 9px");
1058 	create_faceplate(ui);
1059 
1060 	ui->label[0] = robtk_lbl_new("Delay [spl]");
1061 	ui->label[1] = robtk_lbl_new("Output Delay [spl] \u2192 ");
1062 	ui->label[2] = robtk_lbl_new("Mixer Matrix [amp]");
1063 	ui->label[3] = robtk_lbl_new("Channel mod.");
1064 	ui->label[4] = robtk_lbl_new("Out Trigger");
1065 	ui->label[5] = robtk_lbl_new("Gain");
1066 	ui->label[6] = robtk_lbl_new("Trigger");
1067 	// TODO use robtk_info()
1068 #ifdef MIXTRILV2
1069 	ui->label[7] = robtk_lbl_new("x42 MixTri LV2 " VERSION);
1070 #else
1071 	ui->label[7] = robtk_lbl_new("");
1072 #endif
1073 	ui->label[8] = robtk_lbl_new("Trig. Settings");
1074 
1075 	robtk_lbl_set_alignment(ui->label[0], 0.5, 0.5);
1076 	robtk_lbl_set_alignment(ui->label[1], 1.0, 0.25);
1077 	robtk_lbl_set_alignment(ui->label[2], 0.5, 0.5);
1078 	robtk_lbl_set_alignment(ui->label[3], 0.5, 0.5);
1079 	robtk_lbl_set_alignment(ui->label[4], 0.5, 0.5);
1080 	robtk_lbl_set_alignment(ui->label[5], 0.5, 0.5);
1081 	robtk_lbl_set_alignment(ui->label[6], 0.5, 0.5);
1082 	robtk_lbl_set_alignment(ui->label[7], 0.0, 0.5);
1083 	robtk_lbl_set_alignment(ui->label[8], 0.5, 0.5);
1084 
1085 	robtk_lbl_set_color(ui->label[7], .6, .6, .6, 1.0);
1086 
1087 	ui->ctable = rob_table_new(/*rows*/7, /*cols*/ 9, FALSE);
1088 	ui->ctable->expose_event = box_expose_event;
1089 
1090 	rob_table_attach(ui->ctable, robtk_lbl_widget(ui->label[0]),
1091 			1, 2, 0, 1, 0, 0, RTK_EXANDF, RTK_SHRINK);
1092 	rob_table_attach(ui->ctable, robtk_lbl_widget(ui->label[1]),
1093 			2, 5, 5, 6, 0, 0, RTK_EXANDF, RTK_SHRINK);
1094 	rob_table_attach(ui->ctable, robtk_lbl_widget(ui->label[2]),
1095 			5, 8, 0, 1, 0, 0, RTK_EXANDF, RTK_SHRINK);
1096 	rob_table_attach(ui->ctable, robtk_lbl_widget(ui->label[3]),
1097 			2, 4, 0, 1, 0, 0, RTK_EXANDF, RTK_SHRINK);
1098 	rob_table_attach(ui->ctable, robtk_lbl_widget(ui->label[4]),
1099 			8, 9, 6, 7, 0, 0, RTK_EXANDF, RTK_SHRINK);
1100 	rob_table_attach(ui->ctable, robtk_lbl_widget(ui->label[5]),
1101 			4, 5, 0, 1, 0, 0, RTK_EXANDF, RTK_SHRINK);
1102 	rob_table_attach(ui->ctable, robtk_lbl_widget(ui->label[6]),
1103 			8, 9, 0, 1, 0, 0, RTK_EXANDF, RTK_SHRINK);
1104 	rob_table_attach(ui->ctable, robtk_lbl_widget(ui->label[7]),
1105 			0, 3, 6, 7, 0, 0, RTK_EXANDF, RTK_SHRINK);
1106 	rob_table_attach(ui->ctable, robtk_lbl_widget(ui->label[8]),
1107 			10, 12, 0, 1, 0, 0, RTK_EXANDF, RTK_SHRINK);
1108 
1109 
1110 	for (uint32_t i = 0; i < 4; ++i) {
1111 		ui->dial_in[i] = robtk_dial_new_with_size(-60.0, 20.0, .01,
1112 				60, MIX_HEIGHT, 30.5, MIX_CY, MIX_RADIUS);
1113 		robtk_dial_set_value(ui->dial_in[i], 0);
1114 		robtk_dial_set_surface(ui->dial_in[i],ui->routeI);
1115 		robtk_dial_set_default(ui->dial_in[i], 0);
1116 		robtk_dial_set_callback(ui->dial_in[i], cb_set_in, ui);
1117 		robtk_dial_annotation_callback(ui->dial_in[i], dial_annotation_db, ui);
1118 		rob_table_attach(ui->ctable, robtk_dial_widget(ui->dial_in[i]),
1119 				4, 5, i+1, i+2, 0, 0, RTK_EXANDF, RTK_SHRINK);
1120 	}
1121 
1122 	for (uint32_t i = 0; i < 12; ++i) {
1123 		ui->dial_mix[i] = robtk_dial_new_with_size(-20.0, 20.0, .01,
1124 				MIX_WIDTH, MIX_HEIGHT, MIX_CX, MIX_CY, MIX_RADIUS);
1125 		const int g = ((i%3) == (i/3)) ? 0 : 1;
1126 		robtk_dial_enable_states(ui->dial_mix[i], 2);
1127 		robtk_dial_set_state_color(ui->dial_mix[i], 1, .15, .15, .15, 1.0);
1128 		robtk_dial_set_state_color(ui->dial_mix[i], 2, 1.0, .0, .0, .3);
1129 		robtk_dial_set_default(ui->dial_mix[i], 0);
1130 		robtk_dial_set_default_state(ui->dial_mix[i], g);
1131 		robtk_dial_set_state(ui->dial_mix[i], g);
1132 		robtk_dial_set_value(ui->dial_mix[i], 0);
1133 		robtk_dial_set_callback(ui->dial_mix[i], cb_set_mix, ui);
1134 		robtk_dial_annotation_callback(ui->dial_mix[i], dial_annotation_val, ui);
1135 		rob_table_attach(ui->ctable, robtk_dial_widget(ui->dial_mix[i]),
1136 				(i%3)+5, (i%3)+6, (i/3)+1, (i/3)+2,
1137 				0, 0, RTK_EXANDF, RTK_SHRINK);
1138 	}
1139 
1140 	for (uint32_t i = 0; i < 3; ++i) {
1141 		robtk_dial_set_surface(ui->dial_mix[i], (i%3)==2 ? ui->routeC : ui->routeT);
1142 	}
1143 	for (uint32_t i = 3; i < 12; ++i) {
1144 		robtk_dial_set_surface(ui->dial_mix[i], (i%3)==2 ? ui->routeE : ui->routeM);
1145 	}
1146 
1147 	for (uint32_t i = 0; i < 4; ++i) {
1148 		char tmp[16];
1149 		snprintf(tmp, 16, "In %d ", i+1);
1150 		ui->lbl_in[i] = robtk_lbl_new(tmp);
1151 		robtk_lbl_set_alignment(ui->lbl_in[i], 0.0, 0.3);
1152 		robtk_lbl_set_min_geometry(ui->lbl_in[i], 32, 0);
1153 
1154 		ui->btn_mute_in[i]  = robtk_cbtn_new("Mute", GBT_LED_LEFT, false);
1155 		ui->btn_hpfilt_in[i]  = robtk_cbtn_new("HPF", GBT_LED_LEFT, false);
1156 		robtk_cbtn_set_alignment(ui->btn_mute_in[i], .5, 0.25);
1157 		robtk_cbtn_set_alignment(ui->btn_hpfilt_in[i], .5, 0.25);
1158 		robtk_cbtn_set_color_on(ui->btn_hpfilt_in[i],  .1, .2, .9);
1159 		robtk_cbtn_set_color_off(ui->btn_hpfilt_in[i], .1, .1, .3);
1160 		robtk_cbtn_set_callback(ui->btn_mute_in[i], cb_set_fm, ui);
1161 		robtk_cbtn_set_callback(ui->btn_hpfilt_in[i], cb_set_fm, ui);
1162 		rob_table_attach(ui->ctable, robtk_lbl_widget(ui->lbl_in[i]),
1163 				0, 1, i+1, i+2, 0, 0, RTK_EXANDF, RTK_EXANDF);
1164 		rob_table_attach(ui->ctable, robtk_cbtn_widget(ui->btn_mute_in[i]),
1165 				2, 3, i+1, i+2, 0, 0, RTK_SHRINK, RTK_SHRINK);
1166 		rob_table_attach(ui->ctable, robtk_cbtn_widget(ui->btn_hpfilt_in[i]),
1167 				3, 4, i+1, i+2, 0, 0, RTK_SHRINK, RTK_SHRINK);
1168 
1169 		ui->spb_delay_in[i]  = robtk_spin_new(0, MAXDELAY-1, 1);
1170 		ui->spb_delay_in[i]->dial->displaymode = 3;
1171 		robtk_lbl_set_text(ui->spb_delay_in[i]->lbl_r, "000000");
1172 		robtk_spin_set_default(ui->spb_delay_in[i], 0);
1173 		robtk_spin_set_value(ui->spb_delay_in[i], 0);
1174 		robtk_spin_set_callback(ui->spb_delay_in[i], cb_set_delay, ui);
1175 		robtk_spin_label_width(ui->spb_delay_in[i], -1, MIX_WIDTH - GSP_WIDTH - 8);
1176 
1177 		robtk_dial_set_surface(ui->spb_delay_in[i]->dial,ui->delayI);
1178 		robtk_lbl_set_alignment(ui->spb_delay_in[i]->lbl_r, 0, 0.3);
1179 		robtk_dial_set_alignment(ui->spb_delay_in[i]->dial, .5, 1.0);
1180 		ui->spb_delay_in[i]->rw->yalign = .45;
1181 
1182 		rob_table_attach(ui->ctable, robtk_spin_widget(ui->spb_delay_in[i]),
1183 				1, 2, i+1, i+2, 0, 0, RTK_EXANDF, RTK_SHRINK);
1184 	}
1185 
1186 	for (uint32_t i = 0; i < 3; ++i) {
1187 		char tmp[16];
1188 		snprintf(tmp, 16, "Out %d", i+1);
1189 		ui->lbl_out[i] = robtk_lbl_new(tmp);
1190 		robtk_lbl_set_alignment(ui->lbl_out[i], 0.5, 0.5);
1191 		rob_table_attach(ui->ctable, robtk_lbl_widget(ui->lbl_out[i]),
1192 				5+i, 6+i, 6, 7, 0, 0, RTK_EXANDF, RTK_SHRINK);
1193 
1194 		ui->spb_delay_out[i] = robtk_spin_new(0, MAXDELAY-1, 1);
1195 		ui->spb_delay_out[i]->dial->displaymode = 3;
1196 		robtk_spin_set_callback(ui->spb_delay_out[i], cb_set_delay, ui);
1197 		robtk_spin_set_default(ui->spb_delay_out[i], 0);
1198 		robtk_spin_set_value(ui->spb_delay_out[i], 0);
1199 		robtk_dial_set_surface(ui->spb_delay_out[i]->dial,ui->delayO);
1200 		robtk_lbl_set_alignment(ui->spb_delay_out[i]->lbl_r, 0, 0.3);
1201 		robtk_spin_label_width(ui->spb_delay_out[i], -1, MIX_WIDTH - GSP_WIDTH - 8);
1202 		rob_table_attach(ui->ctable, robtk_spin_widget(ui->spb_delay_out[i]),
1203 				i+5, i+6, 5, 6, 0, 0, RTK_EXANDF, RTK_SHRINK);
1204 	}
1205 
1206 	ui->sel_trig_mode = robtk_select_new();
1207 	robtk_select_set_alignment(ui->sel_trig_mode, .5, 0);
1208 	ui->sel_trig_mode->t_width = MIX_WIDTH - 36;
1209 	robtk_select_add_item(ui->sel_trig_mode, TRG_PASSTRHU, "-");
1210 	robtk_select_add_item(ui->sel_trig_mode, TRG_EDGE, "Edge");
1211 	robtk_select_add_item(ui->sel_trig_mode, TRG_WINDOW_ENTER, "Enter Window");
1212 	robtk_select_add_item(ui->sel_trig_mode, TRG_WINDOW_LEAVE, "Leave Window");
1213 	robtk_select_add_item(ui->sel_trig_mode, TRG_HYSTERESIS, "Hysteresis");
1214 	robtk_select_add_item(ui->sel_trig_mode, TRG_DROPIN, "Constrained");
1215 	robtk_select_add_item(ui->sel_trig_mode, TRG_DROPOUT, "Dropout");
1216 	robtk_select_add_item(ui->sel_trig_mode, TRG_PULSEWIDTH, "Pulse Width");
1217 	robtk_select_add_item(ui->sel_trig_mode, TRG_PULSETRAIN, "Pulse Train");
1218 	robtk_select_add_item(ui->sel_trig_mode, TRG_RUNT, "Runt");
1219 	robtk_select_add_item(ui->sel_trig_mode, TRG_LTC, "LTC");
1220 	robtk_select_add_item(ui->sel_trig_mode, TRG_RMS, "RMS");
1221 	robtk_select_add_item(ui->sel_trig_mode, TRG_LPF, "LPF");
1222 	robtk_select_set_callback(ui->sel_trig_mode, cb_set_trig_mode, ui);
1223 
1224 	rob_table_attach(ui->ctable, robtk_select_widget(ui->sel_trig_mode),
1225 			8, 9, 5, 6, 0, 0, RTK_SHRINK, RTK_SHRINK);
1226 
1227 	ui->btn_trig_src[0] = robtk_rbtn_new("", NULL);
1228 	ui->btn_trig_src[1] = robtk_rbtn_new("", robtk_rbtn_group(ui->btn_trig_src[0]));
1229 	ui->btn_trig_src[2] = robtk_rbtn_new("", robtk_rbtn_group(ui->btn_trig_src[0]));
1230 	ui->btn_trig_src[3] = robtk_rbtn_new("", robtk_rbtn_group(ui->btn_trig_src[0]));
1231 
1232 	robtk_rbtn_set_active(ui->btn_trig_src[3], true);
1233 
1234 	for (uint32_t i = 0; i < 4; ++i) {
1235 		ui->btn_trig_src[i]->cbtn->flat_button = FALSE;
1236 		robtk_rbtn_set_callback(ui->btn_trig_src[i], cb_set_trig_chn, ui);
1237 		robtk_rbtn_set_alignment(ui->btn_trig_src[i], .5, 0.25);
1238 		rob_table_attach(ui->ctable, robtk_rbtn_widget(ui->btn_trig_src[i]),
1239 				8, 9, i+1, i+2, 0, 0, RTK_EXPAND, RTK_SHRINK);
1240 	}
1241 
1242 #define TBLADD(WIDGET, C0, C1, R) \
1243 	rob_table_attach(ui->ctable, WIDGET, \
1244 			(C0), (C1), (R), (R)+1, 0, 0, RTK_EXANDF, RTK_SHRINK)
1245 
1246 	/* trigger settings */
1247 	ui->lbl_trig[0] = robtk_lbl_new("Level 1: ");
1248 	ui->lbl_trig[1] = robtk_lbl_new("Level 2: ");
1249 	ui->lbl_trig[2] = robtk_lbl_new("Time 1: ");
1250 	ui->lbl_trig[3] = robtk_lbl_new("Time 2: ");
1251 
1252 	for (uint32_t i = 0; i < 4; ++i) {
1253 		robtk_lbl_set_alignment(ui->lbl_trig[i], 1.0, 0.5);
1254 		TBLADD(robtk_lbl_widget(ui->lbl_trig[i]), 10, 11, 2+i);
1255 	}
1256 
1257 	ui->sel_trig_edge = robtk_select_new();
1258 	robtk_select_add_item(ui->sel_trig_edge, 1, "rising edge");
1259 	robtk_select_add_item(ui->sel_trig_edge, 2, "falling edge");
1260 	robtk_select_add_item(ui->sel_trig_edge, 3, "any edge");
1261 	robtk_select_set_callback(ui->sel_trig_edge, cb_set_trig_edge, ui);
1262 	TBLADD(robtk_select_widget(ui->sel_trig_edge), 10, 12, 1);
1263 
1264 	for (uint32_t i = 0; i < 2; ++i) {
1265 		ui->spb_trigger_lvl[i] = robtk_spin_new(-1.f, 1.f, .01);
1266 		robtk_spin_set_default(ui->spb_trigger_lvl[i], 0);
1267 		robtk_spin_set_value(ui->spb_trigger_lvl[i], 0);
1268 		robtk_spin_label_width(ui->spb_trigger_lvl[i], -1, 40); // XXX
1269 		robtk_spin_set_alignment(ui->spb_trigger_lvl[i], 0, .5);
1270 		robtk_spin_set_callback(ui->spb_trigger_lvl[i], cb_set_trig_values, ui);
1271 		TBLADD(robtk_spin_widget(ui->spb_trigger_lvl[i]), 11, 12, 2+i);
1272 
1273 		ui->spb_trigger_tme[i] = robtk_spin_new(0, MAXDELAY-1, 1);
1274 		ui->spb_trigger_tme[i]->dial->displaymode = 3;
1275 		robtk_spin_set_default(ui->spb_trigger_tme[i], 0);
1276 		robtk_spin_set_value(ui->spb_trigger_tme[i], 0);
1277 		robtk_spin_label_width(ui->spb_trigger_tme[i], -1, 50); // XXX
1278 		robtk_spin_set_alignment(ui->spb_trigger_tme[i], 0, .5);
1279 		robtk_spin_set_callback(ui->spb_trigger_tme[i], cb_set_trig_values, ui);
1280 		TBLADD(robtk_spin_widget(ui->spb_trigger_tme[i]), 11, 12, 4+i);
1281 	}
1282 
1283 	ui->btn_show_doc = robtk_cbtn_new("Show Doc", GBT_LED_LEFT, true);
1284 	robtk_cbtn_set_alignment(ui->btn_show_doc, 0, 0.5);
1285 	robtk_cbtn_set_color_on(ui->btn_show_doc,  .8, .8, .9);
1286 	robtk_cbtn_set_color_off(ui->btn_show_doc, .1, .1, .3);
1287 	robtk_cbtn_set_alignment(ui->btn_show_doc, .5, .5);
1288 	robtk_cbtn_set_active(ui->btn_show_doc, false);
1289 	robtk_cbtn_set_callback(ui->btn_show_doc, cb_show_doc, ui);
1290 	rob_table_attach(ui->ctable, robtk_cbtn_widget(ui->btn_show_doc),
1291 			10, 12, 6, 7, 0, 0, RTK_EXANDF, RTK_SHRINK);
1292 
1293 	ui->drawing_area = robtk_darea_new(150, 180, draw_trigger_doc, ui);
1294 	robwidget_hide(ui->drawing_area->rw, false);
1295 	rob_table_attach(ui->ctable, robtk_darea_widget(ui->drawing_area),
1296 			12, 13, 1, 6, 0, 0, RTK_EXANDF, RTK_SHRINK);
1297 
1298 	robtk_select_set_active_item(ui->sel_trig_edge, 0);
1299 	cb_set_trig_mode(NULL, ui);
1300 
1301 	rob_hbox_child_pack(ui->hbox, ui->ctable, FALSE, FALSE);
1302 	return ui->hbox;
1303 #undef TBLADD
1304 }
1305 
1306 /******************************************************************************
1307  * LV2
1308  */
1309 
1310 static void
cleanup_mixtri(LV2UI_Handle handle)1311 cleanup_mixtri(LV2UI_Handle handle)
1312 {
1313 	MixTriUI* ui = (MixTriUI*)handle;
1314 
1315 	for (uint32_t i = 0; i < 12; ++i) {
1316 		robtk_dial_destroy(ui->dial_mix[i]);
1317 	}
1318 	for (uint32_t i = 0; i < 4; ++i) {
1319 		robtk_dial_destroy(ui->dial_in[i]);
1320 		robtk_spin_destroy(ui->spb_delay_in[i]);
1321 		robtk_cbtn_destroy(ui->btn_hpfilt_in[i]);
1322 		robtk_cbtn_destroy(ui->btn_mute_in[i]);
1323 		robtk_lbl_destroy(ui->lbl_in[i]);
1324 		robtk_rbtn_destroy(ui->btn_trig_src[i]);
1325 		robtk_lbl_destroy(ui->lbl_trig[i]);
1326 	}
1327 	for (uint32_t i = 0; i < 3; ++i) {
1328 		robtk_spin_destroy(ui->spb_delay_out[i]);
1329 		robtk_lbl_destroy(ui->lbl_out[i]);
1330 	}
1331 	for (uint32_t i = 0; i < 9; ++i) {
1332 		robtk_lbl_destroy(ui->label[i]);
1333 	}
1334 	for (uint32_t i = 0; i < 2; ++i) {
1335 		robtk_spin_destroy(ui->spb_trigger_lvl[i]);
1336 		robtk_spin_destroy(ui->spb_trigger_tme[i]);
1337 	}
1338 	robtk_select_destroy(ui->sel_trig_mode);
1339 	robtk_select_destroy(ui->sel_trig_edge);
1340 	cairo_surface_destroy(ui->routeT);
1341 	cairo_surface_destroy(ui->routeC);
1342 	cairo_surface_destroy(ui->routeE);
1343 	cairo_surface_destroy(ui->routeM);
1344 	cairo_surface_destroy(ui->routeI);
1345 	cairo_surface_destroy(ui->delayI);
1346 	cairo_surface_destroy(ui->delayO);
1347 	pango_font_description_free(ui->font[0]);
1348 	pango_font_description_free(ui->font[1]);
1349 
1350 	robtk_cbtn_destroy(ui->btn_show_doc);
1351 	robtk_darea_destroy(ui->drawing_area);
1352 
1353 	rob_box_destroy(ui->ctable);
1354 	rob_box_destroy(ui->hbox);
1355 
1356 	free(ui);
1357 }
1358 
1359 static void
port_event_mixtri(LV2UI_Handle handle,uint32_t port,uint32_t buffer_size,uint32_t format,const void * buffer)1360 port_event_mixtri(
1361 		LV2UI_Handle handle,
1362 		uint32_t     port,
1363 		uint32_t     buffer_size,
1364 		uint32_t     format,
1365 		const void*  buffer)
1366 {
1367 	MixTriUI* ui = (MixTriUI*)handle;
1368 
1369 	if (format != 0) return;
1370 	const float v = *(float *)buffer;
1371 	const int vi = *(float *)buffer;
1372 	if (port >= MIXTRI_MIX_0_0 && port <= MIXTRI_MIX_3_2) {
1373 		const int d = port - MIXTRI_MIX_0_0;
1374 		ui->disable_signals = true;
1375 		if (v == 0) {
1376 			robtk_dial_set_state(ui->dial_mix[d], 1);
1377 		} else if (v < 0) {
1378 			robtk_dial_set_state(ui->dial_mix[d], 2);
1379 			robtk_dial_set_value(ui->dial_mix[d], 20 * log10f(-v));
1380 		} else {
1381 			robtk_dial_set_state(ui->dial_mix[d], 0);
1382 			robtk_dial_set_value(ui->dial_mix[d], 20 * log10f(v));
1383 		}
1384 		ui->disable_signals = false;
1385 	}
1386 	else if (port >= MIXTRI_DLY_I_0 && port <= MIXTRI_DLY_I_3) {
1387 		const int d = port - MIXTRI_DLY_I_0;
1388 		ui->disable_signals = true;
1389 		robtk_spin_set_value(ui->spb_delay_in[d], v);
1390 		ui->disable_signals = false;
1391 	}
1392 	else if (port >= MIXTRI_DLY_O_0 && port <= MIXTRI_DLY_O_2) {
1393 		const int d = port - MIXTRI_DLY_O_0;
1394 		ui->disable_signals = true;
1395 		robtk_spin_set_value(ui->spb_delay_out[d], v);
1396 		ui->disable_signals = false;
1397 	}
1398 	else if (port >= MIXTRI_TRIG_CHN && port <= MIXTRI_TRIG_TME1) {
1399 		ui->disable_signals = true;
1400 		switch(port) {
1401 			case MIXTRI_TRIG_CHN:
1402 				if (vi >=0 && vi < 4) robtk_rbtn_set_active(ui->btn_trig_src[vi], true);
1403 				break;
1404 			case MIXTRI_TRIG_MODE:
1405 				robtk_select_set_value(ui->sel_trig_mode, vi);
1406 				break;
1407 			case MIXTRI_TRIG_EDGE:
1408 				robtk_select_set_value(ui->sel_trig_edge, vi);
1409 				break;
1410 			case MIXTRI_TRIG_LVL0:
1411 				robtk_spin_set_value(ui->spb_trigger_lvl[0], v);
1412 				break;
1413 			case MIXTRI_TRIG_LVL1:
1414 				robtk_spin_set_value(ui->spb_trigger_lvl[1], v);
1415 				break;
1416 			case MIXTRI_TRIG_TME0:
1417 				robtk_spin_set_value(ui->spb_trigger_tme[0], v);
1418 				break;
1419 			case MIXTRI_TRIG_TME1:
1420 				robtk_spin_set_value(ui->spb_trigger_tme[1], v);
1421 				break;
1422 		}
1423 		ui->disable_signals = false;
1424 	}
1425 }
1426 
1427 #ifdef MIXTRILV2
1428 
1429 #define RTK_URI MIXTRI_URI
1430 #define RTK_GUI "ui"
1431 
1432 /**
1433  * standalone robtk GUI
1434  */
ui_disable(LV2UI_Handle handle)1435 static void ui_disable(LV2UI_Handle handle) { }
ui_enable(LV2UI_Handle handle)1436 static void ui_enable(LV2UI_Handle handle) { }
1437 
1438 static LV2UI_Handle
instantiate(void * const ui_toplevel,const LV2UI_Descriptor * descriptor,const char * plugin_uri,const char * bundle_path,LV2UI_Write_Function write_function,LV2UI_Controller controller,RobWidget ** widget,const LV2_Feature * const * features)1439 instantiate(
1440 		void* const               ui_toplevel,
1441 		const LV2UI_Descriptor*   descriptor,
1442 		const char*               plugin_uri,
1443 		const char*               bundle_path,
1444 		LV2UI_Write_Function      write_function,
1445 		LV2UI_Controller          controller,
1446 		RobWidget**               widget,
1447 		const LV2_Feature* const* features)
1448 {
1449 	MixTriUI* ui = (MixTriUI*)calloc(1, sizeof(MixTriUI));
1450 
1451 	if (!ui) {
1452 		fprintf(stderr, "MixTri.lv2 UI: out of memory\n");
1453 		return NULL;
1454 	}
1455 
1456 	*widget = NULL;
1457 
1458 	/* initialize private data structure */
1459 	ui->write      = write_function;
1460 	ui->controller = controller;
1461 
1462 	*widget = toplevel_mixtri(ui);
1463 	robwidget_make_toplevel(ui->hbox, ui_toplevel);
1464 	ROBWIDGET_SETNAME(ui->hbox, "mixtri");
1465 
1466 	return ui;
1467 }
1468 
1469 static enum LVGLResize
plugin_scale_mode(LV2UI_Handle handle)1470 plugin_scale_mode(LV2UI_Handle handle)
1471 {
1472 	return LVGL_LAYOUT_TO_FIT;
1473 }
1474 
1475 static void
cleanup(LV2UI_Handle handle)1476 cleanup(LV2UI_Handle handle)
1477 {
1478 	cleanup_mixtri(handle);
1479 }
1480 
1481 static void
port_event(LV2UI_Handle handle,uint32_t port,uint32_t buffer_size,uint32_t format,const void * buffer)1482 port_event(
1483 		LV2UI_Handle handle,
1484 		uint32_t     port,
1485 		uint32_t     buffer_size,
1486 		uint32_t     format,
1487 		const void*  buffer)
1488 {
1489 	port_event_mixtri(handle, port, buffer_size, format, buffer);
1490 }
1491 
1492 static const void*
extension_data(const char * uri)1493 extension_data(const char* uri)
1494 {
1495 	return NULL;
1496 }
1497 #endif
1498 /* vi:set ts=2 sts=2 sw=2: */
1499