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