1 /*
2  * Copyright (C) 2002 2003 2004 2005 2006 2007 2009 2011 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 <string.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 #include <signal.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include "um.h"
31 #include "inifile.h"
32 #include "rawdialog.h"
33 #include "pipedialog.h"
34 #include "mainloop.h"
35 #include "filetypes.h"
36 #include "tempfile.h"
37 #include "combo.h"
38 #include "gettext.h"
39 
40 #define MAX_REAL_SIZE inifile_get_guint32(INI_SETTING_REALMAX,INI_SETTING_REALMAX_DEFAULT)
41 
42 #define WAVE_FORMAT_PCM 1
43 #define WAVE_FORMAT_IEEE_FLOAT 3
44 
45 struct file_type;
46 struct file_type {
47      gchar *name;
48      gchar *extension;
49      gboolean lossy;
50      gboolean (*typecheck)(gchar *filename);
51      Chunk *(*load)(gchar *filename, int dither_mode, StatusBar *bar);
52      gboolean (*save)(Chunk *chunk, gchar *filename, gpointer settings,
53 		      struct file_type *type, int dither_mode, StatusBar *bar,
54 		      gboolean *fatal);
55      gpointer (*get_settings)(void);
56      void (*free_settings)(gpointer settings);
57      gint extra_data;
58 };
59 
60 static GList *file_types = NULL;
61 static struct file_type *raw_type,*mplayer_type;
62 
63 static gboolean wav_check(gchar *filename);
64 static Chunk *wav_load(gchar *filename, int dither_mode, StatusBar *bar);
65 static gint wav_save(Chunk *chunk, gchar *filename, gpointer settings,
66 		     struct file_type *type, int dither_mode,
67 		     StatusBar *bar,gboolean *fatal);
68 
69 #ifdef HAVE_LIBSNDFILE
70 static gboolean sndfile_check(gchar *filename);
71 static Chunk *sndfile_load(gchar *filename, int dither_mode, StatusBar *bar);
72 static gint sndfile_save(Chunk *chunk, gchar *filename, gpointer settings,
73 			 struct file_type *type, int dither_mode,
74 			 StatusBar *bar, gboolean *fatal);
75 static gboolean sndfile_save_main(Chunk *chunk, gchar *filename,
76 				  int format, int dither_mode, StatusBar *bar,
77 				  gboolean *fatal);
78 static gboolean sndfile_ogg_flag = FALSE;
79 #endif
80 
81 static Chunk *raw_load(gchar *filename, int dither_mode, StatusBar *bar);
82 static gint raw_save(Chunk *chunk, gchar *filename, gpointer settings,
83 		     struct file_type *type, int dither_mode,
84 		     StatusBar *bar, gboolean *fatal);
85 
86 static Chunk *ogg_load(gchar *filename, int dither_mode, StatusBar *bar);
87 static gint ogg_save(Chunk *chunk, gchar *filename, gpointer settings,
88 		     struct file_type *type,
89 		     int dither_mode, StatusBar *bar, gboolean *fatal);
90 
91 
92 static gpointer mp3_get_settings(void);
93 static Chunk *mp3_load(gchar *filename, int dither_mode, StatusBar *bar);
94 static gint mp3_save(Chunk *chunk, gchar *filename, gpointer settings,
95 		     struct file_type *type,
96 		     int dither_mode, StatusBar *bar, gboolean *fatal);
97 
98 static Chunk *try_mplayer(gchar *filename, int dither_mode, StatusBar *bar);
99 
xputenv(char * string)100 static gboolean xputenv(char *string)
101 {
102      if (putenv(string)) {
103 	  user_error(_("putenv failed!"));
104 	  return TRUE;
105      }
106      return FALSE;
107 }
108 
xunsetenv(char * varname)109 static gboolean xunsetenv(char *varname)
110 {
111 #ifdef UNSETENV_RETVAL
112      if (unsetenv(varname) != 0) {
113 	  console_message(_("unsetenv failed!"));
114 	  return TRUE;
115      }
116 #else
117      unsetenv(varname);
118 #endif
119      return FALSE;
120 }
121 
register_file_type(gchar * name,gchar * ext,gboolean lossy,gboolean (* typecheck)(gchar * filename),Chunk * (* load)(gchar * filename,int dither_mode,StatusBar * bar),gint (* save)(Chunk * chunk,gchar * filename,gpointer settings,struct file_type * type,int dither_mode,StatusBar * bar,gboolean * fatal),int extra_data)122 static struct file_type *register_file_type
123 (gchar *name, gchar *ext, gboolean lossy,
124  gboolean (*typecheck)(gchar *filename),
125  Chunk *(*load)(gchar *filename, int dither_mode, StatusBar *bar),
126  gint (*save)(Chunk *chunk, gchar *filename,gpointer settings,
127 	      struct file_type *type,int dither_mode,
128 	      StatusBar *bar,gboolean *fatal),
129  int extra_data)
130 {
131      struct file_type *t;
132      t = g_malloc(sizeof(*t));
133      t->name = g_strdup(name);
134      t->extension = g_strdup(ext);
135      t->lossy = lossy;
136      t->typecheck = typecheck;
137      t->load = load;
138      t->save = save;
139      t->extra_data = extra_data;
140      t->get_settings = NULL;
141      t->free_settings = NULL;
142      file_types = g_list_append(file_types,t);
143      return t;
144 }
145 
setup_types(void)146 static void setup_types(void)
147 {
148      struct file_type *t;
149 #if defined(HAVE_LIBSNDFILE)
150      SF_FORMAT_INFO info;
151      int major_count,i;
152      gchar buf[32];
153 #endif
154      if (file_types) return;
155      register_file_type(_("Microsoft WAV format"), ".wav", FALSE, wav_check,
156 			wav_load, wav_save, 0);
157 #if defined(HAVE_LIBSNDFILE)
158      sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof(int));
159      for (i=0; i<major_count; i++) {
160 	  info.format = i;
161 	  if (sf_command(NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof(info))) {
162 	       console_message(sf_strerror(NULL));
163 	       continue;
164 	  }
165 	  if ((info.format&SF_FORMAT_TYPEMASK) == SF_FORMAT_RAW ||
166 	      (info.format&SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) continue;
167 	  if ((info.format&SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG)
168 	       sndfile_ogg_flag=TRUE;
169 	  snprintf(buf,sizeof(buf),".%s",info.extension);
170 	  register_file_type((gchar *)info.name, buf, FALSE, sndfile_check,
171 			     sndfile_load, sndfile_save,
172 			     info.format&SF_FORMAT_TYPEMASK);
173 	  if (!strcmp(info.extension,"oga"))
174 	       register_file_type((gchar *)info.name, ".ogg", FALSE,
175 				  sndfile_check,
176 				  sndfile_load, sndfile_save,
177 				  info.format&SF_FORMAT_TYPEMASK);
178      }
179 #endif
180      if (program_exists("oggenc") || program_exists("oggdec"))
181 	  register_file_type(_("Ogg Vorbis"),".ogg", TRUE, NULL, ogg_load,
182 			     ogg_save, 0);
183      if (program_exists("lame")) {
184 	    t = register_file_type("MP3",".mp3",TRUE,NULL,mp3_load,mp3_save,0);
185 	    t->get_settings = mp3_get_settings;
186 	    t->free_settings = g_free;
187      }
188      raw_type = register_file_type(_("Raw PCM data"), ".raw", FALSE,NULL,
189 				   raw_load, raw_save, 0);
190      mplayer_type = register_file_type(_("Open with MPlayer"), NULL,TRUE,NULL,
191 				       try_mplayer, NULL, 0);
192 }
193 
fileformat_count(void)194 guint fileformat_count(void)
195 {
196      setup_types();
197      return g_list_length(file_types);
198 }
199 
fileformat_name(guint fileformat)200 gchar *fileformat_name(guint fileformat)
201 {
202      struct file_type *t;
203      t = g_list_nth_data(file_types, fileformat);
204      if (t) return t->name;
205      return NULL;
206 }
207 
fileformat_extension(guint fileformat)208 gchar *fileformat_extension(guint fileformat)
209 {
210      struct file_type *t;
211      t = g_list_nth_data(file_types, fileformat);
212      if (t) return t->extension;
213      return NULL;
214 }
215 
fileformat_has_options(guint fileformat)216 gboolean fileformat_has_options(guint fileformat)
217 {
218      struct file_type *t;
219      t = g_list_nth_data(file_types, fileformat);
220      if (t) return ((t->get_settings) != NULL);
221      else return FALSE;
222 }
223 
chunk_load_main(gchar * filename,int dither_mode,StatusBar * bar,struct file_type ** format)224 static Chunk *chunk_load_main(gchar *filename, int dither_mode, StatusBar *bar,
225 			      struct file_type **format)
226 {
227      GList *l;
228      struct file_type *ft;
229      gchar *c;
230      Chunk *chunk;
231 
232      setup_types();
233 
234      /* Search through the extensions */
235      for (l=file_types; l!=NULL; l=l->next) {
236 	  ft = (struct file_type *)l->data;
237 	  if (!ft->extension) continue;
238 	  c = strchr(filename,0) - strlen(ft->extension);
239 	  if (c<filename || strcmp(c,ft->extension)!=0) continue;
240 	  if (ft->typecheck==NULL || ft->typecheck(filename)) {
241 	       *format = ft;
242 	       return ft->load(filename,dither_mode,bar);
243 	  }
244      }
245      /* Use the file checking functions */
246      for (l=file_types; l!=NULL; l=l->next) {
247 	  ft = (struct file_type *)l->data;
248 	  if (ft->typecheck == NULL) continue;
249 	  if (ft->typecheck(filename)) {
250 	       *format = ft;
251 	       return ft->load(filename,dither_mode,bar);
252 	  }
253      }
254      /* Try mplayer if available */
255      chunk = try_mplayer(filename,dither_mode,bar);
256      if (chunk != NULL) {
257 	  *format = mplayer_type;
258 	  return chunk;
259      }
260      /* Use the raw loader */
261      *format = raw_type;
262      return raw_load(filename,dither_mode,bar);
263 }
264 
chunk_load_x(gchar * filename,int dither_mode,StatusBar * bar,gboolean * lossy)265 Chunk *chunk_load_x(gchar *filename, int dither_mode, StatusBar *bar,
266 		    gboolean *lossy)
267 {
268      Chunk *chunk;
269      gchar *c;
270      EFILE *f;
271      struct file_type *ft = NULL;
272 
273      /* First, see if the file exists */
274      if (!file_exists(filename)) {
275 	  c = g_strdup_printf(_("The file %s does not exist!"),filename);
276 	  user_error(c);
277 	  g_free(c);
278 	  return NULL;
279      }
280 
281      if (!file_is_normal(filename)) {
282 	  c = g_strdup_printf(_("The file %s is not a regular file!"),filename);
283 	  user_error(c);
284 	  g_free(c);
285 	  return NULL;
286      }
287 
288      /* Try to open it here so we don't get a lot of 'permission
289       * denied' and similar errors later */
290      f = e_fopen(filename,EFILE_READ);
291      if (f == NULL) return NULL;
292      e_fclose(f);
293 
294      status_bar_begin_progress(bar,1,_("Loading"));
295      chunk = chunk_load_main(filename,dither_mode,bar,&ft);
296      status_bar_end_progress(bar);
297 
298      if (chunk != NULL) *lossy = ft->lossy;
299      return chunk;
300 }
301 
chunk_load(gchar * filename,int dither_mode,StatusBar * bar)302 Chunk *chunk_load(gchar *filename, int dither_mode, StatusBar *bar)
303 {
304      gboolean b;
305      return chunk_load_x(filename,dither_mode,bar,&b);
306 }
307 
308 
choose_format(gchar * filename)309 static struct file_type *choose_format(gchar *filename)
310 {
311      struct file_type *ft;
312      GList *l;
313      gchar *c, **z, **z2;
314      guint i;
315      gint x;
316      setup_types();
317      /* Search through the extensions */
318      for (l=file_types; l!=NULL; l=l->next) {
319 	  ft = (struct file_type *)l->data;
320 	  if (!ft->extension) continue;
321 	  c = strchr(filename,0) - strlen(ft->extension);
322 	  if (c<filename || strcmp(c,ft->extension)!=0) continue;
323 	  if (ft->save != NULL)
324 	       return ft;
325      }
326      /* Ask for file type */
327      z = g_malloc((g_list_length(file_types)+1) * sizeof(char *));
328      for (l=file_types,z2=z; l!=NULL; l=l->next,z2++) {
329 	  ft = (struct file_type *)l->data;
330 	  *z2 = ft->name;
331      }
332      *z2 = NULL;
333      c = g_strdup_printf(_("The file name '%s' has an extension unknown to the "
334 			 "program. Please specify in which format this file "
335 			 "should be saved."),filename);
336      i = (guint)inifile_get_guint32("lastSaveFileType",0);
337      if (i>=g_list_length(file_types)) i=0;
338      x = user_choice(z, (guint)i, _("Unknown file type"), c, TRUE);
339      g_free(z);
340      g_free(c);
341      if (x==-1) return NULL;
342      inifile_set_guint32("lastSaveFileType",(guint32)x);
343      ft = g_list_nth_data( file_types, x );
344      return ft;
345 }
346 
chunk_save(Chunk * chunk,gchar * filename,int filetype,gboolean use_default_settings,int dither_mode,StatusBar * bar)347 gboolean chunk_save(Chunk *chunk, gchar *filename, int filetype,
348 		    gboolean use_default_settings, int dither_mode,
349 		    StatusBar *bar)
350 {
351      gchar *d;
352      gboolean b=FALSE,res;
353      EFILE *f;
354      gpointer settings = NULL;
355      struct file_type *ft;
356      gint i;
357 
358      /* Let the user choose a format first, so that if the user presses cancel,
359       * we haven't done any of the backup/unlink stuff.. */
360      if (filetype < 0)
361 	  ft = choose_format(filename);
362      else
363 	  ft = g_list_nth_data(file_types, filetype);
364      if (ft == NULL) return TRUE;
365 
366      if (!use_default_settings) {
367 	  if (ft->get_settings == NULL) {
368 	       i = user_message("There are no settings for this file format. "
369 				"Proceeding with standard settings.",
370 				UM_OKCANCEL);
371 	       if (i == MR_CANCEL) return TRUE;
372 	  } else {
373 	       settings = ft->get_settings();
374 	       if (settings == NULL) return TRUE;
375 	  }
376      }
377 
378      if (file_exists(filename)) {
379 
380           /* Check that the file is writeable by us. */
381           f = e_fopen(filename,EFILE_APPEND);
382           if (f == NULL) return TRUE;
383           e_fclose(f);
384 
385           /* we may need to move or copy the file into a tempfile... */
386           if (datasource_backup_unlink(filename)) return TRUE;
387      }
388 
389      status_bar_begin_progress(bar,chunk->size,_("Saving"));
390      res = ft->save(chunk,filename,settings,ft,dither_mode,bar,&b);
391 
392      if (res && b && !status_bar_progress(bar,0)) {
393 	  d = g_strdup_printf(_("The file %s may be destroyed since the"
394 			      " saving failed. Try to free up some disk space "
395 			      "and save again. If you exit now the "
396 			      "file's contents could be in a bad state. "),
397 			      filename);
398 	  user_warning(d);
399 	  g_free(d);
400      }
401      status_bar_end_progress(bar);
402 
403      if (settings != NULL) ft->free_settings(settings);
404 
405      return res;
406 }
407 
408 /* WAV */
409 
410 
wav_check(gchar * filename)411 static gboolean wav_check(gchar *filename)
412 {
413      EFILE *f;
414      char buf[12];
415      f = e_fopen ( filename, EFILE_READ );
416      if (!f) return FALSE;
417      if (e_fread(buf,12,f)) { e_fclose(f); return FALSE; }
418      e_fclose(f);
419      if ((memcmp(buf,"RIFF",4) && memcmp(buf,"RIFX",4)) || memcmp(buf+8,"WAVE",4)) return FALSE;
420      return TRUE;
421 }
422 
find_wav_chunk(EFILE * f,char * chunkname,gboolean be)423 static off_t find_wav_chunk(EFILE *f, char *chunkname, gboolean be)
424 {
425      char buf[4];
426      guint32 l;
427      while (1) {
428 	  if (e_fread(buf,4,f) ||
429 	      e_fread_u32_xe(&l,f,be)) return -1;
430 	  if (!memcmp(buf,chunkname,4)) {
431 #if SIZEOF_OFF_T > 4
432 	       return (off_t) l;
433 #else
434 	       return (l>=0x80000000) ? 0x7FFFFFFFL : l;
435 #endif
436 	  }
437 	  if (e_fseek(f,(off_t)l,SEEK_CUR)) return -1;
438      }
439 }
440 
wav_load(char * filename,int dither_mode,StatusBar * bar)441 static Chunk *wav_load(char *filename, int dither_mode, StatusBar *bar)
442 {
443      EFILE *f;
444      char *c;
445      char buf[12];
446      long int m;
447      guint16 s,channels,bits;
448      guint32 samplerate;
449      off_t l,e;
450      gboolean rifx = FALSE;
451      Datasource *ds;
452      Dataformat fmt;
453      f = e_fopen ( filename, EFILE_READ );
454      if (!f) return NULL;
455      if (e_fread(buf,12,f)) { e_fclose(f); return NULL; }
456      if ((memcmp(buf,"RIFF",4) && memcmp(buf,"RIFX",4)) || memcmp(buf+8,"WAVE",4)) {
457 	  c = g_strdup_printf (_("%s is not a valid wav file"),filename);
458 	  user_error(c);
459 	  g_free(c);
460 	  e_fclose(f);
461 	  return NULL;
462      }
463      if (!memcmp(buf,"RIFX",4)) rifx=TRUE;
464      l = find_wav_chunk(f,"fmt ",rifx);
465      if ((l == -1) || e_fread_u16_xe(&s,f,rifx)) { e_fclose(f); return NULL; }
466      if ( l<16 || (s != 0x0001 && s != 0x0003) ) {
467 	  e_fclose(f);
468 #if defined(HAVE_LIBSNDFILE)
469 	  /* Fall back on Libsndfile for reading non-PCM wav files. */
470 	  return sndfile_load(filename,dither_mode,bar);
471 #else
472 	  c = g_strdup_printf (_("The file %s is a compressed wav file. This "
473 			       "program can only work with uncompressed wav "
474 			       "files."),filename);
475 	  user_error(c);
476 	  free(c);
477 	  return NULL;
478 #endif
479      }
480      if (e_fread_u16_xe(&channels,f,rifx) ||
481 	 e_fread_u32_xe(&samplerate,f,rifx) ||
482 	 e_fseek(f,6,SEEK_CUR) ||
483 	 e_fread_u16_xe(&bits,f,rifx) ||
484 	 (l>16 && e_fseek(f,l-16,SEEK_CUR))) {
485 	  e_fclose(f);
486 	  return NULL;
487      }
488      l = find_wav_chunk(f,"data",rifx);
489      m = e_ftell(f);
490      if (l == -1 || m == -1) {
491 	  e_fclose(f);
492 	  return NULL;
493      }
494 
495      /* Find length of file and close it*/
496      if (e_fseek(f,0,SEEK_END)) { e_fclose(f); return NULL; }
497      e = e_ftell(f);
498      e_fclose(f);
499      if (e == -1) { return NULL; }
500 
501      /* Is the file smaller than expected? */
502      if (e-m<l) l=e-m; /* Borde det vara l=e-m+1?? */
503 
504 #if SIZEOF_OFF_T > 4
505      /* Special code to handle very large files. If the specified data
506       * chunk length makes the expected file length within 32 bytes of
507       * the 4GB limit, assume the data chunk continues until EOF */
508      if (m+l >= (off_t)0xFFFFFFE0) l=e-m;
509 #endif
510 
511      fmt.samplerate = samplerate;
512      fmt.channels = channels;
513      if (s == 1)
514 	  fmt.type = DATAFORMAT_PCM;
515      else
516 	  fmt.type = DATAFORMAT_FLOAT;
517      fmt.samplesize = (bits+7) >> 3;
518      fmt.samplebytes = fmt.samplesize * fmt.channels;
519      fmt.sign = (bits>8);
520      fmt.bigendian = rifx;
521      fmt.packing = 0;
522 
523      ds = gtk_type_new(datasource_get_type());
524 
525      memcpy(&(ds->format),&fmt,sizeof(fmt));
526      ds->type = DATASOURCE_VIRTUAL;
527      ds->bytes = l;
528      ds->length = l / (off_t)fmt.samplebytes;
529      ds->data.virtual.filename = g_strdup(filename);
530      ds->data.virtual.offset = m;
531      /* printf("wav_load: samplebytes: %d, length: %ld\n",(int)fmt.samplebytes,
532 	    (long int)ds->length);
533 	    printf("wav_load: bigendian: %d\n",fmt.bigendian); */
534 
535      if (ds->bytes <= MAX_REAL_SIZE)
536 	  datasource_realize(ds,dither_mode);
537 
538      return chunk_new_from_datasource(ds);
539 }
540 
wav_save(Chunk * chunk,char * filename,gpointer settings,struct file_type * type,int dither_mode,StatusBar * bar,gboolean * fatal)541 static gboolean wav_save(Chunk *chunk, char *filename, gpointer settings,
542 			 struct file_type *type, int dither_mode,
543 			 StatusBar *bar, gboolean *fatal)
544 {
545      Chunk *c;
546      EFILE *f;
547      Datasource *ds;
548      DataPart *dp;
549      Dataformat *fmt,cfmt;
550      gboolean b,q;
551 
552      fmt = &(chunk->format);
553 
554 
555      /* Check if the format is OK */
556 
557      if (fmt->type == DATAFORMAT_PCM) {
558 	  /* Check that the sign and endian-ness is correct */
559 	  if (fmt->samplesize == 1) q = FALSE;
560 	  else q = TRUE;
561 	  if (XOR(fmt->sign,q) || fmt->bigendian || fmt->packing!=0) {
562 	       memcpy(&cfmt,fmt,sizeof(cfmt));
563 	       cfmt.sign = q;
564 	       cfmt.bigendian = FALSE;
565 	       if (cfmt.packing != 0) {
566 		    g_assert(cfmt.samplesize == 4);
567 		    cfmt.samplesize = 3;
568 		    cfmt.packing = 0;
569 	       }
570 	       c = chunk_convert(chunk,&cfmt,DITHER_UNSPEC,bar);
571 	       b = wav_save(c,filename,settings,type,dither_mode,bar,fatal);
572 	       gtk_object_sink(GTK_OBJECT(c));
573 	       return b;
574 	  }
575      } else if (ieee_le_compatible || ieee_be_compatible) {
576 	  if (fmt->bigendian) {
577 	       memcpy(&cfmt,fmt,sizeof(cfmt));
578 	       cfmt.bigendian = FALSE;
579 	       c = chunk_convert(chunk,&cfmt,DITHER_UNSPEC,bar);
580 	       b = wav_save(c,filename,settings,type,dither_mode,bar,fatal);
581 	       gtk_object_sink(GTK_OBJECT(c));
582 	       return b;
583 	  }
584      }
585 
586      /* If we can't handle it, delegate the FP chunk saving to libsndfile. */
587      if (chunk->format.type == DATAFORMAT_FLOAT &&
588 	 !ieee_le_compatible && !ieee_be_compatible) {
589 #ifdef HAVE_LIBSNDFILE
590 	  return sndfile_save_main(chunk,filename,SF_FORMAT_WAV,dither_mode,
591 				   bar,fatal);
592 #else
593 	  user_error(_("On this system, libsndfile is required to save "
594 		     "floating-point wav files."));
595 	  return TRUE;
596 #endif
597      }
598 
599      if (fmt->type == DATAFORMAT_PCM && fmt->samplesize==1 && fmt->sign) {
600 	  /* user_error(_("8-bit wav-files must be in unsigned format!")); */
601 	  g_assert_not_reached();
602      }
603      if (fmt->type == DATAFORMAT_PCM && fmt->samplesize>1 && !fmt->sign) {
604 	  /*user_error(_("16/24/32-bit wav-files must be in signed format!"));
605 	  */
606 	  g_assert_not_reached();
607      }
608      if (fmt->type == DATAFORMAT_PCM && fmt->bigendian == TRUE) {
609 	  /* user_error(_("wav files must be in little endian format!")); */
610 	  g_assert_not_reached();
611      }
612 
613      /* Give a warning once if we're saving a file larger than 2GB */
614      if (chunk->size > (off_t)0x7FFFFFD3 &&
615 	 !inifile_get_gboolean("warnLargeWav",FALSE)) {
616 	  user_warning(_("You are saving a wav file larger than 2048MB."
617 		       " Such large files are non-standard and may not be "
618 		       "readable by all programs.\n\n"
619 		       " (this warning will not be displayed again)"));
620 	  inifile_set_gboolean("warnLargeWav",TRUE);
621      }
622 
623      /* If we're saving an entire TEMPFILE and it's in WAV format, we can just
624       * move it (this speeds up things like saving directly after
625       * recording. */
626      dp = (DataPart *)chunk->parts->data;
627      ds = dp->ds;
628      if (chunk->parts->next == NULL &&
629 	 dp->position == 0 && dp->length == ds->length &&
630 	 (ds->type == DATASOURCE_TEMPFILE) &&
631 	 (ds->data.virtual.offset == 44 ||
632 	  (ieee_le_compatible && ds->data.virtual.offset == 58)) &&
633 	 wav_check(ds->data.virtual.filename)) {
634 	  if (xunlink(filename)) return TRUE;
635 	  *fatal = TRUE;
636 	  if (xrename(ds->data.virtual.filename,filename,TRUE)) return TRUE;
637 	  ds->type = DATASOURCE_VIRTUAL;
638 	  g_free(ds->data.virtual.filename);
639 	  ds->data.virtual.filename = g_strdup(filename);
640 	  return FALSE;
641      } else {
642           /* Regular saving */
643 
644 
645 
646 	  f = e_fopen(filename,EFILE_WRITE);
647 	  if (!f) return TRUE;
648 	  *fatal = TRUE;
649 
650 	  if (write_wav_header(f,fmt,chunk->size)) {
651 	       e_fclose(f);
652 	       return TRUE;
653 	  }
654 	  b = chunk_dump(chunk,f,FALSE,dither_mode,bar);
655 	  if (b) {
656 	       e_fclose_remove(f);
657 	       return TRUE;
658 	  }
659 	  e_fclose ( f );
660 	  return FALSE;
661      }
662      return 0;
663 }
664 
665 /* RAW */
666 
raw_load(gchar * filename,int dither_mode,StatusBar * bar)667 static Chunk *raw_load(gchar *filename, int dither_mode, StatusBar *bar)
668 {
669      Datasource *ds;
670      Dataformat *fmt;
671      off_t i;
672      guint offs;
673      i = errdlg_filesize(filename);
674      if (i==-1) return NULL;
675      fmt = rawdialog_execute(filename,i,&offs);
676      if (!fmt) return NULL;
677      ds = (Datasource *)gtk_type_new(datasource_get_type());
678      memcpy(&(ds->format),fmt,sizeof(Dataformat));
679      ds->bytes = i-offs;
680      ds->length = (i-offs)/fmt->samplebytes;
681      ds->type = DATASOURCE_VIRTUAL;
682      ds->data.virtual.filename = g_strdup(filename);
683      ds->data.virtual.offset = offs;
684      return chunk_new_from_datasource(ds);
685 }
686 
raw_save(Chunk * chunk,gchar * filename,gpointer settings,struct file_type * type,int dither_mode,StatusBar * bar,gboolean * fatal)687 static gint raw_save(Chunk *chunk, gchar *filename, gpointer settings,
688 			 struct file_type *type, int dither_mode,
689 			 StatusBar *bar, gboolean *fatal)
690 {
691      EFILE *f;
692      gint i;
693 
694      f = e_fopen(filename,EFILE_WRITE);
695      if (!f) return TRUE;
696 
697      i = chunk_dump(chunk,f,IS_BIGENDIAN,dither_mode,bar);
698      if (i < 0) *fatal = TRUE;
699      e_fclose(f);
700      return i;
701 }
702 
703 /* SNDFILE */
704 
705 #if defined(HAVE_LIBSNDFILE)
706 
sndfile_check(gchar * filename)707 static gboolean sndfile_check(gchar *filename)
708 {
709      SF_INFO info = {};
710      SNDFILE *s;
711      s = sf_open(filename, SFM_READ, &info);
712      if (s) sf_close(s);
713      return (s!=NULL && ((info.format&SF_FORMAT_SUBMASK) != SF_FORMAT_RAW)
714 	     && ((info.format&SF_FORMAT_SUBMASK) != SF_FORMAT_VORBIS ||
715 		 inifile_get_guint32("sndfileOggMode",1)!=2));
716 }
717 
sndfile_load(gchar * filename,int dither_mode,StatusBar * bar)718 static Chunk *sndfile_load(gchar *filename, int dither_mode, StatusBar *bar)
719 {
720      SF_INFO info = {};
721      SNDFILE *s;
722      Datasource *ds;
723      Dataformat f;
724      gchar *ch;
725      int rawbuf_size;
726      void *rawbuf;
727      guint st;
728      gboolean raw_readable=FALSE;
729      TempFile tmp;
730      s = sf_open(filename, SFM_READ, &info);
731      if (!s) {
732           ch = g_strdup_printf(_("Failed to open '%s'!"),filename);
733           user_error(ch);
734           g_free(ch);
735           return NULL;
736      }
737      /* printf("info.seekable = %d, info.samples = %d\n",info.seekable,info.samples); */
738      memset(&f,0,sizeof(f));
739      f.type = DATAFORMAT_PCM;
740      f.bigendian = IS_BIGENDIAN;
741      /* Fix samplesize parameter */
742      switch (info.format&SF_FORMAT_SUBMASK) {
743      case SF_FORMAT_PCM_U8: f.sign=FALSE;f.samplesize=1;raw_readable=TRUE;break;
744      case SF_FORMAT_PCM_S8: f.sign=TRUE; f.samplesize=1;raw_readable=TRUE;break;
745      case SF_FORMAT_PCM_16: f.sign=TRUE; f.samplesize=2;break;
746      case SF_FORMAT_PCM_24: f.sign=TRUE; f.samplesize=4; f.packing=1; break;
747      case SF_FORMAT_PCM_32: f.sign=TRUE; f.samplesize=4; break;
748 
749      case SF_FORMAT_VORBIS:
750 	  if (inifile_get_guint32("sndfileOggMode",1)!=0)
751 	       info.seekable = 0;
752 	  /* (Fall through on purpose) */
753 
754 	  /* Default to floating point */
755      default: memcpy(&f,&dataformat_sample_t,sizeof(f)); break;
756      }
757      f.samplerate = info.samplerate;
758      f.channels = info.channels;
759      f.samplebytes = f.channels * f.samplesize;
760      ds = gtk_type_new(datasource_get_type());
761      ds->type = DATASOURCE_SNDFILE;
762      memcpy(&(ds->format),&f,sizeof(f));
763      ds->length = (off_t) info.frames;
764      ds->bytes = (off_t) (ds->length * f.samplebytes);
765      ds->opencount = 1;
766      ds->data.sndfile.filename = g_strdup(filename);
767      ds->data.sndfile.raw_readable = raw_readable;
768      ds->data.sndfile.handle = s;
769      ds->data.sndfile.pos = 0;
770      /* Handle seekable chunks */
771      if (info.seekable) {
772 	  datasource_close(ds);
773 	  return chunk_new_from_datasource(ds);
774      }
775      /* Chunk is not seekable, we must dump it into a tempfile. */
776      /* puts("Non-native format!!"); */
777      tmp = tempfile_init(&f,FALSE);
778      status_bar_begin_progress(bar,info.frames,NULL);
779      rawbuf_size = f.samplesize * f.channels * 1024;
780      rawbuf = g_malloc(rawbuf_size);
781 
782      while (ds->data.sndfile.pos < ds->length) {
783 
784 	  st = datasource_read_array(ds,ds->data.sndfile.pos,rawbuf_size,
785 				     rawbuf, dither_mode, NULL);
786 
787 	  if (st == 0 || tempfile_write(tmp,rawbuf,st) ||
788 	      status_bar_progress(bar, st/f.samplebytes)) {
789 	       datasource_close(ds);
790 	       gtk_object_sink(GTK_OBJECT(ds));
791 	       tempfile_abort(tmp);
792 	       g_free(rawbuf);
793 	       status_bar_end_progress(bar);
794 	       return NULL;
795 	  }
796      }
797      g_free(rawbuf);
798      datasource_close(ds);
799      gtk_object_sink(GTK_OBJECT(ds));
800      status_bar_end_progress(bar);
801      return tempfile_finished(tmp);
802 }
803 
find_nearest_sndfile_format(Dataformat * fmt,int format,SF_INFO * info,gchar * filename)804 static gboolean find_nearest_sndfile_format(Dataformat *fmt, int format,
805 					    SF_INFO *info, gchar *filename)
806 {
807   int suggestions[] = { SF_FORMAT_PCM_U8, SF_FORMAT_PCM_S8, SF_FORMAT_PCM_16,
808 			   SF_FORMAT_PCM_24, SF_FORMAT_PCM_32, SF_FORMAT_FLOAT,
809                            SF_FORMAT_DOUBLE };
810      int i,j,k;
811      SF_INFO sfi;
812      info->samplerate = fmt->samplerate;
813      info->channels = fmt->channels;
814      /* Try suggestions first... */
815      if (fmt->type == DATAFORMAT_PCM) {
816 	  i = fmt->samplesize;
817 	  if (fmt->packing != 0) i--;
818 	  if (i == 1) i--;
819      } else if (fmt->samplesize == sizeof(float))
820 	  i = 5;
821      else
822 	  i = 6;
823      for (; i<ARRAY_LENGTH(suggestions); i++) {
824 	  info->format = format | suggestions[i];
825 	  if (sf_format_check(info)) return FALSE;
826      }
827      /* Now try the formats that were not in the suggestion array... */
828      sf_command(NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &i, sizeof(int));
829      for (j=0; j<i; j++) {
830 	  sfi.format = j;
831 	  sf_command(NULL, SFC_GET_FORMAT_SUBTYPE, &sfi, sizeof(sfi));
832 	  for (k=0; k<ARRAY_LENGTH(suggestions); k++)
833 	       if ((sfi.format & SF_FORMAT_SUBMASK) == suggestions[k])
834 		    break;
835 	  if (k<ARRAY_LENGTH(suggestions)) continue;
836 	  info->format = (sfi.format&SF_FORMAT_SUBMASK) | format;
837 	  if (sf_format_check(info)) return FALSE;
838      }
839      /* Try the left out suggestions finally... */
840      i = fmt->samplesize;
841      if (i > 1) {
842 	  i--;
843 	  for (; i>=0; i--) {
844 	       info->format = format | suggestions[i];
845 	       if (sf_format_check(info)) return FALSE;
846 	  }
847      }
848 
849      user_error(_("Invalid sample format or number of channels for this file"
850 		" format"));
851      return TRUE;
852 
853 }
854 
855 
sndfile_save_main(Chunk * chunk,gchar * filename,int format,int dither_mode,StatusBar * bar,gboolean * fatal)856 static gboolean sndfile_save_main(Chunk *chunk, gchar *filename,
857 				  int format, int dither_mode, StatusBar *bar,
858 				  gboolean *fatal)
859 {
860      gchar *c;
861      off_t i;
862      guint n;
863      SNDFILE *s;
864      SF_INFO info;
865      void *samplebuf;
866      ChunkHandle *ch;
867      off_t clipcount = 0;
868      Dataformat fmt;
869      Chunk *convchunk;
870      sf_count_t wc;
871 
872      if (find_nearest_sndfile_format(&(chunk->format),format,&info,filename))
873 	  return -1;
874 
875      s = sf_open(filename, SFM_WRITE, &info);
876      if (!s) {
877           c = g_strdup_printf(_("Failed to open '%s'!"),filename);
878           user_error(c);
879           g_free(c);
880           return -1;
881      }
882 
883      /* Decide on a format suitable for passing into one of the
884       * sndfile_write functions (byte,short,int or float) */
885      memcpy(&fmt,&(chunk->format),sizeof(fmt));
886      fmt.bigendian = IS_BIGENDIAN;
887      if (fmt.samplesize > 1) {
888 	  fmt.sign = TRUE;
889 	  if (fmt.samplesize == 3) {
890 	       fmt.samplesize = 4;
891 	       fmt.packing = 1;
892 	  }
893 	  if (fmt.packing == 2) fmt.packing=1;
894 	  fmt.samplebytes = fmt.samplesize * fmt.channels;
895      }
896      if (dataformat_samples_equal(&fmt,&(chunk->format))) {
897 	  convchunk = chunk;
898 	  gtk_object_ref(GTK_OBJECT(convchunk));
899      } else {
900 	  convchunk = chunk_convert_sampletype(chunk, &fmt);
901 	  gtk_object_ref(GTK_OBJECT(convchunk));
902 	  gtk_object_sink(GTK_OBJECT(convchunk));
903      }
904 
905      ch = chunk_open(convchunk);
906      if (ch == NULL) {
907 	  gtk_object_unref(GTK_OBJECT(convchunk));
908 	  sf_close(s);
909 	  return -1;
910      }
911 
912      samplebuf = g_malloc(fmt.samplebytes * 1024);
913 
914      for (i=0; i<convchunk->length; i+=n) {
915 	  n = chunk_read_array(ch,i,1024*fmt.samplebytes,samplebuf,dither_mode,&clipcount);
916 	  if (!n) {
917 	       chunk_close(ch);
918 	       sf_close(s);
919 	       g_free(samplebuf);
920 	       gtk_object_unref(GTK_OBJECT(convchunk));
921 	       *fatal = TRUE;
922 	       return -1;
923 	  }
924 	  n /= fmt.samplebytes;
925 	  if (fmt.type == DATAFORMAT_FLOAT) {
926 	       if (fmt.samplesize == sizeof(float)) {
927 		    wc = sf_writef_float(s,samplebuf,n);
928 	       } else {
929 		    wc = sf_writef_double(s,samplebuf,n);
930 	       }
931 	  } else if (fmt.type == DATAFORMAT_PCM) {
932 	       if (fmt.samplesize == 1) {
933 		    wc = sf_write_raw(s,samplebuf,n*fmt.samplebytes);
934 		    wc /= fmt.samplebytes;
935 	       } else if (fmt.samplesize == 2) {
936 		    wc = sf_writef_short(s,samplebuf,n);
937 	       } else {
938 		    wc = sf_writef_int(s,samplebuf,n);
939 	       }
940 	  }
941 
942 	  if (wc != n) {
943 	       c = g_strdup_printf(_("Failed to write to '%s'!"),filename);
944 	       user_error(c);
945 	       g_free(c);
946 	       chunk_close(ch);
947 	       sf_close(s);
948 	       g_free(samplebuf);
949 	       gtk_object_unref(GTK_OBJECT(convchunk));
950 	       *fatal = TRUE;
951 	       return -1;
952 	  }
953 	  if (status_bar_progress(bar, n*fmt.samplebytes)) {
954 	       chunk_close(ch);
955 	       sf_close(s);
956 	       g_free(samplebuf);
957 	       xunlink(filename);
958 	       gtk_object_unref(GTK_OBJECT(convchunk));
959 	       return -1;
960 	  }
961      }
962      chunk_close(ch);
963      sf_close(s);
964      g_free(samplebuf);
965      gtk_object_unref(GTK_OBJECT(convchunk));
966      return clipwarn(clipcount,TRUE);
967 
968 }
969 
sndfile_save(Chunk * chunk,gchar * filename,gpointer settings,struct file_type * type,int dither_mode,StatusBar * bar,gboolean * fatal)970 static gint sndfile_save(Chunk *chunk, gchar *filename, gpointer settings,
971 			 struct file_type *type, int dither_mode,
972 			 StatusBar *bar, gboolean *fatal)
973 {
974      return sndfile_save_main(chunk,filename,type->extra_data,dither_mode,
975 			      bar,fatal);
976 }
977 
978 #endif
979 
sndfile_ogg_supported(void)980 gboolean sndfile_ogg_supported(void)
981 {
982 #if defined(HAVE_LIBSNDFILE)
983      setup_types();
984      return sndfile_ogg_flag;
985 #else
986      return FALSE;
987 #endif
988 }
989 
990 /* OGG and MP3 */
991 
run_decoder_cs(gpointer csource,gpointer user_data)992 int run_decoder_cs(gpointer csource, gpointer user_data)
993 {
994      return 0;
995 }
996 
run_decoder(gchar * filename,gchar * tempname,gchar * progname,gchar ** argv,int dither_mode,StatusBar * bar)997 static Chunk *run_decoder(gchar *filename, gchar *tempname, gchar *progname,
998 			  gchar **argv, int dither_mode, StatusBar *bar)
999 {
1000      gchar *c;
1001      Chunk *r=NULL;
1002      Datasource *ds;
1003      pid_t p,q;
1004      gboolean b;
1005      off_t o;
1006      gpointer cs;
1007      /* Using oggdec in raw mode to decode to stdout, it seems there is no
1008       * way to determine the sample rate of the output, so we decode it to a
1009       * temporary wave file instead */
1010      p = fork();
1011      if (p == -1) {
1012 	  c = g_strdup_printf("Error: fork: %s",strerror(errno));
1013 	  user_error(c);
1014 	  g_free(c);
1015 	  return NULL;
1016      } else if (p == 0) {
1017 	  close_all_files();
1018 	  execvp(progname,argv);
1019 	  fprintf(stderr,"execvp: %s: %s\n",progname,strerror(errno));
1020 	  _exit(1);
1021      }
1022      b = um_use_gtk;
1023      um_use_gtk = FALSE;
1024      /* Estimate the output wav's size as 20 times the original's size or 64MB */
1025      o = errdlg_filesize(filename);
1026      if (o<0) o=64*1024*1024;
1027      else o *= 20;
1028      status_bar_begin_progress(bar,o,_("Decoding"));
1029 
1030      /* Hack: To avoid the potential race condition between mainloop and waitpid
1031       * in this loop, we add a constant event source to make sure the mainloop
1032       * never waits indefinitely. */
1033      cs = mainloop_constant_source_add(run_decoder_cs,NULL,TRUE);
1034 
1035      while (1) {
1036 	  mainloop();
1037 	  /* See if the child has exited */
1038 	  q = waitpid(p,NULL,WNOHANG);
1039 	  if (q == p) break;
1040 	  if (q == -1) {
1041 	       console_perror("waitpid");
1042 	       break;
1043 	  }
1044 	  /* See how large the file is */
1045 	  if (!file_exists(tempname)) continue;
1046 	  o = errdlg_filesize(tempname);
1047 	  if (o > 0 && status_bar_progress(bar,o-bar->progress_cur)) {
1048 	       kill(p,SIGKILL);
1049 	       waitpid(p,NULL,0);
1050 	       xunlink(tempname);
1051 	       break;
1052 	  }
1053      }
1054      um_use_gtk = b;
1055      if (file_exists(tempname)) {
1056 	  r=wav_load(tempname,dither_mode,bar);
1057 	  if (r != NULL) {
1058 	       ds = ((DataPart *)(r->parts->data))->ds;
1059 	       if (ds->type == DATASOURCE_VIRTUAL)
1060 	            ds->type = DATASOURCE_TEMPFILE;
1061 	  }
1062      }
1063      status_bar_end_progress(bar);
1064      mainloop_constant_source_free(cs);
1065      return r;
1066 }
1067 
ogg_load(gchar * filename,int dither_mode,StatusBar * bar)1068 static Chunk *ogg_load(gchar *filename, int dither_mode, StatusBar *bar)
1069 {
1070      gchar *tempname = get_temp_filename(0);
1071      gchar *argv[] = { "oggdec", "--quiet", "-o", tempname, filename, NULL };
1072      Chunk *c;
1073      c = run_decoder(filename,tempname,"oggdec",argv,dither_mode,bar);
1074      g_free(tempname);
1075      return c;
1076 }
1077 
ogg_save(Chunk * chunk,gchar * filename,gpointer settings,struct file_type * type,int dither_mode,StatusBar * bar,gboolean * fatal)1078 static gboolean ogg_save(Chunk *chunk, gchar *filename, gpointer settings,
1079 			 struct file_type *type,
1080 			 int dither_mode, StatusBar *bar, gboolean *fatal)
1081 {
1082      Chunk *x=NULL,*y;
1083      Dataformat fmt;
1084      gchar *c,*d;
1085      gboolean b;
1086      off_t clipcount = 0;
1087      xunlink(filename);
1088      d = g_strdup_printf("OUTFILE=%s",filename);
1089      if (xputenv(d)) { g_free(d); return TRUE; }
1090      if (chunk->format.type == DATAFORMAT_FLOAT) {
1091 	  memcpy(&fmt,&(chunk->format),sizeof(Dataformat));
1092 	  fmt.type = DATAFORMAT_PCM;
1093 	  fmt.samplesize = 4;
1094 	  fmt.packing = 0;
1095 	  fmt.samplebytes = fmt.samplesize * fmt.channels;
1096 	  x = chunk_convert_sampletype(chunk,&fmt);
1097 	  g_assert(x != NULL);
1098      }
1099      y = (x==NULL) ? chunk : x;
1100      c = g_strdup_printf("oggenc --raw -B %d -C %d -R %d --raw-endianness %d "
1101 			 "--quiet -o \"$OUTFILE\" -",
1102 			 y->format.samplesize*8,
1103 			 y->format.channels, y->format.samplerate,
1104 			 y->format.bigendian?1:0);
1105      b = pipe_dialog_send_chunk(y,c,FALSE,dither_mode,bar,&clipcount);
1106      g_free(c);
1107      if (x != NULL) gtk_object_sink(GTK_OBJECT(x));
1108      if (!xunsetenv("OUTFILE")) g_free(d);
1109      if (b || !file_exists(filename)) {
1110 	  *fatal = TRUE;
1111 	  return -1;
1112      }
1113      return clipwarn(clipcount,TRUE);
1114 }
1115 
1116 static struct {
1117      Combo *type_combo, *subtype_combo;
1118      GtkEntry *arg_entry;
1119      GtkToggleButton *set_default;
1120      gboolean ok_flag, destroyed_flag;
1121      GList *preset_list, *bitrate_list;
1122 } mp3_get_settings_data;
1123 
set_flag(gboolean * flag)1124 static void set_flag(gboolean *flag) {
1125      *flag = TRUE;
1126 }
1127 
mp3_get_settings_type_changed(Combo * obj,gpointer user_data)1128 static void mp3_get_settings_type_changed(Combo *obj, gpointer user_data)
1129 {
1130      int i;
1131      i = combo_selected_index(obj);
1132      gtk_widget_set_sensitive(GTK_WIDGET(mp3_get_settings_data.subtype_combo),
1133 			      (i<3));
1134      gtk_widget_set_sensitive(GTK_WIDGET(mp3_get_settings_data.arg_entry),
1135 			      (i==3));
1136      if (i == 0)
1137 	  combo_set_items(mp3_get_settings_data.subtype_combo,
1138 			  mp3_get_settings_data.preset_list, 0);
1139      else if (i < 3)
1140 	  combo_set_items(mp3_get_settings_data.subtype_combo,
1141 			  mp3_get_settings_data.bitrate_list, 5);
1142 
1143 }
1144 
mp3_get_settings(void)1145 static gpointer mp3_get_settings(void)
1146 {
1147      gchar *type_strings[] = { "vbr_preset", "abr_preset", "cbr_preset",
1148 			       "custom" };
1149      gchar *type_names[] = { _("Variable bitrate (default)"),
1150 			     _("Average bitrate"),
1151 			     _("Constant bitrate"),
1152 			     _("Custom argument") };
1153      gchar *bitrate_strings[] = { "80", "96", "112", "128", "160", "192",
1154 				  "224", "256", "320" };
1155      gchar *vbr_preset_strings[] = { "standard", "extreme", "insane" };
1156      gchar *vbr_preset_names[] = { _("Standard (high quality)"),
1157 				   _("Extreme (higher quality)"),
1158 				   _("Insane (highest possible quality)") };
1159 
1160 
1161      int type; /* 0 = VBR preset, 1 = ABR preset, 2 = CBR preset, 3 = custom */
1162      int subtype = 0;
1163      gchar *custom_arg = NULL;
1164 
1165      GList *l;
1166      GtkWidget *a,*b,*c,*d;
1167 
1168      gchar *p;
1169      int i,j;
1170 
1171      /* Setup lists */
1172      for (l=NULL, i=0; i<ARRAY_LENGTH(vbr_preset_names); i++)
1173 	  l = g_list_append(l, vbr_preset_names[i]);
1174      mp3_get_settings_data.preset_list = l;
1175      for (l=NULL, i=0; i<ARRAY_LENGTH(bitrate_strings); i++) {
1176 	  p = g_strdup_printf(_("%s kbit/s"),bitrate_strings[i]);
1177 	  l = g_list_append(l,p);
1178      }
1179      mp3_get_settings_data.bitrate_list = l;
1180 
1181      /* Get default settings */
1182      p = inifile_get("mp3_EncodingType",type_strings[0]);
1183      for (i=0; i<ARRAY_LENGTH(type_strings); i++)
1184 	  if (!strcmp(p,type_strings[i])) break;
1185      if (i < ARRAY_LENGTH(type_strings)) type = i;
1186      else type = 0;
1187      p = inifile_get("mp3_EncodingArg","");
1188      if (type == 0) {
1189 	  for (i=0; i<ARRAY_LENGTH(vbr_preset_strings); i++)
1190 	       if (!strcmp(p,vbr_preset_strings[i])) break;
1191 	  if (i < ARRAY_LENGTH(vbr_preset_strings)) subtype = i;
1192 	  else subtype = 0;
1193      } else if (type < 3) {
1194 	  for (i=0; i<ARRAY_LENGTH(bitrate_strings); i++)
1195 	       if (!strcmp(p,bitrate_strings[i])) break;
1196 	  if (i<ARRAY_LENGTH(bitrate_strings)) subtype = i;
1197 	  else subtype = 5;
1198      } else {
1199 	  custom_arg = p;
1200      }
1201 
1202      /* Create the window */
1203 
1204      a = gtk_window_new(GTK_WINDOW_DIALOG);
1205      gtk_window_set_title(GTK_WINDOW(a),_("MP3 Preferences"));
1206      gtk_window_set_modal(GTK_WINDOW(a),TRUE);
1207      gtk_signal_connect_object(GTK_OBJECT(a),"destroy",
1208 			       GTK_SIGNAL_FUNC(set_flag),
1209 			       (GtkObject *)
1210 			       &(mp3_get_settings_data.destroyed_flag));
1211      b = gtk_vbox_new(FALSE,6);
1212      gtk_container_add(GTK_CONTAINER(a),b);
1213      gtk_container_set_border_width(GTK_CONTAINER(b),6);
1214      c = gtk_hbox_new(FALSE,6);
1215      gtk_box_pack_start(GTK_BOX(b),c,FALSE,FALSE,0);
1216      d = gtk_label_new(_("Encoding type: "));
1217      gtk_box_pack_start(GTK_BOX(c),d,FALSE,FALSE,0);
1218      for (l=NULL, i=0; i<ARRAY_LENGTH(type_names); i++)
1219 	  l = g_list_append(l,type_names[i]);
1220      d = combo_new();
1221      mp3_get_settings_data.type_combo = COMBO(d);
1222      combo_set_items(mp3_get_settings_data.type_combo, l, 0);
1223      gtk_signal_connect(GTK_OBJECT(d),"selection_changed",
1224 			GTK_SIGNAL_FUNC(mp3_get_settings_type_changed),NULL);
1225      gtk_box_pack_start(GTK_BOX(c),d,TRUE,TRUE,0);
1226      g_list_free(l);
1227      c = gtk_hbox_new(FALSE,4);
1228      gtk_box_pack_start(GTK_BOX(b),c,FALSE,FALSE,0);
1229      d = gtk_label_new(_("Quality: "));
1230      gtk_box_pack_start(GTK_BOX(c),d,FALSE,FALSE,0);
1231      d = combo_new();
1232      mp3_get_settings_data.subtype_combo = COMBO(d);
1233      combo_set_items(mp3_get_settings_data.subtype_combo,
1234 		     mp3_get_settings_data.preset_list, 0);
1235      gtk_box_pack_start(GTK_BOX(c),d,TRUE,TRUE,0);
1236      c = gtk_hbox_new(FALSE, 4);
1237      gtk_box_pack_start(GTK_BOX(b),c,FALSE,FALSE,0);
1238      d = gtk_label_new(_("Custom argument: "));
1239      gtk_box_pack_start(GTK_BOX(c),d,FALSE,FALSE,0);
1240      d = gtk_entry_new();
1241      mp3_get_settings_data.arg_entry = GTK_ENTRY(d);
1242      gtk_widget_set_sensitive(d,FALSE);
1243      gtk_box_pack_start(GTK_BOX(c),d,TRUE,TRUE,0);
1244      c = gtk_check_button_new_with_label(_("Use this setting by default"));
1245      mp3_get_settings_data.set_default = GTK_TOGGLE_BUTTON(c);
1246      gtk_box_pack_start(GTK_BOX(b),c,FALSE,FALSE,0);
1247      c = gtk_hbutton_box_new();
1248      gtk_box_pack_end(GTK_BOX(b),c,FALSE,FALSE,0);
1249      d = gtk_button_new_with_label(_("OK"));
1250      gtk_signal_connect_object(GTK_OBJECT(d),"clicked",
1251 			       GTK_SIGNAL_FUNC(set_flag),
1252 			       (GtkObject *)&(mp3_get_settings_data.ok_flag));
1253      gtk_signal_connect_object(GTK_OBJECT(d),"clicked",
1254 			       GTK_SIGNAL_FUNC(gtk_widget_destroy),
1255 			       GTK_OBJECT(a));
1256      gtk_container_add(GTK_CONTAINER(c),d);
1257      d = gtk_button_new_with_label(_("Cancel"));
1258      gtk_signal_connect_object(GTK_OBJECT(d),"clicked",
1259 			       GTK_SIGNAL_FUNC(gtk_widget_destroy),
1260 			       GTK_OBJECT(a));
1261      gtk_container_add(GTK_CONTAINER(c),d);
1262      c = gtk_hseparator_new();
1263      gtk_box_pack_end(GTK_BOX(b),c,FALSE,FALSE,0);
1264      gtk_widget_show_all(a);
1265 
1266      gtk_object_ref(GTK_OBJECT(mp3_get_settings_data.type_combo));
1267      gtk_object_ref(GTK_OBJECT(mp3_get_settings_data.subtype_combo));
1268      gtk_object_ref(GTK_OBJECT(mp3_get_settings_data.arg_entry));
1269      gtk_object_ref(GTK_OBJECT(mp3_get_settings_data.set_default));
1270 
1271      mp3_get_settings_data.destroyed_flag = FALSE;
1272      mp3_get_settings_data.ok_flag = FALSE;
1273 
1274      combo_set_selection(mp3_get_settings_data.type_combo, type);
1275      if (type < 3)
1276 	  combo_set_selection(mp3_get_settings_data.subtype_combo, subtype);
1277      else
1278 	  gtk_entry_set_text(mp3_get_settings_data.arg_entry, custom_arg);
1279 
1280      while (!mp3_get_settings_data.destroyed_flag)
1281 	  mainloop();
1282 
1283      if (mp3_get_settings_data.ok_flag) {
1284 	  i = combo_selected_index(mp3_get_settings_data.type_combo);
1285 	  j = combo_selected_index(mp3_get_settings_data.subtype_combo);
1286 	  switch (i) {
1287 	  case 0:
1288 	       p = g_strdup_printf("--preset %s",vbr_preset_strings[j]);
1289 	       break;
1290 	  case 1:
1291 	       p = g_strdup_printf("--preset %s",bitrate_strings[j]);
1292 	       break;
1293 	  case 2:
1294 	       p = g_strdup_printf("--preset cbr %s",bitrate_strings[j]);
1295 	       break;
1296 	  case 3:
1297 	       p=g_strdup(gtk_entry_get_text(mp3_get_settings_data.arg_entry));
1298 	       break;
1299 	  default:
1300 	       g_assert_not_reached();
1301 	  }
1302 	  if (gtk_toggle_button_get_active(mp3_get_settings_data.set_default)){
1303 	       inifile_set("mp3_EncodingType",type_strings[i]);
1304 	       if (i == 0)
1305 		    inifile_set("mp3_EncodingArg",vbr_preset_strings[j]);
1306 	       else if (i < 3)
1307 		    inifile_set("mp3_EncodingArg",bitrate_strings[j]);
1308 	       else
1309 		    inifile_set("mp3_EncodingArg",p);
1310 	  }
1311      } else p = NULL;
1312 
1313      gtk_object_unref(GTK_OBJECT(mp3_get_settings_data.type_combo));
1314      gtk_object_unref(GTK_OBJECT(mp3_get_settings_data.subtype_combo));
1315      gtk_object_unref(GTK_OBJECT(mp3_get_settings_data.arg_entry));
1316      gtk_object_unref(GTK_OBJECT(mp3_get_settings_data.set_default));
1317 
1318      g_list_free(mp3_get_settings_data.preset_list);
1319      g_list_foreach(mp3_get_settings_data.bitrate_list,(GFunc)g_free,NULL);
1320      g_list_free(mp3_get_settings_data.bitrate_list);
1321 
1322      return p;
1323 }
1324 
mp3_load(gchar * filename,int dither_mode,StatusBar * bar)1325 static Chunk *mp3_load(gchar *filename, int dither_mode, StatusBar *bar)
1326 {
1327      gchar *tempname = get_temp_filename(0);
1328      gchar *argv[] = { "lame", "--decode", filename, tempname, NULL };
1329      Chunk *c;
1330      c = run_decoder(filename,tempname,"lame",argv,dither_mode,bar);
1331      g_free(tempname);
1332      return c;
1333 }
1334 
wav_regular_format(Dataformat * fmt)1335 static gboolean wav_regular_format(Dataformat *fmt)
1336 {
1337      if (fmt->type == DATAFORMAT_FLOAT) return FALSE;
1338      return (XOR(fmt->samplesize == 1, fmt->sign) && fmt->packing == 0);
1339 }
1340 
mp3_save(Chunk * chunk,gchar * filename,gpointer settings,struct file_type * type,int dither_mode,StatusBar * bar,gboolean * fatal)1341 static gint mp3_save(Chunk *chunk, gchar *filename, gpointer settings,
1342 		     struct file_type *type,
1343 		     int dither_mode, StatusBar *bar, gboolean *fatal)
1344 {
1345      Chunk *x;
1346      Dataformat fmt;
1347      gchar *c;
1348      gboolean b;
1349      off_t clipcount = 0;
1350      xunlink(filename);
1351      c = g_strdup_printf("OUTFILE=%s",filename);
1352      if (xputenv(c)) { g_free(c); return -1; }
1353      c = g_strdup_printf("LAMEFLAGS=%s",(settings == NULL) ?
1354 			 "--preset standard" : (gchar *)settings);
1355      if (xputenv(c)) { g_free(c); return -1; }
1356      if (wav_regular_format(&(chunk->format)))
1357 	 b = pipe_dialog_send_chunk(chunk,
1358 				    "lame --silent $LAMEFLAGS - "
1359 				    "\"$OUTFILE\"",TRUE,dither_mode,bar,
1360 				    &clipcount);
1361      else {
1362 	  memcpy(&fmt,&(chunk->format),sizeof(Dataformat));
1363 	  if (fmt.type == DATAFORMAT_FLOAT || fmt.samplesize == 3 || fmt.packing!=0) {
1364 	       fmt.type = DATAFORMAT_PCM;
1365 	       fmt.samplesize = 4;
1366 	       fmt.packing = 0;
1367 	  }
1368 	  fmt.sign = (fmt.samplesize > 1);
1369 	  fmt.bigendian = FALSE;
1370 	  fmt.samplebytes = fmt.samplesize * fmt.channels;
1371 	  x = chunk_convert_sampletype(chunk,&fmt);
1372 	  if (x == NULL) b=TRUE;
1373 	  else {
1374 	       b = pipe_dialog_send_chunk(x,
1375 					  "lame --silent --preset standard - "
1376 					  "\"$OUTFILE\"",TRUE,dither_mode,bar,
1377 					  &clipcount);
1378 	       gtk_object_sink(GTK_OBJECT(x));
1379 	  }
1380      }
1381      if (!xunsetenv("OUTFILE")) g_free(c);
1382      if (b || !file_exists(filename)) {
1383 	  *fatal = TRUE;
1384 	  return -1;
1385      }
1386      return clipwarn(clipcount,TRUE);
1387 }
1388 
1389 
1390 /* Mplayer */
1391 
try_mplayer(gchar * filename,int dither_mode,StatusBar * bar)1392 static Chunk *try_mplayer(gchar *filename, int dither_mode, StatusBar *bar)
1393 {
1394      gchar *c,*d;
1395      char *tempname;
1396      Chunk *x;
1397      char *argv[] = { "sh", "-c",
1398 		      "mplayer -quiet -noconsolecontrols "
1399 		      "-ao \"pcm:file=$OUTFILE\" -vc dummy -vo null "
1400 		      "\"$INFILE\"", NULL };
1401      if (!program_exists("mplayer")) return NULL;
1402      tempname = get_temp_filename(0);
1403      c = g_strdup_printf("OUTFILE=%s",tempname);
1404      d = g_strdup_printf("INFILE=%s",filename);
1405      if (xputenv(c)) { g_free(d); g_free(c); g_free(tempname); return NULL; }
1406      if (xputenv(d)) {
1407 	  g_free(d);
1408 	  if (!xunsetenv("OUTFILE"))
1409 	       g_free(c);
1410 	  g_free(tempname);
1411 	  return NULL;
1412      }
1413 
1414      x = run_decoder(filename,tempname,"sh",argv,dither_mode,bar);
1415 
1416      if (!xunsetenv("OUTFILE")) g_free(c);
1417      if (!xunsetenv("INFILE")) g_free(d);
1418      g_free(tempname);
1419 
1420      return x;
1421 
1422 }
1423