1 /*
2     DeaDBeeF -- the music player
3     Copyright (C) 2009-2015 Alexey Yakovenko and other contributors
4 
5     This software is provided 'as-is', without any express or implied
6     warranty.  In no event will the authors be held liable for any damages
7     arising from the use of this software.
8 
9     Permission is granted to anyone to use this software for any purpose,
10     including commercial applications, and to alter it and redistribute it
11     freely, subject to the following restrictions:
12 
13     1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17 
18     2. Altered source versions must be plainly marked as such, and must not be
19      misrepresented as being the original software.
20 
21     3. This notice may not be removed or altered from any source distribution.
22 */
23 
24 #include <gtk/gtk.h>
25 #include <string.h>
26 #include <math.h>
27 #include <stdlib.h>
28 #include "gtkui.h"
29 #include "support.h"
30 #include "ddbequalizer.h"
31 
32 static GtkWidget *eqcont;
33 static GtkWidget *eqwin;
34 static GtkWidget *eqenablebtn;
35 
36 static inline float
db_to_amp(float dB)37 db_to_amp (float dB) {
38     const float ln10=2.3025850929940002f;
39     return exp(ln10*dB/20.f);
40 }
41 
42 static inline float
amp_to_db(float amp)43 amp_to_db (float amp) {
44     return 20*log10 (amp);
45 }
46 
47 ddb_dsp_context_t *
get_supereq(void)48 get_supereq (void) {
49     ddb_dsp_context_t *dsp = deadbeef->streamer_get_dsp_chain ();
50     while (dsp) {
51         if (!strcmp (dsp->plugin->plugin.id, "supereq")) {
52             return dsp;
53         }
54         dsp = dsp->next;
55     }
56 
57     return NULL;
58 }
59 
60 static void
set_param(ddb_dsp_context_t * eq,int i,float v)61 set_param (ddb_dsp_context_t *eq, int i, float v) {
62     char fv[100];
63     snprintf (fv, sizeof (fv), "%f", v);
64     eq->plugin->set_param (eq, i, fv);
65 }
66 
67 void
eq_value_changed(DdbEqualizer * widget)68 eq_value_changed (DdbEqualizer *widget)
69 {
70     ddb_dsp_context_t *eq = get_supereq ();
71     if (eq) {
72         for (int i = 0; i < 18; i++) {
73             set_param (eq, i+1, ddb_equalizer_get_band (widget, i));
74         }
75         set_param (eq, 0, ddb_equalizer_get_preamp (widget));
76         deadbeef->streamer_dsp_chain_save ();
77     }
78 }
79 
80 void
on_enable_toggled(GtkToggleButton * togglebutton,gpointer user_data)81 on_enable_toggled         (GtkToggleButton *togglebutton,
82         gpointer         user_data) {
83     ddb_dsp_context_t *eq = get_supereq ();
84     if (eq) {
85         int enabled = gtk_toggle_button_get_active (togglebutton) ? 1 : 0;
86         eq->enabled =  enabled;
87         deadbeef->streamer_dsp_refresh ();
88         deadbeef->streamer_dsp_chain_save ();
89     }
90 }
91 
92 void
on_zero_all_clicked(GtkButton * button,gpointer user_data)93 on_zero_all_clicked                  (GtkButton       *button,
94         gpointer         user_data) {
95     if (eqwin) {
96         ddb_dsp_context_t *eq = get_supereq ();
97         if (eq) {
98             ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), 0);
99             set_param (eq, 0, 0);
100             for (int i = 0; i < 18; i++) {
101                 // set gui
102                 ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, 0);
103 
104                 // set dsp
105                 set_param (eq, i+1, 0);
106             }
107             gtk_widget_queue_draw (eqwin);
108             deadbeef->streamer_dsp_chain_save ();
109         }
110     }
111 }
112 
113 void
on_zero_preamp_clicked(GtkButton * button,gpointer user_data)114 on_zero_preamp_clicked                  (GtkButton       *button,
115         gpointer         user_data) {
116     if (eqwin) {
117         ddb_dsp_context_t *eq = get_supereq ();
118         if (eq) {
119             set_param (eq, 0, 0);
120             ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), 0);
121             gtk_widget_queue_draw (eqwin);
122             deadbeef->streamer_dsp_chain_save ();
123         }
124     }
125 }
126 
127 void
on_zero_bands_clicked(GtkButton * button,gpointer user_data)128 on_zero_bands_clicked                  (GtkButton       *button,
129         gpointer         user_data) {
130     if (eqwin) {
131         ddb_dsp_context_t *eq = get_supereq ();
132         if (eq) {
133             for (int i = 0; i < 18; i++) {
134                 ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, 0);
135                 set_param (eq, i+1, 0);
136             }
137             gtk_widget_queue_draw (eqwin);
138             deadbeef->streamer_dsp_chain_save ();
139         }
140     }
141 }
142 
143 void
on_save_preset_clicked(GtkMenuItem * menuitem,gpointer user_data)144 on_save_preset_clicked                  (GtkMenuItem       *menuitem,
145         gpointer         user_data) {
146     GtkWidget *dlg = gtk_file_chooser_dialog_new (_("Save DeaDBeeF EQ Preset"), GTK_WINDOW (mainwin), GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL);
147 
148     gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dlg), TRUE);
149     gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dlg), "untitled.ddbeq");
150 
151     GtkFileFilter* flt;
152     flt = gtk_file_filter_new ();
153     gtk_file_filter_set_name (flt, _("DeaDBeeF EQ preset files (*.ddbeq)"));
154     gtk_file_filter_add_pattern (flt, "*.ddbeq");
155 
156     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), flt);
157 
158     if (gtk_dialog_run (GTK_DIALOG (dlg)) == GTK_RESPONSE_OK)
159     {
160         gchar *fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
161         gtk_widget_destroy (dlg);
162 
163         if (fname) {
164             FILE *fp = fopen (fname, "w+b");
165             if (fp) {
166                 ddb_dsp_context_t *eq = get_supereq ();
167                 if (eq) {
168                     char fv[100];
169                     float v;
170                     for (int i = 0; i < 18; i++) {
171                         eq->plugin->get_param (eq, i+1, fv, sizeof (fv));
172                         v = atof (fv);
173                         fprintf (fp, "%f\n", v);
174                     }
175                     eq->plugin->get_param (eq, 0, fv, sizeof (fv));
176                     v = atof (fv);
177                     fprintf (fp, "%f\n", v);
178                 }
179                 fclose (fp);
180             }
181             g_free (fname);
182         }
183     }
184     else {
185         gtk_widget_destroy (dlg);
186     }
187 }
188 
189 void
on_load_preset_clicked(GtkMenuItem * menuitem,gpointer user_data)190 on_load_preset_clicked                  (GtkMenuItem       *menuitem,
191         gpointer         user_data) {
192     GtkWidget *dlg = gtk_file_chooser_dialog_new (_("Load DeaDBeeF EQ Preset..."), GTK_WINDOW (mainwin), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);
193 
194     GtkFileFilter* flt;
195     flt = gtk_file_filter_new ();
196     gtk_file_filter_set_name (flt, _("DeaDBeeF EQ presets (*.ddbeq)"));
197     gtk_file_filter_add_pattern (flt, "*.ddbeq");
198     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), flt);
199 
200     gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dlg), FALSE);
201     // restore folder
202     deadbeef->conf_lock ();
203     gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str_fast ("filechooser.lastdir", ""));
204     deadbeef->conf_unlock ();
205     int response = gtk_dialog_run (GTK_DIALOG (dlg));
206     // store folder
207     gchar *folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dlg));
208     if (folder) {
209         deadbeef->conf_set_str ("filechooser.lastdir", folder);
210         g_free (folder);
211     }
212     if (response == GTK_RESPONSE_OK)
213     {
214         gchar *fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
215         if (fname) {
216             FILE *fp = fopen (fname, "rt");
217             if (fp) {
218                 float vals[19]; // float dBs
219                 int i = 0;
220                 while (i < 19) {
221                     char tmp[20];
222                     char *out = fgets (tmp, sizeof (tmp), fp);
223                     if (!out) {
224                         break;
225                     }
226                     vals[i] = atof (tmp);
227                     i++;
228                 }
229                 fclose (fp);
230                 if (i == 19) {
231                     // apply and save config
232                     ddb_dsp_context_t *eq = get_supereq ();
233                     if (eq) {
234                         set_param (eq, 0, vals[18]);
235                         ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), vals[18]);
236                         for (int i = 0; i < 18; i++) {
237                             ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, vals[i]);
238                             set_param (eq, i+1, vals[i]);
239                         }
240                         gtk_widget_queue_draw (eqwin);
241                         deadbeef->streamer_dsp_chain_save ();
242                     }
243                 }
244                 else {
245                     fprintf (stderr, "[eq] corrupted DeaDBeeF preset file, discarded\n");
246                 }
247             }
248             g_free (fname);
249         }
250     }
251     gtk_widget_destroy (dlg);
252 }
253 
254 void
on_import_fb2k_preset_clicked(GtkButton * button,gpointer user_data)255 on_import_fb2k_preset_clicked                  (GtkButton       *button,
256         gpointer         user_data) {
257     GtkWidget *dlg = gtk_file_chooser_dialog_new (_("Import Foobar2000 EQ Preset..."), GTK_WINDOW (mainwin), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);
258 
259     GtkFileFilter* flt;
260     flt = gtk_file_filter_new ();
261     gtk_file_filter_set_name (flt, _("Foobar2000 EQ presets (*.feq)"));
262     gtk_file_filter_add_pattern (flt, "*.feq");
263     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), flt);
264 
265     gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dlg), FALSE);
266     // restore folder
267     deadbeef->conf_lock ();
268     gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dlg), deadbeef->conf_get_str_fast ("filechooser.lastdir", ""));
269     deadbeef->conf_unlock ();
270 
271     int response = gtk_dialog_run (GTK_DIALOG (dlg));
272     // store folder
273     gchar *folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dlg));
274     if (folder) {
275         deadbeef->conf_set_str ("filechooser.lastdir", folder);
276         g_free (folder);
277     }
278     if (response == GTK_RESPONSE_OK)
279     {
280         gchar *fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
281         if (fname) {
282             FILE *fp = fopen (fname, "rt");
283             if (fp) {
284                 int vals[18]; // integer dBs
285                 int i = 0;
286                 while (i < 18) {
287                     char tmp[20];
288                     char *out = fgets (tmp, sizeof (tmp), fp);
289                     if (!out) {
290                         break;
291                     }
292                     vals[i] = atoi (tmp);
293                     i++;
294                 }
295                 fclose (fp);
296                 if (i == 18) {
297                     // apply and save config
298                     ddb_dsp_context_t *eq = get_supereq ();
299                     if (eq) {
300                         set_param (eq, 0, 0);
301                         ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), 0);
302                         for (int i = 0; i < 18; i++) {
303                             ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, vals[i]);
304                             set_param (eq, i+1, vals[i]);
305                         }
306                         gtk_widget_queue_draw (eqwin);
307                         deadbeef->streamer_dsp_chain_save ();
308                     }
309                 }
310                 else {
311                     fprintf (stderr, "[eq] corrupted Foobar2000 preset file, discarded\n");
312                 }
313             }
314             g_free (fname);
315         }
316     }
317     gtk_widget_destroy (dlg);
318 }
319 
320 void
on_presets_clicked(GtkButton * button,gpointer user_data)321 on_presets_clicked                  (GtkButton       *button,
322         gpointer         user_data) {
323     GtkWidget *menu = gtk_menu_new ();
324     GtkWidget *menuitem;
325 
326     menuitem = gtk_menu_item_new_with_mnemonic (_("Save Preset"));
327     gtk_widget_show (menuitem);
328     gtk_container_add (GTK_CONTAINER (menu), menuitem);
329 
330     g_signal_connect ((gpointer) menuitem, "activate",
331             G_CALLBACK (on_save_preset_clicked),
332             NULL);
333 
334     menuitem = gtk_menu_item_new_with_mnemonic (_("Load Preset"));
335     gtk_widget_show (menuitem);
336     gtk_container_add (GTK_CONTAINER (menu), menuitem);
337 
338     g_signal_connect ((gpointer) menuitem, "activate",
339             G_CALLBACK (on_load_preset_clicked),
340             NULL);
341 
342     menuitem = gtk_menu_item_new_with_mnemonic (_("Import Foobar2000 Preset"));
343     gtk_widget_show (menuitem);
344     gtk_container_add (GTK_CONTAINER (menu), menuitem);
345 
346     g_signal_connect ((gpointer) menuitem, "activate",
347             G_CALLBACK (on_import_fb2k_preset_clicked),
348             NULL);
349 
350     gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
351 }
352 
353 void
eq_window_show(void)354 eq_window_show (void) {
355     if (!eqcont) {
356         eqcont = gtk_vbox_new (FALSE, 8);
357         GtkWidget *parent= lookup_widget (mainwin, "plugins_bottom_vbox");
358         gtk_box_pack_start (GTK_BOX (parent), eqcont, FALSE, FALSE, 0);
359 
360         GtkWidget *buttons = gtk_hbox_new (FALSE, 8);
361         gtk_container_set_border_width (GTK_CONTAINER (buttons), 3);
362         gtk_widget_show (buttons);
363         gtk_box_pack_start (GTK_BOX (eqcont), buttons, FALSE, FALSE, 0);
364 
365         GtkWidget *button;
366 
367         eqenablebtn = button = gtk_check_button_new_with_label (_("Enable"));
368         gtk_widget_show (button);
369         gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0);
370         ddb_dsp_context_t *eq = get_supereq ();
371         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (eqenablebtn), eq ? eq->enabled : 0);
372         g_signal_connect ((gpointer) button, "toggled",
373                 G_CALLBACK (on_enable_toggled),
374                 NULL);
375 
376         button = gtk_button_new_with_label (_("Zero All"));
377         gtk_widget_show (button);
378         gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0);
379         g_signal_connect ((gpointer) button, "clicked",
380                 G_CALLBACK (on_zero_all_clicked),
381                 NULL);
382 
383         button = gtk_button_new_with_label (_("Zero Preamp"));
384         gtk_widget_show (button);
385         gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0);
386         g_signal_connect ((gpointer) button, "clicked",
387                 G_CALLBACK (on_zero_preamp_clicked),
388                 NULL);
389 
390         button = gtk_button_new_with_label (_("Zero Bands"));
391         gtk_widget_show (button);
392         gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0);
393         g_signal_connect ((gpointer) button, "clicked",
394                 G_CALLBACK (on_zero_bands_clicked),
395                 NULL);
396 
397         button = gtk_button_new_with_label (_("Presets"));
398         gtk_widget_show (button);
399         gtk_box_pack_start (GTK_BOX (buttons), button, FALSE, FALSE, 0);
400         g_signal_connect ((gpointer) button, "clicked",
401                 G_CALLBACK (on_presets_clicked),
402                 NULL);
403 
404         eqwin = GTK_WIDGET (ddb_equalizer_new());
405         g_signal_connect (eqwin, "on_changed", G_CALLBACK (eq_value_changed), 0);
406         gtk_widget_set_size_request (eqwin, -1, 200);
407 
408         if (eq) {
409             char fv[100];
410             float v;
411             eq->plugin->get_param (eq, 0, fv, sizeof (fv));
412             v = atof (fv);
413             ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), v);
414             for (int i = 0; i < 18; i++) {
415                 if (eq) {
416                     eq->plugin->get_param (eq, i+1, fv, sizeof (fv));
417                     v = atof (fv);
418                     ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, v);
419                 }
420             }
421         }
422 
423         gtk_widget_show (eqwin);
424         gtk_box_pack_start (GTK_BOX (eqcont), eqwin, TRUE, TRUE, 0);
425     }
426     gtk_widget_show (eqcont);
427 }
428 
429 void
eq_window_hide(void)430 eq_window_hide (void) {
431     if (eqcont) {
432         gtk_widget_hide (eqcont);
433     }
434 }
435 
436 void
eq_window_destroy(void)437 eq_window_destroy (void) {
438     if (eqwin) {
439         gtk_widget_destroy (eqwin);
440         eqwin = NULL;
441     }
442 }
443 
444 void
eq_redraw(void)445 eq_redraw (void) {
446     if (eqwin) {
447         gtk_widget_queue_draw (eqwin);
448     }
449 }
450 
451 void
eq_refresh(void)452 eq_refresh (void) {
453     ddb_dsp_context_t *eq = get_supereq ();
454     if (eq && eqwin) {
455         char s[20];
456         eq->plugin->get_param (eq, 0, s, sizeof (s));
457         ddb_equalizer_set_preamp (DDB_EQUALIZER (eqwin), atof(s));
458         for (int i = 0; i < 18; i++) {
459             eq->plugin->get_param (eq, i+1, s, sizeof (s));
460             ddb_equalizer_set_band (DDB_EQUALIZER (eqwin), i, atoi(s));
461         }
462         eq_redraw ();
463     }
464 }
465