1 /*
2     Spectrogram plugin for the DeaDBeeF audio player
3 
4     Copyright (C) 2014 Christian Boxdörfer <christian.boxdoerfer@posteo.de>
5 
6     This program is free software; you can redistribute it and/or
7     modify it under the terms of the GNU General Public License
8     as published by the Free Software Foundation; either version 2
9     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
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
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20 
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26 #include <math.h>
27 #include <fcntl.h>
28 #include <gtk/gtk.h>
29 #include <fftw3.h>
30 
31 #include <deadbeef/deadbeef.h>
32 #include <deadbeef/gtkui_api.h>
33 
34 #include "fastftoi.h"
35 
36 #define GRADIENT_TABLE_SIZE 2048
37 #define FFT_SIZE 8192
38 #define MAX_HEIGHT 4096
39 
40 #define     CONFSTR_SP_LOG_SCALE              "spectrogram.log_scale"
41 #define     CONFSTR_SP_REFRESH_INTERVAL       "spectrogram.refresh_interval"
42 #define     CONFSTR_SP_DB_RANGE               "spectrogram.db_range"
43 #define     CONFSTR_SP_NUM_COLORS             "spectrogram.num_colors"
44 #define     CONFSTR_SP_COLOR_GRADIENT_00      "spectrogram.color.gradient_00"
45 #define     CONFSTR_SP_COLOR_GRADIENT_01      "spectrogram.color.gradient_01"
46 #define     CONFSTR_SP_COLOR_GRADIENT_02      "spectrogram.color.gradient_02"
47 #define     CONFSTR_SP_COLOR_GRADIENT_03      "spectrogram.color.gradient_03"
48 #define     CONFSTR_SP_COLOR_GRADIENT_04      "spectrogram.color.gradient_04"
49 #define     CONFSTR_SP_COLOR_GRADIENT_05      "spectrogram.color.gradient_05"
50 #define     CONFSTR_SP_COLOR_GRADIENT_06      "spectrogram.color.gradient_06"
51 
52 /* Global variables */
53 static DB_misc_t            plugin;
54 static DB_functions_t *     deadbeef = NULL;
55 static ddb_gtkui_t *        gtkui_plugin = NULL;
56 
57 typedef struct {
58     ddb_gtkui_widget_t base;
59     GtkWidget *drawarea;
60     GtkWidget *popup;
61     GtkWidget *popup_item;
62     guint drawtimer;
63     double *data;
64     double window[FFT_SIZE];
65     double *in;
66     //double *out_real;
67     fftw_complex *out_complex;
68     fftw_plan p_r2c;
69     //fftw_plan p_r2r;
70     uint32_t colors[GRADIENT_TABLE_SIZE];
71     double *samples;
72     int *log_index;
73     float samplerate;
74     int height;
75     int low_res_end;
76     int resized;
77     int buffered;
78     intptr_t mutex;
79     cairo_surface_t *surf;
80 } w_spectrogram_t;
81 
82 
83 static int CONFIG_LOG_SCALE = 1;
84 static int CONFIG_DB_RANGE = 70;
85 static int CONFIG_NUM_COLORS = 7;
86 static int CONFIG_REFRESH_INTERVAL = 25;
87 static GdkColor CONFIG_GRADIENT_COLORS[7];
88 
89 static void
save_config(void)90 save_config (void)
91 {
92     deadbeef->conf_set_int (CONFSTR_SP_LOG_SCALE, CONFIG_LOG_SCALE);
93     deadbeef->conf_set_int (CONFSTR_SP_DB_RANGE, CONFIG_DB_RANGE);
94     deadbeef->conf_set_int (CONFSTR_SP_NUM_COLORS, CONFIG_NUM_COLORS);
95     deadbeef->conf_set_int (CONFSTR_SP_REFRESH_INTERVAL, CONFIG_REFRESH_INTERVAL);
96     char color[100];
97     snprintf (color, sizeof (color), "%d %d %d", CONFIG_GRADIENT_COLORS[0].red, CONFIG_GRADIENT_COLORS[0].green, CONFIG_GRADIENT_COLORS[0].blue);
98     deadbeef->conf_set_str (CONFSTR_SP_COLOR_GRADIENT_00, color);
99     snprintf (color, sizeof (color), "%d %d %d", CONFIG_GRADIENT_COLORS[1].red, CONFIG_GRADIENT_COLORS[1].green, CONFIG_GRADIENT_COLORS[1].blue);
100     deadbeef->conf_set_str (CONFSTR_SP_COLOR_GRADIENT_01, color);
101     snprintf (color, sizeof (color), "%d %d %d", CONFIG_GRADIENT_COLORS[2].red, CONFIG_GRADIENT_COLORS[2].green, CONFIG_GRADIENT_COLORS[2].blue);
102     deadbeef->conf_set_str (CONFSTR_SP_COLOR_GRADIENT_02, color);
103     snprintf (color, sizeof (color), "%d %d %d", CONFIG_GRADIENT_COLORS[3].red, CONFIG_GRADIENT_COLORS[3].green, CONFIG_GRADIENT_COLORS[3].blue);
104     deadbeef->conf_set_str (CONFSTR_SP_COLOR_GRADIENT_03, color);
105     snprintf (color, sizeof (color), "%d %d %d", CONFIG_GRADIENT_COLORS[4].red, CONFIG_GRADIENT_COLORS[4].green, CONFIG_GRADIENT_COLORS[4].blue);
106     deadbeef->conf_set_str (CONFSTR_SP_COLOR_GRADIENT_04, color);
107     snprintf (color, sizeof (color), "%d %d %d", CONFIG_GRADIENT_COLORS[5].red, CONFIG_GRADIENT_COLORS[5].green, CONFIG_GRADIENT_COLORS[5].blue);
108     deadbeef->conf_set_str (CONFSTR_SP_COLOR_GRADIENT_05, color);
109     snprintf (color, sizeof (color), "%d %d %d", CONFIG_GRADIENT_COLORS[6].red, CONFIG_GRADIENT_COLORS[6].green, CONFIG_GRADIENT_COLORS[6].blue);
110     deadbeef->conf_set_str (CONFSTR_SP_COLOR_GRADIENT_06, color);
111 }
112 
113 static void
load_config(void)114 load_config (void)
115 {
116     deadbeef->conf_lock ();
117     CONFIG_LOG_SCALE = deadbeef->conf_get_int (CONFSTR_SP_LOG_SCALE,                1);
118     CONFIG_DB_RANGE = deadbeef->conf_get_int (CONFSTR_SP_DB_RANGE,                 70);
119     CONFIG_NUM_COLORS = deadbeef->conf_get_int (CONFSTR_SP_NUM_COLORS,              7);
120     CONFIG_REFRESH_INTERVAL = deadbeef->conf_get_int (CONFSTR_SP_REFRESH_INTERVAL, 25);
121     const char *color;
122     color = deadbeef->conf_get_str_fast (CONFSTR_SP_COLOR_GRADIENT_00,        "65535 0 0");
123     sscanf (color, "%hd %hd %hd", &(CONFIG_GRADIENT_COLORS[0].red), &(CONFIG_GRADIENT_COLORS[0].green), &(CONFIG_GRADIENT_COLORS[0].blue));
124     color = deadbeef->conf_get_str_fast (CONFSTR_SP_COLOR_GRADIENT_01,      "65535 32896 0");
125     sscanf (color, "%hd %hd %hd", &(CONFIG_GRADIENT_COLORS[1].red), &(CONFIG_GRADIENT_COLORS[1].green), &(CONFIG_GRADIENT_COLORS[1].blue));
126     color = deadbeef->conf_get_str_fast (CONFSTR_SP_COLOR_GRADIENT_02,      "65535 65535 0");
127     sscanf (color, "%hd %hd %hd", &(CONFIG_GRADIENT_COLORS[2].red), &(CONFIG_GRADIENT_COLORS[2].green), &(CONFIG_GRADIENT_COLORS[2].blue));
128     color = deadbeef->conf_get_str_fast (CONFSTR_SP_COLOR_GRADIENT_03,    "32896 65535 30840");
129     sscanf (color, "%hd %hd %hd", &(CONFIG_GRADIENT_COLORS[3].red), &(CONFIG_GRADIENT_COLORS[3].green), &(CONFIG_GRADIENT_COLORS[3].blue));
130     color = deadbeef->conf_get_str_fast (CONFSTR_SP_COLOR_GRADIENT_04,      "0 38036 41120");
131     sscanf (color, "%hd %hd %hd", &(CONFIG_GRADIENT_COLORS[4].red), &(CONFIG_GRADIENT_COLORS[4].green), &(CONFIG_GRADIENT_COLORS[4].blue));
132     color = deadbeef->conf_get_str_fast (CONFSTR_SP_COLOR_GRADIENT_05,       "0 8224 25700");
133     sscanf (color, "%hd %hd %hd", &(CONFIG_GRADIENT_COLORS[5].red), &(CONFIG_GRADIENT_COLORS[5].green), &(CONFIG_GRADIENT_COLORS[5].blue));
134     color = deadbeef->conf_get_str_fast (CONFSTR_SP_COLOR_GRADIENT_06,       "0 0 0");
135     sscanf (color, "%hd %hd %hd", &(CONFIG_GRADIENT_COLORS[6].red), &(CONFIG_GRADIENT_COLORS[6].green), &(CONFIG_GRADIENT_COLORS[6].blue));
136     deadbeef->conf_unlock ();
137 }
138 
139 void
do_fft(w_spectrogram_t * w)140 do_fft (w_spectrogram_t *w)
141 {
142     if (!w->samples || w->buffered < FFT_SIZE/2) {
143         return;
144     }
145     deadbeef->mutex_lock (w->mutex);
146     double real,imag;
147 
148     for (int i = 0; i < FFT_SIZE; i++) {
149         w->in[i] = w->samples[i] * w->window[i];
150     }
151     deadbeef->mutex_unlock (w->mutex);
152     //fftw_execute (w->p_r2r);
153     fftw_execute (w->p_r2c);
154     for (int i = 0; i < FFT_SIZE/2; i++)
155     {
156         real = w->out_complex[i][0];
157         imag = w->out_complex[i][1];
158         w->data[i] = (real*real + imag*imag);
159         //w->data[i] = w->out_real[i]*w->out_real[i] + w->out_real[FFT_SIZE/2+i]*w->out_real[FFT_SIZE/2+i];
160     }
161 }
162 
163 static inline void
_draw_point(uint8_t * data,int stride,int x0,int y0,uint32_t color)164 _draw_point (uint8_t *data, int stride, int x0, int y0, uint32_t color) {
165     uint32_t *ptr = (uint32_t*)&data[y0*stride+x0*4];
166     *ptr = color;
167 }
168 
169 /* based on Delphi function by Witold J.Janik */
170 void
create_gradient_table(gpointer user_data,GdkColor * colors,int num_colors)171 create_gradient_table (gpointer user_data, GdkColor *colors, int num_colors)
172 {
173     w_spectrogram_t *w = user_data;
174 
175     num_colors -= 1;
176 
177     for (int i = 0; i < GRADIENT_TABLE_SIZE; i++) {
178         double position = (double)i/GRADIENT_TABLE_SIZE;
179         /* if position > 1 then we have repetition of colors it maybe useful    */
180         if (position > 1.0) {
181             if (position - ftoi (position) == 0.0) {
182                 position = 1.0;
183             }
184             else {
185                 position = position - ftoi (position);
186             }
187         }
188 
189         double m= num_colors * position;
190         int n=(int)m; // integer of m
191         double f=m-n;  // fraction of m
192 
193         w->colors[i] = 0xFF000000;
194         float scale = 255/65535.f;
195         if (num_colors == 0) {
196             w->colors[i] = ((uint32_t)(colors[0].red*scale) & 0xFF) << 16 |
197                 ((uint32_t)(colors[0].green*scale) & 0xFF) << 8 |
198                 ((uint32_t)(colors[0].blue*scale) & 0xFF) << 0;
199         }
200         else if (n < num_colors) {
201             w->colors[i] = ((uint32_t)((colors[n].red*scale) + f * ((colors[n+1].red*scale)-(colors[n].red*scale))) & 0xFF) << 16 |
202                 ((uint32_t)((colors[n].green*scale) + f * ((colors[n+1].green*scale)-(colors[n].green*scale))) & 0xFF) << 8 |
203                 ((uint32_t)((colors[n].blue*scale) + f * ((colors[n+1].blue*scale)-(colors[n].blue*scale))) & 0xFF) << 0;
204         }
205         else if (n == num_colors) {
206             w->colors[i] = ((uint32_t)(colors[n].red*scale) & 0xFF) << 16 |
207                 ((uint32_t)(colors[n].green*scale) & 0xFF) << 8 |
208                 ((uint32_t)(colors[n].blue*scale) & 0xFF) << 0;
209         }
210         else {
211             w->colors[i] = 0xFFFFFFFF;
212         }
213     }
214 }
215 
216 static int
on_config_changed(gpointer user_data,uintptr_t ctx)217 on_config_changed (gpointer user_data, uintptr_t ctx)
218 {
219     create_gradient_table (user_data, CONFIG_GRADIENT_COLORS, CONFIG_NUM_COLORS);
220     load_config ();
221     return 0;
222 }
223 
224 #if !GTK_CHECK_VERSION(2,12,0)
225 #define gtk_widget_get_window(widget) ((widget)->window)
226 #define gtk_dialog_get_content_area(dialog) (dialog->vbox)
227 #define gtk_dialog_get_action_area(dialog) (dialog->action_area)
228 #endif
229 
230 #if !GTK_CHECK_VERSION(2,18,0)
231 void
gtk_widget_get_allocation(GtkWidget * widget,GtkAllocation * allocation)232 gtk_widget_get_allocation (GtkWidget *widget, GtkAllocation *allocation) {
233     (allocation)->x = widget->allocation.x;
234     (allocation)->y = widget->allocation.y;
235     (allocation)->width = widget->allocation.width;
236     (allocation)->height = widget->allocation.height;
237 }
238 #define gtk_widget_set_can_default(widget, candefault) {if (candefault) GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_DEFAULT); else GTK_WIDGET_UNSET_FLAGS(widget, GTK_CAN_DEFAULT);}
239 #endif
240 
241 #pragma GCC diagnostic push
242 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
243 static void
on_button_config(GtkMenuItem * menuitem,gpointer user_data)244 on_button_config (GtkMenuItem *menuitem, gpointer user_data)
245 {
246     GtkWidget *spectrogram_properties;
247     GtkWidget *config_dialog;
248     GtkWidget *vbox01;
249     GtkWidget *vbox02;
250     GtkWidget *hbox01;
251     //GtkWidget *hbox02;
252     GtkWidget *hbox03;
253     GtkWidget *color_label;
254     GtkWidget *color_frame;
255     GtkWidget *color_gradient_00;
256     GtkWidget *color_gradient_01;
257     GtkWidget *color_gradient_02;
258     GtkWidget *color_gradient_03;
259     GtkWidget *color_gradient_04;
260     GtkWidget *color_gradient_05;
261     GtkWidget *color_gradient_06;
262     GtkWidget *num_colors_label;
263     GtkWidget *num_colors;
264     GtkWidget *log_scale;
265     GtkWidget *db_range_label0;
266     GtkWidget *db_range;
267     GtkWidget *dialog_action_area13;
268     GtkWidget *applybutton1;
269     GtkWidget *cancelbutton1;
270     GtkWidget *okbutton1;
271     spectrogram_properties = gtk_dialog_new ();
272     gtk_window_set_title (GTK_WINDOW (spectrogram_properties), "Spectrogram Properties");
273     gtk_window_set_type_hint (GTK_WINDOW (spectrogram_properties), GDK_WINDOW_TYPE_HINT_DIALOG);
274 
275     config_dialog = gtk_dialog_get_content_area (GTK_DIALOG (spectrogram_properties));
276     gtk_widget_show (config_dialog);
277 
278     hbox01 = gtk_hbox_new (FALSE, 8);
279     gtk_widget_show (hbox01);
280     gtk_box_pack_start (GTK_BOX (config_dialog), hbox01, FALSE, FALSE, 0);
281     gtk_container_set_border_width (GTK_CONTAINER (hbox01), 12);
282 
283     color_label = gtk_label_new (NULL);
284     gtk_label_set_markup (GTK_LABEL (color_label),"<b>Colors</b>");
285     gtk_widget_show (color_label);
286 
287     color_frame = gtk_frame_new ("Colors");
288     gtk_frame_set_label_widget ((GtkFrame *)color_frame, color_label);
289     gtk_frame_set_shadow_type ((GtkFrame *)color_frame, GTK_SHADOW_IN);
290     gtk_widget_show (color_frame);
291     gtk_box_pack_start (GTK_BOX (hbox01), color_frame, TRUE, FALSE, 0);
292 
293     vbox02 = gtk_vbox_new (FALSE, 8);
294     gtk_widget_show (vbox02);
295     gtk_container_add (GTK_CONTAINER (color_frame), vbox02);
296     gtk_container_set_border_width (GTK_CONTAINER (vbox02), 12);
297 
298     num_colors_label = gtk_label_new (NULL);
299     gtk_label_set_markup (GTK_LABEL (num_colors_label),"Number of colors:");
300     gtk_widget_show (num_colors_label);
301     gtk_box_pack_start (GTK_BOX (vbox02), num_colors_label, FALSE, FALSE, 0);
302 
303     num_colors = gtk_spin_button_new_with_range (1,7,1);
304     gtk_widget_show (num_colors);
305     gtk_box_pack_start (GTK_BOX (vbox02), num_colors, FALSE, FALSE, 0);
306 
307     color_gradient_00 = gtk_color_button_new ();
308     gtk_color_button_set_use_alpha ((GtkColorButton *)color_gradient_00, TRUE);
309     gtk_widget_show (color_gradient_00);
310     gtk_box_pack_start (GTK_BOX (vbox02), color_gradient_00, TRUE, FALSE, 0);
311     gtk_widget_set_size_request (color_gradient_00, -1, 30);
312 
313     color_gradient_01 = gtk_color_button_new ();
314     gtk_color_button_set_use_alpha ((GtkColorButton *)color_gradient_01, TRUE);
315     gtk_widget_show (color_gradient_01);
316     gtk_box_pack_start (GTK_BOX (vbox02), color_gradient_01, TRUE, FALSE, 0);
317     gtk_widget_set_size_request (color_gradient_01, -1, 30);
318 
319     color_gradient_02 = gtk_color_button_new ();
320     gtk_color_button_set_use_alpha ((GtkColorButton *)color_gradient_02, TRUE);
321     gtk_widget_show (color_gradient_02);
322     gtk_box_pack_start (GTK_BOX (vbox02), color_gradient_02, TRUE, FALSE, 0);
323     gtk_widget_set_size_request (color_gradient_02, -1, 30);
324 
325     color_gradient_03 = gtk_color_button_new ();
326     gtk_color_button_set_use_alpha ((GtkColorButton *)color_gradient_03, TRUE);
327     gtk_widget_show (color_gradient_03);
328     gtk_box_pack_start (GTK_BOX (vbox02), color_gradient_03, TRUE, FALSE, 0);
329     gtk_widget_set_size_request (color_gradient_03, -1, 30);
330 
331     color_gradient_04 = gtk_color_button_new ();
332     gtk_color_button_set_use_alpha ((GtkColorButton *)color_gradient_04, TRUE);
333     gtk_widget_show (color_gradient_04);
334     gtk_box_pack_start (GTK_BOX (vbox02), color_gradient_04, TRUE, FALSE, 0);
335     gtk_widget_set_size_request (color_gradient_04, -1, 30);
336 
337     color_gradient_05 = gtk_color_button_new ();
338     gtk_color_button_set_use_alpha ((GtkColorButton *)color_gradient_05, TRUE);
339     gtk_widget_show (color_gradient_05);
340     gtk_box_pack_start (GTK_BOX (vbox02), color_gradient_05, TRUE, FALSE, 0);
341     gtk_widget_set_size_request (color_gradient_05, -1, 30);
342 
343     color_gradient_06 = gtk_color_button_new ();
344     gtk_color_button_set_use_alpha ((GtkColorButton *)color_gradient_06, TRUE);
345     gtk_widget_show (color_gradient_06);
346     gtk_box_pack_start (GTK_BOX (vbox02), color_gradient_06, TRUE, FALSE, 0);
347     gtk_widget_set_size_request (color_gradient_06, -1, 30);
348 
349     vbox01 = gtk_vbox_new (FALSE, 8);
350     gtk_widget_show (vbox01);
351     gtk_box_pack_start (GTK_BOX (hbox01), vbox01, FALSE, FALSE, 0);
352     gtk_container_set_border_width (GTK_CONTAINER (vbox01), 12);
353 
354     //hbox02 = gtk_hbox_new (FALSE, 8);
355     //gtk_widget_show (hbox02);
356     //gtk_box_pack_start (GTK_BOX (vbox01), hbox02, FALSE, FALSE, 0);
357     //gtk_container_set_border_width (GTK_CONTAINER (hbox01), 12);
358 
359     hbox03 = gtk_hbox_new (FALSE, 8);
360     gtk_widget_show (hbox03);
361     gtk_box_pack_start (GTK_BOX (vbox01), hbox03, FALSE, FALSE, 0);
362 
363     db_range_label0 = gtk_label_new (NULL);
364     gtk_label_set_markup (GTK_LABEL (db_range_label0),"dB range:");
365     gtk_widget_show (db_range_label0);
366     gtk_box_pack_start (GTK_BOX (hbox03), db_range_label0, FALSE, TRUE, 0);
367 
368     db_range = gtk_spin_button_new_with_range (50,120,10);
369     gtk_widget_show (db_range);
370     gtk_box_pack_start (GTK_BOX (hbox03), db_range, TRUE, TRUE, 0);
371 
372     log_scale = gtk_check_button_new_with_label ("Log scale");
373     gtk_widget_show (log_scale);
374     gtk_box_pack_start (GTK_BOX (vbox01), log_scale, FALSE, FALSE, 0);
375 
376     dialog_action_area13 = gtk_dialog_get_action_area (GTK_DIALOG (spectrogram_properties));
377     gtk_widget_show (dialog_action_area13);
378     gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area13), GTK_BUTTONBOX_END);
379 
380     applybutton1 = gtk_button_new_from_stock ("gtk-apply");
381     gtk_widget_show (applybutton1);
382     gtk_dialog_add_action_widget (GTK_DIALOG (spectrogram_properties), applybutton1, GTK_RESPONSE_APPLY);
383     gtk_widget_set_can_default (applybutton1, TRUE);
384 
385     cancelbutton1 = gtk_button_new_from_stock ("gtk-cancel");
386     gtk_widget_show (cancelbutton1);
387     gtk_dialog_add_action_widget (GTK_DIALOG (spectrogram_properties), cancelbutton1, GTK_RESPONSE_CANCEL);
388     gtk_widget_set_can_default (cancelbutton1, TRUE);
389 
390     okbutton1 = gtk_button_new_from_stock ("gtk-ok");
391     gtk_widget_show (okbutton1);
392     gtk_dialog_add_action_widget (GTK_DIALOG (spectrogram_properties), okbutton1, GTK_RESPONSE_OK);
393     gtk_widget_set_can_default (okbutton1, TRUE);
394 
395     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (log_scale), CONFIG_LOG_SCALE);
396     gtk_spin_button_set_value (GTK_SPIN_BUTTON (num_colors), CONFIG_NUM_COLORS);
397     gtk_spin_button_set_value (GTK_SPIN_BUTTON (db_range), CONFIG_DB_RANGE);
398     gtk_color_button_set_color (GTK_COLOR_BUTTON (color_gradient_00), &(CONFIG_GRADIENT_COLORS[0]));
399     gtk_color_button_set_color (GTK_COLOR_BUTTON (color_gradient_01), &(CONFIG_GRADIENT_COLORS[1]));
400     gtk_color_button_set_color (GTK_COLOR_BUTTON (color_gradient_02), &(CONFIG_GRADIENT_COLORS[2]));
401     gtk_color_button_set_color (GTK_COLOR_BUTTON (color_gradient_03), &(CONFIG_GRADIENT_COLORS[3]));
402     gtk_color_button_set_color (GTK_COLOR_BUTTON (color_gradient_04), &(CONFIG_GRADIENT_COLORS[4]));
403     gtk_color_button_set_color (GTK_COLOR_BUTTON (color_gradient_05), &(CONFIG_GRADIENT_COLORS[5]));
404     gtk_color_button_set_color (GTK_COLOR_BUTTON (color_gradient_06), &(CONFIG_GRADIENT_COLORS[6]));
405 
406     switch (CONFIG_NUM_COLORS) {
407         case 1:
408             gtk_widget_hide (color_gradient_01);
409             gtk_widget_hide (color_gradient_02);
410             gtk_widget_hide (color_gradient_03);
411             gtk_widget_hide (color_gradient_04);
412             gtk_widget_hide (color_gradient_05);
413             gtk_widget_hide (color_gradient_06);
414             break;
415         case 2:
416             gtk_widget_show (color_gradient_01);
417             gtk_widget_hide (color_gradient_02);
418             gtk_widget_hide (color_gradient_03);
419             gtk_widget_hide (color_gradient_04);
420             gtk_widget_hide (color_gradient_05);
421             gtk_widget_hide (color_gradient_06);
422             break;
423         case 3:
424             gtk_widget_show (color_gradient_01);
425             gtk_widget_show (color_gradient_02);
426             gtk_widget_hide (color_gradient_03);
427             gtk_widget_hide (color_gradient_04);
428             gtk_widget_hide (color_gradient_05);
429             gtk_widget_hide (color_gradient_06);
430             break;
431         case 4:
432             gtk_widget_show (color_gradient_01);
433             gtk_widget_show (color_gradient_02);
434             gtk_widget_show (color_gradient_03);
435             gtk_widget_hide (color_gradient_04);
436             gtk_widget_hide (color_gradient_05);
437             gtk_widget_hide (color_gradient_06);
438             break;
439         case 5:
440             gtk_widget_show (color_gradient_01);
441             gtk_widget_show (color_gradient_02);
442             gtk_widget_show (color_gradient_03);
443             gtk_widget_show (color_gradient_04);
444             gtk_widget_hide (color_gradient_05);
445             gtk_widget_hide (color_gradient_06);
446             break;
447         case 6:
448             gtk_widget_show (color_gradient_01);
449             gtk_widget_show (color_gradient_02);
450             gtk_widget_show (color_gradient_03);
451             gtk_widget_show (color_gradient_04);
452             gtk_widget_show (color_gradient_05);
453             gtk_widget_hide (color_gradient_06);
454             break;
455         case 7:
456             gtk_widget_show (color_gradient_01);
457             gtk_widget_show (color_gradient_02);
458             gtk_widget_show (color_gradient_03);
459             gtk_widget_show (color_gradient_04);
460             gtk_widget_show (color_gradient_05);
461             gtk_widget_show (color_gradient_06);
462             break;
463     }
464 
465     for (;;) {
466         int response = gtk_dialog_run (GTK_DIALOG (spectrogram_properties));
467         if (response == GTK_RESPONSE_OK || response == GTK_RESPONSE_APPLY) {
468             gtk_color_button_get_color (GTK_COLOR_BUTTON (color_gradient_00), &CONFIG_GRADIENT_COLORS[0]);
469             gtk_color_button_get_color (GTK_COLOR_BUTTON (color_gradient_01), &CONFIG_GRADIENT_COLORS[1]);
470             gtk_color_button_get_color (GTK_COLOR_BUTTON (color_gradient_02), &CONFIG_GRADIENT_COLORS[2]);
471             gtk_color_button_get_color (GTK_COLOR_BUTTON (color_gradient_03), &CONFIG_GRADIENT_COLORS[3]);
472             gtk_color_button_get_color (GTK_COLOR_BUTTON (color_gradient_04), &CONFIG_GRADIENT_COLORS[4]);
473             gtk_color_button_get_color (GTK_COLOR_BUTTON (color_gradient_05), &CONFIG_GRADIENT_COLORS[5]);
474             gtk_color_button_get_color (GTK_COLOR_BUTTON (color_gradient_06), &CONFIG_GRADIENT_COLORS[6]);
475 
476             CONFIG_LOG_SCALE = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (log_scale));
477             CONFIG_DB_RANGE = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (db_range));
478             CONFIG_NUM_COLORS = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (num_colors));
479             switch (CONFIG_NUM_COLORS) {
480                 case 1:
481                     gtk_widget_hide (color_gradient_01);
482                     gtk_widget_hide (color_gradient_02);
483                     gtk_widget_hide (color_gradient_03);
484                     gtk_widget_hide (color_gradient_04);
485                     gtk_widget_hide (color_gradient_05);
486                     gtk_widget_hide (color_gradient_06);
487                     break;
488                 case 2:
489                     gtk_widget_show (color_gradient_01);
490                     gtk_widget_hide (color_gradient_02);
491                     gtk_widget_hide (color_gradient_03);
492                     gtk_widget_hide (color_gradient_04);
493                     gtk_widget_hide (color_gradient_05);
494                     gtk_widget_hide (color_gradient_06);
495                     break;
496                 case 3:
497                     gtk_widget_show (color_gradient_01);
498                     gtk_widget_show (color_gradient_02);
499                     gtk_widget_hide (color_gradient_03);
500                     gtk_widget_hide (color_gradient_04);
501                     gtk_widget_hide (color_gradient_05);
502                     gtk_widget_hide (color_gradient_06);
503                     break;
504                 case 4:
505                     gtk_widget_show (color_gradient_01);
506                     gtk_widget_show (color_gradient_02);
507                     gtk_widget_show (color_gradient_03);
508                     gtk_widget_hide (color_gradient_04);
509                     gtk_widget_hide (color_gradient_05);
510                     gtk_widget_hide (color_gradient_06);
511                     break;
512                 case 5:
513                     gtk_widget_show (color_gradient_01);
514                     gtk_widget_show (color_gradient_02);
515                     gtk_widget_show (color_gradient_03);
516                     gtk_widget_show (color_gradient_04);
517                     gtk_widget_hide (color_gradient_05);
518                     gtk_widget_hide (color_gradient_06);
519                     break;
520                 case 6:
521                     gtk_widget_show (color_gradient_01);
522                     gtk_widget_show (color_gradient_02);
523                     gtk_widget_show (color_gradient_03);
524                     gtk_widget_show (color_gradient_04);
525                     gtk_widget_show (color_gradient_05);
526                     gtk_widget_hide (color_gradient_06);
527                     break;
528                 case 7:
529                     gtk_widget_show (color_gradient_01);
530                     gtk_widget_show (color_gradient_02);
531                     gtk_widget_show (color_gradient_03);
532                     gtk_widget_show (color_gradient_04);
533                     gtk_widget_show (color_gradient_05);
534                     gtk_widget_show (color_gradient_06);
535                     break;
536             }
537             save_config ();
538             deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0);
539         }
540         if (response == GTK_RESPONSE_APPLY) {
541             continue;
542         }
543         break;
544     }
545     gtk_widget_destroy (spectrogram_properties);
546     return;
547 }
548 #pragma GCC diagnostic pop
549 
550 void
w_spectrogram_destroy(ddb_gtkui_widget_t * w)551 w_spectrogram_destroy (ddb_gtkui_widget_t *w) {
552     w_spectrogram_t *s = (w_spectrogram_t *)w;
553     deadbeef->vis_waveform_unlisten (w);
554     if (s->data) {
555         free (s->data);
556         s->data = NULL;
557     }
558     if (s->samples) {
559         free (s->samples);
560         s->samples = NULL;
561     }
562     if (s->log_index) {
563         free (s->log_index);
564         s->log_index = NULL;
565     }
566     //if (s->p_r2r) {
567     //    fftw_destroy_plan (s->p_r2r);
568     //}
569     if (s->p_r2c) {
570         fftw_destroy_plan (s->p_r2c);
571     }
572     if (s->in) {
573         fftw_free (s->in);
574         s->in = NULL;
575     }
576     //if (s->out_real) {
577     //    fftw_free (s->out_real);
578     //    s->out_real = NULL;
579     //}
580     if (s->out_complex) {
581         fftw_free (s->out_complex);
582         s->out_complex = NULL;
583     }
584     if (s->drawtimer) {
585         g_source_remove (s->drawtimer);
586         s->drawtimer = 0;
587     }
588     if (s->surf) {
589         cairo_surface_destroy (s->surf);
590         s->surf = NULL;
591     }
592     if (s->mutex) {
593         deadbeef->mutex_free (s->mutex);
594         s->mutex = 0;
595     }
596 }
597 
598 gboolean
w_spectrogram_draw_cb(void * data)599 w_spectrogram_draw_cb (void *data) {
600     w_spectrogram_t *s = data;
601     gtk_widget_queue_draw (s->drawarea);
602     return TRUE;
603 }
604 
605 static void
spectrogram_wavedata_listener(void * ctx,ddb_audio_data_t * data)606 spectrogram_wavedata_listener (void *ctx, ddb_audio_data_t *data) {
607     w_spectrogram_t *w = ctx;
608     if (!w->samples) {
609         return;
610     }
611     deadbeef->mutex_lock (w->mutex);
612     w->samplerate = (float)data->fmt->samplerate;
613     int nsamples = data->nframes;
614     int sz = MIN (FFT_SIZE, nsamples);
615     int n = FFT_SIZE - sz;
616     memmove (w->samples, w->samples + sz, (FFT_SIZE - sz)*sizeof (double));
617 
618     float pos = 0;
619     for (int i = 0; i < sz && pos < nsamples; i++, pos ++) {
620         w->samples[n+i] = -1000.0;
621         for (int j = 0; j < data->fmt->channels; j++) {
622             w->samples[n + i] = MAX (w->samples[n + i], data->data[ftoi (pos * data->fmt->channels) + j]);
623         }
624     }
625     deadbeef->mutex_unlock (w->mutex);
626     if (w->buffered < FFT_SIZE) {
627         w->buffered += sz;
628     }
629 }
630 
631 static inline float
spectrogram_get_value(gpointer user_data,int start,int end)632 spectrogram_get_value (gpointer user_data, int start, int end)
633 {
634     w_spectrogram_t *w = user_data;
635     if (start >= end) {
636         return w->data[end];
637     }
638     float value = 0.0;
639     for (int i = start; i < end; i++) {
640         value = MAX (w->data[i],value);
641     }
642     return value;
643 }
644 
645 static inline float
linear_interpolate(float y1,float y2,float mu)646 linear_interpolate (float y1, float y2, float mu)
647 {
648        return (y1 * (1 - mu) + y2 * mu);
649 }
650 
651 static gboolean
spectrogram_draw(GtkWidget * widget,cairo_t * cr,gpointer user_data)652 spectrogram_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data) {
653     w_spectrogram_t *w = user_data;
654     GtkAllocation a;
655     gtk_widget_get_allocation (widget, &a);
656     if (!w->samples || a.height < 1) {
657         return FALSE;
658     }
659 
660     int width, height;
661     width = a.width;
662     height = a.height;
663     int ratio = ftoi (FFT_SIZE/(a.height*2));
664     ratio = CLAMP (ratio,0,1023);
665 
666     if (deadbeef->get_output ()->state () == OUTPUT_STATE_PLAYING) {
667         do_fft (w);
668         float log_scale = (log2f(w->samplerate/2)-log2f(25.))/(a.height);
669         float freq_res = w->samplerate / FFT_SIZE;
670 
671         if (a.height != w->height) {
672             w->height = MIN (a.height, MAX_HEIGHT);
673             for (int i = 0; i < w->height; i++) {
674                 w->log_index[i] = ftoi (powf(2.,((float)i) * log_scale + log2f(25.)) / freq_res);
675                 if (i > 0 && w->log_index[i-1] == w->log_index [i]) {
676                     w->low_res_end = i;
677                 }
678             }
679         }
680     }
681 
682     // start drawing
683     if (!w->surf || cairo_image_surface_get_width (w->surf) != a.width || cairo_image_surface_get_height (w->surf) != a.height) {
684         if (w->surf) {
685             cairo_surface_destroy (w->surf);
686             w->surf = NULL;
687         }
688         w->surf = cairo_image_surface_create (CAIRO_FORMAT_RGB24, a.width, a.height);
689     }
690 
691     cairo_surface_flush (w->surf);
692 
693     unsigned char *data = cairo_image_surface_get_data (w->surf);
694     if (!data) {
695         return FALSE;
696     }
697     int stride = cairo_image_surface_get_stride (w->surf);
698 
699     if (deadbeef->get_output ()->state () == OUTPUT_STATE_PLAYING) {
700         for (int i = 0; i < a.height; i++) {
701             // scrolling: move line i 1px to the left
702             memmove (data + (i*stride), data + sizeof (uint32_t) + (i*stride), stride - sizeof (uint32_t));
703         }
704 
705         for (int i = 0; i < a.height; i++)
706         {
707             float f = 1.0;
708             int index0, index1;
709             int bin0, bin1, bin2;
710             if (CONFIG_LOG_SCALE) {
711                 bin0 = w->log_index[CLAMP (i-1,0,height-1)];
712                 bin1 = w->log_index[i];
713                 bin2 = w->log_index[CLAMP (i+1,0,height-1)];
714             }
715             else {
716                 bin0 = (i-1) * ratio;
717                 bin1 = i * ratio;
718                 bin2 = (i+1) * ratio;
719             }
720 
721             index0 = bin0 + ftoi ((bin1 - bin0)/2.f);
722             if (index0 == bin0) index0 = bin1;
723             index1 = bin1 + ftoi ((bin2 - bin1)/2.f);
724             if (index1 == bin2) index1 = bin1;
725 
726             index0 = CLAMP (index0,0,FFT_SIZE/2-1);
727             index1 = CLAMP (index1,0,FFT_SIZE/2-1);
728 
729             f = spectrogram_get_value (w, index0, index1);
730             float x = 10 * log10f (f);
731 
732             // interpolate
733             if (i <= w->low_res_end && CONFIG_LOG_SCALE) {
734                 int j = 0;
735                 // find index of next value
736                 while (i+j < height && w->log_index[i+j] == w->log_index[i]) {
737                     j++;
738                 }
739                 float v0 = x;
740                 float v1 = w->data[w->log_index[i+j]];
741                 if (v1 != 0) {
742                     v1 = 10 * log10f (v1);
743                 }
744 
745                 int k = 0;
746                 while ((k+i) >= 0 && w->log_index[k+i] == w->log_index[i]) {
747                     j++;
748                     k--;
749                 }
750                 x = linear_interpolate (v0,v1,(1.0/(j-1)) * ((-1 * k) - 1));
751             }
752 
753             // TODO: get rid of hardcoding
754             x += CONFIG_DB_RANGE - 63;
755             x = CLAMP (x, 0, CONFIG_DB_RANGE);
756             int color_index = GRADIENT_TABLE_SIZE - ftoi (GRADIENT_TABLE_SIZE/(float)CONFIG_DB_RANGE * x);
757             color_index = CLAMP (color_index, 0, GRADIENT_TABLE_SIZE-1);
758             _draw_point (data, stride, width-1, height-1-i, w->colors[color_index]);
759         }
760     }
761     cairo_surface_mark_dirty (w->surf);
762 
763     cairo_save (cr);
764     cairo_set_source_surface (cr, w->surf, 0, 0);
765     cairo_rectangle (cr, 0, 0, a.width, a.height);
766     cairo_fill (cr);
767     cairo_restore (cr);
768 
769     return FALSE;
770 }
771 
772 
773 gboolean
spectrogram_expose_event(GtkWidget * widget,GdkEventExpose * event,gpointer user_data)774 spectrogram_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) {
775     cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (widget));
776     gboolean res = spectrogram_draw (widget, cr, user_data);
777     cairo_destroy (cr);
778     return res;
779 }
780 
781 
782 gboolean
spectrogram_button_press_event(GtkWidget * widget,GdkEventButton * event,gpointer user_data)783 spectrogram_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
784 {
785     //w_spectrogram_t *w = user_data;
786     if (event->button == 3) {
787       return TRUE;
788     }
789     return TRUE;
790 }
791 
792 gboolean
spectrogram_button_release_event(GtkWidget * widget,GdkEventButton * event,gpointer user_data)793 spectrogram_button_release_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
794 {
795     w_spectrogram_t *w = user_data;
796     if (event->button == 3) {
797       gtk_menu_popup (GTK_MENU (w->popup), NULL, NULL, NULL, w->drawarea, 0, gtk_get_current_event_time ());
798       return TRUE;
799     }
800     return TRUE;
801 }
802 
803 static gboolean
spectrogram_set_refresh_interval(gpointer user_data,int interval)804 spectrogram_set_refresh_interval (gpointer user_data, int interval)
805 {
806     w_spectrogram_t *w = user_data;
807     if (!w || interval <= 0) {
808         return FALSE;
809     }
810     if (w->drawtimer) {
811         g_source_remove (w->drawtimer);
812         w->drawtimer = 0;
813     }
814     w->drawtimer = g_timeout_add (interval, w_spectrogram_draw_cb, w);
815     return TRUE;
816 }
817 
818 static int
spectrogram_message(ddb_gtkui_widget_t * widget,uint32_t id,uintptr_t ctx,uint32_t p1,uint32_t p2)819 spectrogram_message (ddb_gtkui_widget_t *widget, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2)
820 {
821     w_spectrogram_t *w = (w_spectrogram_t *)widget;
822 
823     switch (id) {
824         case DB_EV_CONFIGCHANGED:
825             on_config_changed (w, ctx);
826             spectrogram_set_refresh_interval (w, CONFIG_REFRESH_INTERVAL);
827             break;
828         case DB_EV_SONGSTARTED:
829             spectrogram_set_refresh_interval (w, CONFIG_REFRESH_INTERVAL);
830             break;
831         case DB_EV_PAUSED:
832             if (deadbeef->get_output ()->state () == OUTPUT_STATE_PLAYING) {
833                 spectrogram_set_refresh_interval (w, CONFIG_REFRESH_INTERVAL);
834             }
835             else {
836                 if (w->drawtimer) {
837                     g_source_remove (w->drawtimer);
838                     w->drawtimer = 0;
839                 }
840             }
841             break;
842         case DB_EV_STOP:
843             if (w->drawtimer) {
844                 g_source_remove (w->drawtimer);
845                 w->drawtimer = 0;
846             }
847             break;
848     }
849     return 0;
850 }
851 
852 void
w_spectrogram_init(ddb_gtkui_widget_t * w)853 w_spectrogram_init (ddb_gtkui_widget_t *w) {
854     w_spectrogram_t *s = (w_spectrogram_t *)w;
855     load_config ();
856     deadbeef->mutex_lock (s->mutex);
857     s->samples = malloc (sizeof (double) * FFT_SIZE);
858     memset (s->samples, 0, sizeof (double) * FFT_SIZE);
859     s->data = malloc (sizeof (double) * FFT_SIZE);
860     memset (s->data, 0, sizeof (double) * FFT_SIZE);
861     if (s->drawtimer) {
862         g_source_remove (s->drawtimer);
863         s->drawtimer = 0;
864     }
865     s->samplerate = 44100.0;
866     s->height = 0;
867     s->low_res_end = 0;
868     s->log_index = (int *)malloc (sizeof (int) * MAX_HEIGHT);
869     memset (s->log_index, 0, sizeof (int) * MAX_HEIGHT);
870 
871     for (int i = 0; i < FFT_SIZE; i++) {
872         // Hanning
873         //s->window[i] = (0.5 * (1 - cos (2 * M_PI * i/(FFT_SIZE-1))));
874         // Blackman-Harris
875         s->window[i] = 0.35875 - 0.48829 * cos(2 * M_PI * i /(FFT_SIZE)) + 0.14128 * cos(4 * M_PI * i/(FFT_SIZE)) - 0.01168 * cos(6 * M_PI * i/(FFT_SIZE));;
876     }
877     create_gradient_table (s, CONFIG_GRADIENT_COLORS, CONFIG_NUM_COLORS);
878     s->in = fftw_malloc (sizeof (double) * FFT_SIZE);
879     memset (s->in, 0, sizeof (double) * FFT_SIZE);
880     //s->out_real = fftw_malloc (sizeof (double) * FFT_SIZE);
881     s->out_complex = fftw_malloc (sizeof (fftw_complex) * FFT_SIZE);
882     //s->p_r2r = fftw_plan_r2r_1d (FFT_SIZE, s->in, s->out_real, FFTW_R2HC, FFTW_ESTIMATE);
883     s->p_r2c = fftw_plan_dft_r2c_1d (FFT_SIZE, s->in, s->out_complex, FFTW_ESTIMATE);
884     spectrogram_set_refresh_interval (s, CONFIG_REFRESH_INTERVAL);
885     deadbeef->mutex_unlock (s->mutex);
886 }
887 
888 ddb_gtkui_widget_t *
w_spectrogram_create(void)889 w_spectrogram_create (void) {
890     w_spectrogram_t *w = malloc (sizeof (w_spectrogram_t));
891     memset (w, 0, sizeof (w_spectrogram_t));
892 
893     w->base.widget = gtk_event_box_new ();
894     w->base.init = w_spectrogram_init;
895     w->base.destroy  = w_spectrogram_destroy;
896     w->base.message = spectrogram_message;
897     w->drawarea = gtk_drawing_area_new ();
898     w->popup = gtk_menu_new ();
899     w->popup_item = gtk_menu_item_new_with_mnemonic ("Configure");
900     w->mutex = deadbeef->mutex_create ();
901     gtk_widget_show (w->drawarea);
902     gtk_container_add (GTK_CONTAINER (w->base.widget), w->drawarea);
903     gtk_widget_show (w->popup);
904     //gtk_container_add (GTK_CONTAINER (w->drawarea), w->popup);
905     gtk_widget_show (w->popup_item);
906     gtk_container_add (GTK_CONTAINER (w->popup), w->popup_item);
907 #if !GTK_CHECK_VERSION(3,0,0)
908     g_signal_connect_after ((gpointer) w->drawarea, "expose_event", G_CALLBACK (spectrogram_expose_event), w);
909 #else
910     g_signal_connect_after ((gpointer) w->drawarea, "draw", G_CALLBACK (spectrogram_draw), w);
911 #endif
912     g_signal_connect_after ((gpointer) w->base.widget, "button_press_event", G_CALLBACK (spectrogram_button_press_event), w);
913     g_signal_connect_after ((gpointer) w->base.widget, "button_release_event", G_CALLBACK (spectrogram_button_release_event), w);
914     g_signal_connect_after ((gpointer) w->popup_item, "activate", G_CALLBACK (on_button_config), w);
915     gtkui_plugin->w_override_signals (w->base.widget, w);
916     deadbeef->vis_waveform_listen (w, spectrogram_wavedata_listener);
917     return (ddb_gtkui_widget_t *)w;
918 }
919 
920 int
spectrogram_connect(void)921 spectrogram_connect (void)
922 {
923     gtkui_plugin = (ddb_gtkui_t *) deadbeef->plug_get_for_id (DDB_GTKUI_PLUGIN_ID);
924     if (gtkui_plugin) {
925         //trace("using '%s' plugin %d.%d\n", DDB_GTKUI_PLUGIN_ID, gtkui_plugin->gui.plugin.version_major, gtkui_plugin->gui.plugin.version_minor );
926         if (gtkui_plugin->gui.plugin.version_major == 2) {
927             //printf ("fb api2\n");
928             // 0.6+, use the new widget API
929             gtkui_plugin->w_reg_widget ("Spectrogram", 0, w_spectrogram_create, "spectrogram", NULL);
930             return 0;
931         }
932     }
933     return -1;
934 }
935 
936 int
spectrogram_start(void)937 spectrogram_start (void)
938 {
939     load_config ();
940     return 0;
941 }
942 
943 int
spectrogram_stop(void)944 spectrogram_stop (void)
945 {
946     save_config ();
947     return 0;
948 }
949 
950 int
spectrogram_startup(GtkWidget * cont)951 spectrogram_startup (GtkWidget *cont)
952 {
953     return 0;
954 }
955 
956 int
spectrogram_shutdown(GtkWidget * cont)957 spectrogram_shutdown (GtkWidget *cont)
958 {
959     return 0;
960 }
961 int
spectrogram_disconnect(void)962 spectrogram_disconnect (void)
963 {
964     gtkui_plugin = NULL;
965     return 0;
966 }
967 
968 static const char settings_dlg[] =
969     "property \"Refresh interval (ms): \"          spinbtn[10,1000,1] "      CONFSTR_SP_REFRESH_INTERVAL        " 25 ;\n"
970 ;
971 
972 static DB_misc_t plugin = {
973     //DB_PLUGIN_SET_API_VERSION
974     .plugin.type            = DB_PLUGIN_MISC,
975     .plugin.api_vmajor      = 1,
976     .plugin.api_vminor      = 5,
977     .plugin.version_major   = 0,
978     .plugin.version_minor   = 1,
979 #if GTK_CHECK_VERSION(3,0,0)
980     .plugin.id              = "spectrogram-gtk3",
981 #else
982     .plugin.id              = "spectrogram",
983 #endif
984     .plugin.name            = "Spectrogram",
985     .plugin.descr           = "Spectrogram",
986     .plugin.copyright       =
987         "Copyright (C) 2013 Christian Boxdörfer <christian.boxdoerfer@posteo.de>\n"
988         "\n"
989         "This program is free software; you can redistribute it and/or\n"
990         "modify it under the terms of the GNU General Public License\n"
991         "as published by the Free Software Foundation; either version 2\n"
992         "of the License, or (at your option) any later version.\n"
993         "\n"
994         "This program is distributed in the hope that it will be useful,\n"
995         "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
996         "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
997         "GNU General Public License for more details.\n"
998         "\n"
999         "You should have received a copy of the GNU General Public License\n"
1000         "along with this program; if not, write to the Free Software\n"
1001         "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n"
1002     ,
1003     .plugin.website         = "https://github.com/cboxdoerfer/ddb_spectrogram",
1004     .plugin.start           = spectrogram_start,
1005     .plugin.stop            = spectrogram_stop,
1006     .plugin.connect         = spectrogram_connect,
1007     .plugin.disconnect      = spectrogram_disconnect,
1008     .plugin.configdialog    = settings_dlg,
1009 };
1010 
1011 #if !GTK_CHECK_VERSION(3,0,0)
1012 DB_plugin_t *
ddb_vis_spectrogram_GTK2_load(DB_functions_t * ddb)1013 ddb_vis_spectrogram_GTK2_load (DB_functions_t *ddb) {
1014     deadbeef = ddb;
1015     return &plugin.plugin;
1016 }
1017 #else
1018 DB_plugin_t *
ddb_vis_spectrogram_GTK3_load(DB_functions_t * ddb)1019 ddb_vis_spectrogram_GTK3_load (DB_functions_t *ddb) {
1020     deadbeef = ddb;
1021     return &plugin.plugin;
1022 }
1023 #endif
1024