1 /* meters.lv2
2 *
3 * Copyright (C) 2013 Robin Gareus <robin@gareus.org>
4 * Copyright (C) 2008-2012 Fons Adriaensen <fons@linuxaudio.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <math.h>
25
26 #include "img/screw.c"
27
28 #define RTK_URI "http://gareus.org/oss/lv2/meters#"
29 #define RTK_GUI "needle"
30 #define MTR_URI RTK_URI
31 #define LVGL_RESIZEABLE
32
33 /* meter types */
34 enum MtrType {
35 MT_BBC = 1,
36 MT_BM6,
37 MT_EBU,
38 MT_DIN,
39 MT_NOR,
40 MT_VU,
41 MT_COR
42 };
43
44 typedef struct {
45 RobWidget *rw;
46
47 LV2UI_Write_Function write;
48 LV2UI_Controller controller;
49
50 cairo_surface_t * bg;
51 cairo_surface_t * adj;
52 cairo_surface_t * sf_nfo;
53 unsigned char * img0;
54 unsigned char * img1;
55 float col[3];
56
57 bool naned[2];
58 float lvl[2];
59 float cal;
60 float cal_rad;
61 bool bbc_s20;
62 int chn;
63 enum MtrType type;
64
65 float drag_x, drag_y, drag_cal;
66 int width, height;
67 int x0, y0;
68 int x1, y1;
69
70 PangoFontDescription* font[2];
71
72 /*** pixel design ***/
73 float scale;
74 float s_scale;
75 /* screw area design */
76 float s_xc;
77 float s_yc;
78 float s_w2;
79 float s_h2;
80 cairo_rectangle_t screwrect;
81 cairo_rectangle_t textrect;
82
83 /* bbc +20dB */
84 float bbc_xc;
85 float bbc_yc;
86 float bbc_w2;
87 float bbc_h2;
88 cairo_rectangle_t bbc_rect;
89
90 /* meter size */
91 float m_width;
92 float m_height;
93 float n_height;
94
95 /* needle */
96 float n_xc;
97 float n_yc;
98 float m_r0;
99 float m_r1;
100
101 const char *nfo;
102
103 } MetersLV2UI;
104
105 #include "gui/meterimage.c"
106
setup_images(MetersLV2UI * ui)107 static void setup_images (MetersLV2UI* ui) {
108 ui->bg = render_front_face(ui->type, ui->m_width, ui->m_height);
109 img2surf((struct MyGimpImage const *)&img_screw, &ui->adj, &ui->img1);
110 }
111
width_scale(MetersLV2UI * ui)112 static int width_scale(MetersLV2UI* ui) {
113 switch (ui->type) {
114 case MT_BBC:
115 case MT_BM6:
116 return 1;
117 break;
118 default:
119 return ui->chn;
120 }
121 }
122
set_needle_sizes(MetersLV2UI * ui)123 static void set_needle_sizes(MetersLV2UI* ui) {
124 const float scale = ui->scale;
125 ui->s_scale = scale;
126 if (ui->s_scale > 2.0) ui->s_scale = 2.0;
127
128 /* screw area design */
129 ui->s_xc = 150.0 * scale; // was (300.0 * ui->chn)/2.0;
130 ui->s_yc = 153.0 * scale;
131 ui->s_w2 = ui->s_h2 = 12.5 * ui->s_scale;
132 ui->screwrect.x = (ui->s_xc - ui->s_w2) - 2;
133 ui->screwrect.y = (ui->s_yc - ui->s_w2) - 2;
134 ui->screwrect.width = ui->screwrect.height = 4 + 2 * ui->s_w2;
135
136 ui->textrect.x = (150 + ui->s_w2) * scale;
137 ui->textrect.y = (153 - 15 ) * scale;
138 ui->textrect.width = 150;
139 ui->textrect.height = 30;
140
141 /* BBC switch */
142 ui->bbc_xc = .5 + floor (72.0 * scale);
143 ui->bbc_yc = .5 + floor (153.0 * scale);
144 ui->bbc_w2 = floor (20 * ui->s_scale);
145 ui->bbc_h2 = floor (10 * ui->s_scale);
146 ui->bbc_rect.x = (ui->bbc_xc - ui->bbc_w2) - 2;
147 ui->bbc_rect.y = (ui->bbc_yc - ui->bbc_h2) - 2;
148 ui->bbc_rect.width = 4 + 2 * ui->bbc_w2;
149 ui->bbc_rect.height = 4 + 2 * ui->bbc_h2;
150
151 /* meter size */
152 ui->m_width = rint(300.0 * scale);
153 ui->m_height = rint(170.0 * scale);
154 ui->n_height = rint(135.0 * scale); // bottom separator
155
156 /* needle */
157 ui->n_xc = 149.5 * scale;
158 ui->n_yc = 209.5 * scale;
159 ui->m_r0 = 180.0 * scale;
160 ui->m_r1 = 72.0 * scale;
161
162 ui->width = ui->m_width * width_scale(ui);
163 ui->height = ui->m_height;
164
165 if (ui->bg) cairo_surface_destroy(ui->bg);
166 if (ui->font[0]) pango_font_description_free(ui->font[0]);
167 if (ui->font[1]) pango_font_description_free(ui->font[1]);
168
169 ui->bg = render_front_face(ui->type, ui->m_width, ui->m_height);
170
171 char fontname[32];
172 sprintf(fontname, "Sans %dpx", (int)rint(10.0 * ui->scale));
173 ui->font[0] = pango_font_description_from_string(fontname);
174 sprintf(fontname, "Sans %dpx", (int)rint(8.0 * ui->scale));
175 ui->font[1] = pango_font_description_from_string(fontname);
176
177 if (ui->sf_nfo) {
178 cairo_surface_destroy(ui->sf_nfo);
179 ui->sf_nfo = NULL;
180 }
181 if (ui->nfo) {
182 PangoFontDescription *fd = pango_font_description_from_string("Sans 10px");
183 create_text_surface2(&ui->sf_nfo,
184 ui->width, 12,
185 ui->width - 2, 0,
186 ui->nfo, fd, 0, 7, c_g30);
187 pango_font_description_free(fd);
188 }
189 }
190
draw_background(MetersLV2UI * ui,cairo_t * cr,float xoff,float yoff)191 static void draw_background (MetersLV2UI* ui, cairo_t* cr, float xoff, float yoff) {
192 float w = cairo_image_surface_get_width (ui->bg);
193 float h = cairo_image_surface_get_height (ui->bg);
194
195 cairo_save(cr);
196 cairo_scale(cr, ui->m_width / w, ui->m_height / h);
197 cairo_set_source_surface(cr, ui->bg, xoff * w / ui->m_width, yoff);
198 cairo_rectangle (cr, xoff * w / ui->m_width, 0, w, h);
199 cairo_fill(cr);
200 cairo_restore(cr);
201
202 if (ui->sf_nfo) {
203 cairo_set_source_surface(cr, ui->sf_nfo, 0, ui->m_height - 12);
204 cairo_paint (cr);
205 }
206 }
207
208
209 /******************************************************************************
210 * some simple maths helpers
211 */
212
cal2rad(enum MtrType t,float v)213 static float cal2rad(enum MtrType t, float v) {
214 /* rotate screw [-30..0] -> [-M_PI/4 .. M_PI/4] */
215 return .0837758 * (v + (t == MT_DIN ? 15.0 : 18.0));
216 }
217
calc_needle_pos(MetersLV2UI * ui,float val,const float xoff,float * const x,float * const y)218 static inline void calc_needle_pos(MetersLV2UI* ui, float val, const float xoff, float * const x, float * const y) {
219 const float _xc = ui->n_xc + xoff;
220
221 if (val < 0.00f) val = 0.00f;
222 if (val > 1.05f) val = 1.05f;
223 val = (val - 0.5f) * 1.5708f;
224
225 *x = _xc + sinf (val) * ui->m_r0;
226 *y = ui->n_yc - cosf (val) * ui->m_r0;
227 }
228
calc_needle_area(MetersLV2UI * ui,float val,const float xoff,cairo_rectangle_t * r)229 static inline void calc_needle_area(MetersLV2UI* ui, float val, const float xoff, cairo_rectangle_t *r) {
230 const float _xc = ui->n_xc + xoff;
231
232 if (val < 0.00f) val = 0.00f;
233 if (val > 1.05f) val = 1.05f;
234 val = (val - 0.5f) * 1.5708f;
235
236 const float _sf = sinf (val) ;
237 const float _cf = cosf (val) ;
238
239 const float _x0 = _xc + _sf * ui->m_r0;
240 const float _y0 = ui->n_yc - _cf * ui->m_r0;
241 const float _x1 = _xc + _sf * ui->m_r1;
242 const float _y1 = ui->n_yc - _cf * ui->m_r1;
243
244 r->x = MIN(_x0, _x1) - 3.0 * ui->scale;
245 r->y = MIN(_y0, _y1) - 3.0 * ui->scale;
246 r->width = MAX(_x0 - _x1, _x1 - _x0) + 6.0 * ui->scale;
247 r->height = MAX(0, (ui->n_height - r->y)) + 6.0 * ui->scale;
248 }
249
meter_deflect(int type,float v)250 static float meter_deflect(int type, float v) {
251 switch(type) {
252 case MT_VU:
253 return 5.6234149f * v;
254 case MT_BBC:
255 case MT_BM6:
256 case MT_EBU:
257 v *= 3.17f;
258 if (v < 0.1f) return v * 0.855f;
259 else return 0.3f * logf (v) + 0.77633f;
260 case MT_DIN:
261 v = sqrtf (sqrtf (2.002353f * v)) - 0.1885f;
262 return (v < 0.0f) ? 0.0f : v;
263 case MT_NOR:
264 if (v < 1e-5) return 0;
265 return .4166666f * log10(v) + 1.125f; // (20.0/48.0) *log(v) + (54/48.0) -> -54dBFS ^= 0, -12dB ^= 1.0
266 case MT_COR:
267 return 0.5f * (1.0f + v);
268 default:
269 return 0;
270 }
271 }
272
273 /******************************************************************************
274 * Drawing helpers
275 */
276
draw_needle(MetersLV2UI * ui,cairo_t * cr,float val,const float xoff,const float * const col,const float lw)277 static void draw_needle (MetersLV2UI* ui, cairo_t* cr, float val,
278 const float xoff, const float * const col, const float lw) {
279 cairo_save(cr);
280
281 /* needle area */
282 cairo_rectangle (cr, xoff, 0, ui->m_width, ui->n_height);
283 cairo_clip (cr);
284
285 /* draw needle */
286 const float _xc = ui->n_xc + xoff;
287 float px, py;
288
289 calc_needle_pos(ui, val, xoff, &px, &py);
290
291 cairo_new_path (cr);
292 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
293 cairo_move_to (cr, _xc, ui->n_yc);
294 cairo_line_to (cr, px, py);
295 CairoSetSouerceRGBA(col);
296 cairo_set_line_width (cr, lw * ui->scale);
297 cairo_stroke (cr);
298
299 cairo_restore(cr);
300 }
301
302 #define NANED(X,Y, COL) \
303 cairo_save(cr); \
304 rounded_rectangle (cr, (X) - 30 * ui->scale, (Y) - 5 * ui->scale, 60 * ui->scale, 20 * ui->scale, 4*ui->scale); \
305 CairoSetSouerceRGBA(COL); \
306 cairo_fill_preserve(cr); \
307 cairo_set_line_width (cr, .75 * ui->scale); \
308 CairoSetSouerceRGBA(c_gry); \
309 cairo_stroke(cr); \
310 write_text_full(cr, "NaN", ui->font[0], (X), (Y) + 5 * ui->scale, 0, 2, c_wht); \
311 cairo_restore(cr);
312
313
314 /******************************************************************************
315 * main drawing function
316 */
expose_event(RobWidget * handle,cairo_t * cr,cairo_rectangle_t * ev)317 static bool expose_event(RobWidget* handle, cairo_t* cr, cairo_rectangle_t* ev) {
318 MetersLV2UI* ui = (MetersLV2UI*)GET_HANDLE(handle);
319 float const * col;
320 cairo_rectangle (cr, ev->x, ev->y, ev->width, ev->height);
321 cairo_clip (cr);
322
323 cairo_rectangle (cr, 2.5, 2.5, ui->x1 - 5, ui->y1 - 5);
324 cairo_set_source_rgb (cr, .55, .55, .55);
325 cairo_set_line_width (cr, 6.0);
326 cairo_stroke(cr);
327
328 cairo_translate (cr, ui->x0, ui->y0);
329
330 switch(ui->type) {
331 case MT_VU:
332 col = c_blk;
333 break;
334 default:
335 col = c_wht;
336 break;
337 }
338
339 if (ui->type == MT_COR) {
340 draw_background (ui, cr, 0, 0);
341 draw_needle (ui, cr, ui->lvl[0], 0, col, 2.0);
342 return TRUE;
343 }
344 else if (ui->type == MT_BBC && ui->chn == 2) {
345 draw_background (ui, cr, 0, 0);
346 if (ui->naned[0]) { NANED(ui->m_width/2, ui->height*2/3 - 20 * ui->scale, c_red); }
347 if (ui->naned[1]) { NANED(ui->m_width/2, ui->height*2/3 + 2 * ui->scale, c_grn); }
348 draw_needle (ui, cr, ui->lvl[1], 0, c_grn, 2.0);
349 draw_needle (ui, cr, ui->lvl[0], 0, c_red, 2.0);
350 }
351 else if (ui->type == MT_BM6 && ui->chn == 2) {
352 draw_background (ui, cr, 0, 0);
353 if (ui->naned[0]) { NANED(ui->m_width/2, ui->height*2/3 - 20 * ui->scale, c_red); }
354 if (ui->naned[1]) { NANED(ui->m_width/2, ui->height*2/3 + 2 * ui->scale, c_grn); }
355 draw_needle (ui, cr, ui->lvl[1], 0, c_nyl, 2.0); // XXX
356 draw_needle (ui, cr, ui->lvl[0], 0, c_wht, 2.0);
357 } else {
358 int c;
359 for (c=0; c < ui->chn; ++c) {
360 draw_background (ui, cr, ui->m_width * c, 0);
361 if (ui->naned[c]) { NANED(ui->m_width * c + ui->m_width/2, ui->height*2/3, c_red); }
362 draw_needle (ui, cr, ui->lvl[c], ui->m_width * c, col, 1.4);
363 }
364 }
365
366 /* draw callibration text */
367 if (rect_intersect(ev , &ui->textrect) && (ui->drag_x >= 0 || ui->drag_y >=0)) {
368 char buf[48];
369 /* default gain -18.0dB in meters.cc, except DIN: -15dB (deflection) */
370 switch (ui->type) {
371 case MT_VU:
372 sprintf(buf, "0 VU = %.1f dBFS", -36 - ui->cal);
373 break;
374 case MT_BBC:
375 case MT_BM6:
376 sprintf(buf, " '4' = %.1f dBFS", -36 - ui->cal);
377 break;
378 case MT_DIN:
379 /* specs: -3dBu = '-9' ^= -18 dbFS - so these are eqivalent: */
380 //sprintf(buf, " '-6' = %.1f dBFS", -30 - ui->cal); // no '-6' label
381 //sprintf(buf, "'50%%' = %.1f dBFS", -30 - ui->cal); // mmh
382 //sprintf(buf, " 0dBu = %.1f dBFS", -30 - ui->cal);
383 sprintf(buf, " '-9' = %.1f dBFS", -33 - ui->cal);
384 break;
385 case MT_EBU:
386 case MT_NOR:
387 sprintf(buf, " 'TEST' = %.1f dBFS", -36 - ui->cal);
388 break;
389 default:
390 /* not reached */
391 break;
392 }
393
394 write_text_full(cr, buf, ui->font[0], ui->s_xc + ui->s_w2 + 8, ui->s_yc, 0, 3, c_wht);
395 }
396
397 /* draw callibration screw */
398 if (rect_intersect(ev , &ui->screwrect)) {
399 cairo_save(cr);
400 cairo_translate (cr, ui->s_xc, ui->s_yc);
401 cairo_rotate (cr, ui->cal_rad);
402 cairo_translate (cr, -ui->s_w2, -ui->s_h2);
403 cairo_scale(cr, ui->s_scale, ui->s_scale);
404 cairo_set_source_surface(cr, ui->adj, 0, 0);
405 cairo_rectangle (cr, 0, 0, 25.0, 25.0 /* 2.0 * ui->s_w2 / ui->scale, 2.0 * ui->s_h2 / ui->scale */);
406 cairo_fill(cr);
407 cairo_restore(cr);
408
409 cairo_save(cr);
410 cairo_translate (cr, ui->s_xc, ui->s_yc);
411 CairoSetSouerceRGBA(c_scr);
412 cairo_arc(cr, 0, 0, ui->s_w2, 0, 2 * M_PI);
413 cairo_set_line_width (cr, 1.0);
414 cairo_stroke(cr);
415 cairo_restore(cr);
416 }
417
418 /* draw +20db switch */
419 if (ui->type == MT_BM6 && rect_intersect (ev, &ui->bbc_rect)) {
420 cairo_save(cr);
421 cairo_translate (cr, ui->bbc_xc - ui->bbc_w2, ui->bbc_yc - ui->bbc_h2);
422 cairo_rectangle (cr, 0, 0, 2 * ui->bbc_w2, 2 * ui->bbc_h2);
423 cairo_clip (cr);
424
425 int sw_x1 = 2 * ui->bbc_w2 - 2;
426 int sw_y1 = 2 * ui->bbc_h2 - 3;
427 int sw_ww = floor (7 * ui->s_scale);
428
429
430 if (ui->bbc_s20) {
431 cairo_set_source_rgba (cr, .7, .1, .1, .7);
432 cairo_rectangle (cr, 0, 0, 2 * ui->bbc_w2, 2 * ui->bbc_h2);
433 } else {
434 cairo_set_source_rgba (cr, .7, .1, .1, .7);
435 cairo_rectangle (cr, 0, 0, sw_ww + 4, 2 * ui->bbc_h2);
436 cairo_fill (cr);
437 cairo_set_source_rgba (cr, .1, .1, .1, .7);
438 cairo_rectangle (cr, sw_ww + 4, 0, 2 * ui->bbc_w2 - sw_ww - 4, 2 * ui->bbc_h2);
439 }
440 cairo_fill (cr);
441
442 /* schieber */
443 cairo_save(cr);
444 if (ui->bbc_s20) {
445 cairo_translate (cr, sw_x1 - sw_ww, 1);
446 } else {
447 cairo_translate (cr, 2, 1);
448 }
449
450 cairo_rectangle (cr, 0, 0, sw_ww, sw_y1);
451 cairo_set_source_rgba (cr, 1, .0, .0, .7);
452 cairo_fill (cr);
453
454 cairo_set_line_width (cr, 1);
455 cairo_set_source_rgba (cr, 1, .2, .2, 1);
456
457 cairo_move_to (cr, 0, 0);
458 cairo_rel_line_to (cr, sw_ww, 0);
459 cairo_stroke (cr);
460 cairo_move_to (cr, sw_ww, 0);
461 cairo_rel_line_to (cr, 0, sw_y1);
462 cairo_stroke (cr);
463
464 cairo_set_source_rgba (cr, .5, .0, .0, 1);
465 cairo_move_to (cr, 0, 0);
466 cairo_rel_line_to (cr, 0, sw_y1);
467 cairo_stroke (cr);
468 cairo_move_to (cr, 0, sw_y1);
469 cairo_rel_line_to (cr, sw_ww, 0);
470 cairo_stroke (cr);
471 cairo_restore(cr);
472
473 if (ui->bbc_s20) {
474 cairo_save (cr);
475 write_text_full (cr, "S+20", ui->font[1], ui->bbc_w2 - sw_ww / 2, ui->bbc_h2, 0, 2, c_wht);
476 cairo_restore (cr);
477 }
478
479 cairo_rectangle (cr, 0, 0, 2 * ui->bbc_w2, 2 * ui->bbc_h2);
480 CairoSetSouerceRGBA (c_scr);
481 cairo_set_line_width (cr, 1.0);
482 cairo_stroke (cr);
483
484 cairo_set_line_width (cr, 1.5);
485 cairo_set_source_rgba (cr, .1, .1, .1, .5);
486 cairo_move_to (cr, 1, 0);
487 cairo_rel_line_to (cr, 0, 2 * ui->bbc_h2 - 1);
488 cairo_stroke (cr);
489 cairo_move_to (cr, 0, 2 * ui->bbc_h2 - 1);
490 cairo_rel_line_to (cr, 2 * ui->bbc_w2 - 1, 0);
491 cairo_stroke (cr);
492
493 cairo_restore(cr);
494 }
495
496 return TRUE;
497 }
498
499 /******************************************************************************
500 * UI event handling
501 */
502
503 /* calibration screw drag/drop handling */
mousedown(RobWidget * handle,RobTkBtnEvent * event)504 static RobWidget* mousedown(RobWidget* handle, RobTkBtnEvent *event) {
505 MetersLV2UI* ui = (MetersLV2UI*)GET_HANDLE(handle);
506
507 if (event->state & ROBTK_MOD_CTRL) {
508 robwidget_resize_toplevel(ui->rw, 300 * width_scale(ui), 170);
509 return NULL;
510 }
511
512 if (ui->naned[0]) { ui->naned[0] = FALSE; queue_draw(ui->rw); }
513 if (ui->naned[1]) { ui->naned[1] = FALSE; queue_draw(ui->rw); }
514 if (ui->type == MT_BM6
515 && event->x > ui->bbc_xc - ui->bbc_w2
516 && event->x < ui->bbc_xc + ui->bbc_w2
517 && event->y > ui->bbc_yc - ui->bbc_h2
518 && event->y < ui->bbc_yc + ui->bbc_h2
519 )
520 {
521 /* Toggle BBC +20dB Side switch */
522 float onoff = ui->bbc_s20 ? 0 : 1;
523 ui->write(ui->controller, 7, sizeof(float), 0, (const void*) &onoff);
524 return NULL;
525 }
526
527 if ( event->x < ui->s_xc - ui->s_w2
528 || event->x > ui->s_xc + ui->s_w2
529 || event->y < ui->s_yc - ui->s_h2
530 || event->y > ui->s_yc + ui->s_h2
531 ) {
532 /* outside of adj-screw area */
533 return NULL;
534 }
535
536 if (event->state & ROBTK_MOD_SHIFT) {
537 /* shift-click -> reset to default */
538 switch(ui->type) {
539 case MT_VU: ui->cal = -22; break;
540 case MT_DIN: ui->cal = -15; break;
541 default: ui->cal = -18; break;
542 }
543 ui->write(ui->controller, 0, sizeof(float), 0, (const void*) &ui->cal);
544 ui->cal_rad = cal2rad(ui->type, ui->cal);
545 queue_draw(ui->rw);
546 return NULL;
547 }
548
549 ui->drag_x = event->x;
550 ui->drag_y = event->y;
551 ui->drag_cal = ui->cal;
552 queue_draw(ui->rw);
553 return handle;
554 }
555
556 /* stereo-phase correlation - resize to 100% only */
mousedown_cor(RobWidget * handle,RobTkBtnEvent * event)557 static RobWidget* mousedown_cor(RobWidget* handle, RobTkBtnEvent *event) {
558 MetersLV2UI* ui = (MetersLV2UI*)GET_HANDLE(handle);
559 if (event->state & ROBTK_MOD_CTRL) {
560 robwidget_resize_toplevel(ui->rw, 300 * width_scale(ui), 170);
561 }
562 return NULL;
563 }
564
565
mouseup(RobWidget * handle,RobTkBtnEvent * event)566 static RobWidget* mouseup(RobWidget* handle, RobTkBtnEvent *event) {
567 MetersLV2UI* ui = (MetersLV2UI*)GET_HANDLE(handle);
568 ui->drag_x = ui->drag_y = -1;
569 queue_draw(ui->rw);
570 return NULL;
571 }
572
mousemove(RobWidget * handle,RobTkBtnEvent * event)573 static RobWidget* mousemove(RobWidget* handle, RobTkBtnEvent *event) {
574 MetersLV2UI* ui = (MetersLV2UI*)GET_HANDLE(handle);
575 if (ui->drag_x < 0 || ui->drag_y < 0) return NULL;
576
577 const float diff = rint(((event->x - ui->drag_x) - (event->y - ui->drag_y)) / 5.0 ) * .5;
578 float cal = ui->drag_cal + diff;
579 if (cal < -30.0) cal = -30.0;
580 if (cal > 0.0) cal = 0.0;
581
582 //printf("Mouse move.. %f %f -> %f (%f -> %f)\n", event->x, event->y, diff, ui->drag_cal, cal);
583 ui->write(ui->controller, 0, sizeof(float), 0, (const void*) &cal);
584 ui->cal = cal;
585 ui->cal_rad = cal2rad(ui->type, ui->cal);
586 queue_draw(ui->rw);
587
588 return handle;
589 }
590
591
592 /******************************************************************************
593 * widget hackery
594 */
595
596 static void
size_request(RobWidget * rw,int * w,int * h)597 size_request(RobWidget* rw, int *w, int *h) {
598 MetersLV2UI* ui = (MetersLV2UI*)GET_HANDLE(rw);
599 *w = 300 * .75 * width_scale(ui);
600 *h = 170 * .75;
601 }
602
603 static void
size_default(RobWidget * rw,int * w,int * h)604 size_default(RobWidget* rw, int *w, int *h) {
605 MetersLV2UI* ui = (MetersLV2UI*)GET_HANDLE(rw);
606 *w = 300 * width_scale(ui);
607 *h = 170;
608 }
609
610 static void
size_limit(RobWidget * rw,int * w,int * h)611 size_limit(RobWidget* rw, int *w, int *h) {
612 MetersLV2UI* ui = (MetersLV2UI*)GET_HANDLE(rw);
613 int dflw, dflh;
614 size_default(rw, &dflw, &dflh);
615 float scale = MIN(*w/(float)dflw, *h/(float)dflh);
616 if (scale < .5 ) scale = .5;
617 if (scale > 3.5 ) scale = 3.5;
618 ui->scale = scale;
619 set_needle_sizes(ui); // sets ui->width, ui->height
620 ui->x0 = (*w - ui->width) / 2;
621 ui->y0 = (*h - ui->height) / 2;
622 ui->x1 = *w;
623 ui->y1 = *h;
624 robwidget_set_size(rw, *w, *h);
625 queue_draw(rw);
626 }
627
628 /******************************************************************************
629 * LV2 callbacks
630 */
631
ui_enable(LV2UI_Handle handle)632 static void ui_enable(LV2UI_Handle handle) { }
ui_disable(LV2UI_Handle handle)633 static void ui_disable(LV2UI_Handle handle) { }
634
635 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)636 instantiate(
637 void* const ui_toplevel,
638 const LV2UI_Descriptor* descriptor,
639 const char* plugin_uri,
640 const char* bundle_path,
641 LV2UI_Write_Function write_function,
642 LV2UI_Controller controller,
643 RobWidget** widget,
644 const LV2_Feature* const* features)
645 {
646 MetersLV2UI* ui = (MetersLV2UI*)calloc(1, sizeof(MetersLV2UI));
647 *widget = NULL;
648
649 if (!ui) {
650 fprintf (stderr, "meters.lv2: out of memory.\n");
651 return NULL;
652 }
653
654 if (!strcmp(plugin_uri, MTR_URI "VUmono")) { ui->chn = 1; ui->type = MT_VU; }
655 else if (!strcmp(plugin_uri, MTR_URI "VUstereo")) { ui->chn = 2; ui->type = MT_VU; }
656 else if (!strcmp(plugin_uri, MTR_URI "BBCmono")) { ui->chn = 1; ui->type = MT_BBC; }
657 else if (!strcmp(plugin_uri, MTR_URI "BBCstereo")) { ui->chn = 2; ui->type = MT_BBC; }
658 else if (!strcmp(plugin_uri, MTR_URI "BBCM6")) { ui->chn = 2; ui->type = MT_BM6; }
659 else if (!strcmp(plugin_uri, MTR_URI "EBUmono")) { ui->chn = 1; ui->type = MT_EBU; }
660 else if (!strcmp(plugin_uri, MTR_URI "EBUstereo")) { ui->chn = 2; ui->type = MT_EBU; }
661 else if (!strcmp(plugin_uri, MTR_URI "DINmono")) { ui->chn = 1; ui->type = MT_DIN; }
662 else if (!strcmp(plugin_uri, MTR_URI "DINstereo")) { ui->chn = 2; ui->type = MT_DIN; }
663 else if (!strcmp(plugin_uri, MTR_URI "NORmono")) { ui->chn = 1; ui->type = MT_NOR; }
664 else if (!strcmp(plugin_uri, MTR_URI "NORstereo")) { ui->chn = 2; ui->type = MT_NOR; }
665 else if (!strcmp(plugin_uri, MTR_URI "COR")) { ui->chn = 1; ui->type = MT_COR; }
666
667 if (ui->type == 0) {
668 free(ui);
669 return NULL;
670 }
671
672 ui->write = write_function;
673 ui->controller = controller;
674 ui->lvl[0] = ui->lvl[1] = 0;
675 ui->naned[0] = ui->naned[1] = FALSE;
676 ui->cal = -18.0;
677 ui->cal_rad = cal2rad(ui->type, ui->cal);
678 ui->bbc_s20 = false;
679 ui->bg = NULL;
680 ui->adj = NULL;
681 ui->sf_nfo = NULL;
682 ui->img0 = NULL;
683 ui->drag_x = ui->drag_y = -1;
684 ui->scale = 1.0;
685 ui->font[0] = NULL;
686 ui->font[1] = NULL;
687
688 ui->nfo = robtk_info(ui_toplevel);
689 set_needle_sizes(ui);
690
691 setup_images(ui);
692
693 ui->rw = robwidget_new(ui);
694 robwidget_make_toplevel(ui->rw, ui_toplevel);
695 ROBWIDGET_SETNAME(ui->rw, "needle");
696
697 robwidget_set_expose_event(ui->rw, expose_event);
698 robwidget_set_size_request(ui->rw, size_request);
699 robwidget_set_size_limit(ui->rw, size_limit);
700 robwidget_set_size_default(ui->rw, size_default);
701
702 if (ui->type != MT_COR) {
703 robwidget_set_mousedown(ui->rw, mousedown);
704 robwidget_set_mouseup(ui->rw, mouseup);
705 robwidget_set_mousemove(ui->rw, mousemove)
706 } else {
707 robwidget_set_mousedown(ui->rw, mousedown_cor);
708 }
709
710 *widget = ui->rw;
711
712 return ui;
713 }
714
715 static enum LVGLResize
plugin_scale_mode(LV2UI_Handle handle)716 plugin_scale_mode(LV2UI_Handle handle)
717 {
718 return LVGL_LAYOUT_TO_FIT;
719 }
720
721 static void
cleanup(LV2UI_Handle handle)722 cleanup(LV2UI_Handle handle)
723 {
724 MetersLV2UI* ui = (MetersLV2UI*)handle;
725 cairo_surface_destroy(ui->sf_nfo);
726 cairo_surface_destroy(ui->bg);
727 cairo_surface_destroy(ui->adj);
728 pango_font_description_free(ui->font[0]);
729 pango_font_description_free(ui->font[1]);
730 robwidget_destroy(ui->rw);
731 free(ui->img0);
732 free(ui->img1);
733 free(ui);
734 }
735
736
737 static const void*
extension_data(const char * uri)738 extension_data(const char* uri)
739 {
740 return NULL;
741 }
742
743
744 /******************************************************************************
745 * backend communication
746 */
747
748 #define MIN2(A,B) ( (A) < (B) ? (A) : (B) )
749 #define MAX2(A,B) ( (A) > (B) ? (A) : (B) )
750 #define MIN3(A,B,C) ( (A) < (B) ? MIN2 (A,C) : MIN2 (B,C) )
751 #define MAX3(A,B,C) ( (A) > (B) ? MAX2 (A,C) : MAX2 (B,C) )
752
invalidate_area(MetersLV2UI * ui,int c,float oldval,float newval)753 static void invalidate_area(MetersLV2UI* ui, int c, float oldval, float newval) {
754 if (!ui->naned[c] && (isnan(newval) || isinf(newval))) {
755 ui->naned[c] = TRUE;
756 queue_draw(ui->rw);
757 }
758 if (oldval < 0.00f) oldval = 0.00f;
759 if (oldval > 1.05f) oldval = 1.05f;
760 if (newval < 0.00f) newval = 0.00f;
761 if (newval > 1.05f) newval = 1.05f;
762
763 if (rint(newval * 540) == rint(oldval * 540)) {
764 return;
765 }
766
767 float xoff = ui->m_width * c;
768 if (c == 1 && (ui->type == MT_BBC || ui->type == MT_BM6)) {
769 xoff = 0;
770 }
771 cairo_rectangle_t r1, r2;
772 calc_needle_area(ui, oldval, xoff, &r1);
773 calc_needle_area(ui, newval, xoff, &r2);
774 rect_combine(&r1, &r2, &r1);
775 queue_tiny_area(ui->rw, ui->x0 + r1.x, ui->y0 + r1.y, r1.width, r1.height);
776 }
777
778 static void
port_event(LV2UI_Handle handle,uint32_t port_index,uint32_t buffer_size,uint32_t format,const void * buffer)779 port_event(LV2UI_Handle handle,
780 uint32_t port_index,
781 uint32_t buffer_size,
782 uint32_t format,
783 const void* buffer)
784 {
785 MetersLV2UI* ui = (MetersLV2UI*)handle;
786
787 if ( format != 0 ) { return; }
788
789 if (port_index == 3) {
790 float nl = meter_deflect(ui->type, *(float *)buffer);
791 invalidate_area(ui, 0, ui->lvl[0], nl);
792 ui->lvl[0] = nl;
793 } else
794 if (port_index == 6) {
795 float nl = meter_deflect(ui->type, *(float *)buffer);
796 invalidate_area(ui, 1, ui->lvl[1], nl);
797 ui->lvl[1] = nl;
798 } else
799 if (port_index == 0) {
800 ui->cal = *(float *)buffer;
801 ui->cal_rad = cal2rad(ui->type, ui->cal);
802 queue_draw(ui->rw);
803 } else
804 if (port_index == 7 && ui->type == MT_BM6) {
805 ui->bbc_s20 = *(float *)buffer > 0;
806 queue_draw(ui->rw);
807 }
808 }
809