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