1 /*
2     Copyright (C) 2011 Tom Szilagyi
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <math.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25 #include <errno.h>
26 
27 #include <gtk/gtk.h>
28 
29 #include <lv2.h>
30 #include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
31 #include "lv2/lv2plug.in/ns/ext/instance-access/instance-access.h"
32 
33 #include "ir.h"
34 #include "ir_utils.h"
35 #include "ir_meter.h"
36 #include "ir_modeind.h"
37 #include "ir_wavedisplay.h"
38 
39 #define IR_UI_URI "http://tomszilagyi.github.io/plugins/lv2/ir/gui"
40 
41 #define PAD 2
42 
43 #define ADJ_PREDELAY   0
44 #define ADJ_ATTACK     1
45 #define ADJ_ATTACKTIME 2
46 #define ADJ_ENVELOPE   3
47 #define ADJ_LENGTH     4
48 #define ADJ_STRETCH    5
49 #define ADJ_STEREO_IN  6
50 #define ADJ_STEREO_IR  7
51 #define ADJ_DRY_GAIN   8
52 #define ADJ_WET_GAIN   9
53 #define N_ADJUSTMENTS 10
54 
55 typedef struct {
56 	int id;
57 	gdouble def;
58 	gdouble min;
59 	gdouble max;
60 	int log;
61 } adj_descr_t;
62 
63 #define LIN    0
64 #define LOG    1
65 #define INVLOG 2
66 
67 static adj_descr_t adj_descr_table[N_ADJUSTMENTS] = {
68 	{ADJ_PREDELAY,     0.0,   0.0, 2000.0, INVLOG},
69 	{ADJ_ATTACK,       0.0,   0.0,  100.0, LIN},
70 	{ADJ_ATTACKTIME,   0.0,   0.0,  300.0, LIN},
71 	{ADJ_ENVELOPE,   100.0,   0.0,  100.0, LIN},
72 	{ADJ_LENGTH,     100.0,   0.0,  100.0, LIN},
73 	{ADJ_STRETCH,    100.0,  50.0,  150.0, LIN},
74 	{ADJ_STEREO_IN,  100.0,   0.0,  150.0, LIN},
75 	{ADJ_STEREO_IR,  100.0,   0.0,  150.0, LIN},
76 	{ADJ_DRY_GAIN,     0.0, -90.0,    6.0, LOG},
77 	{ADJ_WET_GAIN,    -6.0, -90.0,    6.0, LOG},
78 };
79 
80 struct control {
81 	LV2UI_Controller controller;
82 	LV2UI_Write_Function write_function;
83 
84 	IR * ir;
85 
86 	float port_buffer[IR_N_PORTS];
87 	GSList * port_event_q;
88 	GtkWidget * vbox_top;
89 	GtkWidget * hbox_waitplugin;
90 
91 	float predelay;
92 	float attack;
93 	float attacktime;
94 	float envelope;
95 	float length;
96 	float stretch;
97 	float stereo_ir;
98 
99 	GtkAdjustment * adj_predelay;
100 	GtkAdjustment * adj_attack;
101 	GtkAdjustment * adj_attacktime;
102 	GtkAdjustment * adj_envelope;
103 	GtkAdjustment * adj_length;
104 	GtkAdjustment * adj_stretch;
105 	GtkAdjustment * adj_stereo_in;
106 	GtkAdjustment * adj_stereo_ir;
107 	GtkAdjustment * adj_dry_gain;
108 	GtkAdjustment * adj_wet_gain;
109 
110 	GtkWidget * scale_predelay;
111 	GtkWidget * scale_attack;
112 	GtkWidget * scale_attacktime;
113 	GtkWidget * scale_envelope;
114 	GtkWidget * scale_length;
115 	GtkWidget * scale_stretch;
116 	GtkWidget * scale_stereo_in;
117 	GtkWidget * scale_stereo_ir;
118 
119 	GtkWidget * label_predelay;
120 	GtkWidget * label_attack;
121 	GtkWidget * label_envelope;
122 	GtkWidget * label_length;
123 	GtkWidget * label_stretch;
124 	GtkWidget * label_stereo;
125 	GtkWidget * label_dry_gain;
126 	GtkWidget * label_wet_gain;
127 
128 	GtkWidget * toggle_reverse;
129 	gulong toggle_reverse_cbid;
130 	GtkWidget * toggle_agc_sw;
131 	GtkWidget * toggle_dry_sw;
132 	GtkWidget * toggle_wet_sw;
133 
134 	GtkWidget * meter_L_dry;
135 	GtkWidget * meter_R_dry;
136 	GtkWidget * meter_L_wet;
137 	GtkWidget * meter_R_wet;
138 
139 	GtkWidget * chan_toggle[4];
140 	gulong chan_toggle_cbid[4];
141 	GtkWidget * log_toggle;
142 	gulong log_toggle_cbid;
143 	GtkWidget * wave_display;
144 	GtkWidget * wave_annot_label;
145 	int disp_chan;
146 	GtkWidget * mode_ind;
147 
148 	GtkTreeModel * model_bookmarks; /* GtkTreeModelSortable on ir->store_bookmarks */
149 	GtkListStore * store_files;
150 	GtkWidget * tree_bookmarks;
151 	GtkWidget * tree_files;
152 	int bookmarks_realized;
153 	int files_realized;
154 
155 	gulong files_sel_cbid;
156 	gulong bookmarks_sel_cbid;
157 	guint timeout_tag;
158 	guint gui_load_timeout_tag;
159 	guint reinit_timeout_tag;
160 	guint waitplugin_timeout_tag;
161 
162 	int interrupt_threads;
163 	GThread * gui_load_thread;
164 	GThread * reinit_thread;
165 
166 	int key_pressed;
167 };
168 
169 typedef struct {
170 	uint32_t port_index;
171 	float value;
172 } port_event_t;
173 
174 static void reset_values(struct control * cp);
175 
176 /* adjustments with linear or logarithmic scale */
177 #define LOG_SCALE_MIN       1.0
178 #define LOG_SCALE_MAX       2.0
179 #define INVLOG_SCALE_MIN   10.0
180 #define INVLOG_SCALE_MAX  100.0
181 
182 static void adjustment_changed_cb(GtkAdjustment * adj, gpointer data);
183 
184 /* return 1 if value has actually changed */
set_port_value(struct control * cp,int port_index,float value)185 static int set_port_value(struct control * cp, int port_index, float value) {
186 	if (fabs(cp->port_buffer[port_index] - value) < 0.000001) {
187 		return 0;
188 	}
189 	cp->port_buffer[port_index] = value;
190 	return 1;
191 }
192 
send_port_value_to_host(struct control * cp,int port_index,float value)193 static void send_port_value_to_host(struct control * cp, int port_index, float value) {
194 	if (set_port_value(cp, port_index, value)) {
195 		cp->write_function(cp->controller, port_index, sizeof(float),
196 				   0 /* default format */, &value);
197 	}
198 }
199 
get_adj_index(struct control * cp,GtkAdjustment * adj)200 static int get_adj_index(struct control * cp, GtkAdjustment * adj) {
201 
202 	if (adj == cp->adj_predelay) {
203 		return ADJ_PREDELAY;
204 	} else if (adj == cp->adj_attack) {
205 		return ADJ_ATTACK;
206 	} else if (adj == cp->adj_attacktime) {
207 		return ADJ_ATTACKTIME;
208 	} else if (adj == cp->adj_envelope) {
209 		return ADJ_ENVELOPE;
210 	} else if (adj == cp->adj_length) {
211 		return ADJ_LENGTH;
212 	} else if (adj == cp->adj_stretch) {
213 		return ADJ_STRETCH;
214 	} else if (adj == cp->adj_stereo_in) {
215 		return ADJ_STEREO_IN;
216 	} else if (adj == cp->adj_stereo_ir) {
217 		return ADJ_STEREO_IR;
218 	} else if (adj == cp->adj_dry_gain) {
219 		return ADJ_DRY_GAIN;
220 	} else if (adj == cp->adj_wet_gain) {
221 		return ADJ_WET_GAIN;
222 	}
223 	return -1;
224 }
225 
convert_scale_to_real(int idx,double scale)226 static double convert_scale_to_real(int idx, double scale) {
227 	int log = adj_descr_table[idx].log;
228 	double y;
229 	double min = adj_descr_table[idx].min;
230 	double max = adj_descr_table[idx].max;
231 	double real = 0.0;
232 	if (log == LIN) {
233 		real = scale;
234 	} else if (log == LOG) {
235 		y = log10(scale);
236 		real = min + (y - LOG_SCALE_MIN) / (LOG_SCALE_MAX - LOG_SCALE_MIN) * (max - min);
237 		real = round(10.0 * real) / 10.0; /* one decimal digit */
238 	} else if (log == INVLOG) {
239 		y = exp10(scale);
240 		real = min + (y - INVLOG_SCALE_MIN) / (INVLOG_SCALE_MAX - INVLOG_SCALE_MIN) * (max - min);
241 		real = round(10.0 * real) / 10.0; /* one decimal digit */
242 	}
243 	return real;
244 }
245 
convert_real_to_scale(int idx,double real)246 static double convert_real_to_scale(int idx, double real) {
247 	int log = adj_descr_table[idx].log;
248 	double min = adj_descr_table[idx].min;
249 	double max = adj_descr_table[idx].max;
250 	double scale = 0.0;
251 	if (log == LIN) {
252 		scale = real;
253 	} else if (log == LOG) {
254 		scale = (real - min) / (max - min) *
255 			(LOG_SCALE_MAX - LOG_SCALE_MIN) + LOG_SCALE_MIN;
256 		scale = exp10(scale);
257 	} else if (log == INVLOG) {
258 		scale = (real - min) / (max - min) *
259 			(INVLOG_SCALE_MAX - INVLOG_SCALE_MIN) + INVLOG_SCALE_MIN;
260 		scale = log10(scale);
261 	}
262 	return scale;
263 }
264 
get_adjustment(struct control * cp,GtkAdjustment * adj)265 static double get_adjustment(struct control * cp, GtkAdjustment * adj) {
266 	double y = gtk_adjustment_get_value(adj);
267 	int idx = get_adj_index(cp, adj);
268 	return convert_scale_to_real(idx, y);
269 }
270 
set_adjustment(struct control * cp,GtkAdjustment * adj,double x)271 static void set_adjustment(struct control * cp, GtkAdjustment * adj, double x) {
272 	int idx = get_adj_index(cp, adj);
273 	gtk_adjustment_set_value(adj, convert_real_to_scale(idx, x));
274 }
275 
create_adjustment(int idx,gpointer data)276 static GtkAdjustment * create_adjustment(int idx, gpointer data) {
277 	GtkObject * adj = NULL;
278 	gdouble def = adj_descr_table[idx].def;
279 	gdouble min = adj_descr_table[idx].min;
280 	gdouble max = adj_descr_table[idx].max;
281 	int log = adj_descr_table[idx].log;
282 	if ((log == LOG) || (log == INVLOG)) {
283 		adj = gtk_adjustment_new(convert_real_to_scale(idx, def),
284 					 convert_real_to_scale(idx, min),
285 					 convert_real_to_scale(idx, max) + 1.0,
286 					 0.01, 1.0, 1.0);
287 	} else {
288 		adj = gtk_adjustment_new(def, min, max + 1.0, 0.1, 1.0, 1.0);
289 	}
290 	g_signal_connect(adj, "value_changed", G_CALLBACK(adjustment_changed_cb), data);
291 	return (GtkAdjustment *)adj;
292 }
293 
294 
set_agc_label(struct control * cp)295 static void set_agc_label(struct control * cp) {
296 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cp->toggle_agc_sw))) {
297 		char str[32];
298 		snprintf(str, 32, "Autogain %+.1f dB", cp->ir->autogain_new);
299 		gtk_button_set_label(GTK_BUTTON(cp->toggle_agc_sw), str);
300 	} else {
301 		gtk_button_set_label(GTK_BUTTON(cp->toggle_agc_sw), "Autogain off");
302 	}
303 }
304 
305 #define S1 "<span size=\"small\">"
306 #define S2 "</span>"
307 #define XS1 "<span size=\"x-small\">"
308 #define XS2 "</span>"
set_label(struct control * cp,int idx)309 static void set_label(struct control * cp, int idx) {
310 
311 	char str[1024];
312 	GtkWidget * label = NULL;
313 	float v;
314 
315 	switch (idx) {
316 	case ADJ_PREDELAY:
317 		label = cp->label_predelay;
318 		v = get_adjustment(cp, cp->adj_predelay);
319 		snprintf(str, 1024, S1 "<b>Predelay</b>" S2 "\n" XS1 "%0.1fms" XS2,
320 			 fabs(v)); /* kill the spurious negative zero */
321 		break;
322 	case ADJ_ATTACK:
323 	case ADJ_ATTACKTIME:
324 		label = cp->label_attack; /* spaces eliminate label-positioning problem */
325 		snprintf(str, 1024, S1 "<b>      Attack</b>" S2 "\n" XS1 "%0.0f%%  %0.0fms" XS2,
326 			 get_adjustment(cp, cp->adj_attack),
327 			 get_adjustment(cp, cp->adj_attacktime));
328 		break;
329 	case ADJ_ENVELOPE:
330 		label = cp->label_envelope;
331 		snprintf(str, 1024, S1 "<b>Envelope</b>" S2 "\n" XS1 "%0.1f%%" XS2,
332 			 get_adjustment(cp, cp->adj_envelope));
333 		break;
334 	case ADJ_LENGTH:
335 		label = cp->label_length;
336 		snprintf(str, 1024, S1 "<b>Length</b>" S2 "\n" XS1"%0.1f%%" XS2,
337 			 get_adjustment(cp, cp->adj_length));
338 		break;
339 	case ADJ_STRETCH:
340 		label = cp->label_stretch;
341 		snprintf(str, 1024, S1 "<b>Stretch</b>" S2 "\n" XS1 "%0.1f%%" XS2,
342 			 get_adjustment(cp, cp->adj_stretch));
343 		break;
344 	case ADJ_STEREO_IN:
345 	case ADJ_STEREO_IR:
346 		label = cp->label_stereo;
347 		snprintf(str, 1024, S1 "<b>Stereo in/IR</b>" S2 "\n" XS1 "%0.0f%% / %0.0f%%" XS2,
348 			 get_adjustment(cp, cp->adj_stereo_in),
349 			 get_adjustment(cp, cp->adj_stereo_ir));
350 		break;
351 	case ADJ_DRY_GAIN:
352 		label = cp->label_dry_gain;
353 		v = get_adjustment(cp, cp->adj_dry_gain);
354 		if (v > 0.0) {
355 			snprintf(str, 1024, S1 "%+0.1f dB" S2, v);
356 		} else if (v == 0.0) {
357 			snprintf(str, 1024, S1 "0.0 dB" S2);
358 		} else if (v > -90.0) {
359 			snprintf(str, 1024, S1 "%+0.1f dB" S2, v);
360 		} else {
361 			snprintf(str, 1024, S1 "mute" S2);
362 		}
363 		break;
364 	case ADJ_WET_GAIN:
365 		label = cp->label_wet_gain;
366 		v = get_adjustment(cp, cp->adj_wet_gain);
367 		if (v > 0.0) {
368 			snprintf(str, 1024, S1 "%+0.1f dB" S2, v);
369 		} else if (v == 0.0) {
370 			snprintf(str, 1024, S1 "0.0 dB" S2);
371 		} else if (v > -90.0) {
372 			snprintf(str, 1024, S1 "%+0.1f dB" S2, v);
373 		} else {
374 			snprintf(str, 1024, S1 "mute" S2);
375 		}
376 		break;
377 	}
378 
379 	gtk_label_set_markup(GTK_LABEL(label), str);
380 }
381 
refresh_gui_on_load(struct control * cp)382 static void refresh_gui_on_load(struct control * cp) {
383 	char str[1024];
384 	const char * chn = (cp->ir->nchan > 1) ? "channels" : "channel";
385 	float secs = (float)cp->ir->source_nfram / cp->ir->source_samplerate;
386 	char * filename_esc = g_markup_escape_text(cp->ir->source_path, -1);
387 	if (cp->ir->source_samplerate == (unsigned int)cp->ir->sample_rate) {
388 		snprintf(str, 1024,
389 			 XS1 "<b>%s</b>" XS2 "\n"
390 			 S1 "%d %s, %d samples, %d Hz, %.3f seconds" S2,
391 			 filename_esc,
392 			 cp->ir->nchan, chn, cp->ir->source_nfram,
393 			 cp->ir->source_samplerate, secs);
394 	} else {
395 		snprintf(str, 1024,
396 			 XS1 "<b>%s</b>" XS2 "\n"
397 			 S1 "%d %s, %d samples, %d Hz (resampled to %d Hz), %.3f seconds" S2,
398 			 filename_esc,
399 			 cp->ir->nchan, chn, cp->ir->source_nfram,
400 			 cp->ir->source_samplerate, (int)cp->ir->sample_rate, secs);
401 	}
402 	free(filename_esc);
403 	gtk_label_set_markup(GTK_LABEL(cp->wave_annot_label), str);
404 
405 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cp->chan_toggle[0]), FALSE);
406 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cp->chan_toggle[0]), TRUE);
407 	gtk_widget_set_sensitive(cp->chan_toggle[0], cp->ir->nchan > 1);
408 	for (int i = 1; i < 4; i++) {
409 		gtk_widget_set_sensitive(cp->chan_toggle[i], i < cp->ir->nchan);
410 	}
411 
412 	set_agc_label(cp);
413 	ir_modeind_set_channels(IR_MODEIND(cp->mode_ind), cp->ir->nchan);
414 }
415 
gui_load_thread(gpointer data)416 static gpointer gui_load_thread(gpointer data) {
417 	struct control * cp = (struct control*)data;
418 	int r = cp->ir->resample_init(cp->ir);
419 	if (r == 0) {
420 		while (r == 0) {
421 			r = cp->ir->resample_do(cp->ir);
422 			if (cp->interrupt_threads) {
423 				break;
424 			}
425 		}
426 		cp->ir->resample_cleanup(cp->ir);
427 	}
428 	if (r >= 0) {
429 		cp->ir->prepare_convdata(cp->ir);
430 		cp->ir->init_conv(cp->ir);
431 	}
432 	cp->ir->reinit_running = 0;
433 	return NULL;
434 }
435 
gui_load_timeout_callback(gpointer data)436 static gint gui_load_timeout_callback(gpointer data) {
437 	struct control * cp = (struct control*)data;
438 	if (cp->ir->reinit_running) {
439 		ir_wavedisplay_set_progress(IR_WAVEDISPLAY(cp->wave_display), cp->ir->src_progress);
440 		return TRUE;
441 	}
442 	g_thread_join(cp->gui_load_thread);
443 	cp->gui_load_thread = NULL;
444 	ir_wavedisplay_set_progress(IR_WAVEDISPLAY(cp->wave_display), -1.0);
445 	ir_wavedisplay_set_message(IR_WAVEDISPLAY(cp->wave_display), NULL);
446 	refresh_gui_on_load(cp);
447 	reset_values(cp);
448 	cp->gui_load_timeout_tag = 0;
449 	return FALSE;
450 }
451 
gui_load_sndfile(struct control * cp,char * filename)452 static void gui_load_sndfile(struct control * cp, char * filename) {
453 
454 	if (cp->ir->reinit_running || cp->gui_load_thread) {
455 		return;
456 	}
457 
458 	if (cp->ir->source_path) {
459 		free(cp->ir->source_path);
460 	}
461 	cp->ir->source_path = strdup(filename);
462 	ir_wavedisplay_set_message(IR_WAVEDISPLAY(cp->wave_display), "Loading...");
463 	ir_wavedisplay_set_progress(IR_WAVEDISPLAY(cp->wave_display), 0.0);
464 	if (cp->ir->load_sndfile(cp->ir) < 0) {
465 		fprintf(stderr, "IR: load_sndfile error\n");
466 		ir_wavedisplay_set_message(IR_WAVEDISPLAY(cp->wave_display), NULL);
467 	} else {
468 		uint64_t hash = fhash(filename);
469 		float value0, value1, value2;
470 		ports_from_fhash(hash, &value0, &value1, &value2);
471 		cp->write_function(cp->controller, IR_PORT_FHASH_0, sizeof(float),
472 				   0 /* default format */, &value0);
473 		cp->write_function(cp->controller, IR_PORT_FHASH_1, sizeof(float),
474 				   0 /* default format */, &value1);
475 		cp->write_function(cp->controller, IR_PORT_FHASH_2, sizeof(float),
476 				   0 /* default format */, &value2);
477 
478 		cp->ir->reinit_running = 1;
479 		cp->gui_load_thread = g_thread_new("gui_load_thread", gui_load_thread, cp);
480 		cp->gui_load_timeout_tag = g_timeout_add(100, gui_load_timeout_callback, cp);
481 	}
482 }
483 
browse_button_clicked(GtkWidget * w,gpointer data)484 static void browse_button_clicked(GtkWidget * w, gpointer data) {
485 
486 	struct control * cp = (struct control *)data;
487 	GtkWidget * dialog;
488 	if (cp->ir->reinit_running) {
489 		return;
490 	}
491 	dialog = gtk_file_chooser_dialog_new("Open File",
492 					     NULL,
493 					     GTK_FILE_CHOOSER_ACTION_OPEN,
494 					     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
495 					     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
496 					     NULL);
497 
498 	GtkFileFilter * filter = gtk_file_filter_new();
499 	gtk_file_filter_set_name(filter, "All files");
500 	gtk_file_filter_add_pattern(filter, "*");
501 	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
502 
503 	filter = gtk_file_filter_new();
504 	gtk_file_filter_set_name(filter, "Soundfiles");
505 	gtk_file_filter_add_pattern(filter, "*.wav");
506 	gtk_file_filter_add_pattern(filter, "*.WAV");
507 	gtk_file_filter_add_pattern(filter, "*.aiff");
508 	gtk_file_filter_add_pattern(filter, "*.AIFF");
509 	gtk_file_filter_add_pattern(filter, "*.au");
510 	gtk_file_filter_add_pattern(filter, "*.AU");
511 	gtk_file_filter_add_pattern(filter, "*.flac");
512 	gtk_file_filter_add_pattern(filter, "*.FLAC");
513 	gtk_file_filter_add_pattern(filter, "*.ogg");
514 	gtk_file_filter_add_pattern(filter, "*.OGG");
515 	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
516 	gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
517 
518 	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
519 		char * filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (dialog));
520 		gui_load_sndfile(cp, filename);
521 		char * dirname = g_path_get_dirname(filename);
522 		load_files(cp->store_files, dirname);
523 		GtkTreeSelection * select =
524 			gtk_tree_view_get_selection(GTK_TREE_VIEW(cp->tree_bookmarks));
525 		g_signal_handler_block(select, cp->bookmarks_sel_cbid);
526 		select_entry(cp->model_bookmarks, select, dirname);
527 		g_signal_handler_unblock(select, cp->bookmarks_sel_cbid);
528 
529 		select = gtk_tree_view_get_selection(GTK_TREE_VIEW(cp->tree_files));
530 		g_signal_handler_block(select, cp->files_sel_cbid);
531 		select_entry(GTK_TREE_MODEL(cp->store_files), select, filename);
532 		g_signal_handler_unblock(select, cp->files_sel_cbid);
533 		g_free(filename);
534 		g_free(dirname);
535 	}
536 	gtk_widget_destroy(dialog);
537 }
538 
key_pressed_cb(GtkWidget * widget,GdkEventKey * event,gpointer data)539 static int key_pressed_cb(GtkWidget * widget, GdkEventKey * event, gpointer data) {
540 	struct control * cp = (struct control *)data;
541 	if (cp->ir->reinit_running) {
542 		return FALSE;
543 	}
544 	cp->key_pressed = 1;
545 	return FALSE;
546 }
547 
save_value(struct control * cp,int port,float value)548 static void save_value(struct control * cp, int port, float value) {
549 	switch (port) {
550 	case IR_PORT_PREDELAY: cp->predelay = value;
551 		break;
552 	case IR_PORT_ATTACK: cp->attack = value;
553 		break;
554 	case IR_PORT_ATTACKTIME: cp->attacktime = value;
555 		break;
556 	case IR_PORT_ENVELOPE: cp->envelope = value;
557 		break;
558 	case IR_PORT_LENGTH: cp->length = value;
559 		break;
560 	case IR_PORT_STRETCH: cp->stretch = value;
561 		break;
562 	case IR_PORT_STEREO_IR: cp->stereo_ir = value;
563 		break;
564 	}
565 }
566 
reset_values(struct control * cp)567 static void reset_values(struct control * cp) {
568 	set_adjustment(cp, cp->adj_predelay, cp->predelay);
569 	set_adjustment(cp, cp->adj_attack, cp->attack);
570 	set_adjustment(cp, cp->adj_attacktime, cp->attacktime);
571 	set_adjustment(cp, cp->adj_envelope, cp->envelope);
572 	set_adjustment(cp, cp->adj_length, cp->length);
573 	set_adjustment(cp, cp->adj_stretch, cp->stretch);
574 	set_adjustment(cp, cp->adj_stereo_ir, cp->stereo_ir);
575 }
576 
reset_value(struct control * cp,GtkAdjustment * adj)577 static void reset_value(struct control * cp, GtkAdjustment * adj) {
578 	if (adj == cp->adj_predelay) {
579 		set_adjustment(cp, adj, cp->predelay);
580 	} else if (adj == cp->adj_attack) {
581 		set_adjustment(cp, adj, cp->attack);
582 	} else if (adj == cp->adj_attacktime) {
583 		set_adjustment(cp, adj, cp->attacktime);
584 	} else if (adj == cp->adj_envelope) {
585 		set_adjustment(cp, adj, cp->envelope);
586 	} else if (adj == cp->adj_length) {
587 		set_adjustment(cp, adj, cp->length);
588 	} else if (adj == cp->adj_stretch) {
589 		set_adjustment(cp, adj, cp->stretch);
590 	} else if (adj == cp->adj_stereo_ir) {
591 		set_adjustment(cp, adj, cp->stereo_ir);
592 	}
593 }
594 
key_released_cb(GtkWidget * widget,GdkEventKey * event,gpointer data)595 static int key_released_cb(GtkWidget * widget, GdkEventKey * event, gpointer data) {
596 	struct control * cp = (struct control *)data;
597 	GtkAdjustment * adj = NULL;
598 	int port = 0;
599 
600 	cp->key_pressed = 0;
601 	if (cp->ir->reinit_running) {
602 		return FALSE;
603 	}
604 
605 	if (widget == cp->scale_predelay) {
606 		adj = cp->adj_predelay;
607 		port = IR_PORT_PREDELAY;
608 	} else if (widget == cp->scale_attack) {
609 		adj = cp->adj_attack;
610 		port = IR_PORT_ATTACK;
611 	} else if (widget == cp->scale_attacktime) {
612 		adj = cp->adj_attacktime;
613 		port = IR_PORT_ATTACKTIME;
614 	} else if (widget == cp->scale_envelope) {
615 		adj = cp->adj_envelope;
616 		port = IR_PORT_ENVELOPE;
617 	} else if (widget == cp->scale_length) {
618 		adj = cp->adj_length;
619 		port = IR_PORT_LENGTH;
620 	} else if (widget == cp->scale_stretch) {
621 		adj = cp->adj_stretch;
622 		port = IR_PORT_STRETCH;
623 		cp->ir->resample_pending = 1;
624 	} else if (widget == cp->scale_stereo_ir) {
625 		adj = cp->adj_stereo_ir;
626 		port = IR_PORT_STEREO_IR;
627 	}
628 
629 	if (port == 0) {
630 		return FALSE;
631 	}
632 
633 	float value = get_adjustment(cp, adj);
634 	save_value(cp, port, value);
635 
636 	//printf("on button_release adj value = %f\n", value);
637 	send_port_value_to_host(cp, port, value);
638 
639 	cp->ir->run = 0;
640 	cp->ir->reinit_pending = 1;
641 	return FALSE;
642 }
643 
update_envdisplay(struct control * cp)644 static void update_envdisplay(struct control * cp) {
645 	int attack_time_s =
646 		get_adjustment(cp, cp->adj_attacktime) *
647 		(float)cp->ir->sample_rate / 1000.0;
648 	float attack_pc = get_adjustment(cp, cp->adj_attack);
649 	float env_pc = get_adjustment(cp, cp->adj_envelope);
650 	float length_pc = get_adjustment(cp, cp->adj_length);
651 	int reverse = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cp->toggle_reverse)) ? 1 : 0;
652 	ir_wavedisplay_set_envparams(IR_WAVEDISPLAY(cp->wave_display),
653 				     attack_time_s, attack_pc,
654 				     env_pc, length_pc, reverse);
655 }
656 
adjustment_changed_cb(GtkAdjustment * adj,gpointer data)657 static void adjustment_changed_cb(GtkAdjustment * adj, gpointer data) {
658 	struct control * cp = (struct control *)data;
659 	float value = get_adjustment(cp, adj);
660 	int port = 0;
661 	int update_ui = 0;
662 	int label_idx = 0;
663 	if (adj == cp->adj_predelay) {
664 		label_idx = ADJ_PREDELAY;
665 	} else if (adj == cp->adj_attack) {
666 		label_idx = ADJ_ATTACK;
667 		update_ui = 1;
668 	} else if (adj == cp->adj_attacktime) {
669 		label_idx = ADJ_ATTACK;
670 		update_ui = 1;
671 	} else if (adj == cp->adj_envelope) {
672 		label_idx = ADJ_ENVELOPE;
673 		update_ui = 1;
674 	} else if (adj == cp->adj_length) {
675 		label_idx = ADJ_LENGTH;
676 		update_ui = 1;
677 	} else if (adj == cp->adj_stretch) {
678 		label_idx = ADJ_STRETCH;
679 	} else if (adj == cp->adj_stereo_in) {
680 		label_idx = ADJ_STEREO_IN;
681 		port = IR_PORT_STEREO_IN;
682 	} else if (adj == cp->adj_stereo_ir) {
683 		label_idx = ADJ_STEREO_IR;
684 	} else if (adj == cp->adj_dry_gain) {
685 		label_idx = ADJ_DRY_GAIN;
686 		port = IR_PORT_DRY_GAIN;
687 	} else if (adj == cp->adj_wet_gain) {
688 		label_idx = ADJ_WET_GAIN;
689 		port = IR_PORT_WET_GAIN;
690 	}
691 
692 	if (cp->ir->reinit_running && !port) {
693 		return;
694 	}
695 
696 	set_label(cp, label_idx);
697 
698 	if (port == 0) {
699 		if (cp->key_pressed) {
700 			save_value(cp, port, value);
701 			if (update_ui) {
702 				update_envdisplay(cp);
703 			}
704 		} else {
705 			reset_value(cp, adj);
706 		}
707 		return;
708 	}
709 
710 	//printf("adj changed to %f\n", value);
711 	send_port_value_to_host(cp, port, value);
712 }
713 
toggle_button_cb(GtkWidget * widget,gpointer data)714 static void toggle_button_cb(GtkWidget * widget, gpointer data) {
715 
716 	struct control * cp = (struct control *)data;
717 	if (cp->ir->reinit_running && (widget == cp->toggle_reverse)) {
718 		g_signal_handler_block(widget, cp->toggle_reverse_cbid);
719 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
720 					     !(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))));
721 		g_signal_handler_unblock(widget, cp->toggle_reverse_cbid);
722 		return;
723 	}
724 	int port = 0;
725 	float value;
726 	const char * text;
727 	if (widget == cp->toggle_dry_sw) {
728 		port = IR_PORT_DRY_SW;
729 	} else if (widget == cp->toggle_wet_sw) {
730 		port = IR_PORT_WET_SW;
731 	} else if (widget == cp->toggle_reverse) {
732 		port = IR_PORT_REVERSE;
733 	}
734 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
735 		value = 1.0f;
736 		text = "ON";
737 	} else {
738 		value = 0.0f;
739 		text = "off";
740 	}
741 	send_port_value_to_host(cp, port, value);
742 
743 	if (port == IR_PORT_REVERSE) {
744 		cp->ir->run = 0;
745 		cp->ir->reinit_pending = 1;
746 		update_envdisplay(cp);
747 	} else if ((port == IR_PORT_DRY_SW) ||
748 		   (port == IR_PORT_WET_SW)) {
749 		gtk_button_set_label(GTK_BUTTON(widget), text);
750 	}
751 }
752 
irctrl_add_row(GtkWidget * table,int row,GtkWidget ** label,GtkAdjustment * adj,GtkWidget ** scale,int idx,gpointer data)753 static void irctrl_add_row(GtkWidget * table, int row, GtkWidget ** label, GtkAdjustment * adj,
754 			   GtkWidget ** scale, int idx, gpointer data) {
755 
756 	struct control * cp = (struct control *)data;
757 	*label = gtk_label_new("");
758 	gtk_label_set_justify(GTK_LABEL(*label), GTK_JUSTIFY_RIGHT);
759 	set_label(cp, idx);
760 	gtk_misc_set_alignment(GTK_MISC(*label), 1.0f, 0.0f);
761 	gtk_table_attach(GTK_TABLE(table), *label, 0, 1, row, row + 1,
762 			 GTK_FILL, GTK_FILL, 0, 0);
763 
764 	*scale = gtk_hscale_new(adj);
765 	gtk_scale_set_draw_value(GTK_SCALE(*scale), FALSE);
766 	gtk_widget_add_events(*scale, GDK_BUTTON_RELEASE_MASK);
767 	g_signal_connect(*scale, "button_press_event",
768 			 G_CALLBACK(key_pressed_cb), data);
769 	g_signal_connect(*scale, "button_release_event",
770 			 G_CALLBACK(key_released_cb), data);
771 	gtk_table_attach_defaults(GTK_TABLE(table), *scale, 1, 3, row, row + 1);
772 }
773 
irctrl_add_row2(GtkWidget * table,int row,GtkWidget ** label,GtkAdjustment * adj1,GtkAdjustment * adj2,GtkWidget ** scale1,GtkWidget ** scale2,int idx,gpointer data)774 static void irctrl_add_row2(GtkWidget * table, int row,
775 			    GtkWidget ** label, GtkAdjustment * adj1, GtkAdjustment * adj2,
776 			    GtkWidget ** scale1, GtkWidget ** scale2,
777 			    int idx, gpointer data) {
778 
779 	struct control * cp = (struct control *)data;
780 	*label = gtk_label_new("");
781 	gtk_label_set_justify(GTK_LABEL(*label), GTK_JUSTIFY_RIGHT);
782 	set_label(cp, idx);
783 	gtk_misc_set_alignment(GTK_MISC(*label), 1.0f, 0.0f);
784 	gtk_table_attach(GTK_TABLE(table), *label, 0, 1, row, row + 1,
785 			 GTK_FILL, GTK_FILL, 0, 0);
786 
787 	*scale1 = gtk_hscale_new(adj1);
788 	gtk_scale_set_draw_value(GTK_SCALE(*scale1), FALSE);
789 	gtk_widget_add_events(*scale1, GDK_BUTTON_RELEASE_MASK);
790 	g_signal_connect(*scale1, "button_press_event",
791 			 G_CALLBACK(key_pressed_cb), data);
792 	g_signal_connect(*scale1, "button_release_event",
793 			 G_CALLBACK(key_released_cb), data);
794 	gtk_table_attach_defaults(GTK_TABLE(table), *scale1, 1, 2, row, row + 1);
795 
796 	*scale2 = gtk_hscale_new(adj2);
797 	gtk_scale_set_draw_value(GTK_SCALE(*scale2), FALSE);
798 	gtk_widget_add_events(*scale2, GDK_BUTTON_RELEASE_MASK);
799 	g_signal_connect(*scale2, "button_press_event",
800 			 G_CALLBACK(key_pressed_cb), data);
801 	g_signal_connect(*scale2, "button_release_event",
802 			 G_CALLBACK(key_released_cb), data);
803 	gtk_table_attach_defaults(GTK_TABLE(table), *scale2, 2, 3, row, row + 1);
804 }
805 
make_irctrl_frame(struct control * cp)806 static GtkWidget * make_irctrl_frame(struct control * cp) {
807 
808 	GtkWidget * frame = gtk_frame_new(NULL);
809 	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
810 
811 	GtkWidget * table = gtk_table_new(6, 3, FALSE);
812 	gtk_table_set_row_spacings(GTK_TABLE(table), PAD);
813 	gtk_table_set_col_spacings(GTK_TABLE(table), 2*PAD);
814 	gtk_container_add(GTK_CONTAINER(frame), table);
815 
816 	/* Predelay */
817 	cp->adj_predelay = create_adjustment(ADJ_PREDELAY, cp);
818 	irctrl_add_row(table, 0, &cp->label_predelay, cp->adj_predelay, &cp->scale_predelay,
819 		       ADJ_PREDELAY, cp);
820 
821 	/* Attack */
822 	cp->adj_attack = create_adjustment(ADJ_ATTACK, cp);
823 	cp->adj_attacktime = create_adjustment(ADJ_ATTACKTIME, cp);
824 	irctrl_add_row2(table, 1, &cp->label_attack, cp->adj_attack, cp->adj_attacktime,
825 			&cp->scale_attack, &cp->scale_attacktime,
826 			ADJ_ATTACK, cp);
827 
828 	/* Envelope */
829 	cp->adj_envelope = create_adjustment(ADJ_ENVELOPE, cp);
830 	irctrl_add_row(table, 2, &cp->label_envelope, cp->adj_envelope, &cp->scale_envelope,
831 		       ADJ_ENVELOPE, cp);
832 
833 	/* Length */
834 	cp->adj_length = create_adjustment(ADJ_LENGTH, cp);
835 	irctrl_add_row(table, 3, &cp->label_length, cp->adj_length, &cp->scale_length,
836 		       ADJ_LENGTH, cp);
837 
838 	/* Stretch */
839 	cp->adj_stretch = create_adjustment(ADJ_STRETCH, cp);
840 	irctrl_add_row(table, 4, &cp->label_stretch, cp->adj_stretch, &cp->scale_stretch,
841 		       ADJ_STRETCH, cp);
842 
843 	/* Stereo width in/IR */
844 	cp->adj_stereo_in = create_adjustment(ADJ_STEREO_IN, cp);
845 	cp->adj_stereo_ir = create_adjustment(ADJ_STEREO_IR, cp);
846 	irctrl_add_row2(table, 5, &cp->label_stereo, cp->adj_stereo_in, cp->adj_stereo_ir,
847 			&cp->scale_stereo_in, &cp->scale_stereo_ir,
848 			ADJ_STEREO_IN, cp);
849 
850 	gtk_scale_add_mark(GTK_SCALE(cp->scale_stretch), 100.0, GTK_POS_BOTTOM, XS1 " " XS2);
851 	gtk_scale_add_mark(GTK_SCALE(cp->scale_stereo_in), 100.0, GTK_POS_BOTTOM, XS1 " " XS2);
852 	gtk_scale_add_mark(GTK_SCALE(cp->scale_stereo_ir), 100.0, GTK_POS_BOTTOM, XS1 " " XS2);
853 
854 	gtk_widget_set_size_request(cp->scale_attack, 125, -1);
855 	gtk_widget_set_size_request(cp->scale_attacktime, 125, -1);
856 	return frame;
857 }
858 
agc_toggle_cb(GtkWidget * widget,gpointer data)859 static void agc_toggle_cb(GtkWidget * widget, gpointer data) {
860 	struct control * cp = (struct control *)data;
861 	float value;
862 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
863 		value = 1.0f;
864 	} else {
865 		value = 0.0f;
866 	}
867 
868 	send_port_value_to_host(cp, IR_PORT_AGC_SW, value);
869 	set_agc_label(cp);
870 }
871 
make_mixer_frame(struct control * cp)872 static GtkWidget * make_mixer_frame(struct control * cp) {
873 
874 	GtkWidget * frame = gtk_frame_new(NULL);
875 	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
876 
877 	GtkWidget * vbox_top = gtk_vbox_new(FALSE, 0);
878 	gtk_container_add(GTK_CONTAINER(frame), vbox_top);
879 
880 	GtkWidget * hbox = gtk_hbox_new(TRUE, PAD);
881 	gtk_box_pack_start(GTK_BOX(vbox_top), hbox, TRUE, TRUE, PAD);
882 
883 	cp->adj_dry_gain = create_adjustment(ADJ_DRY_GAIN, cp);
884 	cp->adj_wet_gain = create_adjustment(ADJ_WET_GAIN, cp);
885 
886 	/* Dry */
887 	GtkWidget * vbox = gtk_vbox_new(FALSE, 0);
888 	gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, PAD);
889 
890 	GtkWidget * label = gtk_label_new("");
891 	gtk_label_set_markup(GTK_LABEL(label), S1 "<b>Dry</b>" S2);
892 	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, PAD);
893 
894 	GtkWidget * hbox_i = gtk_hbox_new(FALSE, 0);
895 	gtk_box_pack_start(GTK_BOX(vbox), hbox_i, TRUE, TRUE, PAD);
896 
897 	GtkWidget * scale;
898 	scale = gtk_vscale_new(cp->adj_dry_gain);
899 	gtk_range_set_inverted(GTK_RANGE(scale), TRUE);
900 	gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE);
901 	gtk_scale_add_mark(GTK_SCALE(scale), convert_real_to_scale(ADJ_DRY_GAIN, 0.0),
902 			   GTK_POS_RIGHT, XS1 " " XS2);
903 	gtk_box_pack_start(GTK_BOX(hbox_i), scale, TRUE, TRUE, 0);
904 
905 	cp->meter_L_dry = ir_meter_new();
906 	gtk_widget_set_size_request(cp->meter_L_dry, 5, -1);
907 	gtk_box_pack_start(GTK_BOX(hbox_i), cp->meter_L_dry, FALSE, TRUE, 1);
908 
909 	cp->meter_R_dry = ir_meter_new();
910 	gtk_widget_set_size_request(cp->meter_R_dry, 5, -1);
911 	gtk_box_pack_start(GTK_BOX(hbox_i), cp->meter_R_dry, FALSE, TRUE, 0);
912 
913 
914 	cp->label_dry_gain = gtk_label_new("0.0 dB");
915 	gtk_box_pack_start(GTK_BOX(vbox), cp->label_dry_gain, FALSE, TRUE, PAD);
916 
917 	cp->toggle_dry_sw = gtk_toggle_button_new_with_label("off");
918 	g_signal_connect(cp->toggle_dry_sw, "toggled",
919 			 G_CALLBACK(toggle_button_cb), cp);
920 	gtk_box_pack_start(GTK_BOX(vbox), cp->toggle_dry_sw, FALSE, FALSE, PAD);
921 
922 	/* Wet */
923 	vbox = gtk_vbox_new(FALSE, 0);
924 	gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, PAD);
925 
926 	label = gtk_label_new("");
927 	gtk_label_set_markup(GTK_LABEL(label), S1 "<b>Wet</b>" S2);
928 	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, PAD);
929 
930 	hbox_i = gtk_hbox_new(FALSE, 0);
931 	gtk_box_pack_start(GTK_BOX(vbox), hbox_i, TRUE, TRUE, PAD);
932 
933 	scale = gtk_vscale_new(cp->adj_wet_gain);
934 	gtk_range_set_inverted(GTK_RANGE(scale), TRUE);
935 	gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE);
936 	gtk_scale_add_mark(GTK_SCALE(scale), convert_real_to_scale(ADJ_WET_GAIN, 0.0),
937 			   GTK_POS_RIGHT, XS1 " " XS2);
938 	gtk_box_pack_start(GTK_BOX(hbox_i), scale, TRUE, TRUE, 0);
939 
940 	cp->meter_L_wet = ir_meter_new();
941 	gtk_widget_set_size_request(cp->meter_L_wet, 5, -1);
942 	gtk_box_pack_start(GTK_BOX(hbox_i), cp->meter_L_wet, FALSE, TRUE, 1);
943 
944 	cp->meter_R_wet = ir_meter_new();
945 	gtk_widget_set_size_request(cp->meter_R_wet, 5, -1);
946 	gtk_box_pack_start(GTK_BOX(hbox_i), cp->meter_R_wet, FALSE, TRUE, 0);
947 
948 
949 	cp->label_wet_gain = gtk_label_new("-6.0 dB");
950 	gtk_box_pack_start(GTK_BOX(vbox), cp->label_wet_gain, FALSE, TRUE, PAD);
951 
952 	cp->toggle_wet_sw = gtk_toggle_button_new_with_label("off");
953 	g_signal_connect(cp->toggle_wet_sw, "toggled",
954 			 G_CALLBACK(toggle_button_cb), cp);
955 	gtk_box_pack_start(GTK_BOX(vbox), cp->toggle_wet_sw, FALSE, FALSE, PAD);
956 
957 	/* Autogain */
958 	hbox = gtk_hbox_new(FALSE, PAD);
959 	gtk_box_pack_start(GTK_BOX(vbox_top), hbox, FALSE, TRUE, 0);
960 	cp->toggle_agc_sw = gtk_toggle_button_new_with_label("Autogain");
961 	g_signal_connect(cp->toggle_agc_sw, "toggled",
962 			 G_CALLBACK(agc_toggle_cb), cp);
963 	gtk_box_pack_start(GTK_BOX(hbox), cp->toggle_agc_sw, TRUE, TRUE, PAD);
964 
965 	return frame;
966 }
967 
add_bookmark_button_clicked(GtkWidget * w,gpointer data)968 static void add_bookmark_button_clicked(GtkWidget * w, gpointer data) {
969 	struct control * cp = (struct control *)data;
970 	GtkWidget * dialog;
971 	dialog = gtk_file_chooser_dialog_new("Select directory",
972 					     NULL,
973 					     GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
974 					     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
975 					     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
976 					     NULL);
977 
978 	GtkWidget * hbox = gtk_hbox_new(FALSE, PAD);
979 	GtkWidget * label = gtk_label_new("Bookmark name (optional):");
980 	gtk_widget_show(label);
981 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, PAD);
982 	GtkWidget * entry = gtk_entry_new();
983 	gtk_widget_show(entry);
984 	gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, PAD);
985 	gtk_widget_show(hbox);
986 	gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), hbox);
987 
988 	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
989 		char * filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
990 		const gchar * bookmark = gtk_entry_get_text(GTK_ENTRY(entry));
991 		char * name;
992 
993 		if ((bookmark != NULL) && (strlen(bookmark) > 0)) {
994 			name = strdup(bookmark);
995 		} else {
996 			/* use last path component as name */
997 			name = g_path_get_basename(filename);
998 		}
999 
1000 		char * path = lookup_bookmark_in_store(cp->model_bookmarks, name);
1001 		if (path) {
1002 			fprintf(stderr, "IR: bookmark already exists!\n");
1003 			g_free(path);
1004 		} else {
1005 			GtkTreeIter iter;
1006 			gtk_list_store_append(cp->ir->store_bookmarks, &iter);
1007 			gtk_list_store_set(cp->ir->store_bookmarks, &iter,
1008 					   0, name, 1, filename, -1);
1009 			store_bookmark(cp->ir->keyfile, name, filename);
1010 		}
1011 		g_free(name);
1012 		g_free(filename);
1013 	}
1014 	gtk_widget_destroy(dialog);
1015 }
1016 
del_bookmark_button_clicked(GtkWidget * w,gpointer data)1017 static void del_bookmark_button_clicked(GtkWidget * w, gpointer data) {
1018 	struct control * cp = (struct control *)data;
1019         GtkTreeIter iter; /* on sorted model */
1020         GtkTreeIter real_iter; /* on underlying model */
1021         GtkTreeModel * model;
1022 	GtkTreeSelection * select;
1023         gchar * key;
1024 
1025 	select = gtk_tree_view_get_selection(GTK_TREE_VIEW(cp->tree_bookmarks));
1026         if (gtk_tree_selection_get_selected(select, &model, &iter)) {
1027                 gtk_tree_model_get(model, &iter, 0, &key, -1);
1028 		delete_bookmark(cp->ir->keyfile, key);
1029 		gtk_tree_model_sort_convert_iter_to_child_iter(
1030 			GTK_TREE_MODEL_SORT(cp->model_bookmarks),
1031 			&real_iter, &iter);
1032 		gtk_list_store_remove(cp->ir->store_bookmarks, &real_iter);
1033                 g_free(key);
1034         }
1035 }
1036 
bookmarks_selection_changed_cb(GtkTreeSelection * selection,gpointer data)1037 static void bookmarks_selection_changed_cb(GtkTreeSelection * selection, gpointer data) {
1038 	struct control * cp = (struct control *)data;
1039         GtkTreeIter iter;
1040         GtkTreeModel * model;
1041         gchar * bookmark;
1042         gchar * dirpath;
1043 
1044         if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
1045                 gtk_tree_model_get(model, &iter, 0, &bookmark, 1, &dirpath, -1);
1046 		load_files(cp->store_files, dirpath);
1047                 g_free(bookmark);
1048                 g_free(dirpath);
1049         }
1050 }
1051 
files_selection_changed_cb(GtkTreeSelection * selection,gpointer data)1052 static void files_selection_changed_cb(GtkTreeSelection * selection, gpointer data) {
1053 
1054 	struct control * cp = (struct control *)data;
1055 
1056         GtkTreeIter iter;
1057         GtkTreeModel * model;
1058         gchar * filename;
1059 
1060         if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
1061                 gtk_tree_model_get(model, &iter, 1, &filename, -1);
1062 		if (g_file_test(filename, G_FILE_TEST_IS_DIR)) {
1063 			load_files(cp->store_files, filename);
1064 			/* clear bookmark selection so it is clickable again
1065 			 * for fast upwards navigation */
1066 			gtk_tree_selection_unselect_all(
1067 				gtk_tree_view_get_selection(GTK_TREE_VIEW(cp->tree_bookmarks)));
1068 		} else {
1069 			gui_load_sndfile(cp, filename);
1070 		}
1071                 g_free(filename);
1072         }
1073 }
1074 
tree_view_realized_cb(GtkWidget * widget,gpointer data)1075 static void tree_view_realized_cb(GtkWidget * widget, gpointer data) {
1076 
1077 	struct control * cp = (struct control *)data;
1078 
1079 	if (widget == cp->tree_bookmarks) {
1080 		cp->bookmarks_realized = 1;
1081 	} else if (widget == cp->tree_files) {
1082 		cp->files_realized = 1;
1083 	}
1084 
1085 	if (cp->bookmarks_realized && cp->files_realized && cp->ir->source_path) {
1086 		/* select appropriate entries if plugin loaded */
1087 		char * dirpath = g_path_get_dirname(cp->ir->source_path);
1088 		load_files(cp->store_files, dirpath);
1089 
1090 		GtkTreeSelection * select =
1091 			gtk_tree_view_get_selection(GTK_TREE_VIEW(cp->tree_bookmarks));
1092 		g_signal_handler_block(select, cp->bookmarks_sel_cbid);
1093 		select_entry(cp->model_bookmarks, select, dirpath);
1094 		g_signal_handler_unblock(select, cp->bookmarks_sel_cbid);
1095 
1096 		select = gtk_tree_view_get_selection(GTK_TREE_VIEW(cp->tree_files));
1097 		g_signal_handler_block(select, cp->files_sel_cbid);
1098 		select_entry(GTK_TREE_MODEL(cp->store_files), select, cp->ir->source_path);
1099 		g_signal_handler_unblock(select, cp->files_sel_cbid);
1100 
1101 		g_free(dirpath);
1102 
1103 		refresh_gui_on_load(cp);
1104 	}
1105 }
1106 
make_lists_box(struct control * cp)1107 static GtkWidget * make_lists_box(struct control * cp) {
1108 
1109 	GtkWidget * hbox = gtk_hbox_new(FALSE, 0);
1110 	GtkWidget * vbox = gtk_vbox_new(FALSE, 0);
1111 
1112 	GtkTreeModel * model = GTK_TREE_MODEL(cp->ir->store_bookmarks);
1113 	cp->model_bookmarks = gtk_tree_model_sort_new_with_model(model);
1114 	cp->tree_bookmarks = gtk_tree_view_new_with_model(cp->model_bookmarks);
1115 	g_signal_connect(G_OBJECT(cp->tree_bookmarks), "realize",
1116 			 G_CALLBACK(tree_view_realized_cb), cp);
1117 	GtkWidget * scroll_win = gtk_scrolled_window_new(NULL, NULL);
1118 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_win),
1119 				       GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1120 	gtk_container_add(GTK_CONTAINER(scroll_win), cp->tree_bookmarks);
1121 	gtk_box_pack_start(GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0);
1122 
1123 	GtkCellRenderer * renderer;
1124 	GtkTreeViewColumn * column;
1125 	GtkTreeSelection * select_b;
1126 	renderer = gtk_cell_renderer_text_new();
1127 	g_object_set(renderer, "scale", 0.8, NULL);
1128 	g_object_set(renderer, "scale-set", TRUE, NULL);
1129 	column = gtk_tree_view_column_new_with_attributes("Bookmarks", renderer, "text", 0, NULL);
1130 	gtk_tree_view_column_set_sort_column_id(column, 0);
1131 	gtk_tree_view_column_set_sort_indicator(column, TRUE);
1132 	gtk_tree_view_column_set_sort_order(column, GTK_SORT_ASCENDING);
1133 	gtk_tree_view_append_column(GTK_TREE_VIEW(cp->tree_bookmarks), column);
1134 	gtk_tree_view_column_clicked(column);
1135 
1136 	select_b = gtk_tree_view_get_selection(GTK_TREE_VIEW(cp->tree_bookmarks));
1137 	gtk_tree_selection_set_mode(select_b, GTK_SELECTION_SINGLE);
1138 	cp->bookmarks_sel_cbid =
1139 		g_signal_connect(G_OBJECT(select_b), "changed",
1140 				 G_CALLBACK(bookmarks_selection_changed_cb), cp);
1141 
1142 	GtkWidget * hbox_1 = gtk_hbox_new(TRUE, PAD);
1143 	gtk_box_pack_start(GTK_BOX(vbox), hbox_1, FALSE, FALSE, PAD);
1144 
1145 	GtkWidget * button = gtk_button_new_with_label("Add...");
1146 	g_signal_connect(G_OBJECT(button), "clicked",
1147 			 G_CALLBACK(add_bookmark_button_clicked), cp);
1148 	gtk_box_pack_start(GTK_BOX(hbox_1), button, TRUE, TRUE, PAD);
1149 
1150 	button = gtk_button_new_with_label("Remove");
1151 	g_signal_connect(G_OBJECT(button), "clicked",
1152 			 G_CALLBACK(del_bookmark_button_clicked), cp);
1153 	gtk_box_pack_start(GTK_BOX(hbox_1), button, TRUE, TRUE, PAD);
1154 
1155 	gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, PAD);
1156 
1157 	vbox = gtk_vbox_new(FALSE, 0);
1158 
1159 	cp->store_files = gtk_list_store_new(2,
1160 					     G_TYPE_STRING,  /* visible name (key) */
1161 					     G_TYPE_STRING); /* full pathname (value) */
1162 	cp->tree_files = gtk_tree_view_new_with_model(GTK_TREE_MODEL(cp->store_files));
1163 	g_signal_connect(G_OBJECT(cp->tree_files), "realize",
1164 			 G_CALLBACK(tree_view_realized_cb), cp);
1165 	scroll_win = gtk_scrolled_window_new(NULL, NULL);
1166 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_win),
1167 				       GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1168 	gtk_container_add(GTK_CONTAINER(scroll_win), cp->tree_files);
1169 	gtk_box_pack_start(GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0);
1170 
1171 	renderer = gtk_cell_renderer_text_new();
1172 	g_object_set(renderer, "scale", 0.8, NULL);
1173 	g_object_set(renderer, "scale-set", TRUE, NULL);
1174 	column = gtk_tree_view_column_new_with_attributes("Files", renderer, "text", 0, NULL);
1175 	gtk_tree_view_column_set_sort_column_id(column, 0);
1176 	gtk_tree_view_column_set_sort_indicator(column, TRUE);
1177 	gtk_tree_view_column_set_sort_order(column, GTK_SORT_ASCENDING);
1178 	gtk_tree_view_append_column(GTK_TREE_VIEW(cp->tree_files), column);
1179 	gtk_tree_view_column_clicked(column);
1180 	GtkTreeSelection * select_f;
1181 	select_f = gtk_tree_view_get_selection(GTK_TREE_VIEW(cp->tree_files));
1182 	gtk_tree_selection_set_mode(select_f, GTK_SELECTION_SINGLE);
1183 	cp->files_sel_cbid =
1184 		g_signal_connect(G_OBJECT(select_f), "changed",
1185 				 G_CALLBACK(files_selection_changed_cb), cp);
1186 
1187 	GtkWidget * browse_button = gtk_button_new_with_label("Open File...");
1188 	gtk_box_pack_start(GTK_BOX(vbox), browse_button, FALSE, FALSE, PAD);
1189 	g_signal_connect(G_OBJECT(browse_button), "clicked",
1190 			 G_CALLBACK(browse_button_clicked), cp);
1191 	gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, PAD);
1192 
1193 	return hbox;
1194 }
1195 
reinit_thread(gpointer data)1196 static gpointer reinit_thread(gpointer data) {
1197 	struct control * cp = (struct control*)data;
1198 	if (cp->ir->resample_pending) {
1199 		int r = cp->ir->resample_init(cp->ir);
1200 		if (r == 0) {
1201 			while (r == 0) {
1202 				r = cp->ir->resample_do(cp->ir);
1203 				if (cp->interrupt_threads) {
1204 					break;
1205 				}
1206 			}
1207 			cp->ir->resample_cleanup(cp->ir);
1208 		}
1209 		cp->ir->resample_pending = 0;
1210 	}
1211 	cp->ir->prepare_convdata(cp->ir);
1212 	cp->ir->init_conv(cp->ir);
1213 	cp->ir->reinit_pending = 0;
1214 	cp->ir->reinit_running = 0;
1215 	return NULL;
1216 }
1217 
reinit_timeout_callback(gpointer data)1218 static gint reinit_timeout_callback(gpointer data) {
1219 	struct control * cp = (struct control*)data;
1220 	if (!cp->ir->ir_samples || !cp->ir->ir_nfram) {
1221 		ir_wavedisplay_set_message(IR_WAVEDISPLAY(cp->wave_display), NULL);
1222 		cp->reinit_timeout_tag = 0;
1223 		return FALSE;
1224 	}
1225 	if (cp->ir->reinit_running) {
1226 		if (cp->ir->resample_pending) {
1227 			ir_wavedisplay_set_progress(IR_WAVEDISPLAY(cp->wave_display),
1228 						    cp->ir->src_progress);
1229 		}
1230 		return TRUE;
1231 	}
1232 	g_thread_join(cp->reinit_thread);
1233 	cp->reinit_thread = NULL;
1234 	ir_wavedisplay_set_progress(IR_WAVEDISPLAY(cp->wave_display), -1.0);
1235 	ir_wavedisplay_set_message(IR_WAVEDISPLAY(cp->wave_display), NULL);
1236 	ir_wavedisplay_set_wave(IR_WAVEDISPLAY(cp->wave_display),
1237 				cp->ir->ir_samples[cp->disp_chan],
1238 				cp->ir->ir_nfram);
1239 	reset_values(cp);
1240 	cp->reinit_timeout_tag = 0;
1241 	return FALSE;
1242 }
1243 
timeout_callback(gpointer data)1244 static gint timeout_callback(gpointer data) {
1245 	struct control * cp = (struct control*)data;
1246 	if (cp->interrupt_threads) {
1247 		cp->timeout_tag = 0;
1248 		return FALSE;
1249 	}
1250 	if (cp->ir->reinit_running) {
1251 		return TRUE;
1252 	}
1253 	if (cp->ir->run && cp->ir->reinit_pending) {
1254 		if (cp->ir->resample_pending) {
1255 			ir_wavedisplay_set_progress(IR_WAVEDISPLAY(cp->wave_display), 0.0);
1256 		}
1257 		ir_wavedisplay_set_message(IR_WAVEDISPLAY(cp->wave_display), "Calculating...");
1258 		cp->ir->reinit_running = 1;
1259 		cp->reinit_thread = g_thread_new("reinit_thread", reinit_thread, cp);
1260 		cp->reinit_timeout_tag = g_timeout_add(100, reinit_timeout_callback, cp);
1261 		cp->ir->run = 0;
1262 	}
1263 	return TRUE;
1264 }
1265 
chan_toggle_cb(GtkWidget * widget,gpointer data)1266 static void chan_toggle_cb(GtkWidget * widget, gpointer data) {
1267 
1268 	struct control * cp = (struct control *)data;
1269 	int i;
1270 	for (i = 0; i < 4; i++) {
1271 		if (widget == cp->chan_toggle[i]) {
1272 			break;
1273 		}
1274 	}
1275 	if (cp->ir->reinit_running) {
1276 		g_signal_handler_block(widget, cp->chan_toggle_cbid[i]);
1277 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
1278 					     !(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))));
1279 		g_signal_handler_unblock(widget, cp->chan_toggle_cbid[i]);
1280 		return;
1281 	}
1282 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
1283 		for (int j = 0; j < 4; j++) {
1284 			if (i != j) {
1285 				gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cp->chan_toggle[j]), FALSE);
1286 			}
1287 		}
1288 		cp->disp_chan = i;
1289 		if (cp->ir->ir_nfram) {
1290 			ir_wavedisplay_set_wave(IR_WAVEDISPLAY(cp->wave_display),
1291 						cp->ir->ir_samples[i], cp->ir->ir_nfram);
1292 		}
1293 	}
1294 }
1295 
log_toggle_cb(GtkWidget * widget,gpointer data)1296 static void log_toggle_cb(GtkWidget * widget, gpointer data) {
1297 	struct control * cp = (struct control *)data;
1298 	if (cp->ir->reinit_running) {
1299 		g_signal_handler_block(widget, cp->log_toggle_cbid);
1300 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
1301 					     !(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))));
1302 		g_signal_handler_unblock(widget, cp->log_toggle_cbid);
1303 		return;
1304 	}
1305 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
1306 		gtk_button_set_label(GTK_BUTTON(widget), " log ");
1307 		ir_wavedisplay_set_logarithmic(IR_WAVEDISPLAY(cp->wave_display), 1);
1308 	} else {
1309 		gtk_button_set_label(GTK_BUTTON(widget), " lin ");
1310 		ir_wavedisplay_set_logarithmic(IR_WAVEDISPLAY(cp->wave_display), 0);
1311 	}
1312 }
1313 
about_button_cb(GtkWidget * about_button,gpointer data)1314 static void about_button_cb(GtkWidget * about_button, gpointer data) {
1315 
1316 	GtkWidget * dialog;
1317 	GtkWidget * frame = gtk_frame_new(NULL);
1318 	GtkWidget * label = gtk_label_new("");
1319 	GtkWidget * content_area;
1320 
1321 	dialog = gtk_dialog_new_with_buttons("About IR",
1322 					     NULL,
1323 					     GTK_DIALOG_DESTROY_WITH_PARENT,
1324 					     GTK_STOCK_OK,
1325 					     GTK_RESPONSE_NONE,
1326 					     NULL);
1327 	content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1328 
1329 	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
1330 	gtk_label_set_markup(GTK_LABEL(label),
1331 			     "<span size=\"x-large\" weight=\"heavy\">"
1332 			     "IR</span><span size=\"x-large\">: LV2 Convolution Reverb\n"
1333 			     "</span>"
1334 			     S1 "version 1.3.2" S2
1335 			     "\n\nCopyright (C) 2011-2012 <b>Tom Szilagyi</b>\n"
1336 			     XS1 "\nIR is free software under the GNU GPL. There is ABSOLUTELY\n"
1337 			     "NO WARRANTY, not even for MERCHANTABILITY or FITNESS\n"
1338 			     "FOR A PARTICULAR PURPOSE." XS2 "\n\n"
1339 			     "<small>Homepage: <b>http://tomszilagyi.github.io/plugins/ir.lv2</b></small>");
1340 	gtk_label_set_selectable(GTK_LABEL(label), TRUE);
1341 	gtk_container_add(GTK_CONTAINER(frame), label);
1342 	gtk_container_set_border_width(GTK_CONTAINER(frame), 2*PAD);
1343 
1344 	g_signal_connect_swapped(dialog, "response",
1345 				 G_CALLBACK(gtk_widget_destroy),
1346 				 dialog);
1347 
1348 	gtk_container_add(GTK_CONTAINER(content_area), frame);
1349 	gtk_container_set_border_width(GTK_CONTAINER(dialog), 2*PAD);
1350 	gtk_widget_show_all(dialog);
1351 }
1352 
make_top_hbox(struct control * cp)1353 static GtkWidget * make_top_hbox(struct control * cp) {
1354 	GtkWidget * hbox = gtk_hbox_new(FALSE, PAD);
1355 	GtkWidget * frame = gtk_frame_new(NULL);
1356 	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
1357 	gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, PAD);
1358 	GtkWidget * vbox = gtk_vbox_new(FALSE, 0);
1359 	gtk_container_add(GTK_CONTAINER(frame), vbox);
1360 
1361 	GtkWidget * hbox_wave = gtk_hbox_new(FALSE, PAD);
1362 	gtk_box_pack_start(GTK_BOX(vbox), hbox_wave, TRUE, TRUE, PAD);
1363 
1364 	GtkWidget * vbox_toggle = gtk_vbox_new(TRUE, 0);
1365 	gtk_box_pack_start(GTK_BOX(hbox_wave), vbox_toggle, FALSE, TRUE, PAD);
1366 
1367 	for (int i = 0; i < 4; i++) {
1368 		char str[4];
1369 		snprintf(str, 4, " %d ", i+1);
1370 		cp->chan_toggle[i] = gtk_toggle_button_new_with_label(str);
1371 		cp->chan_toggle_cbid[i] = g_signal_connect(cp->chan_toggle[i], "toggled",
1372 							   G_CALLBACK(chan_toggle_cb), cp);
1373 		gtk_box_pack_start(GTK_BOX(vbox_toggle), cp->chan_toggle[i], TRUE, TRUE, PAD);
1374 		gtk_widget_set_sensitive(cp->chan_toggle[i], FALSE);
1375 	}
1376 
1377 	cp->wave_display = ir_wavedisplay_new();
1378 	gtk_box_pack_start(GTK_BOX(hbox_wave), cp->wave_display, TRUE, TRUE, 0);
1379 
1380 	cp->mode_ind = ir_modeind_new();
1381 	gtk_widget_set_size_request(cp->mode_ind, 100, -1);
1382 	gtk_box_pack_start(GTK_BOX(hbox_wave), cp->mode_ind, FALSE, FALSE, PAD);
1383 
1384 	GtkWidget * hbox2 = gtk_hbox_new(FALSE, PAD);
1385 	gtk_box_pack_start(GTK_BOX(vbox), hbox2, FALSE, FALSE, PAD);
1386 
1387 	cp->log_toggle = gtk_toggle_button_new_with_label(" lin ");
1388 	cp->log_toggle_cbid = g_signal_connect(cp->log_toggle, "toggled",
1389 					       G_CALLBACK(log_toggle_cb), cp);
1390 	gtk_widget_set_size_request(cp->log_toggle, 50, -1);
1391 	gtk_box_pack_start(GTK_BOX(hbox2), cp->log_toggle, FALSE, TRUE, PAD);
1392 
1393 	cp->wave_annot_label = gtk_label_new("");
1394 	gtk_misc_set_alignment(GTK_MISC(cp->wave_annot_label), 0.0f, 0.5f);
1395 	gtk_box_pack_start(GTK_BOX(hbox2), cp->wave_annot_label, TRUE, TRUE, PAD);
1396 
1397 	GtkWidget * about_button = gtk_button_new_with_label(" About ");
1398 	g_signal_connect(about_button, "clicked",
1399 			 G_CALLBACK(about_button_cb), cp);
1400 	gtk_box_pack_start(GTK_BOX(hbox2), about_button, FALSE, TRUE, PAD);
1401 
1402 	return hbox;
1403 }
1404 
make_gui_proper(struct control * cp)1405 static void make_gui_proper(struct control * cp) {
1406 
1407 	GtkWidget * vbox_top = cp->vbox_top;
1408 
1409 	cp->toggle_reverse = gtk_toggle_button_new_with_label("Reverse");
1410 	cp->toggle_reverse_cbid = g_signal_connect(cp->toggle_reverse, "toggled",
1411 						   G_CALLBACK(toggle_button_cb), cp);
1412 
1413 	/* upper half */
1414 	gtk_box_pack_start(GTK_BOX(vbox_top), make_top_hbox(cp), TRUE, TRUE, PAD);
1415 
1416 	GtkWidget * hbox = gtk_hbox_new(FALSE, PAD);
1417 	gtk_box_pack_start(GTK_BOX(vbox_top), hbox, TRUE, TRUE, 0);
1418 
1419 	GtkWidget * hpaned = gtk_hpaned_new();
1420 	GtkWidget * hbox_1 = gtk_hbox_new(FALSE, PAD);
1421 	gtk_paned_pack1(GTK_PANED(hpaned), hbox_1, TRUE, TRUE);
1422 	gtk_box_pack_start(GTK_BOX(hbox_1), make_lists_box(cp), TRUE, TRUE, 0);
1423 
1424 	GtkWidget * hbox_2 = gtk_hbox_new(FALSE, 0);
1425 	gtk_paned_pack2(GTK_PANED(hpaned), hbox_2, TRUE, FALSE);
1426 	GtkWidget * vbox = gtk_vbox_new(FALSE, 0);
1427 	gtk_box_pack_start(GTK_BOX(vbox), make_irctrl_frame(cp), TRUE, TRUE, 0);
1428 	gtk_box_pack_start(GTK_BOX(vbox), cp->toggle_reverse, FALSE, TRUE, PAD);
1429 	gtk_box_pack_start(GTK_BOX(hbox_2), vbox, TRUE, TRUE, PAD);
1430 	gtk_box_pack_start(GTK_BOX(hbox_2), make_mixer_frame(cp), FALSE, TRUE, PAD);
1431 
1432 	gtk_box_pack_start(GTK_BOX(hbox), hpaned, TRUE, TRUE, 0);
1433 
1434 	cp->timeout_tag = g_timeout_add(100, timeout_callback, cp);
1435 
1436 	gtk_widget_show_all(vbox_top);
1437 }
1438 
1439 static void port_event(LV2UI_Handle ui, uint32_t port_index, uint32_t buffer_size,
1440 		       uint32_t format, const void * buffer);
1441 
replay_func(gpointer data,gpointer user_data)1442 static void replay_func(gpointer data, gpointer user_data) {
1443 	port_event_t * pe = (port_event_t *)data;
1444 	struct control * cp = (struct control*)user_data;
1445 	port_event((LV2UI_Handle)cp, pe->port_index, 0, 0, &pe->value);
1446 	free(pe);
1447 }
1448 
replay_port_events(struct control * cp)1449 static void replay_port_events(struct control * cp) {
1450 	GSList * q = cp->port_event_q;
1451 	g_slist_foreach(q, replay_func, cp);
1452 	g_slist_free(q);
1453 }
1454 
waitplugin_timeout_callback(gpointer data)1455 static gint waitplugin_timeout_callback(gpointer data) {
1456 	struct control * cp = (struct control*)data;
1457 	if (cp->ir->first_conf_done) {
1458 		gtk_widget_destroy(cp->hbox_waitplugin);
1459 		make_gui_proper(cp);
1460 		replay_port_events(cp);
1461 		cp->waitplugin_timeout_tag = 0;
1462 		return FALSE;
1463 	}
1464 	if (cp->interrupt_threads) {
1465 		cp->waitplugin_timeout_tag = 0;
1466 		return FALSE;
1467 	}
1468 	return TRUE;
1469 }
1470 
make_gui(struct control * cp)1471 static GtkWidget * make_gui(struct control * cp) {
1472 
1473 	cp->toggle_reverse = gtk_toggle_button_new_with_label("Reverse");
1474 	g_signal_connect(cp->toggle_reverse, "toggled",
1475 			 G_CALLBACK(toggle_button_cb), cp);
1476 
1477 	cp->vbox_top = gtk_vbox_new(FALSE, PAD);
1478 
1479 	if (cp->ir->first_conf_done) {
1480 		make_gui_proper(cp);
1481 	} else {
1482 		cp->hbox_waitplugin = gtk_hbox_new(FALSE, PAD);
1483 		gtk_box_pack_start(GTK_BOX(cp->vbox_top), cp->hbox_waitplugin, TRUE, TRUE, PAD);
1484 #ifdef _HAVE_GTK_ATLEAST_2_20
1485 		GtkWidget * spinner = gtk_spinner_new();
1486 		gtk_spinner_start(GTK_SPINNER(spinner));
1487 		gtk_box_pack_start(GTK_BOX(cp->hbox_waitplugin), spinner, TRUE, TRUE, PAD);
1488 #endif /* _HAVE_GTK_ATLEAST_2_20 */
1489 		GtkWidget * label = gtk_label_new("");
1490 		gtk_label_set_markup(GTK_LABEL(label),
1491 				     "<span size=\"large\" weight=\"bold\">"
1492 				     " Please wait while plugin is initialised... "
1493 				     "</span>\n"
1494 				     XS1 "  If the plugin is in BYPASS (Deactivated), please un-BYPASS (Activate) it." XS2);
1495 		gtk_box_pack_start(GTK_BOX(cp->hbox_waitplugin), label, TRUE, TRUE, PAD);
1496 		cp->waitplugin_timeout_tag = g_timeout_add(100, waitplugin_timeout_callback, cp);
1497 		gtk_widget_show_all(cp->vbox_top);
1498 	}
1499 	return cp->vbox_top;
1500 }
1501 
1502 /* join any threads and wait for timeouts to exit */
join_timeouts(struct control * cp)1503 static void join_timeouts(struct control * cp) {
1504 	cp->interrupt_threads = 1;
1505 	while (cp->timeout_tag ||
1506 	       cp->gui_load_timeout_tag ||
1507 	       cp->reinit_timeout_tag ||
1508 	       cp->waitplugin_timeout_tag) {
1509 
1510 		gtk_main_iteration_do(FALSE);
1511 	}
1512 }
1513 
cleanup(LV2UI_Handle ui)1514 static void cleanup(LV2UI_Handle ui) {
1515 	//printf("cleanup()\n");
1516 	struct control * cp = (struct control *)ui;
1517 
1518 	join_timeouts(cp);
1519 	if (cp->store_files) {
1520 		g_object_unref(cp->store_files);
1521 		cp->store_files = 0;
1522 	}
1523 	free(cp);
1524 }
1525 
instantiate(const struct LV2UI_Descriptor * descriptor,const char * plugin_uri,const char * bundle_path,LV2UI_Write_Function write_function,LV2UI_Controller controller,LV2UI_Widget * widget,const LV2_Feature * const * features)1526 static LV2UI_Handle instantiate(const struct LV2UI_Descriptor * descriptor,
1527 				const char * plugin_uri,
1528 				const char * bundle_path,
1529 				LV2UI_Write_Function write_function,
1530 				LV2UI_Controller controller,
1531 				LV2UI_Widget * widget,
1532 				const LV2_Feature * const * features) {
1533 
1534 	int instance_access_found = 0;
1535 	struct control * cp;
1536 	//printf("instantiate('%s', '%s') called\n", plugin_uri, bundle_path);
1537 
1538 	if (strcmp(plugin_uri, IR_URI) != 0) {
1539 		fprintf(stderr, "IR_UI error: this GUI does not support plugin with URI %s\n", plugin_uri);
1540 		goto fail;
1541 	}
1542 
1543 	cp = (struct control*)calloc(1, sizeof(struct control));
1544 	if (cp == NULL) {
1545 		goto fail;
1546 	}
1547 
1548 	if (features != NULL) {
1549 		int i = 0;
1550 		while (features[i] != NULL) {
1551 			if (strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0) {
1552 				cp->ir = (IR *)(features[i]->data);
1553 				instance_access_found = 1;
1554 			}
1555 			++i;
1556 		}
1557 		if (!instance_access_found) {
1558 			goto fail_free;
1559 		}
1560 	} else {
1561 		goto fail_free;
1562 	}
1563 
1564 	if (cp->ir == NULL) {
1565 		goto fail_free;
1566 	}
1567 
1568 	cp->controller = controller;
1569 	cp->write_function = write_function;
1570 
1571 	*widget = (LV2UI_Widget)make_gui(cp);
1572 	return (LV2UI_Handle)cp;
1573 
1574  fail_free:
1575 	if (!instance_access_found) {
1576 		fprintf(stderr, "IR UI: error: required LV2 feature %s missing!\n", LV2_INSTANCE_ACCESS_URI);
1577 	}
1578 
1579 	free(cp);
1580  fail:
1581 	return NULL;
1582 }
1583 
port_event(LV2UI_Handle ui,uint32_t port_index,uint32_t buffer_size,uint32_t format,const void * buffer)1584 static void port_event(LV2UI_Handle ui,
1585 		       uint32_t port_index,
1586 		       uint32_t buffer_size,
1587 		       uint32_t format,
1588 		       const void * buffer) {
1589 
1590 	struct control * cp = (struct control *)ui;
1591 	float * pval = (float *)buffer;
1592 	//printf("port_event(%u, %f) called\n", (unsigned int)port_index, *(float *)buffer);
1593 
1594 	if (format != 0) {
1595 		return;
1596 	}
1597 
1598 	if ((port_index < 0) || (port_index >= IR_N_PORTS)) {
1599 		return;
1600 	}
1601 
1602 	if (!set_port_value(cp, port_index, *pval)) {
1603 		return;
1604 	}
1605 	cp->port_buffer[port_index] = *pval;
1606 
1607 	if (!cp->ir->first_conf_done) {
1608 		port_event_t * pe = (port_event_t *)malloc(sizeof(port_event_t));
1609 		pe->port_index = port_index;
1610 		pe->value = *pval;
1611 		cp->port_event_q = g_slist_prepend(cp->port_event_q, pe);
1612 		return;
1613 	}
1614 
1615 	int update_ui = 0;
1616 	if (port_index == IR_PORT_REVERSE) {
1617 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cp->toggle_reverse),
1618 					     (*pval > 0.0f));
1619 		update_ui = 1;
1620 	} else if (port_index == IR_PORT_PREDELAY) {
1621 		cp->predelay = *pval;
1622 		set_adjustment(cp, cp->adj_predelay, *pval);
1623 		update_ui = 1;
1624 	} else if (port_index == IR_PORT_ATTACK) {
1625 		cp->attack = *pval;
1626 		set_adjustment(cp, cp->adj_attack, *pval);
1627 		update_ui = 1;
1628 	} else if (port_index == IR_PORT_ATTACKTIME) {
1629 		cp->attacktime = *pval;
1630 		set_adjustment(cp, cp->adj_attacktime, *pval);
1631 		update_ui = 1;
1632 	} else if (port_index == IR_PORT_ENVELOPE) {
1633 		cp->envelope = *pval;
1634 		set_adjustment(cp, cp->adj_envelope, *pval);
1635 		update_ui = 1;
1636 	} else if (port_index == IR_PORT_LENGTH) {
1637 		cp->length = *pval;
1638 		set_adjustment(cp, cp->adj_length, *pval);
1639 		update_ui = 1;
1640 	} else if (port_index == IR_PORT_STRETCH) {
1641 		cp->stretch = *pval;
1642 		set_adjustment(cp, cp->adj_stretch, *pval);
1643 		update_ui = 1;
1644 	} else if (port_index == IR_PORT_STEREO_IN) {
1645 		set_adjustment(cp, cp->adj_stereo_in, *pval);
1646 	} else if (port_index == IR_PORT_STEREO_IR) {
1647 		cp->stereo_ir = *pval;
1648 		set_adjustment(cp, cp->adj_stereo_ir, *pval);
1649 	} else if (port_index == IR_PORT_AGC_SW) {
1650 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cp->toggle_agc_sw),
1651 					     (*pval > 0.0f));
1652 	} else if (port_index == IR_PORT_DRY_SW) {
1653 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cp->toggle_dry_sw),
1654 					     (*pval > 0.0f));
1655 	} else if (port_index == IR_PORT_DRY_GAIN) {
1656 		set_adjustment(cp, cp->adj_dry_gain, *pval);
1657 	} else if (port_index == IR_PORT_WET_SW) {
1658 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cp->toggle_wet_sw),
1659 					     (*pval > 0.0f));
1660 	} else if (port_index == IR_PORT_WET_GAIN) {
1661 		set_adjustment(cp, cp->adj_wet_gain, *pval);
1662 	} else if (port_index == IR_PORT_FHASH_0) { /* NOP: plugin itself handles IR loading on session resume */
1663 	} else if (port_index == IR_PORT_FHASH_1) { /* NOP */
1664 	} else if (port_index == IR_PORT_FHASH_2) { /* NOP */
1665 	} else if (port_index == IR_PORT_METER_DRY_L) {
1666 		ir_meter_set_level(IR_METER(cp->meter_L_dry), convert_real_to_scale(ADJ_DRY_GAIN, CO_DB(*pval)));
1667 	} else if (port_index == IR_PORT_METER_DRY_R) {
1668 		ir_meter_set_level(IR_METER(cp->meter_R_dry), convert_real_to_scale(ADJ_DRY_GAIN, CO_DB(*pval)));
1669 	} else if (port_index == IR_PORT_METER_WET_L) {
1670 		ir_meter_set_level(IR_METER(cp->meter_L_wet), convert_real_to_scale(ADJ_WET_GAIN, CO_DB(*pval)));
1671 	} else if (port_index == IR_PORT_METER_WET_R) {
1672 		ir_meter_set_level(IR_METER(cp->meter_R_wet), convert_real_to_scale(ADJ_WET_GAIN, CO_DB(*pval)));
1673 	}
1674 
1675 	if (update_ui) {
1676 		update_envdisplay(cp);
1677 	}
1678 }
1679 
1680 static LV2UI_Descriptor descriptors[] = {
1681 	{IR_UI_URI, instantiate, cleanup, port_event, NULL}
1682 };
1683 
lv2ui_descriptor(uint32_t index)1684 const LV2UI_Descriptor * lv2ui_descriptor(uint32_t index) {
1685 	//printf("lv2ui_descriptor(%u) called\n", (unsigned int)index);
1686 	if (index >= sizeof(descriptors) / sizeof(descriptors[0])) {
1687 		return NULL;
1688 	}
1689 	return descriptors + index;
1690 }
1691