1 /*
2 * Copyright (C) 2003 2004 2005 2006 2007 2009 2012, Magnus Hjorth
3 *
4 * This file is part of mhWaveEdit.
5 *
6 * mhWaveEdit is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * mhWaveEdit is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with mhWaveEdit; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21
22 #include <config.h>
23
24 #include <unistd.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #include <gtk/gtk.h>
30
31 #include "main.h"
32 #include "soxdialog.h"
33 #include "pipedialog.h"
34 #include "effectbrowser.h"
35 #include "inifile.h"
36 #include "um.h"
37 #include "gettext.h"
38
39 static GtkObjectClass *parent_class;
40
41 static gchar *supported_effects[] = {
42 "echo","echos","reverb","chorus","flanger","phaser",
43 "compand","pitch","stretch",
44 "dcshift","mask","reverse","earwax","vibro",
45 "lowp","highp","band",
46 "lowpass","highpass","bandpass","bandreject","filter",NULL
47 };
48
49 static gboolean sox_support_map[22] = { FALSE };
50 static gboolean v13_mode = FALSE;
51 static gboolean v14_mode = FALSE;
52
53 static gchar *supported_effect_names[] = {
54 N_("Echo"),
55 N_("Echo sequence"),
56 N_("Reverb"),
57 N_("Chorus"),
58 N_("Flanger"),
59 N_("Phaser"),
60 N_("Compress/Expand"),
61 N_("Pitch adjust"),
62 N_("Time stretch"),
63 N_("DC Shift"),
64 N_("Masking noise"),
65 N_("Reverse"),
66 N_("Earwax"),
67 N_("Vibro"),
68 N_("Lowpass filter (single-pole)"),
69 N_("Highpass filter (single-pole)"),
70 N_("Bandpass filter"),
71 N_("Butterworth lowpass filter"),
72 N_("Butterworth highpass filter"),
73 N_("Butterworth bandpass filter"),
74 N_("Butterworth bandreject filter"),
75 N_("Sinc-windowed filter"),
76 NULL
77 };
78
79 static gchar *samplesize_switch[] = { NULL,"-b","-w",NULL,"-l" };
80 static gchar *samplesize_switch_v13[] = { NULL,"-1","-2","-3","-4" };
81
82 #define COMPAND_LINES 6
83
sox_dialog_format_string(gchar * buf,guint bufsize,Dataformat * fmt)84 void sox_dialog_format_string(gchar *buf, guint bufsize, Dataformat *fmt)
85 {
86 g_assert(fmt->type == DATAFORMAT_PCM && fmt->samplesize != 3 && fmt->packing == 0);
87
88 if (v14_mode)
89 g_snprintf(buf,bufsize,"-t raw -r %d -e %s -b %d -c %d",fmt->samplerate,
90 fmt->sign?"signed":"unsigned",(8 * fmt->samplesize),
91 fmt->channels);
92 else if (v13_mode)
93 g_snprintf(buf,bufsize,"-t raw -r %d %s %s -c %d",fmt->samplerate,
94 fmt->sign?"-s":"-u",samplesize_switch_v13[fmt->samplesize],
95 fmt->channels);
96 else
97 g_snprintf(buf,bufsize,"-t raw -r %d %s %s -c %d",fmt->samplerate,
98 fmt->sign?"-s":"-u",samplesize_switch[fmt->samplesize],
99 fmt->channels);
100 }
101
sox_dialog_apply_proc_main(Chunk * chunk,StatusBar * bar,gpointer user_data)102 static Chunk *sox_dialog_apply_proc_main(Chunk *chunk, StatusBar *bar,
103 gpointer user_data)
104 {
105 SoxDialog *sd = SOX_DIALOG(user_data);
106 EffectDialog *ed = &(sd->ed);
107 gchar fmt_buf[45];
108 gchar cmd_buf[512],*c;
109 gchar *t1,*t2;
110 gfloat f;
111 guint i,j;
112 gint idx;
113 gboolean b;
114 off_t clipcount = 0;
115 Chunk *r;
116
117 sox_dialog_format_string(fmt_buf,sizeof(fmt_buf),&(chunk->format));
118 g_snprintf(cmd_buf,sizeof(cmd_buf),"sox %s - %s - ",fmt_buf,fmt_buf);
119
120 c=strchr(cmd_buf,0);
121 if (!strcmp(ed->effect_name,"echo") ||
122 !strcmp(ed->effect_name,"echos") ||
123 !strcmp(ed->effect_name,"reverb") ||
124 !strcmp(ed->effect_name,"chorus") ||
125 !strcmp(ed->effect_name,"flanger") ||
126 !strcmp(ed->effect_name,"phaser")) {
127 g_snprintf(fmt_buf,sizeof(fmt_buf),"sox_%s_gain",ed->effect_name);
128 inifile_set_gfloat(fmt_buf,sd->fb1->val);
129 if (!strcmp(ed->effect_name,"reverb")) {
130 f = sd->fb2->val;
131 inifile_set_gfloat("sox_reverb_rtime",f);
132 } else
133 f = 1.0;
134 g_snprintf(c,sizeof(cmd_buf)-(c-cmd_buf),"%s %f %f",ed->effect_name,
135 sd->fb1->val,f);
136 for (i=0; i<sd->i1; i++) {
137 if (sd->fba[0][i]->val > 0.0) {
138 for (j=0; j<4; j++)
139 if (sd->fba[j] != NULL) {
140 c = strchr(c,0);
141 g_snprintf(c,sizeof(cmd_buf)-(c-cmd_buf)," %f",
142 sd->fba[j][i]->val);
143 }
144 if (sd->ca != NULL) {
145 c = strchr(c,0);
146 idx = combo_selected_index(sd->ca[i]);
147 if (idx == 0) strcpy(c," -s");
148 else strcpy(c," -t");
149 }
150 }
151 }
152 } else if (!strcmp(ed->effect_name,"lowp") ||
153 !strcmp(ed->effect_name,"highp") ||
154 !strcmp(ed->effect_name,"highp") ||
155 !strcmp(ed->effect_name,"band") ||
156 !strcmp(ed->effect_name,"lowpass") ||
157 !strcmp(ed->effect_name,"highpass") ||
158 !strcmp(ed->effect_name,"bandpass") ||
159 !strcmp(ed->effect_name,"bandreject")) {
160 g_snprintf(fmt_buf,sizeof(fmt_buf),"sox_%s_freq",ed->effect_name);
161 inifile_set_gfloat(fmt_buf,sd->fb1->val);
162 if (sd->fb2 != NULL) {
163 g_snprintf(fmt_buf,sizeof(fmt_buf),"sox_%s_width",
164 ed->effect_name);
165 inifile_set_gfloat(fmt_buf,sd->fb2->val);
166 }
167 if (sd->tb1 != NULL)
168 inifile_set_gboolean("sox_band_noisemode",
169 gtk_toggle_button_get_active(sd->tb1));
170 g_snprintf(c,sizeof(cmd_buf)-(c-cmd_buf),"%s %s %f",
171 ed->effect_name,
172 (sd->tb1!=NULL && gtk_toggle_button_get_active(sd->tb1))?
173 "-n":"",sd->fb1->val);
174 if (sd->fb2 != NULL) {
175 c = strchr(c,0);
176 g_snprintf(c,sizeof(cmd_buf)-(c-cmd_buf)," %f",sd->fb2->val);
177 }
178 } else if (!strcmp(ed->effect_name,"filter")) {
179 inifile_set_guint32("sox_filter_type",sd->i1);
180 inifile_set_guint32("sox_filter_low",sd->ib1->val);
181 inifile_set_guint32("sox_filter_high",sd->ib2->val);
182 inifile_set_guint32("sox_filter_length",sd->ib3->val);
183 inifile_set_gfloat("sox_filter_beta",sd->fb1->val);
184 if (sd->i1 == 0)
185 g_snprintf(fmt_buf,sizeof(fmt_buf),"-%d",(int)sd->ib2->val);
186 else if (sd->i1 == 1)
187 g_snprintf(fmt_buf,sizeof(fmt_buf),"%d-",(int)sd->ib1->val);
188 else
189 g_snprintf(fmt_buf,sizeof(fmt_buf),"%d-%d",(int)sd->ib1->val,
190 (int)sd->ib2->val);
191 g_snprintf(c,sizeof(cmd_buf)-(c-cmd_buf),"filter %s %d %f",fmt_buf,
192 (int)sd->ib3->val,sd->fb1->val);
193 } else if (!strcmp(ed->effect_name,"compand")) {
194 inifile_set_gfloat("sox_compand_attack",sd->fb1->val);
195 inifile_set_gfloat("sox_compand_decay",sd->fb2->val);
196 inifile_set_gfloat("sox_compand_gain",sd->fb3->val);
197 inifile_set_gfloat("sox_compand_startvol",sd->fb4->val);
198 inifile_set_gfloat("sox_compand_delay",sd->fb5->val);
199 g_snprintf(c,sizeof(cmd_buf)-(c-cmd_buf),"compand %f,%f ",
200 sd->fb1->val,sd->fb2->val);
201 for (i=0; i<COMPAND_LINES; i++) {
202 c = strchr(c,0);
203 g_snprintf(c,sizeof(cmd_buf)-(c-cmd_buf),(i>0)?",%f,%f":"%f,%f",
204 sd->fba[0][i]->val,sd->fba[1][i]->val);
205 if (sd->fba[0][i]->val >= 0.0) break;
206 }
207 c = strchr(c,0);
208 g_snprintf(c,sizeof(cmd_buf)-(c-cmd_buf)," %f %f %f",sd->fb3->val,
209 sd->fb4->val,sd->fb5->val);
210 } else if (!strcmp(ed->effect_name,"dcshift")) {
211 b = gtk_toggle_button_get_active(sd->tb1);
212 inifile_set_gfloat("sox_dcshift_amount",sd->fb1->val);
213 inifile_set_gboolean("sox_dcshift_limiter",b);
214 if (b) inifile_set_gfloat("sox_dcshift_gain",sd->fb2->val);
215 if (b)
216 g_snprintf(c,sizeof(cmd_buf)-(c-cmd_buf),"dcshift %f %f",
217 sd->fb1->val,sd->fb2->val);
218 else
219 g_snprintf(c,sizeof(cmd_buf)-(c-cmd_buf),"dcshift %f",
220 sd->fb1->val);
221 } else if (!strcmp(ed->effect_name,"pitch")) {
222 inifile_set_gfloat("sox_pitch_amount",sd->fb1->val);
223 inifile_set_gfloat("sox_pitch_width",sd->fb2->val);
224 t1 = combo_selected_string(sd->c1);
225 t2 = combo_selected_string(sd->c2);
226 g_snprintf(c,sizeof(cmd_buf)-(c-cmd_buf),"pitch %f %f %s %s",
227 sd->fb1->val,sd->fb2->val,t1,t2);
228 g_free(t1);
229 g_free(t2);
230 g_strdown(c);
231 } else if (!strcmp(ed->effect_name,"stretch")) {
232 inifile_set_gfloat("sox_stretch_factor",sd->fb1->val);
233 inifile_set_gfloat("sox_stretch_window",sd->fb2->val);
234 g_snprintf(c,sizeof(cmd_buf)-(c-cmd_buf),"stretch %f %f",
235 sd->fb1->val,sd->fb2->val);
236 } else if (!strcmp(ed->effect_name,"vibro")) {
237 inifile_set_gfloat("sox_vibro_speed",sd->fb1->val);
238 inifile_set_gfloat("sox_vibro_depth",sd->fb2->val);
239 g_snprintf(c,sizeof(cmd_buf)-(c-cmd_buf),"vibro %f %f",
240 sd->fb1->val,sd->fb2->val);
241 } else if (!strcmp(ed->effect_name,"mask") ||
242 !strcmp(ed->effect_name,"earwax") ||
243 !strcmp(ed->effect_name,"reverse")) {
244 strcpy(c,ed->effect_name);
245 } else {
246 g_assert_not_reached();
247 }
248 r = pipe_dialog_pipe_chunk(chunk,cmd_buf,FALSE,dither_editing,bar,
249 &clipcount);
250 if (r != NULL && clipwarn(clipcount,TRUE)) {
251 gtk_object_sink(GTK_OBJECT(r));
252 return NULL;
253 }
254 return r;
255 }
256
sox_dialog_apply_proc(Chunk * chunk,StatusBar * bar,gpointer user_data)257 static Chunk *sox_dialog_apply_proc(Chunk *chunk, StatusBar *bar,
258 gpointer user_data)
259 {
260 Chunk *c,*d,*r;
261 Dataformat stype = { DATAFORMAT_PCM, 44100, 4, 1, 4, TRUE, IS_BIGENDIAN };
262 if ((chunk->format.type == DATAFORMAT_FLOAT) ||
263 (chunk->format.type == DATAFORMAT_PCM &&
264 (chunk->format.samplesize == 3 || chunk->format.packing!=0))) {
265 c = chunk_convert_sampletype(chunk,&stype);
266 d = sox_dialog_apply_proc_main(c,bar,user_data);
267 gtk_object_sink(GTK_OBJECT(c));
268 r = chunk_convert_sampletype(d,&(chunk->format));
269 gtk_object_sink(GTK_OBJECT(d));
270 return r;
271 } else
272 return sox_dialog_apply_proc_main(chunk,bar,user_data);
273 }
274
sox_dialog_apply(EffectDialog * ed)275 static gboolean sox_dialog_apply(EffectDialog *ed)
276 {
277 SoxDialog *sd = SOX_DIALOG(ed);
278 Document *d = EFFECT_BROWSER(ed->eb)->dl->selected;
279 /* Chunk *chunk = d->chunk; */
280 guint i,j;
281 /*
282 if (chunk->format.samplesize != 1 &&
283 chunk->format.samplesize != 2 &&
284 chunk->format.samplesize != 4) {
285 user_info(_("SoX only supports 8, 16 and 32-bit sample sizes"));
286 return TRUE;
287 }
288 */
289 if ((sd->fb1!=NULL && floatbox_check(sd->fb1)) ||
290 (sd->fb2!=NULL && floatbox_check(sd->fb2)) ||
291 (sd->fb3!=NULL && floatbox_check(sd->fb3)) ||
292 (sd->fb4!=NULL && floatbox_check(sd->fb4)) ||
293 (sd->fb5!=NULL && floatbox_check(sd->fb5)) ||
294 (sd->ib1!=NULL && intbox_check(sd->ib1)) ||
295 (sd->ib2!=NULL && intbox_check(sd->ib2)) ||
296 (sd->ib3!=NULL && intbox_check(sd->ib3)))
297 return TRUE;
298 for (i=0; i<ARRAY_LENGTH(sd->fba); i++)
299 if (sd->fba[i] != NULL)
300 for (j=0; j<sd->i1; j++)
301 if (floatbox_check(sd->fba[i][j])) return TRUE;
302 return document_apply_cb(d,sox_dialog_apply_proc,TRUE,ed);
303 }
304
setup_filter(EffectDialog * ed,gchar * bw_name,gboolean nm)305 static void setup_filter(EffectDialog *ed, gchar *bw_name, gboolean nm)
306 {
307 SoxDialog *sd = SOX_DIALOG(ed);
308 GtkWidget *a,*b;
309 gchar c[64];
310 a = gtk_table_new(3,3,FALSE);
311 gtk_container_add(GTK_CONTAINER(ed->input_area),a);
312 attach_label(_("Frequency: "),a,0,0);
313 g_snprintf(c,sizeof(c),"sox_%s_freq",ed->effect_name);
314 b = floatbox_new(inifile_get_gfloat(c,440.0));
315 gtk_table_attach(GTK_TABLE(a),b,1,2,0,1,0,0,0,0);
316 sd->fb1 = FLOATBOX(b);
317 attach_label(_("Hz"),a,0,2);
318 if (bw_name != NULL) {
319 attach_label(bw_name,a,1,0);
320 g_snprintf(c,sizeof(c),"sox_%s_width",ed->effect_name);
321 b = floatbox_new(inifile_get_gfloat(c,50.0));
322 gtk_table_attach(GTK_TABLE(a),b,1,2,1,2,0,0,0,0);
323 sd->fb2 = FLOATBOX(b);
324 attach_label(_("Hz"),a,1,2);
325 }
326 if (nm) {
327 b = gtk_check_button_new_with_label(_("Noise mode"));
328 gtk_toggle_button_set_active
329 (GTK_TOGGLE_BUTTON(b),
330 inifile_get_gboolean("sox_band_noisemode",FALSE));
331 gtk_table_attach(GTK_TABLE(a),b,0,3,2,3,0,0,0,0);
332 sd->tb1 = GTK_TOGGLE_BUTTON(b);
333 }
334 gtk_widget_show_all(a);
335 }
336
sox_filter_type_changed(Combo * combo,gpointer user_data)337 static void sox_filter_type_changed(Combo *combo, gpointer user_data)
338 {
339 SoxDialog *sd = SOX_DIALOG(user_data);
340 guint i;
341 i = combo_selected_index(combo);
342 sd->i1 = i;
343 gtk_widget_set_sensitive(GTK_WIDGET(sd->ib1),(i==1 || i==2));
344 gtk_widget_set_sensitive(GTK_WIDGET(sd->ib2),(i==0 || i==2));
345 }
346
347 #define ECHOS_MAX 7
348
setup_echo_column(SoxDialog * sd,GtkTable * t,guint col,gint lines)349 static void setup_echo_column(SoxDialog *sd, GtkTable *t, guint col,
350 gint lines)
351 {
352 guint i;
353 GtkWidget *w;
354 sd->fba[col] = g_malloc(lines*sizeof(EffectDialog *));
355 for (i=0; i<lines; i++) {
356 w = floatbox_new(0.0);
357 sd->fba[col][i] = FLOATBOX(w);
358 gtk_table_attach(t,w,col,col+1,i+1,i+2,GTK_FILL,0,0,0);
359 }
360 }
361
setup_echos(EffectDialog * ed,gchar * gain_name,gint lines,gboolean show_decay,gboolean show_speed,gboolean show_depth,gboolean show_mtype)362 static void setup_echos(EffectDialog *ed, gchar *gain_name, gint lines,
363 gboolean show_decay, gboolean show_speed,
364 gboolean show_depth, gboolean show_mtype)
365 {
366 SoxDialog *sd = SOX_DIALOG(ed);
367 GtkWidget *a,*b,*c;
368 gchar buf[64];
369 guint i;
370 GList *l;
371 GtkRequisition req;
372
373 sd->i1 = lines;
374 a = gtk_vbox_new(FALSE,8);
375 gtk_container_add(GTK_CONTAINER(ed->input_area),a);
376 b = gtk_hbox_new(FALSE,2);
377 gtk_box_pack_start(GTK_BOX(a),b,FALSE,FALSE,0);
378 c = gtk_label_new(gain_name);
379 gtk_box_pack_start(GTK_BOX(b),c,FALSE,FALSE,0);
380 g_snprintf(buf,sizeof(buf),"sox_%s_gain",ed->effect_name);
381 c = floatbox_new(inifile_get_gfloat(buf,1.0));
382 gtk_box_pack_start(GTK_BOX(b),c,FALSE,FALSE,0);
383 sd->fb1 = FLOATBOX(c);
384 if (!strcmp(ed->effect_name,"reverb")) {
385 b = gtk_hbox_new(FALSE,2);
386 gtk_box_pack_start(GTK_BOX(a),b,FALSE,FALSE,0);
387 c = gtk_label_new(_("Reverb time: "));
388 gtk_box_pack_start(GTK_BOX(b),c,FALSE,FALSE,0);
389 c = floatbox_new(inifile_get_gfloat("sox_reverb_rtime",500.0));
390 gtk_box_pack_start(GTK_BOX(b),c,FALSE,FALSE,0);
391 sd->fb2 = FLOATBOX(c);
392 c = gtk_label_new(_("ms"));
393 gtk_box_pack_start(GTK_BOX(b),c,FALSE,FALSE,0);
394 }
395 b = gtk_table_new(lines+1,5,FALSE);
396 gtk_box_pack_start(GTK_BOX(a),b,FALSE,FALSE,0);
397 attach_label(_("Delay (ms) "),b,0,0);
398 setup_echo_column(sd,GTK_TABLE(b),0,lines);
399 if (show_decay) {
400 attach_label(_("Decay "),b,0,1);
401 setup_echo_column(sd,GTK_TABLE(b),1,lines);
402 }
403 if (show_speed) {
404 attach_label(_("Speed (Hz) "),b,0,2);
405 setup_echo_column(sd,GTK_TABLE(b),2,lines);
406 }
407 if (show_depth) {
408 attach_label(_("Depth (ms) "),b,0,3);
409 setup_echo_column(sd,GTK_TABLE(b),3,lines);
410 }
411 if (show_mtype) {
412 attach_label(_("Modulation "),b,0,4);
413 sd->ca = g_malloc(lines*sizeof(GtkCombo *));
414 l = NULL;
415 l = g_list_append(l,translate_strip(N_("Modulation|Sinusoidal")));
416 l = g_list_append(l,translate_strip(N_("Modulation|Triangular")));
417 for (i=0; i<lines; i++) {
418 c = combo_new();
419 gtk_widget_size_request(c,&req);
420 #ifdef COMBO_OLDSCHOOL
421 gtk_widget_set_usize(c,req.width/2,req.height);
422 #endif
423 sd->ca[i] = COMBO(c);
424 combo_set_items(COMBO(c),l,0);
425 gtk_table_attach(GTK_TABLE(b),c,4,5,i+1,i+2,GTK_FILL,0,0,0);
426 }
427 g_list_free(l);
428 }
429 b = gtk_label_new(_("(Lines with delay=0 will be ignored.)"));
430 gtk_box_pack_start(GTK_BOX(a),b,FALSE,FALSE,0);
431 gtk_widget_show_all(a);
432 }
433
toggle_sensitive(GtkToggleButton * togglebutton,gpointer user_data)434 static void toggle_sensitive(GtkToggleButton *togglebutton, gpointer user_data)
435 {
436 gtk_widget_set_sensitive(GTK_WIDGET(user_data),
437 gtk_toggle_button_get_active(togglebutton));
438 }
439
sox_dialog_browser_setup(EffectDialog * ed)440 static void sox_dialog_browser_setup(EffectDialog *ed)
441 {
442 SoxDialog *sd = SOX_DIALOG(ed);
443 GtkWidget *a,*b,*c,*w1;
444 GList *l=NULL;
445 guint i;
446
447 if (!strcmp(ed->effect_name,"echo")) {
448 setup_echos(ed,_("Input gain: "),ECHOS_MAX,TRUE,FALSE,FALSE,FALSE);
449 } else if (!strcmp(ed->effect_name,"echos")) {
450 setup_echos(ed,_("Input gain: "),ECHOS_MAX,TRUE,FALSE,FALSE,FALSE);
451 } else if (!strcmp(ed->effect_name,"reverb")) {
452 setup_echos(ed,_("Output gain: "),ECHOS_MAX,FALSE,FALSE,FALSE,FALSE);
453 } else if (!strcmp(ed->effect_name,"chorus")) {
454 setup_echos(ed,_("Input gain: "),ECHOS_MAX,TRUE,TRUE,TRUE,TRUE);
455 } else if (!strcmp(ed->effect_name,"flanger")) {
456 setup_echos(ed,_("Input gain: "),1,TRUE,TRUE,FALSE,TRUE);
457 } else if (!strcmp(ed->effect_name,"phaser")) {
458 setup_echos(ed,_("Input gain: "),1,TRUE,TRUE,FALSE,TRUE);
459 } else if (!strcmp(ed->effect_name,"lowp")) {
460 setup_filter(ed,NULL,FALSE);
461 } else if (!strcmp(ed->effect_name,"highp")) {
462 setup_filter(ed,NULL,FALSE);
463 } else if (!strcmp(ed->effect_name,"band")) {
464 setup_filter(ed,_("Width: "),TRUE);
465 } else if (!strcmp(ed->effect_name,"lowpass")) {
466 setup_filter(ed,NULL,FALSE);
467 } else if (!strcmp(ed->effect_name,"highpass")) {
468 setup_filter(ed,NULL,FALSE);
469 } else if (!strcmp(ed->effect_name,"bandpass")) {
470 setup_filter(ed,_("Bandwidth: "),FALSE);
471 } else if (!strcmp(ed->effect_name,"bandreject")) {
472 setup_filter(ed,_("Bandwidth: "),FALSE);
473 } else if (!strcmp(ed->effect_name,"filter")) {
474
475 sd->i1 = inifile_get_guint32("sox_filter_type",0);
476 if (sd->i1 < 0 || sd->i1 > 2) sd->i1=0;
477
478 a = gtk_table_new(6,3,FALSE);
479 gtk_container_add(GTK_CONTAINER(ed->input_area),a);
480
481 attach_label(_("Filter type: "),a,0,0);
482 attach_label(_("Low 6dB corner: "),a,1,0);
483 attach_label(_("Hz"),a,1,2);
484 attach_label(_("High 6db corner: "),a,2,0);
485 attach_label(_("Hz"),a,2,2);
486 attach_label(_("Window length: "),a,4,0);
487 attach_label(_("samples"),a,4,2);
488 attach_label(_("Beta"),a,5,0);
489
490 w1 = b = combo_new();
491 l = g_list_append(l,_("Lowpass"));
492 l = g_list_append(l,_("Highpass"));
493 l = g_list_append(l,_("Bandpass"));
494 combo_set_items(COMBO(b),l,2);
495 g_list_free(l);
496 gtk_table_attach(GTK_TABLE(a),b,1,3,0,1,0,0,0,7);
497
498 b = intbox_new(inifile_get_guint32("sox_filter_low",100));
499 sd->ib1 = INTBOX(b);
500 gtk_table_attach(GTK_TABLE(a),b,1,2,1,2,GTK_FILL,0,0,0);
501
502 b = intbox_new(inifile_get_gfloat("sox_filter_high",400));
503 sd->ib2 = INTBOX(b);
504 gtk_table_attach(GTK_TABLE(a),b,1,2,2,3,GTK_FILL,0,0,0);
505
506 b = gtk_fixed_new();
507 gtk_table_attach(GTK_TABLE(a),b,0,3,3,4,GTK_FILL,0,0,4);
508
509 b = intbox_new(inifile_get_guint32("sox_filter_length",128));
510 sd->ib3 = INTBOX(b);
511 gtk_table_attach(GTK_TABLE(a),b,1,2,4,5,GTK_FILL,0,0,0);
512
513 b = floatbox_new(inifile_get_gfloat("sox_filter_beta",16.0));
514 sd->fb1 = FLOATBOX(b);
515 gtk_table_attach(GTK_TABLE(a),b,1,2,5,6,GTK_FILL,0,0,0);
516
517 gtk_signal_connect(GTK_OBJECT(w1),"selection_changed",
518 GTK_SIGNAL_FUNC(sox_filter_type_changed),sd);
519 combo_set_selection(COMBO(w1),sd->i1);
520 gtk_widget_show_all(a);
521 } else if (!strcmp(ed->effect_name,"compand")) {
522 a = gtk_vbox_new(FALSE,8);
523 gtk_container_add(GTK_CONTAINER(ed->input_area),a);
524 b = gtk_table_new(2,3,FALSE);
525 gtk_box_pack_start(GTK_BOX(a),b,FALSE,FALSE,0);
526 attach_label(_("Attack integration time: "),b,0,0);
527 c = floatbox_new(inifile_get_gfloat("sox_compand_attack",0.3));
528 gtk_table_attach(GTK_TABLE(b),c,1,2,0,1,0,0,0,0);
529 sd->fb1 = FLOATBOX(c);
530 attach_label(_(" seconds"),b,0,2);
531 attach_label(_("Decay integration time: "),b,1,0);
532 c = floatbox_new(inifile_get_gfloat("sox_compand_decay",1.0));
533 gtk_table_attach(GTK_TABLE(b),c,1,2,1,2,0,0,0,0);
534 sd->fb2 = FLOATBOX(c);
535 attach_label(_(" seconds"),b,1,2);
536 b = gtk_table_new(2,COMPAND_LINES+1,FALSE);
537 gtk_box_pack_start(GTK_BOX(a),b,FALSE,FALSE,0);
538 attach_label(_("Input level (dB)"),b,0,0);
539 attach_label(_("Output level (dB)"),b,1,0);
540 sd->fba[0] = g_malloc(COMPAND_LINES*sizeof(Floatbox *));
541 sd->fba[1] = g_malloc(COMPAND_LINES*sizeof(Floatbox *));
542 for (i=0; i<COMPAND_LINES; i++) {
543 c = floatbox_new(0.0);
544 sd->fba[0][i] = FLOATBOX(c);
545 gtk_table_attach(GTK_TABLE(b),c,i+1,i+2,0,1,0,0,0,0);
546 c = floatbox_new(0.0);
547 sd->fba[1][i] = FLOATBOX(c);
548 gtk_table_attach(GTK_TABLE(b),c,i+1,i+2,1,2,0,0,0,0);
549 }
550 b = gtk_table_new(3,3,FALSE);
551 gtk_box_pack_start(GTK_BOX(a),b,FALSE,FALSE,0);
552 attach_label(_("Post-processing gain: "),b,0,0);
553 c = floatbox_new(inifile_get_gfloat("sox_compand_gain",0.0));
554 gtk_table_attach(GTK_TABLE(b),c,1,2,0,1,0,0,0,0);
555 sd->fb3 = FLOATBOX(c);
556 attach_label(_(" dB"),b,0,2);
557 attach_label(_("Initial volume: "),b,1,0);
558 c = floatbox_new(inifile_get_gfloat("sox_compand_startvol",0.0));
559 gtk_table_attach(GTK_TABLE(b),c,1,2,1,2,0,0,0,0);
560 sd->fb4 = FLOATBOX(c);
561 attach_label(_("Delay time: "),b,2,0);
562 c = floatbox_new(inifile_get_gfloat("sox_compand_delay",0.0));
563 gtk_table_attach(GTK_TABLE(b),c,1,2,2,3,0,0,0,0);
564 sd->fb5 = FLOATBOX(c);
565 attach_label(_(" seconds"),b,2,2);
566 gtk_widget_show_all(a);
567 } else if (!strcmp(ed->effect_name,"dcshift")) {
568 a = gtk_table_new(3,2,FALSE);
569 gtk_container_add(GTK_CONTAINER(ed->input_area),a);
570 attach_label(_("Shift amount: "),a,0,0);
571 b = floatbox_new(inifile_get_gfloat("sox_dcshift_amount",0.0));
572 gtk_table_attach(GTK_TABLE(a),b,1,2,0,1,0,0,0,0);
573 sd->fb1 = FLOATBOX(b);
574 b = w1 = gtk_check_button_new_with_label(_("Peak limiter"));
575 gtk_table_attach(GTK_TABLE(a),b,0,2,1,2,GTK_FILL,0,0,0);
576 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b),TRUE);
577 sd->tb1 = GTK_TOGGLE_BUTTON(b);
578 attach_label(_("Limiter gain: "),a,2,0);
579 b = floatbox_new(inifile_get_gfloat("sox_dcshift_gain",0.05));
580 gtk_table_attach(GTK_TABLE(a),b,1,2,2,3,0,0,0,0);
581 sd->fb2 = FLOATBOX(b);
582 gtk_signal_connect(GTK_OBJECT(w1),"toggled",
583 GTK_SIGNAL_FUNC(toggle_sensitive),sd->fb2);
584 gtk_toggle_button_set_active
585 (GTK_TOGGLE_BUTTON(w1),
586 inifile_get_gboolean("sox_dcshift_limiter",FALSE));
587 gtk_widget_show_all(a);
588 } else if (!strcmp(ed->effect_name,"pitch")) {
589 a = gtk_table_new(4,3,FALSE);
590 gtk_container_add(GTK_CONTAINER(ed->input_area),a);
591 attach_label(_("Amount: "),a,0,0);
592 attach_label(_(" cents"),a,0,2);
593 attach_label(_("Window width: "),a,1,0);
594 attach_label(_(" ms"),a,1,2);
595 attach_label(_("Interpolation: "),a,2,0);
596 attach_label(_("Fade: "),a,3,0);
597 b = floatbox_new(inifile_get_gfloat("sox_pitch_amount",0.0));
598 gtk_table_attach(GTK_TABLE(a),b,1,2,0,1,GTK_FILL,0,0,0);
599 sd->fb1 = FLOATBOX(b);
600 b = floatbox_new(inifile_get_gfloat("sox_pitch_width",20.0));
601 gtk_table_attach(GTK_TABLE(a),b,1,2,1,2,GTK_FILL,0,0,0);
602 sd->fb2 = FLOATBOX(b);
603 b = combo_new();
604 gtk_table_attach(GTK_TABLE(a),b,1,3,2,3,0,0,0,0);
605 l = g_list_append(NULL,translate_strip(N_("Interpolation|Cubic")));
606 l = g_list_append(l,translate_strip(N_("Interpolation|Linear")));
607 combo_set_items(COMBO(b),l,0);
608 g_list_free(l);
609 sd->c1 = COMBO(b);
610 b = combo_new();
611 gtk_table_attach(GTK_TABLE(a),b,1,3,3,4,0,0,0,0);
612 l = g_list_append(NULL,translate_strip(N_("Fade|Cos")));
613 l = g_list_append(l,translate_strip(N_("Fade|Hamming")));
614 l = g_list_append(l,translate_strip(N_("Fade|Linear")));
615 l = g_list_append(l,translate_strip(N_("Fade|Trapezoid")));
616 combo_set_items(COMBO(b),l,0);
617 g_list_free(l);
618 sd->c2 = COMBO(b);
619 gtk_widget_show_all(a);
620 } else if (!strcmp(ed->effect_name,"stretch")) {
621 a = gtk_table_new(2,3,FALSE);
622 gtk_container_add(GTK_CONTAINER(ed->input_area),a);
623 attach_label(_("Factor: "),a,0,0);
624 attach_label(_("Window size: "),a,1,0);
625 attach_label(_(" ms"),a,1,2);
626 b = floatbox_new(inifile_get_gfloat("sox_stretch_factor",1.0));
627 gtk_table_attach(GTK_TABLE(a),b,1,2,0,1,GTK_FILL,0,0,0);
628 sd->fb1 = FLOATBOX(b);
629 b = floatbox_new(inifile_get_gfloat("sox_stretch_window",20.0));
630 gtk_table_attach(GTK_TABLE(a),b,1,2,1,2,GTK_FILL,0,0,0);
631 sd->fb2 = FLOATBOX(b);
632 gtk_widget_show_all(a);
633 } else if (!strcmp(ed->effect_name,"vibro")) {
634 a = gtk_table_new(2,3,FALSE);
635 gtk_container_add(GTK_CONTAINER(ed->input_area),a);
636 attach_label(_("Speed: "),a,0,0);
637 attach_label(_(" Hz"),a,0,2);
638 attach_label(_("Depth: "),a,1,0);
639 b = floatbox_new(inifile_get_gfloat("sox_vibro_speed",5.0));
640 gtk_table_attach(GTK_TABLE(a),b,1,2,0,1,GTK_FILL,0,0,0);
641 sd->fb1 = FLOATBOX(b);
642 b = floatbox_new(inifile_get_gfloat("sox_vibro_depth",0.5));
643 gtk_table_attach(GTK_TABLE(a),b,1,2,1,2,GTK_FILL,0,0,0);
644 sd->fb2 = FLOATBOX(b);
645 gtk_widget_show_all(a);
646 } else if (!strcmp(ed->effect_name,"mask") ||
647 !strcmp(ed->effect_name,"earwax") ||
648 !strcmp(ed->effect_name,"reverse")) {
649 a = gtk_label_new(_("This effect has no options."));
650 gtk_container_add(GTK_CONTAINER(ed->input_area),a);
651 gtk_widget_show(a);
652 } else {
653 /* The effect name doesn't match any of the known ones. This
654 shouldn't happen. */
655 g_assert_not_reached();
656 }
657 }
658
sox_dialog_destroy(GtkObject * obj)659 static void sox_dialog_destroy(GtkObject *obj)
660 {
661 SoxDialog *sd = SOX_DIALOG(obj);
662 g_free(sd->fba[0]);
663 g_free(sd->fba[1]);
664 g_free(sd->fba[2]);
665 g_free(sd->fba[3]);
666 memset(sd->fba,0,sizeof(sd->fba));
667 g_free(sd->ca);
668 parent_class->destroy(obj);
669 }
670
sox_dialog_class_init(GtkObjectClass * klass)671 static void sox_dialog_class_init(GtkObjectClass *klass)
672 {
673 parent_class = gtk_type_class(effect_dialog_get_type());
674 EFFECT_DIALOG_CLASS(klass)->apply = sox_dialog_apply;
675 EFFECT_DIALOG_CLASS(klass)->setup = sox_dialog_browser_setup;
676 klass->destroy = sox_dialog_destroy;
677 }
678
sox_dialog_init(GtkObject * obj)679 static void sox_dialog_init(GtkObject *obj)
680 {
681 SoxDialog *sd = SOX_DIALOG(obj);
682 sd->fb1 = sd->fb2 = sd->fb3 = sd->fb4 = sd->fb5 = NULL;
683 sd->tb1 = NULL;
684 sd->ib1 = sd->ib2 = sd->ib3 = NULL;
685 memset(sd->fba,0,sizeof(sd->fba));
686 sd->ca = NULL;
687 /* We wait with proper initialization until we know
688 * which effect we represent (in sox_dialog_mainwindow_set). */
689 }
690
sox_dialog_get_type(void)691 GtkType sox_dialog_get_type(void)
692 {
693 static GtkType id = 0;
694 if (!id) {
695 GtkTypeInfo info = {
696 "SoxDialog",
697 sizeof(SoxDialog),
698 sizeof(SoxDialogClass),
699 (GtkClassInitFunc)sox_dialog_class_init,
700 (GtkObjectInitFunc)sox_dialog_init
701 };
702 id = gtk_type_unique(effect_dialog_get_type(),&info);
703 }
704 return id;
705 }
706
sox_dialog_register_main(gchar source_tag)707 gboolean sox_dialog_register_main(gchar source_tag)
708 {
709 int fd[2],fd2[2],i,j,lb_pos=0;
710 pid_t p;
711 gchar *c,*d,**s,**sn;
712 gchar linebuf[8192];
713 gboolean *map;
714 int sox_maj=0;
715
716 if (!program_exists("sox")) return FALSE;
717
718 /* Run the command 'sox -h' and try to see which effects it
719 * supports. */
720 i = pipe(fd);
721 j = pipe(fd2);
722 if (i == -1 || j == -1) {
723 console_perror(_("Error creating pipe"));
724 if (i == 0) { close(fd[0]); close(fd[1]); }
725 if (j == 0) { close(fd2[0]); close(fd2[1]); }
726 return TRUE;
727 }
728 p = fork();
729 if (p == -1) {
730 console_perror(_("Couldn't fork"));
731 close(fd[0]);
732 close(fd[1]);
733 close(fd2[0]);
734 close(fd2[1]);
735 return TRUE;
736 }
737 if (p == 0) {
738 /* Child process - run 'sox -h' and catch stdout/stderr output */
739 /* Put stdout descriptor in fd[0] */
740 fd[0] = fd2[1];
741 close_all_files_except(fd,2);
742 if (dup2(fd[0],1)==-1 || dup2(fd[1],2)==-1 ||
743 execlp("sox","sox","-h",NULL)==-1)
744
745 printf(_("Error running 'sox -h': %s\n"),strerror(errno));
746 else
747 puts(_("Should not reach this point"));
748 _exit(1);
749 } else {
750 /* Parent process - read data */
751 close(fd[1]);
752 close(fd2[1]);
753 /* Put stdout descriptor in fd array */
754 fd[1] = fd2[0];
755 /* Read input */
756 j = 0;
757 while (lb_pos < sizeof(linebuf)-1 && j<2) {
758 i = read(fd[j],linebuf+lb_pos,sizeof(linebuf)-lb_pos-1);
759 if (i == 0) j++; /* Read from other descriptor */
760 if (i < 0) {
761 if (errno == EINTR) continue;
762 console_perror(_("Error reading sox output"));
763 return TRUE;
764 }
765 lb_pos += i;
766 }
767 linebuf[lb_pos] = 0;
768 if (lb_pos == 0) return TRUE;
769 /* printf("Sox output: %s\n",linebuf); */
770 /* Look at first line to see if it's SoX version >= 13 */
771 c = strchr(linebuf,'\n');
772 *c = 0;
773 d = strstr(linebuf,"SoX Version ");
774 if (d != NULL) sox_maj = strtol(d+12,NULL,10);
775 d = strstr(linebuf,"SoX v");
776 if (d != NULL && sox_maj==0) sox_maj = strtol(d+5,NULL,10);
777 if (sox_maj > 13) {
778 /* printf("SoX version %d detected\n",sox_maj); */
779 v14_mode = TRUE;
780 } else if (sox_maj > 12) {
781 /* printf("SoX version %d detected\n",sox_maj); */
782 v13_mode = TRUE;
783 }
784 *c = '\n';
785 /* Scan for available effects */
786 c = strstr(linebuf,"effect: ");
787 if (c == NULL) {
788 c = strstr(linebuf,"effects: ");
789 if (c == NULL) {
790 c = strstr(linebuf,"SUPPORTED EFFECTS: ");
791 if (c == NULL) {
792 c = strstr(linebuf,"\nEFFECTS: ");
793 if (c == NULL) {
794 console_message(_("Unable to detect supported "
795 "SoX effects"));
796 return TRUE;
797 }
798 c += 10;
799 } else
800 c += 19;
801 } else
802 c += 9;
803 } else
804 c += 8;
805
806 d = strchr(c,'\n');
807 if (d) *d=0;
808 for (d=strtok(c," "); d!=NULL; d=strtok(NULL," ")) {
809 /* printf("SoX supports effect '%s'\n",d); */
810 for (s=supported_effects,
811 map=sox_support_map; *s!=NULL && strcmp(*s,d);
812 s++,map++) { /*Empty for loop*/ }
813 if (*s != NULL) *map = TRUE;
814 }
815 /* Finished.. */
816 close(fd[0]);
817 wait(NULL);
818 /* Register the effects we found */
819 for (s=supported_effects,sn=supported_effect_names,
820 map=sox_support_map; *s!=NULL; s++,sn++,map++)
821 if (*map)
822 effect_register_add_effect(source_tag,*s,_(*sn),
823 "Chris Bagwell","");
824 return FALSE;
825 }
826 return FALSE;
827 }
828
sox_dialog_rebuild_func(gchar source_tag,gpointer user_data)829 void sox_dialog_rebuild_func(gchar source_tag, gpointer user_data)
830 {
831 if (sox_dialog_register_main(source_tag))
832 console_message(_("Sox support couldn't be initialized"));
833 }
834
sox_dialog_get_func(gchar * name,gchar source_tag,gpointer user_data)835 EffectDialog *sox_dialog_get_func(gchar *name, gchar source_tag,
836 gpointer user_data)
837 {
838 char **c;
839 gboolean *b;
840 for (c=supported_effects,b=sox_support_map; *c!=NULL; c++,b++) {
841 if (*b && !strcmp(*c,name))
842 return gtk_type_new(sox_dialog_get_type());
843 }
844 return NULL;
845 }
846
sox_dialog_register(void)847 void sox_dialog_register(void)
848 {
849 effect_register_add_source("SoX",'S',sox_dialog_rebuild_func,NULL,
850 sox_dialog_get_func,NULL);
851 }
852
sox_dialog_first_effect(void)853 gchar *sox_dialog_first_effect(void)
854 {
855 gboolean *map;
856 gchar **s;
857 s = supported_effects;
858 map = sox_support_map;
859 while (*map == FALSE && *s != NULL) { map++; s++; }
860 return *s;
861 }
862