1 /* Calf DSP Library
2  * Custom controls (line graph, knob).
3  * Copyright (C) 2007-2010 Krzysztof Foltman, Torben Hohn, Markus Schmidt
4  * and others
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General
17  * Public License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA  02110-1301  USA
20  */
21 
22 #include <calf/ctl_vumeter.h>
23 
24 
25 ///////////////////////////////////////// vu meter ///////////////////////////////////////////////
26 
27 
28 static gboolean
calf_vumeter_expose(GtkWidget * widget,GdkEventExpose * event)29 calf_vumeter_expose (GtkWidget *widget, GdkEventExpose *event)
30 {
31     g_assert(CALF_IS_VUMETER(widget));
32 
33     CalfVUMeter *vu = CALF_VUMETER(widget);
34     cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(widget->window));
35 
36     float r, g, b;
37 
38     int x = widget->allocation.x;
39     int y = widget->allocation.y;
40     int width = widget->allocation.width;
41     int height = widget->allocation.height;
42     int border_x = widget->style->xthickness;
43     int border_y = widget->style->ythickness;
44     int space_x = 1; int space_y = 1; // inner border around led bar
45     int led = 2; // single LED size
46     int led_m = 1; // margin between LED
47     int led_s = led + led_m; // size of LED with margin
48     int led_x = widget->style->xthickness;
49     int led_y = widget->style->ythickness; // position of first LED
50     int led_w = width - 2 * led_x + led_m; // width of LED bar w/o text calc (additional led margin, is removed later; used for filling the led bar completely w/o margin gap)
51     int led_h = height - 2 * led_y; // height of LED bar w/o text calc
52     int text_x = 0; int text_y = 0;
53     int text_w = 0; int text_h = 0;
54 
55     // only valid if vumeter is enabled
56     cairo_text_extents_t extents;
57 
58     if(vu->vumeter_position) {
59         cairo_select_font_face(c, "cairo:sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
60         cairo_set_font_size(c, 8);
61 
62         cairo_text_extents(c, "-88.88", &extents);
63         text_w = extents.width;
64         text_h = extents.height;
65         switch(vu->vumeter_position) {
66             case 1:
67                 text_x = width / 2 - text_w / 2;
68                 text_y = border_y + led_y - extents.y_bearing;
69                 led_y += text_h + led_y;
70                 led_h -= text_h + led_y;
71                 break;
72             case 2:
73                 text_x = width - border_x - led_x - text_w;
74                 text_y = height / 2 - text_h / 2 - extents.y_bearing;
75                 led_w -= led_x + text_w;
76                 break;
77             case 3:
78                 text_x = width / 2 - text_w / 2;
79                 text_y = height - border_y - led_y - text_h - extents.y_bearing;
80                 led_h -= led_y + text_h;
81                 break;
82             case 4:
83                 text_x = border_x + led_x;
84                 text_y = height / 2 - text_h / 2 - extents.y_bearing;
85                 led_x += led_x + text_w;
86                 led_w -= led_x + text_w;
87                 break;
88         }
89     }
90 
91     led_w -= led_w % led_s + led_m; //round LED width to LED size and remove margin gap, width is filled with LED without margin gap now
92 
93     if( vu->cache_surface == NULL ) {
94         // looks like its either first call or the widget has been resized.
95         // create the cache_surface.
96         vu->cache_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height );
97         cairo_t *cache_cr = cairo_create( vu->cache_surface );
98 
99         float radius, bevel, glass;
100         get_bg_color(widget, NULL, &r, &g, &b);
101         gtk_widget_style_get(widget, "border-radius", &radius, "bevel",  &bevel, "glass", &glass, NULL);
102         create_rectangle(cache_cr, 0, 0, width, height, radius);
103         cairo_set_source_rgb(cache_cr, r, g, b);
104         cairo_fill(cache_cr);
105         draw_bevel(cache_cr, 0, 0, width, height, radius, bevel);
106 
107         // border around LED
108         cairo_rectangle(cache_cr, led_x, led_y, led_w, led_h);
109         cairo_set_source_rgb (cache_cr, 0, 0, 0);
110         cairo_fill(cache_cr);
111 
112         led_x += space_x;
113         led_y += space_y;
114         led_w -= 2 * space_x;
115         led_h -= 2 * space_y;
116 
117         // LED bases
118         cairo_set_line_width(cache_cr, 1);
119         for (int x = led_x; x + led <= led_x + led_w; x += led_s)
120         {
121             float ts = (x - led_x) * 1.0 / led_w;
122             float r = 0.f, g = 0.f, b = 0.f;
123             switch(vu->mode)
124             {
125                 case VU_STANDARD:
126                 default:
127                     if (ts < 0.75)
128                     r = ts / 0.75, g = 0.5 + ts * 0.66, b = 1 - ts / 0.75;
129                     else
130                     r = 1, g = 1 - (ts - 0.75) / 0.25, b = 0;
131                     //                if (vu->value < ts || vu->value <= 0)
132                     //                    r *= 0.5, g *= 0.5, b *= 0.5;
133                     break;
134                 case VU_STANDARD_CENTER:
135                     if (ts < 0.25)
136                         // 0.0 -> 0.25
137                         // green: 0.f -> 1.f
138                         r = 1, g = (ts) / 0.25, b = 0;
139                     else if (ts > 0.75)
140                         // 0.75 -> 1.0
141                         // green: 1.f -> 0.f
142                         r = 1, g = 1 - (ts - 0.75) / 0.25, b = 0;
143                     else if (ts > 0.5)
144                         // 0.5 -> 0.75
145                         // red: 0.f -> 1.f
146                         // green: 0.5 -> 1.f
147                         // blue: 1.f -> 0.f
148                         r = (ts - 0.5) / 0.25, g = 0.5 + (ts - 0.5) * 2.f, b = 1 - (ts - 0.5) / 0.25;
149                     else
150                         // 0.25 -> 0.5
151                         // red: 1.f -> 0.f
152                         // green: 1.f -> 0.5
153                         // blue: 0.f -> 1.f
154                         r = 1 - (ts - 0.25) / 0.25, g = 1.f - (ts * 2.f - .5f), b = (ts - 0.25) / 0.25;
155                     //                if (vu->value < ts || vu->value <= 0)
156                     //                    r *= 0.5, g *= 0.5, b *= 0.5;
157                     break;
158                 case VU_MONOCHROME_REVERSE:
159                     r = 0, g = 170.0 / 255.0, b = 1;
160                     //                if (!(vu->value < ts) || vu->value >= 1.0)
161                     //                    r *= 0.5, g *= 0.5, b *= 0.5;
162                     break;
163                 case VU_MONOCHROME:
164                     r = 0, g = 170.0 / 255.0, b = 1;
165                     //                if (vu->value < ts || vu->value <= 0)
166                     //                    r *= 0.5, g *= 0.5, b *= 0.5;
167                     break;
168                 case VU_MONOCHROME_CENTER:
169                     r = 0, g = 170.0 / 255.0, b = 1;
170                     //                if (vu->value < ts || vu->value <= 0)
171                     //                    r *= 0.5, g *= 0.5, b *= 0.5;
172                     break;
173             }
174             GdkColor sc2 = { 0, (guint16)(65535 * r + 0.2), (guint16)(65535 * g), (guint16)(65535 * b) };
175             GdkColor sc3 = { 0, (guint16)(65535 * r * 0.7), (guint16)(65535 * g * 0.7), (guint16)(65535 * b * 0.7) };
176             gdk_cairo_set_source_color(cache_cr, &sc2);
177             cairo_move_to(cache_cr, x + 0.5, led_y);
178             cairo_line_to(cache_cr, x + 0.5, led_y + led_h);
179             cairo_stroke(cache_cr);
180             gdk_cairo_set_source_color(cache_cr, &sc3);
181             cairo_move_to(cache_cr, x + 1.5, led_y + led_h);
182             cairo_line_to(cache_cr, x + 1.5, led_y);
183             cairo_stroke(cache_cr);
184         }
185         // create blinder pattern
186         cairo_pattern_t *pat = cairo_pattern_create_linear (led_x, led_y, led_x, led_y + led_h);
187         if (glass > 0.f) {
188             cairo_pattern_add_color_stop_rgba (pat, 0, 1, 1, 1, 0.25 * glass);
189             cairo_pattern_add_color_stop_rgba (pat, 0.5, 0.5, 0.5, 0.5, 0.0);
190             cairo_pattern_add_color_stop_rgba (pat, 1, 0.0, 0.0, 0.0, 0.25 * glass);
191             cairo_rectangle(cache_cr, led_x, led_y, led_w, led_h);
192             cairo_set_source(cache_cr, pat);
193             cairo_fill(cache_cr);
194         }
195 
196         // create overlay
197         vu->cache_overlay = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
198         cairo_t *over_cr = cairo_create(vu->cache_overlay);
199 
200         // copy surface to overlay
201         cairo_set_source_surface(over_cr, vu->cache_surface, 0, 0);
202         cairo_rectangle(over_cr, 0, 0, width, height);
203         cairo_fill(over_cr);
204 
205         // create blinder pattern
206         pat = cairo_pattern_create_linear (led_x, led_y, led_x, led_y + led_h);
207         float c1 = 0.05 + 0.15 * glass;
208         cairo_pattern_add_color_stop_rgba (pat, 0, c1, c1, c1, 0.7);
209         cairo_pattern_add_color_stop_rgba (pat, 0.4, 0.05, 0.05, 0.05, 0.7);
210         cairo_pattern_add_color_stop_rgba (pat, 0.401, 0.05, 0.05, 0.05, 0.7 + 0.2 * glass);
211         cairo_pattern_add_color_stop_rgba (pat, 1, 0.05, 0.05, 0.05, 0.7 + 0.05 * glass);
212 
213         // draw on top of overlay
214         cairo_set_source(over_cr, pat);
215         cairo_rectangle(over_cr, 0, 0, width, height);
216         cairo_paint(over_cr);
217 
218         // clean up
219         cairo_destroy(cache_cr);
220         cairo_destroy(over_cr);
221     } else {
222         led_x += space_x;
223         led_y += space_y;
224         led_w -= 2 * space_x;
225         led_h -= 2 * space_y;
226     }
227     led_x += x;
228     led_y += y;
229     text_x += x;
230     text_y += y;
231     // draw LED blinder
232     cairo_set_source_surface( c, vu->cache_surface, x, y );
233     cairo_paint( c );
234     cairo_set_source_surface( c, vu->cache_overlay, x, y );
235 
236     // get microseconds
237     timeval tv;
238     gettimeofday(&tv, 0);
239     long time = tv.tv_sec * 1000 * 1000 + tv.tv_usec;
240 
241     // limit to 1.f
242     float value_orig = std::max(std::min(vu->value, 1.f), 0.f);
243     float value = 0.f;
244 
245     // falloff?
246     if(vu->vumeter_falloff > 0.f and vu->mode != VU_MONOCHROME_REVERSE) {
247         // fall off a bit
248         float s = ((float)(time - vu->last_falltime) / 1000000.0);
249         float m = vu->last_falloff * s * vu->vumeter_falloff;
250         vu->last_falloff -= m;
251         // new max value?
252         if(value_orig > vu->last_falloff) {
253             vu->last_falloff = value_orig;
254         }
255         value = vu->last_falloff;
256         vu->last_falltime = time;
257         vu->falling = vu->last_falloff > 0.00000001;
258     } else {
259         // falloff disabled
260         vu->last_falloff = 0.f;
261         vu->last_falltime = 0.f;
262         value = value_orig;
263         vu->falling = false;
264     }
265 
266     float draw = 0.f;
267     float draw_last = 0.f;
268 
269     if(vu->vumeter_hold > 0.0) {
270         // peak hold timer
271         if(time - (long)(vu->vumeter_hold * 1000 * 1000) > vu->last_hold) {
272             // time's up, reset
273             vu->last_value = value;
274             vu->last_hold = time;
275             vu->holding = false;
276             vu->disp_value = value_orig;
277         }
278         if( vu->mode == VU_MONOCHROME_REVERSE ) {
279             if(value < vu->last_value) {
280                 // value is above peak hold
281                 vu->last_value = value;
282                 vu->last_hold = time;
283                 vu->holding = true;
284             }
285             draw = log10(1 + value * 9);
286             draw_last = log10(1 + vu->last_value * 9);
287 
288             // blinder left -> hold LED
289             int hold_x = round((draw_last) * (led_w + led_m)); // add last led_m removed earlier
290             hold_x -= hold_x % led_s + led_m;
291             hold_x = std::max(0, hold_x);
292             cairo_rectangle( c, led_x, led_y, hold_x, led_h);
293 
294             // blinder hold LED -> value
295             int val_x = round((1 - draw) * (led_w + led_m)); // add last led_m removed earlier
296             val_x -= val_x % led_s;
297             int blind_x = std::min(hold_x + led_s, led_w);
298             int blind_w = std::min(std::max(led_w - val_x - hold_x - led_s, 0), led_w);
299             cairo_rectangle(c, led_x + blind_x, led_y, blind_w, led_h);
300         } else  if( vu->mode == VU_STANDARD_CENTER ) {
301             if(value > vu->last_value) {
302                 // value is above peak hold
303                 vu->last_value = value;
304                 vu->last_hold = time;
305                 vu->holding = true;
306             }
307             draw = log10(1 + value * 9);
308             int val_x = round((1 - draw) / 2.f * (led_w + led_m)); // add last led_m removed earlier
309             cairo_rectangle(c, led_x, led_y, val_x, led_h);
310             cairo_rectangle(c, led_x + led_w - val_x, led_y, val_x, led_h);
311 
312         } else {
313             if(value > vu->last_value) {
314                 // value is above peak hold
315                 vu->last_value = value;
316                 vu->last_hold = time;
317                 vu->holding = true;
318             }
319             draw = log10(1 + value * 9);
320             draw_last = log10(1 + vu->last_value * 9);
321 
322             int hold_x = round((1 - draw_last) * (led_w + led_m)); // add last led_m removed earlier
323             hold_x -= hold_x % led_s;
324             int val_x = round(draw * (led_w + led_m)); // add last led_m removed earlier
325             val_x -= val_x % led_s;
326             int blind_w = led_w - hold_x - led_s - val_x;
327             blind_w = std::min(std::max(blind_w, 0), led_w);
328             cairo_rectangle(c, led_x + val_x, led_y, blind_w, led_h);
329             cairo_rectangle( c, led_x + led_w - hold_x, led_y, hold_x, led_h);
330         }
331     } else {
332         // darken normally
333         float draw = log10(1 + value * 9);
334         if( vu->mode == VU_MONOCHROME_REVERSE )
335             cairo_rectangle( c, led_x, led_y, draw * led_w, led_h);
336         else if( vu->mode == VU_STANDARD_CENTER ) {
337             int val_x = round((1 - draw) / 2.f * (led_w + led_m)); // add last led_m removed earlier
338             cairo_rectangle(c, led_x, led_y, val_x, led_h);
339             cairo_rectangle(c, led_x + led_w - val_x, led_y, val_x, led_h);
340         } else
341             cairo_rectangle( c, led_x + draw * led_w, led_y, led_w * (1 - draw), led_h);
342     }
343     cairo_fill( c );
344 
345     if (vu->vumeter_position)
346     {
347         char str[32];
348         if((vu->value > vu->disp_value and vu->mode != VU_MONOCHROME_REVERSE)
349         or (vu->value < vu->disp_value and vu->mode == VU_MONOCHROME_REVERSE))
350             vu->disp_value = vu->value;
351         if (vu->disp_value < 1.0 / 32768.0)
352             snprintf(str, sizeof(str), "-inf");
353         else
354             snprintf(str, sizeof(str), "%0.2f", dsp::amp2dB(vu->disp_value));
355         // draw value as number
356         cairo_text_extents(c, str, &extents);
357         cairo_move_to(c, text_x + (text_w - extents.width) / 2.0, text_y);
358         GtkStateType state;
359         if(vu->disp_value > 1.f and vu->mode != VU_MONOCHROME_REVERSE)
360             state = GTK_STATE_ACTIVE;
361         else
362             state = GTK_STATE_NORMAL;
363         get_fg_color(widget, &state, &r, &g, &b);
364         cairo_set_source_rgba (c, r, g, b, 1);
365         cairo_show_text(c, str);
366         cairo_fill(c);
367     }
368     cairo_destroy(c);
369     //gtk_paint_shadow(widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL, widget, NULL, ox - 2, oy - 2, sx + 4, sy + 4);
370     //printf("exposed %p %d+%d\n", widget->window, widget->allocation.x, widget->allocation.y);
371 
372     return TRUE;
373 }
374 
375 static void
calf_vumeter_size_request(GtkWidget * widget,GtkRequisition * requisition)376 calf_vumeter_size_request (GtkWidget *widget,
377                            GtkRequisition *requisition)
378 {
379     g_assert(CALF_IS_VUMETER(widget));
380     CalfVUMeter *self = CALF_VUMETER(widget);
381     requisition->width = self->vumeter_width;
382     requisition->height = self->vumeter_height;
383 }
384 
385 static void
calf_vumeter_unrealize(GtkWidget * widget,CalfVUMeter * vu)386 calf_vumeter_unrealize (GtkWidget *widget, CalfVUMeter *vu)
387 {
388     if( vu->cache_surface )
389         cairo_surface_destroy( vu->cache_surface );
390     vu->cache_surface = NULL;
391     if( vu->cache_overlay )
392         cairo_surface_destroy( vu->cache_overlay );
393     vu->cache_overlay = NULL;
394 }
395 
396 static void
calf_vumeter_size_allocate(GtkWidget * widget,GtkAllocation * allocation)397 calf_vumeter_size_allocate (GtkWidget *widget,
398                            GtkAllocation *allocation)
399 {
400     g_assert(CALF_IS_VUMETER(widget));
401     CalfVUMeter *vu = CALF_VUMETER(widget);
402 
403     GtkWidgetClass *parent_class = (GtkWidgetClass *) g_type_class_peek_parent( CALF_VUMETER_GET_CLASS( vu ) );
404 
405     parent_class->size_allocate( widget, allocation );
406 
407     calf_vumeter_unrealize(widget, vu);
408 }
409 
410 static void
calf_vumeter_class_init(CalfVUMeterClass * klass)411 calf_vumeter_class_init (CalfVUMeterClass *klass)
412 {
413     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
414     widget_class->expose_event = calf_vumeter_expose;
415     widget_class->size_request = calf_vumeter_size_request;
416     widget_class->size_allocate = calf_vumeter_size_allocate;
417     gtk_widget_class_install_style_property(
418         widget_class, g_param_spec_float("border-radius", "Border Radius", "Generate round edges",
419         0, 24, 4, GParamFlags(G_PARAM_READWRITE)));
420     gtk_widget_class_install_style_property(
421         widget_class, g_param_spec_float("bevel", "Bevel", "Bevel the object",
422         -2, 2, 0.2, GParamFlags(G_PARAM_READWRITE)));
423     gtk_widget_class_install_style_property(
424         widget_class, g_param_spec_float("glass", "Glass", "Glass effect on top",
425         0, 1, 1, GParamFlags(G_PARAM_READWRITE)));
426 }
427 
428 static void
calf_vumeter_init(CalfVUMeter * self)429 calf_vumeter_init (CalfVUMeter *self)
430 {
431     GtkWidget *widget = GTK_WIDGET(self);
432     //GTK_WIDGET_SET_FLAGS (widget, GTK_NO_WINDOW);
433     widget->requisition.width =  self->vumeter_width;
434     widget->requisition.height = self->vumeter_height;
435     self->cache_surface = NULL;
436     self->falling = false;
437     self->holding = false;
438     self->meter_width = 0;
439     self->disp_value = 0.f;
440     self->value = 0.f;
441     gtk_widget_set_has_window(widget, FALSE);
442     g_signal_connect(GTK_OBJECT(widget), "unrealize", G_CALLBACK(calf_vumeter_unrealize), (gpointer)self);
443 }
444 
445 GtkWidget *
calf_vumeter_new()446 calf_vumeter_new()
447 {
448     return GTK_WIDGET( g_object_new (CALF_TYPE_VUMETER, NULL ));
449 }
450 
451 GType
calf_vumeter_get_type(void)452 calf_vumeter_get_type (void)
453 {
454     static GType type = 0;
455     if (!type) {
456         static const GTypeInfo type_info = {
457             sizeof(CalfVUMeterClass),
458             NULL, /* base_init */
459             NULL, /* base_finalize */
460             (GClassInitFunc)calf_vumeter_class_init,
461             NULL, /* class_finalize */
462             NULL, /* class_data */
463             sizeof(CalfVUMeter),
464             0,    /* n_preallocs */
465             (GInstanceInitFunc)calf_vumeter_init
466         };
467 
468         GTypeInfo *type_info_copy = new GTypeInfo(type_info);
469 
470         for (int i = 0; ; i++) {
471             const char *name = "CalfVUMeter";
472             //char *name = g_strdup_printf("CalfVUMeter%u%d", ((unsigned int)(intptr_t)calf_vumeter_class_init) >> 16, i);
473             if (g_type_from_name(name)) {
474                 //free(name);
475                 continue;
476             }
477             type = g_type_register_static( GTK_TYPE_DRAWING_AREA,
478                                            name,
479                                            type_info_copy,
480                                            (GTypeFlags)0);
481             //free(name);
482             break;
483         }
484     }
485     return type;
486 }
487 
calf_vumeter_set_value(CalfVUMeter * meter,float value)488 extern void calf_vumeter_set_value(CalfVUMeter *meter, float value)
489 {
490     if (value != meter->value or meter->holding or meter->falling)
491     {
492         meter->value = value;
493         gtk_widget_queue_draw(GTK_WIDGET(meter));
494     }
495 }
496 
calf_vumeter_get_value(CalfVUMeter * meter)497 extern float calf_vumeter_get_value(CalfVUMeter *meter)
498 {
499     return meter->value;
500 }
501 
calf_vumeter_set_mode(CalfVUMeter * meter,CalfVUMeterMode mode)502 extern void calf_vumeter_set_mode(CalfVUMeter *meter, CalfVUMeterMode mode)
503 {
504     if (mode != meter->mode)
505     {
506         meter->mode = mode;
507         if(mode == VU_MONOCHROME_REVERSE) {
508             meter->value = 1.f;
509             meter->last_value = 1.f;
510         } else {
511             meter->value = 0.f;
512             meter->last_value = 0.f;
513         }
514         meter->vumeter_falloff = 0.f;
515         meter->last_falloff = (long)0;
516         meter->last_hold = (long)0;
517         gtk_widget_queue_draw(GTK_WIDGET(meter));
518     }
519 }
520 
calf_vumeter_get_mode(CalfVUMeter * meter)521 extern CalfVUMeterMode calf_vumeter_get_mode(CalfVUMeter *meter)
522 {
523     return meter->mode;
524 }
525 
calf_vumeter_set_falloff(CalfVUMeter * meter,float value)526 extern void calf_vumeter_set_falloff(CalfVUMeter *meter, float value)
527 {
528     if (value != meter->vumeter_falloff)
529     {
530         meter->vumeter_falloff = value;
531         gtk_widget_queue_draw(GTK_WIDGET(meter));
532     }
533 }
534 
calf_vumeter_get_falloff(CalfVUMeter * meter)535 extern float calf_vumeter_get_falloff(CalfVUMeter *meter)
536 {
537     return meter->vumeter_falloff;
538 }
539 
calf_vumeter_set_hold(CalfVUMeter * meter,float value)540 extern void calf_vumeter_set_hold(CalfVUMeter *meter, float value)
541 {
542     if (value != meter->vumeter_hold)
543     {
544         meter->vumeter_hold = value;
545         gtk_widget_queue_draw(GTK_WIDGET(meter));
546     }
547 }
548 
calf_vumeter_get_hold(CalfVUMeter * meter)549 extern float calf_vumeter_get_hold(CalfVUMeter *meter)
550 {
551     return meter->vumeter_hold;
552 }
553 
calf_vumeter_set_width(CalfVUMeter * meter,int value)554 extern void calf_vumeter_set_width(CalfVUMeter *meter, int value)
555 {
556     if (value != meter->vumeter_width)
557     {
558         meter->vumeter_width = value;
559         gtk_widget_queue_draw(GTK_WIDGET(meter));
560     }
561 }
562 
calf_vumeter_get_width(CalfVUMeter * meter)563 extern int calf_vumeter_get_width(CalfVUMeter *meter)
564 {
565     return meter->vumeter_width;
566 }
567 
calf_vumeter_set_height(CalfVUMeter * meter,int value)568 extern void calf_vumeter_set_height(CalfVUMeter *meter, int value)
569 {
570     if (value != meter->vumeter_height)
571     {
572         meter->vumeter_height = value;
573         gtk_widget_queue_draw(GTK_WIDGET(meter));
574     }
575 }
576 
calf_vumeter_get_height(CalfVUMeter * meter)577 extern int calf_vumeter_get_height(CalfVUMeter *meter)
578 {
579     return meter->vumeter_height;
580 }
calf_vumeter_set_position(CalfVUMeter * meter,int value)581 extern void calf_vumeter_set_position(CalfVUMeter *meter, int value)
582 {
583     if (value != meter->vumeter_height)
584     {
585         meter->vumeter_position = value;
586         gtk_widget_queue_draw(GTK_WIDGET(meter));
587     }
588 }
589 
calf_vumeter_get_position(CalfVUMeter * meter)590 extern int calf_vumeter_get_position(CalfVUMeter *meter)
591 {
592     return meter->vumeter_position;
593 }
594