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