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 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
27
28 #include <gtk/gtk.h>
29 #include <math.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <assert.h>
34 #include <dirent.h>
35 #include <sys/stat.h>
36 #include "callbacks.h"
37 #include "interface.h"
38 #include "support.h"
39 #include "../../deadbeef.h"
40 #include "gtkui.h"
41 #include "pluginconf.h"
42
43 static ddb_dsp_context_t *chain;
44 static GtkWidget *prefwin;
45
46 static ddb_dsp_context_t *
dsp_clone(ddb_dsp_context_t * from)47 dsp_clone (ddb_dsp_context_t *from) {
48 ddb_dsp_context_t *dsp = from->plugin->open ();
49 char param[2000];
50 if (from->plugin->num_params) {
51 int n = from->plugin->num_params ();
52 for (int i = 0; i < n; i++) {
53 from->plugin->get_param (from, i, param, sizeof (param));
54 dsp->plugin->set_param (dsp, i, param);
55 }
56 }
57 dsp->enabled = from->enabled;
58 return dsp;
59 }
60
61 static void
fill_dsp_chain(GtkListStore * mdl)62 fill_dsp_chain (GtkListStore *mdl) {
63 ddb_dsp_context_t *dsp = chain;
64 while (dsp) {
65 GtkTreeIter iter;
66 gtk_list_store_append (mdl, &iter);
67 gtk_list_store_set (mdl, &iter, 0, dsp->plugin->plugin.name, -1);
68 dsp = dsp->next;
69 }
70 }
71
dirent_alphasort(const struct dirent ** a,const struct dirent ** b)72 static int dirent_alphasort (const struct dirent **a, const struct dirent **b) {
73 return strcmp ((*a)->d_name, (*b)->d_name);
74 }
75
76 static int
scandir_preset_filter(const struct dirent * ent)77 scandir_preset_filter (const struct dirent *ent) {
78 char *ext = strrchr (ent->d_name, '.');
79 if (ext && !strcasecmp (ext, ".txt")) {
80 return 1;
81 }
82 return 0;
83 }
84
85 static void
dsp_fill_preset_list(GtkWidget * combobox)86 dsp_fill_preset_list (GtkWidget *combobox) {
87 // fill list of presets
88 GtkListStore *mdl;
89 mdl = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combobox)));
90 gtk_list_store_clear (mdl);
91 struct dirent **namelist = NULL;
92 char path[1024];
93 if (snprintf (path, sizeof (path), "%s/presets/dsp", deadbeef->get_config_dir ()) > 0) {
94 int n = scandir (path, &namelist, scandir_preset_filter, dirent_alphasort);
95 int i;
96 for (i = 0; i < n; i++) {
97 char title[100];
98 strcpy (title, namelist[i]->d_name);
99 char *e = strrchr (title, '.');
100 if (e) {
101 *e = 0;
102 }
103 GtkTreeIter iter;
104 gtk_list_store_append (mdl, &iter);
105 gtk_list_store_set (mdl, &iter, 0, title, -1);
106 free (namelist[i]);
107 }
108 free (namelist);
109 }
110
111 // set last preset name
112 GtkWidget *entry = gtk_bin_get_child (GTK_BIN (combobox));
113 if (entry) {
114 deadbeef->conf_lock ();
115 gtk_entry_set_text (GTK_ENTRY (entry), deadbeef->conf_get_str_fast ("gtkui.conf_dsp_preset", ""));
116 deadbeef->conf_unlock ();
117 }
118 }
119
120 void
dsp_setup_init(GtkWidget * _prefwin)121 dsp_setup_init (GtkWidget *_prefwin) {
122 prefwin = _prefwin;
123 // copy current dsp chain
124 ddb_dsp_context_t *streamer_chain = deadbeef->streamer_get_dsp_chain ();
125
126 ddb_dsp_context_t *tail = NULL;
127 while (streamer_chain) {
128 ddb_dsp_context_t *new = dsp_clone (streamer_chain);
129 if (tail) {
130 tail->next = new;
131 tail = new;
132 }
133 else {
134 chain = tail = new;
135 }
136 streamer_chain = streamer_chain->next;
137 }
138
139 // fill dsp_listview
140 GtkWidget *listview = lookup_widget (prefwin, "dsp_listview");
141
142
143 GtkCellRenderer *title_cell = gtk_cell_renderer_text_new ();
144 GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes (_("Plugin"), title_cell, "text", 0, NULL);
145 gtk_tree_view_append_column (GTK_TREE_VIEW (listview), GTK_TREE_VIEW_COLUMN (col));
146 GtkListStore *mdl = gtk_list_store_new (1, G_TYPE_STRING);
147 gtk_tree_view_set_model (GTK_TREE_VIEW (listview), GTK_TREE_MODEL (mdl));
148
149 fill_dsp_chain (mdl);
150 GtkTreePath *path = gtk_tree_path_new_from_indices (0, -1);
151 gtk_tree_view_set_cursor (GTK_TREE_VIEW (listview), path, NULL, FALSE);
152 gtk_tree_path_free (path);
153
154 GtkWidget *combobox = lookup_widget (prefwin, "dsp_preset");
155 dsp_fill_preset_list (combobox);
156 }
157
158 void
dsp_setup_free(void)159 dsp_setup_free (void) {
160 while (chain) {
161 ddb_dsp_context_t *next = chain->next;
162 chain->plugin->close (chain);
163 chain = next;
164 }
165 prefwin = NULL;
166 }
167
168 static void
fill_dsp_plugin_list(GtkListStore * mdl)169 fill_dsp_plugin_list (GtkListStore *mdl) {
170 struct DB_dsp_s **dsp = deadbeef->plug_get_dsp_list ();
171 int i;
172 for (i = 0; dsp[i]; i++) {
173 GtkTreeIter iter;
174 gtk_list_store_append (mdl, &iter);
175 gtk_list_store_set (mdl, &iter, 0, dsp[i]->plugin.name, -1);
176 }
177 }
178
179 static void
update_streamer(void)180 update_streamer (void) {
181 deadbeef->streamer_set_dsp_chain (chain);
182 }
183
184 void
on_dsp_add_clicked(GtkButton * button,gpointer user_data)185 on_dsp_add_clicked (GtkButton *button,
186 gpointer user_data)
187 {
188 GtkWidget *dlg = create_select_dsp_plugin ();
189 gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_OK);
190 gtk_window_set_transient_for (GTK_WINDOW (dlg), GTK_WINDOW (prefwin));
191 gtk_window_set_title (GTK_WINDOW (dlg), _("Add plugin to DSP chain"));
192
193 GtkComboBox *combo;
194 // fill encoder presets
195 combo = GTK_COMBO_BOX (lookup_widget (dlg, "plugin"));
196 GtkListStore *mdl = GTK_LIST_STORE (gtk_combo_box_get_model (combo));
197 fill_dsp_plugin_list (mdl);
198 gtk_combo_box_set_active (combo, deadbeef->conf_get_int ("converter.last_selected_dsp", 0));
199
200 int r = gtk_dialog_run (GTK_DIALOG (dlg));
201 if (r == GTK_RESPONSE_OK) {
202 // create new instance of the selected plugin
203 int idx = gtk_combo_box_get_active (combo);
204 struct DB_dsp_s **dsp = deadbeef->plug_get_dsp_list ();
205 int i;
206 ddb_dsp_context_t *inst = NULL;
207 for (i = 0; dsp[i]; i++) {
208 if (i == idx) {
209 inst = dsp[i]->open ();
210 break;
211 }
212 }
213 if (inst) {
214 // append to DSP chain
215 ddb_dsp_context_t *tail = chain;
216 while (tail && tail->next) {
217 tail = tail->next;
218 }
219 if (tail) {
220 tail->next = inst;
221 }
222 else {
223 chain = inst;
224 }
225
226 // reinit list of instances
227 GtkWidget *list = lookup_widget (prefwin, "dsp_listview");
228 GtkListStore *mdl = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW(list)));
229 gtk_list_store_clear (mdl);
230 fill_dsp_chain (mdl);
231 update_streamer ();
232 }
233 else {
234 fprintf (stderr, "prefwin: failed to add DSP plugin to chain\n");
235 }
236 }
237 gtk_widget_destroy (dlg);
238 }
239
240 static int
listview_get_index(GtkWidget * list)241 listview_get_index (GtkWidget *list) {
242 GtkTreePath *path;
243 GtkTreeViewColumn *col;
244 gtk_tree_view_get_cursor (GTK_TREE_VIEW (list), &path, &col);
245 if (!path) {
246 // nothing selected
247 return -1;
248 }
249 int *indices = gtk_tree_path_get_indices (path);
250 int idx = *indices;
251 g_free (indices);
252 return idx;
253 }
254
255 void
on_dsp_remove_clicked(GtkButton * button,gpointer user_data)256 on_dsp_remove_clicked (GtkButton *button,
257 gpointer user_data)
258 {
259 GtkWidget *list = lookup_widget (prefwin, "dsp_listview");
260 int idx = listview_get_index (list);
261 if (idx == -1) {
262 return;
263 }
264
265 ddb_dsp_context_t *p = chain;
266 ddb_dsp_context_t *prev = NULL;
267 int i = idx;
268 while (p && i--) {
269 prev = p;
270 p = p->next;
271 }
272 if (p) {
273 if (prev) {
274 prev->next = p->next;
275 }
276 else {
277 chain = p->next;
278 }
279 p->plugin->close (p);
280 GtkListStore *mdl = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW(list)));
281 gtk_list_store_clear (mdl);
282 fill_dsp_chain (mdl);
283 GtkTreePath *path = gtk_tree_path_new_from_indices (idx, -1);
284 gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, NULL, FALSE);
285 gtk_tree_path_free (path);
286 update_streamer ();
287 }
288 }
289
290 static ddb_dsp_context_t *current_dsp_context = NULL;
291
292 void
dsp_ctx_set_param(const char * key,const char * value)293 dsp_ctx_set_param (const char *key, const char *value) {
294 current_dsp_context->plugin->set_param (current_dsp_context, atoi (key), value);
295 }
296
297 void
dsp_ctx_get_param(const char * key,char * value,int len,const char * def)298 dsp_ctx_get_param (const char *key, char *value, int len, const char *def) {
299 strncpy (value, def, len);
300 current_dsp_context->plugin->get_param (current_dsp_context, atoi (key), value, len);
301 }
302
303 int
button_cb(int btn,void * ctx)304 button_cb (int btn, void *ctx) {
305 if (btn == ddb_button_apply) {
306 update_streamer ();
307 }
308 return 1;
309 }
310
311 void
on_dsp_configure_clicked(GtkButton * button,gpointer user_data)312 on_dsp_configure_clicked (GtkButton *button,
313 gpointer user_data)
314 {
315 GtkWidget *list = lookup_widget (prefwin, "dsp_listview");
316 int idx = listview_get_index (list);
317 if (idx == -1) {
318 return;
319 }
320 ddb_dsp_context_t *p = chain;
321 int i = idx;
322 while (p && i--) {
323 p = p->next;
324 }
325 if (!p || !p->plugin->configdialog) {
326 return;
327 }
328 current_dsp_context = p;
329 ddb_dialog_t conf = {
330 .title = p->plugin->plugin.name,
331 .layout = p->plugin->configdialog,
332 .set_param = dsp_ctx_set_param,
333 .get_param = dsp_ctx_get_param,
334 };
335 int response = gtkui_run_dialog (prefwin, &conf, 0, button_cb, NULL);
336 if (response == ddb_button_ok) {
337 update_streamer ();
338 }
339 current_dsp_context = NULL;
340 }
341
342 static int
swap_items(GtkWidget * list,int idx)343 swap_items (GtkWidget *list, int idx) {
344 ddb_dsp_context_t *prev = NULL;
345 ddb_dsp_context_t *p = chain;
346
347 int n = idx;
348 while (n > 0 && p) {
349 prev = p;
350 p = p->next;
351 n--;
352 }
353
354 if (!p || !p->next) {
355 return -1;
356 }
357
358 ddb_dsp_context_t *moved = p->next;
359
360 if (!moved) {
361 return -1;
362 }
363
364 ddb_dsp_context_t *last = moved ? moved->next : NULL;
365
366 if (prev) {
367 p->next = last;
368 prev->next = moved;
369 moved->next = p;
370 }
371 else {
372 p->next = last;
373 chain = moved;
374 moved->next = p;
375 }
376 GtkListStore *mdl = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW(list)));
377 gtk_list_store_clear (mdl);
378 fill_dsp_chain (mdl);
379 return 0;
380 }
381
382
383 void
on_dsp_up_clicked(GtkButton * button,gpointer user_data)384 on_dsp_up_clicked (GtkButton *button,
385 gpointer user_data)
386 {
387 GtkWidget *list = lookup_widget (prefwin, "dsp_listview");
388 int idx = listview_get_index (list);
389 if (idx <= 0) {
390 return;
391 }
392
393 if (-1 == swap_items (list, idx-1)) {
394 return;
395 }
396 GtkTreePath *path = gtk_tree_path_new_from_indices (idx-1, -1);
397 gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, NULL, FALSE);
398 gtk_tree_path_free (path);
399 update_streamer ();
400 }
401
402
403 void
on_dsp_down_clicked(GtkButton * button,gpointer user_data)404 on_dsp_down_clicked (GtkButton *button,
405 gpointer user_data)
406 {
407 GtkWidget *list = lookup_widget (prefwin, "dsp_listview");
408 int idx = listview_get_index (list);
409 if (idx == -1) {
410 return;
411 }
412
413 if (-1 == swap_items (list, idx)) {
414 return;
415 }
416 GtkTreePath *path = gtk_tree_path_new_from_indices (idx+1, -1);
417 gtk_tree_view_set_cursor (GTK_TREE_VIEW (list), path, NULL, FALSE);
418 gtk_tree_path_free (path);
419 update_streamer ();
420 }
421
422 void
on_dsp_preset_changed(GtkComboBox * combobox,gpointer user_data)423 on_dsp_preset_changed (GtkComboBox *combobox,
424 gpointer user_data)
425 {
426 GtkWidget *entry = gtk_bin_get_child (GTK_BIN (combobox));
427 if (entry) {
428 deadbeef->conf_set_str ("gtkui.conf_dsp_preset", gtk_entry_get_text (GTK_ENTRY (entry)));
429 }
430 }
431
432
433 void
on_dsp_preset_save_clicked(GtkButton * button,gpointer user_data)434 on_dsp_preset_save_clicked (GtkButton *button,
435 gpointer user_data)
436 {
437 const char *confdir = deadbeef->get_config_dir ();
438 char path[1024];
439 if (snprintf (path, sizeof (path), "%s/presets", confdir) < 0) {
440 return;
441 }
442 mkdir (path, 0755);
443 if (snprintf (path, sizeof (path), "%s/presets/dsp", confdir) < 0) {
444 return;
445 }
446 GtkWidget *combobox = lookup_widget (prefwin, "dsp_preset");
447 GtkWidget *entry = gtk_bin_get_child (GTK_BIN (combobox));
448 if (!entry) {
449 return;
450 }
451
452 const char *text = gtk_entry_get_text (GTK_ENTRY (entry));
453 mkdir (path, 0755);
454 if (snprintf (path, sizeof (path), "%s/presets/dsp/%s.txt", confdir, text) < 0) {
455 return;
456 }
457 deadbeef->dsp_preset_save (path, chain);
458
459 dsp_fill_preset_list (combobox);
460 }
461
462
463 void
on_dsp_preset_load_clicked(GtkButton * button,gpointer user_data)464 on_dsp_preset_load_clicked (GtkButton *button,
465 gpointer user_data)
466 {
467 GtkWidget *combobox = lookup_widget (prefwin, "dsp_preset");
468 GtkWidget *entry = gtk_bin_get_child (GTK_BIN (combobox));
469 if (entry) {
470 const char *text = gtk_entry_get_text (GTK_ENTRY (entry));
471 char path[PATH_MAX];
472 if (snprintf (path, sizeof (path), "%s/presets/dsp/%s.txt", deadbeef->get_config_dir (), text) > 0) {
473 ddb_dsp_context_t *new_chain = NULL;
474 int res = deadbeef->dsp_preset_load (path, &new_chain);
475 if (!res) {
476 deadbeef->dsp_preset_free (chain);
477 chain = new_chain;
478 GtkWidget *list = lookup_widget (prefwin, "dsp_listview");
479 GtkListStore *mdl = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW(list)));
480 gtk_list_store_clear (mdl);
481 fill_dsp_chain (mdl);
482 update_streamer ();
483 }
484 }
485 }
486 }
487
488