1 /*
2     Volume Meter plugin for the DeaDBeeF audio player
3 
4     Copyright (C) 2014 Christian Boxdörfer <christian.boxdoerfer@posteo.de>
5 
6     Based on DeaDBeeFs stock vumeter.
7     Copyright (c) 2009-2014 Alexey Yakovenko <waker@users.sourceforge.net>
8     Copyright (c) 2011 William Pitcock <nenolod@dereferenced.org>
9 
10     This program is free software; you can redistribute it and/or
11     modify it under the terms of the GNU General Public License
12     as published by the Free Software Foundation; either version 2
13     of the License, or (at your option) any later version.
14 
15     This program is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18     GNU General Public License for more details.
19 
20     You should have received a copy of the GNU General Public License
21     along with this program; if not, write to the Free Software
22     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
23 */
24 
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <assert.h>
30 #include <math.h>
31 #include <fcntl.h>
32 #include <gtk/gtk.h>
33 
34 #include <deadbeef/deadbeef.h>
35 #include <deadbeef/gtkui_api.h>
36 
37 #include "fastftoi.h"
38 #include "support.h"
39 
40 #define REFRESH_INTERVAL 25
41 #define MAX_CHANNELS 6
42 #define GRADIENT_TABLE_SIZE 1024
43 
44 #define     STR_GRADIENT_VERTICAL "Vertical"
45 #define     STR_GRADIENT_HORIZONTAL "Horizontal"
46 
47 #define     CONFSTR_MS_REFRESH_INTERVAL       "vu_meter.refresh_interval"
48 #define     CONFSTR_MS_DB_RANGE               "vu_meter.db_range"
49 #define     CONFSTR_MS_ENABLE_HGRID           "vu_meter.enable_hgrid"
50 #define     CONFSTR_MS_ENABLE_VGRID           "vu_meter.enable_vgrid"
51 #define     CONFSTR_MS_ENABLE_BAR_MODE        "vu_meter.enable_bar_mode"
52 #define     CONFSTR_MS_BAR_FALLOFF            "vu_meter.bar_falloff"
53 #define     CONFSTR_MS_BAR_DELAY              "vu_meter.bar_delay"
54 #define     CONFSTR_MS_PEAK_FALLOFF           "vu_meter.peak_falloff"
55 #define     CONFSTR_MS_PEAK_DELAY             "vu_meter.peak_delay"
56 #define     CONFSTR_MS_GRADIENT_ORIENTATION   "vu_meter.gradient_orientation"
57 #define     CONFSTR_MS_COLOR_BG               "vu_meter.color.background"
58 #define     CONFSTR_MS_COLOR_VGRID            "vu_meter.color.vgrid"
59 #define     CONFSTR_MS_COLOR_HGRID            "vu_meter.color.hgrid"
60 #define     CONFSTR_MS_COLOR_GRADIENT_00      "vu_meter.color.gradient_00"
61 #define     CONFSTR_MS_COLOR_GRADIENT_01      "vu_meter.color.gradient_01"
62 #define     CONFSTR_MS_COLOR_GRADIENT_02      "vu_meter.color.gradient_02"
63 #define     CONFSTR_MS_COLOR_GRADIENT_03      "vu_meter.color.gradient_03"
64 #define     CONFSTR_MS_COLOR_GRADIENT_04      "vu_meter.color.gradient_04"
65 #define     CONFSTR_MS_COLOR_GRADIENT_05      "vu_meter.color.gradient_05"
66 
67 /* Global variables */
68 static DB_misc_t            plugin;
69 static DB_functions_t *     deadbeef = NULL;
70 static ddb_gtkui_t *        gtkui_plugin = NULL;
71 
72 typedef struct {
73     ddb_gtkui_widget_t base;
74     GtkWidget *drawarea;
75     GtkWidget *popup;
76     GtkWidget *popup_item;
77     cairo_surface_t *surf;
78     cairo_surface_t *surf_png;
79     guint drawtimer;
80     uint32_t colors[GRADIENT_TABLE_SIZE];
81     float data[MAX_CHANNELS];
82     float bars[MAX_CHANNELS];
83     int delay[MAX_CHANNELS];
84     float peaks[MAX_CHANNELS];
85     int delay_peak[MAX_CHANNELS];
86     int channels;
87     intptr_t mutex;
88 } w_vumeter_t;
89 
90 enum STYLE { STYLE_BARS = 0, STYLE_RETRO = 1 };
91 
92 static int CONFIG_STYLE = STYLE_BARS;
93 static int CONFIG_REFRESH_INTERVAL = 25;
94 static int CONFIG_DB_RANGE = 70;
95 static int CONFIG_ENABLE_HGRID = 1;
96 static int CONFIG_ENABLE_VGRID = 1;
97 static int CONFIG_ENABLE_BAR_MODE = 0;
98 static int CONFIG_BAR_FALLOFF = -1;
99 static int CONFIG_BAR_DELAY = 0;
100 static int CONFIG_PEAK_FALLOFF = 90;
101 static int CONFIG_PEAK_DELAY = 500;
102 static int CONFIG_GRADIENT_ORIENTATION = 0;
103 static int CONFIG_NUM_COLORS = 6;
104 static GdkColor CONFIG_COLOR_BG;
105 static GdkColor CONFIG_COLOR_VGRID;
106 static GdkColor CONFIG_COLOR_HGRID;
107 static GdkColor CONFIG_GRADIENT_COLORS[6];
108 static uint32_t CONFIG_COLOR_BG32 = 0xff222222;
109 static uint32_t CONFIG_COLOR_VGRID32 = 0xff000000;
110 static uint32_t CONFIG_COLOR_HGRID32 = 0xff666666;
111 
112 static void
save_config(void)113 save_config (void)
114 {
115     deadbeef->conf_set_int (CONFSTR_MS_REFRESH_INTERVAL,            CONFIG_REFRESH_INTERVAL);
116     deadbeef->conf_set_int (CONFSTR_MS_DB_RANGE,                    CONFIG_DB_RANGE);
117     deadbeef->conf_set_int (CONFSTR_MS_ENABLE_HGRID,                CONFIG_ENABLE_HGRID);
118     deadbeef->conf_set_int (CONFSTR_MS_ENABLE_VGRID,                CONFIG_ENABLE_VGRID);
119     deadbeef->conf_set_int (CONFSTR_MS_ENABLE_BAR_MODE,             CONFIG_ENABLE_BAR_MODE);
120     deadbeef->conf_set_int (CONFSTR_MS_BAR_FALLOFF,                 CONFIG_BAR_FALLOFF);
121     deadbeef->conf_set_int (CONFSTR_MS_BAR_DELAY,                   CONFIG_BAR_DELAY);
122     deadbeef->conf_set_int (CONFSTR_MS_PEAK_FALLOFF,                CONFIG_PEAK_FALLOFF);
123     deadbeef->conf_set_int (CONFSTR_MS_PEAK_DELAY,                  CONFIG_PEAK_DELAY);
124     deadbeef->conf_set_int (CONFSTR_MS_GRADIENT_ORIENTATION,        CONFIG_GRADIENT_ORIENTATION);
125     char color[100];
126     snprintf (color, sizeof (color), "%d %d %d", CONFIG_COLOR_BG.red, CONFIG_COLOR_BG.green, CONFIG_COLOR_BG.blue);
127     deadbeef->conf_set_str (CONFSTR_MS_COLOR_BG, color);
128     snprintf (color, sizeof (color), "%d %d %d", CONFIG_COLOR_VGRID.red, CONFIG_COLOR_VGRID.green, CONFIG_COLOR_VGRID.blue);
129     deadbeef->conf_set_str (CONFSTR_MS_COLOR_VGRID, color);
130     snprintf (color, sizeof (color), "%d %d %d", CONFIG_COLOR_HGRID.red, CONFIG_COLOR_HGRID.green, CONFIG_COLOR_HGRID.blue);
131     deadbeef->conf_set_str (CONFSTR_MS_COLOR_HGRID, color);
132     snprintf (color, sizeof (color), "%d %d %d", CONFIG_GRADIENT_COLORS[0].red, CONFIG_GRADIENT_COLORS[0].green, CONFIG_GRADIENT_COLORS[0].blue);
133     deadbeef->conf_set_str (CONFSTR_MS_COLOR_GRADIENT_00, color);
134     snprintf (color, sizeof (color), "%d %d %d", CONFIG_GRADIENT_COLORS[1].red, CONFIG_GRADIENT_COLORS[1].green, CONFIG_GRADIENT_COLORS[1].blue);
135     deadbeef->conf_set_str (CONFSTR_MS_COLOR_GRADIENT_01, color);
136     snprintf (color, sizeof (color), "%d %d %d", CONFIG_GRADIENT_COLORS[2].red, CONFIG_GRADIENT_COLORS[2].green, CONFIG_GRADIENT_COLORS[2].blue);
137     deadbeef->conf_set_str (CONFSTR_MS_COLOR_GRADIENT_02, color);
138     snprintf (color, sizeof (color), "%d %d %d", CONFIG_GRADIENT_COLORS[3].red, CONFIG_GRADIENT_COLORS[3].green, CONFIG_GRADIENT_COLORS[3].blue);
139     deadbeef->conf_set_str (CONFSTR_MS_COLOR_GRADIENT_03, color);
140     snprintf (color, sizeof (color), "%d %d %d", CONFIG_GRADIENT_COLORS[4].red, CONFIG_GRADIENT_COLORS[4].green, CONFIG_GRADIENT_COLORS[4].blue);
141     deadbeef->conf_set_str (CONFSTR_MS_COLOR_GRADIENT_04, color);
142     snprintf (color, sizeof (color), "%d %d %d", CONFIG_GRADIENT_COLORS[5].red, CONFIG_GRADIENT_COLORS[5].green, CONFIG_GRADIENT_COLORS[5].blue);
143     deadbeef->conf_set_str (CONFSTR_MS_COLOR_GRADIENT_05, color);
144 }
145 
146 static void
load_config(void)147 load_config (void)
148 {
149     deadbeef->conf_lock ();
150     CONFIG_GRADIENT_ORIENTATION = deadbeef->conf_get_int (CONFSTR_MS_GRADIENT_ORIENTATION,   0);
151     CONFIG_DB_RANGE = deadbeef->conf_get_int (CONFSTR_MS_DB_RANGE,                          70);
152     CONFIG_ENABLE_HGRID = deadbeef->conf_get_int (CONFSTR_MS_ENABLE_HGRID,                   1);
153     CONFIG_ENABLE_VGRID = deadbeef->conf_get_int (CONFSTR_MS_ENABLE_VGRID,                   1);
154     CONFIG_ENABLE_BAR_MODE = deadbeef->conf_get_int (CONFSTR_MS_ENABLE_BAR_MODE,             0);
155     CONFIG_REFRESH_INTERVAL = deadbeef->conf_get_int (CONFSTR_MS_REFRESH_INTERVAL,          25);
156     CONFIG_BAR_FALLOFF = deadbeef->conf_get_int (CONFSTR_MS_BAR_FALLOFF,                    -1);
157     CONFIG_BAR_DELAY = deadbeef->conf_get_int (CONFSTR_MS_BAR_DELAY,                         0);
158     CONFIG_PEAK_FALLOFF = deadbeef->conf_get_int (CONFSTR_MS_PEAK_FALLOFF,                  90);
159     CONFIG_PEAK_DELAY = deadbeef->conf_get_int (CONFSTR_MS_PEAK_DELAY,                     500);
160     const char *color;
161     color = deadbeef->conf_get_str_fast (CONFSTR_MS_COLOR_BG,                   "8738 8738 8738");
162     sscanf (color, "%hd %hd %hd", &CONFIG_COLOR_BG.red, &CONFIG_COLOR_BG.green, &CONFIG_COLOR_BG.blue);
163     color = deadbeef->conf_get_str_fast (CONFSTR_MS_COLOR_VGRID,                         "0 0 0");
164     sscanf (color, "%hd %hd %hd", &CONFIG_COLOR_VGRID.red, &CONFIG_COLOR_VGRID.green, &CONFIG_COLOR_VGRID.blue);
165     color = deadbeef->conf_get_str_fast (CONFSTR_MS_COLOR_HGRID,             "26214 26214 26214");
166     sscanf (color, "%hd %hd %hd", &CONFIG_COLOR_HGRID.red, &CONFIG_COLOR_HGRID.green, &CONFIG_COLOR_HGRID.blue);
167     color = deadbeef->conf_get_str_fast (CONFSTR_MS_COLOR_GRADIENT_00,        "65535 0 0");
168     sscanf (color, "%hd %hd %hd", &(CONFIG_GRADIENT_COLORS[0].red), &(CONFIG_GRADIENT_COLORS[0].green), &(CONFIG_GRADIENT_COLORS[0].blue));
169     color = deadbeef->conf_get_str_fast (CONFSTR_MS_COLOR_GRADIENT_01,      "65535 32896 0");
170     sscanf (color, "%hd %hd %hd", &(CONFIG_GRADIENT_COLORS[1].red), &(CONFIG_GRADIENT_COLORS[1].green), &(CONFIG_GRADIENT_COLORS[1].blue));
171     color = deadbeef->conf_get_str_fast (CONFSTR_MS_COLOR_GRADIENT_02,      "65535 65535 0");
172     sscanf (color, "%hd %hd %hd", &(CONFIG_GRADIENT_COLORS[2].red), &(CONFIG_GRADIENT_COLORS[2].green), &(CONFIG_GRADIENT_COLORS[2].blue));
173     color = deadbeef->conf_get_str_fast (CONFSTR_MS_COLOR_GRADIENT_03,    "32896 65535 30840");
174     sscanf (color, "%hd %hd %hd", &(CONFIG_GRADIENT_COLORS[3].red), &(CONFIG_GRADIENT_COLORS[3].green), &(CONFIG_GRADIENT_COLORS[3].blue));
175     color = deadbeef->conf_get_str_fast (CONFSTR_MS_COLOR_GRADIENT_04,      "0 38036 41120");
176     sscanf (color, "%hd %hd %hd", &(CONFIG_GRADIENT_COLORS[4].red), &(CONFIG_GRADIENT_COLORS[4].green), &(CONFIG_GRADIENT_COLORS[4].blue));
177     color = deadbeef->conf_get_str_fast (CONFSTR_MS_COLOR_GRADIENT_05,       "0 8224 25700");
178     sscanf (color, "%hd %hd %hd", &(CONFIG_GRADIENT_COLORS[5].red), &(CONFIG_GRADIENT_COLORS[5].green), &(CONFIG_GRADIENT_COLORS[5].blue));
179 
180     float scale = 255/65535.f;
181     CONFIG_COLOR_BG32 = ((uint32_t)(CONFIG_COLOR_BG.red * scale) & 0xFF) << 16 |
182                         ((uint32_t)(CONFIG_COLOR_BG.green * scale) & 0xFF) << 8 |
183                         ((uint32_t)(CONFIG_COLOR_BG.blue * scale) & 0xFF) << 0;
184 
185     CONFIG_COLOR_VGRID32 = ((uint32_t)(CONFIG_COLOR_VGRID.red * scale) & 0xFF) << 16 |
186                         ((uint32_t)(CONFIG_COLOR_VGRID.green * scale) & 0xFF) << 8 |
187                         ((uint32_t)(CONFIG_COLOR_VGRID.blue * scale) & 0xFF) << 0;
188 
189     CONFIG_COLOR_HGRID32 = ((uint32_t)(CONFIG_COLOR_HGRID.red * scale) & 0xFF) << 16 |
190                         ((uint32_t)(CONFIG_COLOR_HGRID.green * scale) & 0xFF) << 8 |
191                         ((uint32_t)(CONFIG_COLOR_HGRID.blue * scale) & 0xFF) << 0;
192     deadbeef->conf_unlock ();
193 }
194 
195 static inline void
_memset_pattern(char * data,const void * pattern,size_t data_len,size_t pattern_len)196 _memset_pattern (char *data, const void* pattern, size_t data_len, size_t pattern_len)
197 {
198     memmove ((char *)data, pattern, pattern_len);
199     char *start = (char *)data;
200     char *current = (char *)data + pattern_len;
201     char *end = start + data_len;
202     while(current + pattern_len < end) {
203         memmove (current, start, pattern_len);
204         current += pattern_len;
205         pattern_len *= 2;
206     }
207     memmove (current, start, end-current);
208 }
209 
210 static inline void
_draw_vline(uint8_t * data,int stride,int x0,int y0,int y1,uint32_t color)211 _draw_vline (uint8_t *data, int stride, int x0, int y0, int y1, uint32_t color) {
212     if (y0 > y1) {
213         int tmp = y0;
214         y0 = y1;
215         y1 = tmp;
216         y1--;
217     }
218     else if (y0 < y1) {
219         y0++;
220     }
221     uint32_t *ptr = (uint32_t*)&data[y0*stride+x0*4];
222     while (y0 <= y1) {
223         *ptr = color;
224         ptr += stride/4;
225         y0++;
226     }
227 }
228 
229 static inline void
_draw_hline(uint8_t * data,int stride,int x0,int y0,int x1,uint32_t color)230 _draw_hline (uint8_t *data, int stride, int x0, int y0, int x1, uint32_t color) {
231     uint32_t *ptr = (uint32_t*)&data[y0*stride+x0*4];
232     while (x0 <= x1) {
233         *ptr++ = color;
234         x0++;
235     }
236 }
237 
238 static inline void
_draw_background(uint8_t * data,int w,int h,uint32_t color)239 _draw_background (uint8_t *data, int w, int h, uint32_t color)
240 {
241     size_t fillLen = w * h * sizeof (uint32_t);
242     _memset_pattern ((char *)data, &color, fillLen, sizeof (uint32_t));
243 }
244 
245 static inline void
_draw_bar(uint8_t * data,int stride,int x0,int y0,int w,int h,uint32_t color)246 _draw_bar (uint8_t *data, int stride, int x0, int y0, int w, int h, uint32_t color) {
247     int y1 = y0+h-1;
248     int x1 = x0+w-1;
249     uint32_t *ptr = (uint32_t*)&data[y0*stride+x0*4];
250     while (y0 <= y1) {
251         int x = x0;
252         while (x++ <= x1) {
253             *ptr++ = color;
254         }
255         y0++;
256         ptr += stride/4-w;
257     }
258 }
259 
260 static inline void
_draw_bar_gradient_v(gpointer user_data,uint8_t * data,int stride,int x0,int y0,int w,int h,int total_h)261 _draw_bar_gradient_v (gpointer user_data, uint8_t *data, int stride, int x0, int y0, int w, int h, int total_h) {
262     w_vumeter_t *s = user_data;
263     int y1 = y0+h-1;
264     int x1 = x0+w-1;
265     uint32_t *ptr = (uint32_t*)&data[y0*stride+x0*4];
266     while (y0 <= y1) {
267         int x = x0;
268         int index = ftoi(((double)y0/(double)total_h) * (GRADIENT_TABLE_SIZE - 1));
269         index = CLAMP (index, 0, GRADIENT_TABLE_SIZE - 1);
270         while (x++ <= x1) {
271             *ptr++ = s->colors[index];
272         }
273         y0++;
274         ptr += stride/4-w;
275     }
276 }
277 
278 static inline void
_draw_bar_gradient_h(gpointer user_data,uint8_t * data,int stride,int x0,int y0,int w,int h,int total_w)279 _draw_bar_gradient_h (gpointer user_data, uint8_t *data, int stride, int x0, int y0, int w, int h, int total_w) {
280     w_vumeter_t *s = user_data;
281     int y1 = y0+h-1;
282     int x1 = x0+w-1;
283     uint32_t *ptr = (uint32_t*)&data[y0*stride+x0*4];
284     while (y0 <= y1) {
285         int x = x0;
286         while (x++ <= x1) {
287             int index = ftoi(((double)x/(double)total_w) * (GRADIENT_TABLE_SIZE - 1));
288             index = CLAMP (index, 0, GRADIENT_TABLE_SIZE - 1);
289             *ptr++ = s->colors[index];
290         }
291         y0++;
292         ptr += stride/4-w;
293     }
294 }
295 
296 static inline void
_draw_bar_gradient_bar_mode_v(gpointer user_data,uint8_t * data,int stride,int x0,int y0,int w,int h,int total_h)297 _draw_bar_gradient_bar_mode_v (gpointer user_data, uint8_t *data, int stride, int x0, int y0, int w, int h, int total_h) {
298     w_vumeter_t *s = user_data;
299     int y1 = y0+h-1;
300     int x1 = x0+w-1;
301     y0 -= y0 % 2;
302     uint32_t *ptr = (uint32_t*)&data[y0*stride+x0*4];
303     while (y0 <= y1) {
304         int x = x0;
305         int index = ftoi(((double)y0/(double)total_h) * (GRADIENT_TABLE_SIZE - 1));
306         index = CLAMP (index, 0, GRADIENT_TABLE_SIZE - 1);
307         while (x++ <= x1) {
308             *ptr++ = s->colors[index];
309         }
310         y0 += 2;
311         ptr += stride/2-w;
312     }
313 }
314 
315 static inline void
_draw_bar_gradient_bar_mode_h(gpointer user_data,uint8_t * data,int stride,int x0,int y0,int w,int h,int total_w)316 _draw_bar_gradient_bar_mode_h (gpointer user_data, uint8_t *data, int stride, int x0, int y0, int w, int h, int total_w) {
317     w_vumeter_t *s = user_data;
318     int y1 = y0+h-1;
319     int x1 = x0+w-1;
320     y0 -= y0 % 2;
321     uint32_t *ptr = (uint32_t*)&data[y0*stride+x0*4];
322     while (y0 <= y1) {
323         int x = x0;
324         while (x++ <= x1) {
325             int index = ftoi(((double)x/(double)total_w) * (GRADIENT_TABLE_SIZE - 1));
326             index = CLAMP (index, 0, GRADIENT_TABLE_SIZE - 1);
327             *ptr++ = s->colors[index];
328         }
329         y0 += 2;
330         ptr += stride/2-w;
331     }
332 }
333 
334 /* based on Delphi function by Witold J.Janik */
335 void
create_gradient_table(gpointer user_data,GdkColor * colors,int num_colors)336 create_gradient_table (gpointer user_data, GdkColor *colors, int num_colors)
337 {
338     w_vumeter_t *w = user_data;
339 
340     num_colors -= 1;
341 
342     for (int i = 0; i < GRADIENT_TABLE_SIZE; i++) {
343         double position = (double)i/GRADIENT_TABLE_SIZE;
344         /* if position > 1 then we have repetition of colors it maybe useful    */
345         if (position > 1.0) {
346             if (position - ftoi (position) == 0.0) {
347                 position = 1.0;
348             }
349             else {
350                 position = position - ftoi (position);
351             }
352         }
353 
354         double m= num_colors * position;
355         int n=(int)m; // integer of m
356         double f=m-n;  // fraction of m
357 
358         w->colors[i] = 0xFF000000;
359         float scale = 255/65535.f;
360         if (num_colors == 0) {
361             w->colors[i] = (uint32_t)(colors[0].red*scale) << 16 |
362                 (uint32_t)(colors[0].green*scale) << 8 |
363                 (uint32_t)(colors[0].blue*scale) << 0;
364         }
365         else if (n < num_colors) {
366             w->colors[i] = (uint32_t)((colors[n].red*scale) + f * ((colors[n+1].red*scale)-(colors[n].red*scale))) << 16 |
367                 (uint32_t)((colors[n].green*scale) + f * ((colors[n+1].green*scale)-(colors[n].green*scale))) << 8 |
368                 (uint32_t)((colors[n].blue*scale) + f * ((colors[n+1].blue*scale)-(colors[n].blue*scale))) << 0;
369         }
370         else if (n == num_colors) {
371             w->colors[i] = (uint32_t)(colors[n].red*scale) << 16 |
372                 (uint32_t)(colors[n].green*scale) << 8 |
373                 (uint32_t)(colors[n].blue*scale) << 0;
374         }
375         else {
376             w->colors[i] = 0xFFFFFFFF;
377         }
378     }
379 }
380 
381 static int
on_config_changed(gpointer user_data,uintptr_t ctx)382 on_config_changed (gpointer user_data, uintptr_t ctx)
383 {
384     w_vumeter_t *w = user_data;
385     load_config ();
386     return 0;
387 }
388 
389 #pragma GCC diagnostic push
390 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
391 static void
on_button_config(GtkMenuItem * menuitem,gpointer user_data)392 on_button_config (GtkMenuItem *menuitem, gpointer user_data)
393 {
394     GtkWidget *vumeter_properties;
395     GtkWidget *config_dialog;
396     GtkWidget *vbox01;
397     GtkWidget *vbox02;
398     GtkWidget *vbox03;
399     GtkWidget *vbox04;
400     GtkWidget *hbox01;
401     GtkWidget *hbox02;
402     GtkWidget *valign_01;
403     GtkWidget *valign_02;
404     GtkWidget *valign_03;
405     GtkWidget *color_label;
406     GtkWidget *color_frame;
407     GtkWidget *color_bg_label;
408     GtkWidget *color_bg;
409     GtkWidget *color_vgrid_label;
410     GtkWidget *color_vgrid;
411     GtkWidget *color_hgrid_label;
412     GtkWidget *color_hgrid;
413     GtkWidget *hseparator_01;
414     GtkWidget *color_gradient_00;
415     GtkWidget *color_gradient_01;
416     GtkWidget *color_gradient_02;
417     GtkWidget *color_gradient_03;
418     GtkWidget *color_gradient_04;
419     GtkWidget *color_gradient_05;
420     GtkWidget *num_colors_label;
421     GtkWidget *num_colors;
422     GtkWidget *hbox03;
423     GtkWidget *db_range_label0;
424     GtkWidget *db_range;
425     GtkWidget *hgrid;
426     GtkWidget *vgrid;
427     GtkWidget *bar_mode;
428     GtkWidget *hbox06;
429     GtkWidget *style_label;
430     GtkWidget *style;
431     GtkWidget *hbox05;
432     GtkWidget *gradient_orientation_label;
433     GtkWidget *gradient_orientation;
434     GtkWidget *dialog_action_area13;
435     GtkWidget *applybutton1;
436     GtkWidget *cancelbutton1;
437     GtkWidget *okbutton1;
438     vumeter_properties = gtk_dialog_new ();
439     gtk_window_set_title (GTK_WINDOW (vumeter_properties), "vumeter Properties");
440     gtk_window_set_type_hint (GTK_WINDOW (vumeter_properties), GDK_WINDOW_TYPE_HINT_DIALOG);
441 
442     config_dialog = gtk_dialog_get_content_area (GTK_DIALOG (vumeter_properties));
443     gtk_widget_show (config_dialog);
444 
445     hbox01 = gtk_hbox_new (FALSE, 8);
446     gtk_widget_show (hbox01);
447     gtk_box_pack_start (GTK_BOX (config_dialog), hbox01, FALSE, FALSE, 0);
448     gtk_container_set_border_width (GTK_CONTAINER (hbox01), 12);
449 
450     color_label = gtk_label_new (NULL);
451     gtk_label_set_markup (GTK_LABEL (color_label),"<b>Colors</b>");
452     gtk_widget_show (color_label);
453 
454     color_frame = gtk_frame_new ("Colors");
455     gtk_frame_set_label_widget ((GtkFrame *)color_frame, color_label);
456     gtk_frame_set_shadow_type ((GtkFrame *)color_frame, GTK_SHADOW_IN);
457     gtk_widget_show (color_frame);
458     gtk_box_pack_start (GTK_BOX (hbox01), color_frame, TRUE, FALSE, 0);
459 
460     vbox01 = gtk_vbox_new (FALSE, 8);
461     gtk_widget_show (vbox01);
462     gtk_container_add (GTK_CONTAINER (color_frame), vbox01);
463     gtk_container_set_border_width (GTK_CONTAINER (vbox01), 12);
464 
465     hbox02 = gtk_hbox_new (FALSE, 8);
466     gtk_widget_show (hbox02);
467     gtk_box_pack_start (GTK_BOX (vbox01), hbox02, TRUE, TRUE, 0);
468 
469     vbox03 = gtk_vbox_new (FALSE, 8);
470     gtk_widget_show (vbox03);
471     gtk_box_pack_start (GTK_BOX (hbox02), vbox03, TRUE, TRUE, 0);
472 
473     vbox04 = gtk_vbox_new (FALSE, 8);
474     gtk_widget_show (vbox04);
475     gtk_box_pack_start (GTK_BOX (hbox02), vbox04, TRUE, TRUE, 0);
476 
477     valign_01 = gtk_alignment_new(0, 1, 0, 1);
478     gtk_container_add(GTK_CONTAINER(vbox03), valign_01);
479     gtk_widget_show (valign_01);
480 
481     color_bg_label = gtk_label_new (NULL);
482     gtk_label_set_markup (GTK_LABEL (color_bg_label),"Background:");
483     gtk_widget_show (color_bg_label);
484     gtk_container_add(GTK_CONTAINER(valign_01), color_bg_label);
485 
486     color_bg = gtk_color_button_new ();
487     gtk_color_button_set_use_alpha ((GtkColorButton *)color_bg, TRUE);
488     gtk_widget_show (color_bg);
489     gtk_box_pack_start (GTK_BOX (vbox04), color_bg, TRUE, TRUE, 0);
490 
491     valign_02 = gtk_alignment_new(0, 1, 0, 1);
492     gtk_container_add(GTK_CONTAINER(vbox03), valign_02);
493     gtk_widget_show (valign_02);
494 
495     color_vgrid_label = gtk_label_new (NULL);
496     gtk_label_set_markup (GTK_LABEL (color_vgrid_label),"Vertical grid:");
497     gtk_widget_show (color_vgrid_label);
498     gtk_container_add(GTK_CONTAINER(valign_02), color_vgrid_label);
499 
500     color_vgrid = gtk_color_button_new ();
501     gtk_color_button_set_use_alpha ((GtkColorButton *)color_vgrid, TRUE);
502     gtk_widget_show (color_vgrid);
503     gtk_box_pack_start (GTK_BOX (vbox04), color_vgrid, TRUE, TRUE, 0);
504 
505     valign_03 = gtk_alignment_new(0, 1, 0, 1);
506     gtk_container_add(GTK_CONTAINER(vbox03), valign_03);
507     gtk_widget_show (valign_03);
508 
509     color_hgrid_label = gtk_label_new (NULL);
510     gtk_label_set_markup (GTK_LABEL (color_hgrid_label),"Horizontal grid:");
511     gtk_widget_show (color_hgrid_label);
512     gtk_container_add(GTK_CONTAINER(valign_03), color_hgrid_label);
513 
514     color_hgrid = gtk_color_button_new ();
515     gtk_color_button_set_use_alpha ((GtkColorButton *)color_hgrid, TRUE);
516     gtk_widget_show (color_hgrid);
517     gtk_box_pack_start (GTK_BOX (vbox04), color_hgrid, TRUE, TRUE, 0);
518 
519     hseparator_01 = gtk_hseparator_new ();
520     gtk_widget_show (hseparator_01);
521     gtk_box_pack_start (GTK_BOX (vbox01), hseparator_01, TRUE, TRUE, 0);
522 
523     num_colors_label = gtk_label_new (NULL);
524     gtk_label_set_markup (GTK_LABEL (num_colors_label),"Number of colors:");
525     gtk_widget_show (num_colors_label);
526     gtk_box_pack_start (GTK_BOX (vbox01), num_colors_label, FALSE, FALSE, 0);
527 
528     num_colors = gtk_spin_button_new_with_range (1,6,1);
529     gtk_widget_show (num_colors);
530     gtk_box_pack_start (GTK_BOX (vbox01), num_colors, FALSE, FALSE, 0);
531 
532     color_gradient_00 = gtk_color_button_new ();
533     gtk_color_button_set_use_alpha ((GtkColorButton *)color_gradient_00, TRUE);
534     gtk_widget_show (color_gradient_00);
535     gtk_box_pack_start (GTK_BOX (vbox01), color_gradient_00, TRUE, FALSE, 0);
536     gtk_widget_set_size_request (color_gradient_00, -1, 30);
537 
538     color_gradient_01 = gtk_color_button_new ();
539     gtk_color_button_set_use_alpha ((GtkColorButton *)color_gradient_01, TRUE);
540     gtk_widget_show (color_gradient_01);
541     gtk_box_pack_start (GTK_BOX (vbox01), color_gradient_01, TRUE, FALSE, 0);
542     gtk_widget_set_size_request (color_gradient_01, -1, 30);
543 
544     color_gradient_02 = gtk_color_button_new ();
545     gtk_color_button_set_use_alpha ((GtkColorButton *)color_gradient_02, TRUE);
546     gtk_widget_show (color_gradient_02);
547     gtk_box_pack_start (GTK_BOX (vbox01), color_gradient_02, TRUE, FALSE, 0);
548     gtk_widget_set_size_request (color_gradient_02, -1, 30);
549 
550     color_gradient_03 = gtk_color_button_new ();
551     gtk_color_button_set_use_alpha ((GtkColorButton *)color_gradient_03, TRUE);
552     gtk_widget_show (color_gradient_03);
553     gtk_box_pack_start (GTK_BOX (vbox01), color_gradient_03, TRUE, FALSE, 0);
554     gtk_widget_set_size_request (color_gradient_03, -1, 30);
555 
556     color_gradient_04 = gtk_color_button_new ();
557     gtk_color_button_set_use_alpha ((GtkColorButton *)color_gradient_04, TRUE);
558     gtk_widget_show (color_gradient_04);
559     gtk_box_pack_start (GTK_BOX (vbox01), color_gradient_04, TRUE, FALSE, 0);
560     gtk_widget_set_size_request (color_gradient_04, -1, 30);
561 
562     color_gradient_05 = gtk_color_button_new ();
563     gtk_color_button_set_use_alpha ((GtkColorButton *)color_gradient_05, TRUE);
564     gtk_widget_show (color_gradient_05);
565     gtk_box_pack_start (GTK_BOX (vbox01), color_gradient_05, TRUE, FALSE, 0);
566     gtk_widget_set_size_request (color_gradient_05, -1, 30);
567 
568     vbox02 = gtk_vbox_new (FALSE, 8);
569     gtk_widget_show (vbox02);
570     gtk_box_pack_start (GTK_BOX (hbox01), vbox02, FALSE, FALSE, 0);
571     gtk_container_set_border_width (GTK_CONTAINER (vbox02), 12);
572 
573     hbox03 = gtk_hbox_new (FALSE, 8);
574     gtk_widget_show (hbox03);
575     gtk_box_pack_start (GTK_BOX (vbox02), hbox03, FALSE, FALSE, 0);
576 
577     db_range_label0 = gtk_label_new (NULL);
578     gtk_label_set_markup (GTK_LABEL (db_range_label0),"dB range:");
579     gtk_widget_show (db_range_label0);
580     gtk_box_pack_start (GTK_BOX (hbox03), db_range_label0, FALSE, TRUE, 0);
581 
582     db_range = gtk_spin_button_new_with_range (50,120,10);
583     gtk_widget_show (db_range);
584     gtk_box_pack_start (GTK_BOX (hbox03), db_range, TRUE, TRUE, 0);
585 
586     hbox05 = gtk_hbox_new (FALSE, 8);
587     gtk_widget_show (hbox05);
588     gtk_box_pack_start (GTK_BOX (vbox02), hbox05, FALSE, FALSE, 0);
589 
590     gradient_orientation_label = gtk_label_new (NULL);
591     gtk_label_set_markup (GTK_LABEL (gradient_orientation_label),"Gradient orientation:");
592     gtk_widget_show (gradient_orientation_label);
593     gtk_box_pack_start (GTK_BOX (hbox05), gradient_orientation_label, FALSE, TRUE, 0);
594 
595     gradient_orientation = gtk_combo_box_text_new ();
596     gtk_widget_show (gradient_orientation);
597     gtk_box_pack_start (GTK_BOX (hbox05), gradient_orientation, TRUE, TRUE, 0);
598     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(gradient_orientation), STR_GRADIENT_VERTICAL);
599     gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(gradient_orientation), STR_GRADIENT_HORIZONTAL);
600 
601     hbox06 = gtk_hbox_new (FALSE, 8);
602     gtk_widget_show (hbox06);
603     gtk_box_pack_start (GTK_BOX (vbox02), hbox06, FALSE, FALSE, 0);
604 
605     style_label = gtk_label_new (NULL);
606     gtk_label_set_markup (GTK_LABEL (style_label),"Style:");
607     gtk_widget_show (style_label);
608     gtk_box_pack_start (GTK_BOX (hbox06), style_label, FALSE, TRUE, 0);
609 
610     style = gtk_combo_box_text_new ();
611     gtk_widget_show (style);
612     gtk_box_pack_start (GTK_BOX (hbox06), style, TRUE, TRUE, 0);
613     gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(style), "Bars");
614     gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(style), "Retro");
615 
616     hgrid = gtk_check_button_new_with_label ("Horizontal grid");
617     gtk_widget_show (hgrid);
618     gtk_box_pack_start (GTK_BOX (vbox02), hgrid, FALSE, FALSE, 0);
619 
620     vgrid = gtk_check_button_new_with_label ("Vertical grid");
621     gtk_widget_show (vgrid);
622     gtk_box_pack_start (GTK_BOX (vbox02), vgrid, FALSE, FALSE, 0);
623 
624     bar_mode = gtk_check_button_new_with_label ("Bar mode");
625     gtk_widget_show (bar_mode);
626     gtk_box_pack_start (GTK_BOX (vbox02), bar_mode, FALSE, FALSE, 0);
627 
628     dialog_action_area13 = gtk_dialog_get_action_area (GTK_DIALOG (vumeter_properties));
629     gtk_widget_show (dialog_action_area13);
630     gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area13), GTK_BUTTONBOX_END);
631 
632     applybutton1 = gtk_button_new_from_stock ("gtk-apply");
633     gtk_widget_show (applybutton1);
634     gtk_dialog_add_action_widget (GTK_DIALOG (vumeter_properties), applybutton1, GTK_RESPONSE_APPLY);
635     gtk_widget_set_can_default (applybutton1, TRUE);
636 
637     cancelbutton1 = gtk_button_new_from_stock ("gtk-cancel");
638     gtk_widget_show (cancelbutton1);
639     gtk_dialog_add_action_widget (GTK_DIALOG (vumeter_properties), cancelbutton1, GTK_RESPONSE_CANCEL);
640     gtk_widget_set_can_default (cancelbutton1, TRUE);
641 
642     okbutton1 = gtk_button_new_from_stock ("gtk-ok");
643     gtk_widget_show (okbutton1);
644     gtk_dialog_add_action_widget (GTK_DIALOG (vumeter_properties), okbutton1, GTK_RESPONSE_OK);
645     gtk_widget_set_can_default (okbutton1, TRUE);
646 
647     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (hgrid), CONFIG_ENABLE_HGRID);
648     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vgrid), CONFIG_ENABLE_VGRID);
649     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bar_mode), CONFIG_ENABLE_BAR_MODE);
650     gtk_combo_box_set_active (GTK_COMBO_BOX (gradient_orientation), CONFIG_GRADIENT_ORIENTATION);
651     gtk_combo_box_set_active (GTK_COMBO_BOX (style), CONFIG_STYLE);
652     gtk_spin_button_set_value (GTK_SPIN_BUTTON (num_colors), CONFIG_NUM_COLORS);
653     gtk_spin_button_set_value (GTK_SPIN_BUTTON (db_range), CONFIG_DB_RANGE);
654     gtk_color_button_set_color (GTK_COLOR_BUTTON (color_bg), &CONFIG_COLOR_BG);
655     gtk_color_button_set_color (GTK_COLOR_BUTTON (color_vgrid), &CONFIG_COLOR_VGRID);
656     gtk_color_button_set_color (GTK_COLOR_BUTTON (color_hgrid), &CONFIG_COLOR_HGRID);
657     gtk_color_button_set_color (GTK_COLOR_BUTTON (color_gradient_00), &(CONFIG_GRADIENT_COLORS[0]));
658     gtk_color_button_set_color (GTK_COLOR_BUTTON (color_gradient_01), &(CONFIG_GRADIENT_COLORS[1]));
659     gtk_color_button_set_color (GTK_COLOR_BUTTON (color_gradient_02), &(CONFIG_GRADIENT_COLORS[2]));
660     gtk_color_button_set_color (GTK_COLOR_BUTTON (color_gradient_03), &(CONFIG_GRADIENT_COLORS[3]));
661     gtk_color_button_set_color (GTK_COLOR_BUTTON (color_gradient_04), &(CONFIG_GRADIENT_COLORS[4]));
662     gtk_color_button_set_color (GTK_COLOR_BUTTON (color_gradient_05), &(CONFIG_GRADIENT_COLORS[5]));
663 
664     char text[100];
665     for (;;) {
666         int response = gtk_dialog_run (GTK_DIALOG (vumeter_properties));
667         if (response == GTK_RESPONSE_OK || response == GTK_RESPONSE_APPLY) {
668             gtk_color_button_get_color (GTK_COLOR_BUTTON (color_bg), &CONFIG_COLOR_BG);
669             gtk_color_button_get_color (GTK_COLOR_BUTTON (color_vgrid), &CONFIG_COLOR_VGRID);
670             gtk_color_button_get_color (GTK_COLOR_BUTTON (color_hgrid), &CONFIG_COLOR_HGRID);
671             gtk_color_button_get_color (GTK_COLOR_BUTTON (color_gradient_00), &CONFIG_GRADIENT_COLORS[0]);
672             gtk_color_button_get_color (GTK_COLOR_BUTTON (color_gradient_01), &CONFIG_GRADIENT_COLORS[1]);
673             gtk_color_button_get_color (GTK_COLOR_BUTTON (color_gradient_02), &CONFIG_GRADIENT_COLORS[2]);
674             gtk_color_button_get_color (GTK_COLOR_BUTTON (color_gradient_03), &CONFIG_GRADIENT_COLORS[3]);
675             gtk_color_button_get_color (GTK_COLOR_BUTTON (color_gradient_04), &CONFIG_GRADIENT_COLORS[4]);
676             gtk_color_button_get_color (GTK_COLOR_BUTTON (color_gradient_05), &CONFIG_GRADIENT_COLORS[5]);
677 
678             CONFIG_ENABLE_HGRID = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (hgrid));
679             CONFIG_ENABLE_VGRID = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vgrid));
680             CONFIG_ENABLE_BAR_MODE = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bar_mode));
681             CONFIG_DB_RANGE = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (db_range));
682             CONFIG_NUM_COLORS = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (num_colors));
683             switch (CONFIG_NUM_COLORS) {
684                 case 1:
685                     gtk_widget_hide (color_gradient_01);
686                     gtk_widget_hide (color_gradient_02);
687                     gtk_widget_hide (color_gradient_03);
688                     gtk_widget_hide (color_gradient_04);
689                     gtk_widget_hide (color_gradient_05);
690                     break;
691                 case 2:
692                     gtk_widget_show (color_gradient_01);
693                     gtk_widget_hide (color_gradient_02);
694                     gtk_widget_hide (color_gradient_03);
695                     gtk_widget_hide (color_gradient_04);
696                     gtk_widget_hide (color_gradient_05);
697                     break;
698                 case 3:
699                     gtk_widget_show (color_gradient_01);
700                     gtk_widget_show (color_gradient_02);
701                     gtk_widget_hide (color_gradient_03);
702                     gtk_widget_hide (color_gradient_04);
703                     gtk_widget_hide (color_gradient_05);
704                     break;
705                 case 4:
706                     gtk_widget_show (color_gradient_01);
707                     gtk_widget_show (color_gradient_02);
708                     gtk_widget_show (color_gradient_03);
709                     gtk_widget_hide (color_gradient_04);
710                     gtk_widget_hide (color_gradient_05);
711                     break;
712                 case 5:
713                     gtk_widget_show (color_gradient_01);
714                     gtk_widget_show (color_gradient_02);
715                     gtk_widget_show (color_gradient_03);
716                     gtk_widget_show (color_gradient_04);
717                     gtk_widget_hide (color_gradient_05);
718                     break;
719                 case 6:
720                     gtk_widget_show (color_gradient_01);
721                     gtk_widget_show (color_gradient_02);
722                     gtk_widget_show (color_gradient_03);
723                     gtk_widget_show (color_gradient_04);
724                     gtk_widget_show (color_gradient_05);
725                     break;
726             }
727             snprintf (text, sizeof (text), "%s", gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (gradient_orientation)));
728             if (strcmp (text, STR_GRADIENT_VERTICAL) == 0) {
729                 CONFIG_GRADIENT_ORIENTATION = 0;
730             }
731             else if (strcmp (text, STR_GRADIENT_HORIZONTAL) == 0) {
732                 CONFIG_GRADIENT_ORIENTATION = 1;
733             }
734             else {
735                 CONFIG_GRADIENT_ORIENTATION = -1;
736             }
737             snprintf (text, sizeof (text), "%s", gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (style)));
738             if (strcmp (text, "Bars") == 0) {
739                 CONFIG_STYLE = STYLE_BARS;
740             }
741             else if (strcmp (text, "Retro") == 0) {
742                 CONFIG_STYLE = STYLE_RETRO;
743             }
744             else {
745                 CONFIG_STYLE = STYLE_BARS;
746             }
747             save_config ();
748             deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0);
749         }
750         if (response == GTK_RESPONSE_APPLY) {
751             continue;
752         }
753         break;
754     }
755     gtk_widget_destroy (vumeter_properties);
756     return;
757 }
758 #pragma GCC diagnostic pop
759 
760 ///// vumeter vis
761 void
w_vumeter_destroy(ddb_gtkui_widget_t * w)762 w_vumeter_destroy (ddb_gtkui_widget_t *w) {
763     w_vumeter_t *s = (w_vumeter_t *)w;
764     deadbeef->vis_waveform_unlisten (w);
765     if (s->drawtimer) {
766         g_source_remove (s->drawtimer);
767         s->drawtimer = 0;
768     }
769     if (s->surf) {
770         cairo_surface_destroy (s->surf);
771         s->surf = NULL;
772     }
773     if (s->surf_png) {
774         cairo_surface_destroy (s->surf_png);
775         s->surf_png = NULL;
776     }
777     if (s->mutex) {
778         deadbeef->mutex_free (s->mutex);
779         s->mutex = 0;
780     }
781 }
782 
783 gboolean
w_vumeter_draw_cb(void * data)784 w_vumeter_draw_cb (void *data) {
785     w_vumeter_t *s = data;
786     gtk_widget_queue_draw (s->drawarea);
787     return TRUE;
788 }
789 
790 static void
vumeter_wavedata_listener(void * ctx,ddb_audio_data_t * data)791 vumeter_wavedata_listener (void *ctx, ddb_audio_data_t *data) {
792     w_vumeter_t *w = ctx;
793     deadbeef->mutex_lock (w->mutex);
794     w->channels = MIN (MAX_CHANNELS, data->fmt->channels);
795     int nsamples = data->nframes/w->channels;
796 
797     for (int c = 0; c < w->channels; c++) {
798         w->data[c] = 0;
799         for (int s = 0; s < nsamples + c; s++) {
800             w->data[c] += (data->data[ftoi (s * data->fmt->channels) + c] * data->data[ftoi (s * data->fmt->channels) + c]);
801         }
802         w->data[c] = sqrt (w->data[c]/nsamples);
803     }
804     deadbeef->mutex_unlock (w->mutex);
805 }
806 
807 static void
vumeter_draw_retro(w_vumeter_t * w,cairo_t * cr,int width,int height)808 vumeter_draw_retro (w_vumeter_t *w, cairo_t *cr, int width, int height)
809 {
810     if (!w->surf_png) {
811         char path[PATH_MAX];
812         if (snprintf (path, PATH_MAX, "%s/vumeter.png",
813           deadbeef->get_pixmap_dir()) < 0) {
814             return;
815         }
816         w->surf_png = cairo_image_surface_create_from_png (path);
817     }
818 
819     int m_radius = 130;
820     float start = M_PI * 3/4;
821     float value = 0;
822     int c = 0;
823     for (; c < w->channels; c++) {
824         value = MAX (value, w->bars[c]);
825     }
826     cairo_set_source_surface (cr, w->surf_png, 0, 0);
827     cairo_paint (cr);
828     cairo_set_source_rgb(cr, 0.3, 0.3, 0.3);
829     cairo_set_line_width(cr, 2);
830     int surf_width = cairo_image_surface_get_width (w->surf_png);
831     int surf_height = cairo_image_surface_get_height (w->surf_png);
832     cairo_move_to (cr, surf_width/2, surf_height - 23);
833     cairo_line_to (cr, surf_width/2 + m_radius * cos (value * M_PI / (CONFIG_DB_RANGE*2.5) - start), surf_height - 23 + m_radius * sin (value * M_PI / (CONFIG_DB_RANGE*2.5)-start));
834     cairo_stroke (cr);
835 }
836 
837 static void
vumeter_draw_bars(w_vumeter_t * w,cairo_t * cr,int width,int height)838 vumeter_draw_bars (w_vumeter_t *w, cairo_t *cr, int width, int height)
839 {
840     // start drawing
841     int bands = MIN (w->channels, MAX_CHANNELS);
842     bands = MAX (bands, 1);
843     if (!w->surf || cairo_image_surface_get_width (w->surf) != width || cairo_image_surface_get_height (w->surf) != height) {
844         printf("create surf\n");
845         if (w->surf) {
846             cairo_surface_destroy (w->surf);
847             w->surf = NULL;
848         }
849         w->surf = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
850     }
851     float base_s = (height / (float)CONFIG_DB_RANGE);
852 
853     cairo_surface_flush (w->surf);
854 
855     unsigned char *data = cairo_image_surface_get_data (w->surf);
856     if (!data) {
857         return;
858     }
859     int stride = cairo_image_surface_get_stride (w->surf);
860     memset (data, 0, height * stride);
861 
862     int barw = CLAMP (width / bands, 2, 1000);
863 
864     //draw background
865     _draw_background (data, width, height, CONFIG_COLOR_BG32);
866     // draw vertical grid
867     if (CONFIG_ENABLE_VGRID) {
868         int num_lines = MIN (width/barw, bands);
869         for (int i = 1; i < num_lines; i++) {
870             _draw_vline (data, stride, barw * i, 0, height-1, CONFIG_COLOR_VGRID32);
871         }
872     }
873 
874     int hgrid_num = CONFIG_DB_RANGE/10;
875     // draw horizontal grid
876     if (CONFIG_ENABLE_HGRID && height > 2*hgrid_num && width > 1) {
877         for (int i = 1; i < hgrid_num; i++) {
878             _draw_hline (data, stride, 0, ftoi (i/(float)hgrid_num * height), width-1, CONFIG_COLOR_HGRID32);
879         }
880     }
881 
882     for (gint i = 0; i < bands; i++)
883     {
884         int x = barw * i;
885         int y = height - ftoi (w->bars[i] * base_s);
886         if (y < 0) {
887             y = 0;
888         }
889         int bw = barw-1;
890         if (x + bw >= width) {
891             bw = width-x-1;
892         }
893         if (CONFIG_GRADIENT_ORIENTATION == 0) {
894             if (CONFIG_ENABLE_BAR_MODE == 0) {
895                 _draw_bar_gradient_v (w, data, stride, x+1, y, bw, height-y, height);
896             }
897             else {
898                 _draw_bar_gradient_bar_mode_v (w, data, stride, x+1, y, bw, height-y, height);
899             }
900         }
901         else {
902             if (CONFIG_ENABLE_BAR_MODE == 0) {
903                 _draw_bar_gradient_h (w, data, stride, x+1, y, bw, height-y, width);
904             }
905             else {
906                 _draw_bar_gradient_bar_mode_h (w, data, stride, x+1, y, bw, height-y, width);
907             }
908         }
909         y = height - w->peaks[i] * base_s;
910         if (y < height-1) {
911             if (CONFIG_GRADIENT_ORIENTATION == 0) {
912                 _draw_bar_gradient_v (w, data, stride, x + 1, y, bw, 1, height);
913             }
914             else {
915                 _draw_bar_gradient_h (w, data, stride, x + 1, y, bw, 1, width);
916             }
917         }
918     }
919 
920     cairo_surface_mark_dirty (w->surf);
921 
922     cairo_save (cr);
923     cairo_set_source_surface (cr, w->surf, 0, 0);
924     cairo_rectangle (cr, 0, 0, width, height);
925     cairo_fill (cr);
926     cairo_restore (cr);
927 }
928 
929 static gboolean
vumeter_draw(GtkWidget * widget,cairo_t * cr,gpointer user_data)930 vumeter_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data) {
931     w_vumeter_t *w = user_data;
932     GtkAllocation a;
933     gtk_widget_get_allocation (widget, &a);
934 
935     deadbeef->mutex_lock (w->mutex);
936     int width, height, bands;
937     bands = MIN (w->channels, MAX_CHANNELS);
938     bands = MAX (bands, 1);
939     width = a.width;
940     height = a.height;
941 
942     float bar_falloff = CONFIG_BAR_FALLOFF/1000.0 * CONFIG_REFRESH_INTERVAL;
943     float peak_falloff = CONFIG_PEAK_FALLOFF/1000.0 * CONFIG_REFRESH_INTERVAL;
944     int bar_delay = ftoi (CONFIG_BAR_DELAY/CONFIG_REFRESH_INTERVAL);
945     int peak_delay = ftoi (CONFIG_PEAK_DELAY/CONFIG_REFRESH_INTERVAL);
946 
947     if (deadbeef->get_output ()->state () == OUTPUT_STATE_PLAYING) {
948         for (int i = 0; i < bands; i++)
949         {
950             float x = CONFIG_DB_RANGE + (10.0 * log10f (w->data[i]));
951             // TODO: get rid of hardcoding
952             //x += CONFIG_DB_RANGE - 63;
953             //if (x > CONFIG_DB_RANGE) {
954             //    x = w->peaks[i];
955             //}
956             w->bars[i] = CLAMP (w->bars[i], 0, CONFIG_DB_RANGE);
957             w->peaks[i] = CLAMP (w->peaks[i], 0, CONFIG_DB_RANGE);
958 
959             if (CONFIG_BAR_FALLOFF != -1) {
960                 if (w->delay[i] < 0) {
961                     w->bars[i] -= bar_falloff;
962                 }
963                 else {
964                     w->delay[i]--;
965                 }
966             }
967             else {
968                 w->bars[i] = 0;
969             }
970             if (CONFIG_PEAK_FALLOFF != -1) {
971                 if (w->delay_peak[i] < 0) {
972                     w->peaks[i] -= peak_falloff;
973                 }
974                 else {
975                     w->delay_peak[i]--;
976                 }
977             }
978             else {
979                 w->peaks[i] = 0;
980             }
981 
982             if (x > w->bars[i])
983             {
984                 w->bars[i] = x;
985                 w->delay[i] = bar_delay;
986             }
987             if (x > w->peaks[i]) {
988                 w->peaks[i] = x;
989                 w->delay_peak[i] = peak_delay;
990             }
991             if (w->peaks[i] < w->bars[i]) {
992                 w->peaks[i] = w->bars[i];
993             }
994         }
995     }
996 
997     switch (CONFIG_STYLE) {
998         case STYLE_BARS:
999             vumeter_draw_bars (w, cr, width, height);
1000             break;
1001         case STYLE_RETRO:
1002             vumeter_draw_retro (w, cr, width, height);
1003             break;
1004         default:
1005             vumeter_draw_bars (w, cr, width, height);
1006             break;
1007     }
1008     deadbeef->mutex_unlock (w->mutex);
1009     return FALSE;
1010 }
1011 
1012 
1013 gboolean
vumeter_expose_event(GtkWidget * widget,GdkEventExpose * event,gpointer user_data)1014 vumeter_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) {
1015     cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (widget));
1016     gboolean res = vumeter_draw (widget, cr, user_data);
1017     cairo_destroy (cr);
1018     return res;
1019 }
1020 
1021 
1022 gboolean
vumeter_button_press_event(GtkWidget * widget,GdkEventButton * event,gpointer user_data)1023 vumeter_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
1024 {
1025     //w_vumeter_t *w = user_data;
1026     if (event->button == 3) {
1027       return TRUE;
1028     }
1029     return TRUE;
1030 }
1031 
1032 gboolean
vumeter_button_release_event(GtkWidget * widget,GdkEventButton * event,gpointer user_data)1033 vumeter_button_release_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
1034 {
1035     w_vumeter_t *w = user_data;
1036     if (event->button == 3) {
1037       gtk_menu_popup (GTK_MENU (w->popup), NULL, NULL, NULL, w->drawarea, 0, gtk_get_current_event_time ());
1038       return TRUE;
1039     }
1040     return TRUE;
1041 }
1042 
1043 static int
vumeter_message(ddb_gtkui_widget_t * widget,uint32_t id,uintptr_t ctx,uint32_t p1,uint32_t p2)1044 vumeter_message (ddb_gtkui_widget_t *widget, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2)
1045 {
1046     w_vumeter_t *w = (w_vumeter_t *)widget;
1047 
1048     switch (id) {
1049         case DB_EV_CONFIGCHANGED:
1050             on_config_changed (w, ctx);
1051             if (w->drawtimer) {
1052                 g_source_remove (w->drawtimer);
1053                 w->drawtimer = 0;
1054             }
1055             w->drawtimer = g_timeout_add (CONFIG_REFRESH_INTERVAL, w_vumeter_draw_cb, w);
1056             break;
1057     }
1058     return 0;
1059 }
1060 
1061 void
w_vumeter_init(ddb_gtkui_widget_t * w)1062 w_vumeter_init (ddb_gtkui_widget_t *w) {
1063     w_vumeter_t *s = (w_vumeter_t *)w;
1064     load_config ();
1065     deadbeef->mutex_lock (s->mutex);
1066     create_gradient_table (s, CONFIG_GRADIENT_COLORS, CONFIG_NUM_COLORS);
1067 
1068     if (s->drawtimer) {
1069         g_source_remove (s->drawtimer);
1070         s->drawtimer = 0;
1071     }
1072     s->drawtimer = g_timeout_add (CONFIG_REFRESH_INTERVAL, w_vumeter_draw_cb, w);
1073     deadbeef->mutex_unlock (s->mutex);
1074 }
1075 
1076 ddb_gtkui_widget_t *
w_vu_meter_create(void)1077 w_vu_meter_create (void) {
1078     w_vumeter_t *w = malloc (sizeof (w_vumeter_t));
1079     memset (w, 0, sizeof (w_vumeter_t));
1080 
1081     w->base.widget = gtk_event_box_new ();
1082     w->base.init = w_vumeter_init;
1083     w->base.destroy  = w_vumeter_destroy;
1084     w->base.message = vumeter_message;
1085     w->drawarea = gtk_drawing_area_new ();
1086     w->popup = gtk_menu_new ();
1087     w->popup_item = gtk_menu_item_new_with_mnemonic ("Configure");
1088     w->mutex = deadbeef->mutex_create ();
1089     gtk_widget_set_size_request (w->base.widget, 16, 16);
1090 
1091     gtk_container_add (GTK_CONTAINER (w->base.widget), w->drawarea);
1092     gtk_container_add (GTK_CONTAINER (w->popup), w->popup_item);
1093     gtk_widget_show (w->drawarea);
1094     gtk_widget_show (w->popup);
1095     gtk_widget_show (w->popup_item);
1096 
1097 #if !GTK_CHECK_VERSION(3,0,0)
1098     g_signal_connect_after ((gpointer) w->drawarea, "expose_event", G_CALLBACK (vumeter_expose_event), w);
1099 #else
1100     g_signal_connect_after ((gpointer) w->drawarea, "draw", G_CALLBACK (vumeter_draw), w);
1101 #endif
1102     g_signal_connect_after ((gpointer) w->base.widget, "button_press_event", G_CALLBACK (vumeter_button_press_event), w);
1103     g_signal_connect_after ((gpointer) w->base.widget, "button_release_event", G_CALLBACK (vumeter_button_release_event), w);
1104     g_signal_connect_after ((gpointer) w->popup_item, "activate", G_CALLBACK (on_button_config), w);
1105     gtkui_plugin->w_override_signals (w->base.widget, w);
1106     gtk_widget_set_events (w->base.widget, GDK_EXPOSURE_MASK
1107                                          | GDK_LEAVE_NOTIFY_MASK
1108                                          | GDK_BUTTON_PRESS_MASK
1109                                          | GDK_POINTER_MOTION_MASK
1110                                          | GDK_POINTER_MOTION_HINT_MASK);
1111     deadbeef->vis_waveform_listen (w, vumeter_wavedata_listener);
1112     return (ddb_gtkui_widget_t *)w;
1113 }
1114 
1115 int
vu_meter_connect(void)1116 vu_meter_connect (void)
1117 {
1118     gtkui_plugin = (ddb_gtkui_t *) deadbeef->plug_get_for_id (DDB_GTKUI_PLUGIN_ID);
1119     if (gtkui_plugin) {
1120         //trace("using '%s' plugin %d.%d\n", DDB_GTKUI_PLUGIN_ID, gtkui_plugin->gui.plugin.version_major, gtkui_plugin->gui.plugin.version_minor );
1121         if (gtkui_plugin->gui.plugin.version_major == 2) {
1122             //printf ("fb api2\n");
1123             // 0.6+, use the new widget API
1124             gtkui_plugin->w_reg_widget ("VU Meter", 0, w_vu_meter_create, "vu_meter", NULL);
1125             return 0;
1126         }
1127     }
1128     return -1;
1129 }
1130 
1131 int
vu_meter_start(void)1132 vu_meter_start (void)
1133 {
1134     load_config ();
1135     return 0;
1136 }
1137 
1138 int
vu_meter_stop(void)1139 vu_meter_stop (void)
1140 {
1141     save_config ();
1142     return 0;
1143 }
1144 
1145 int
vu_meter_startup(GtkWidget * cont)1146 vu_meter_startup (GtkWidget *cont)
1147 {
1148     return 0;
1149 }
1150 
1151 int
vu_meter_shutdown(GtkWidget * cont)1152 vu_meter_shutdown (GtkWidget *cont)
1153 {
1154     return 0;
1155 }
1156 int
vu_meter_disconnect(void)1157 vu_meter_disconnect (void)
1158 {
1159     gtkui_plugin = NULL;
1160     return 0;
1161 }
1162 
1163 static const char settings_dlg[] =
1164     "property \"Refresh interval (ms): \"           spinbtn[10,1000,1] "      CONFSTR_MS_REFRESH_INTERVAL         " 25 ;\n"
1165     "property \"Bar falloff (dB/s): \"           spinbtn[-1,1000,1] "      CONFSTR_MS_BAR_FALLOFF         " -1 ;\n"
1166     "property \"Bar delay (ms): \"                spinbtn[0,10000,100] "      CONFSTR_MS_BAR_DELAY           " 0 ;\n"
1167     "property \"Peak falloff (dB/s): \"          spinbtn[-1,1000,1] "      CONFSTR_MS_PEAK_FALLOFF        " 90 ;\n"
1168     "property \"Peak delay (ms): \"               spinbtn[0,10000,100] "      CONFSTR_MS_PEAK_DELAY          " 500 ;\n"
1169 ;
1170 
1171 static DB_misc_t plugin = {
1172     //DB_PLUGIN_SET_API_VERSION
1173     .plugin.type            = DB_PLUGIN_MISC,
1174     .plugin.api_vmajor      = 1,
1175     .plugin.api_vminor      = 5,
1176     .plugin.version_major   = 0,
1177     .plugin.version_minor   = 1,
1178 #if GTK_CHECK_VERSION(3,0,0)
1179     .plugin.id              = "vu_meter-gtk3",
1180 #else
1181     .plugin.id              = "vu_meter",
1182 #endif
1183     .plugin.name            = "VU Meter",
1184     .plugin.descr           = "VU Meter",
1185     .plugin.copyright       =
1186         "Copyright (C) 2013 Christian Boxdörfer <christian.boxdoerfer@posteo.de>\n"
1187         "\n"
1188         "This program is free software; you can redistribute it and/or\n"
1189         "modify it under the terms of the GNU General Public License\n"
1190         "as published by the Free Software Foundation; either version 2\n"
1191         "of the License, or (at your option) any later version.\n"
1192         "\n"
1193         "This program is distributed in the hope that it will be useful,\n"
1194         "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1195         "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
1196         "GNU General Public License for more details.\n"
1197         "\n"
1198         "You should have received a copy of the GNU General Public License\n"
1199         "along with this program; if not, write to the Free Software\n"
1200         "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n"
1201     ,
1202     .plugin.website         = "https://github.com/cboxdoerfer/ddb_vu_meter",
1203     .plugin.start           = vu_meter_start,
1204     .plugin.stop            = vu_meter_stop,
1205     .plugin.connect         = vu_meter_connect,
1206     .plugin.disconnect      = vu_meter_disconnect,
1207     .plugin.configdialog    = settings_dlg,
1208 };
1209 
1210 #if !GTK_CHECK_VERSION(3,0,0)
1211 DB_plugin_t *
ddb_vis_vu_meter_GTK2_load(DB_functions_t * ddb)1212 ddb_vis_vu_meter_GTK2_load (DB_functions_t *ddb) {
1213     deadbeef = ddb;
1214     return &plugin.plugin;
1215 }
1216 #else
1217 DB_plugin_t *
ddb_vis_vu_meter_GTK3_load(DB_functions_t * ddb)1218 ddb_vis_vu_meter_GTK3_load (DB_functions_t *ddb) {
1219     deadbeef = ddb;
1220     return &plugin.plugin;
1221 }
1222 #endif
1223