1 /*
2 * function: ReplayGain support for Wave files (http://www.replaygain.org)
3 *
4 * Reads in a Vorbis file, figures out the peak and ReplayGain levels and
5 * applies the Gain tags to the Wave file
6 *
7 * This program is distributed under the GNU General Public License, version
8 * 2.1. A copy of this license is included with this source.
9 *
10 * Copyright (C) 2002-2004 John Edwards
11 * Additional code by Magnus Holmgren and Gian-Carlo Pascutto
12 * Linux patch by Marc Brooker
13 */
14
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif
18
19 #include <ctype.h>
20 #include "getopt.h"
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <math.h>
25 #include "config.h"
26 #include "gain_analysis.h"
27 #include "i18n.h"
28 #include "audio.h"
29 #include "misc.h"
30 #include "wavegain.h"
31 #include "main.h"
32 #include "dither.h"
33
34 #ifdef _WIN32
35 #include <windows.h>
36 #endif
37
38 #ifdef ENABLE_RECURSIVE
39 #include "recurse.h"
40 #endif
41
42 /*This is an ugly way of doing this - but it works for the moment*/
43 #ifndef MAX_PATH
44 #define MAX_PATH 512
45 #endif
46
47 int write_to_log = 0;
48 char log_file_name[MAX_PATH];
49 extern double total_samples;
50 extern double total_files;
51
52 /**
53 * \brief Allocate a FILE_LIST node.
54 *
55 * Allocate a FILE_LIST node. filename is set to file while track_peak and
56 * track_gain are set to NO_PEAK and NO_GAIN respectively. The rest of the
57 * fields are set to zero.
58 *
59 * \param file name of file to get a node for.
60 * \return the allocated node or NULL (in which case a message has been
61 * printed).
62 */
alloc_node(const char * file)63 static FILE_LIST* alloc_node(const char* file)
64 {
65 FILE_LIST* node;
66
67 node = calloc(1, sizeof(*node));
68
69 if (node != NULL) {
70 node->filename = strdup(file);
71
72 if (node->filename != NULL) {
73 node->track_peak = NO_PEAK;
74 node->track_gain = NO_GAIN;
75 node->dc_offset[0] = 0.;
76 node->dc_offset[1] = 0.;
77 node->offset[0] = 0.;
78 node->offset[1] = 0.;
79 return node;
80 }
81 free(node);
82 }
83
84 fprintf(stderr, _("Out of memory\n"));
85 return NULL;
86 }
87
88
89 /**
90 * \brief Allocate a new FILE_LIST node and add it to the end of a list.
91 *
92 * Allocate a new FILE_LIST node and add it to the end of a list.
93 *
94 * \param list pointer to the list (first node) pointer. If the list
95 * pointer contains NULL, it is set to the new node. Otherwise
96 * the new node is added the last node in the list.
97 * \param file name of the file to get a node for.
98 * \return 0 if successful and -1 if the node couldn't be allocated (in
99 * which case a message has been printed).
100 */
add_to_list(FILE_LIST ** list,const char * file)101 int add_to_list(FILE_LIST** list, const char* file)
102 {
103 if (*list == NULL) {
104 *list = alloc_node(file);
105
106 if (*list == NULL)
107 return -1;
108 }
109 else {
110 FILE_LIST* node = *list;
111
112 while (node->next_file != NULL)
113 node = node->next_file;
114
115 node->next_file = alloc_node(file);
116
117 if (node->next_file == NULL)
118 return -1;
119 }
120
121 return 0;
122 }
123
124
125 /**
126 * Free all nodes in a FILE_LIST list.
127 *
128 * \param list pointer to first node in list. Can be NULL, in which case
129 * nothing is done.
130 */
free_list(FILE_LIST * list)131 void free_list(FILE_LIST* list)
132 {
133 FILE_LIST* next;
134
135 while (list != NULL) {
136 next = list->next_file;
137 free((void *) list->filename);
138 free(list);
139 list = next;
140 }
141 }
142
143 static char s_floatToAscii[256];
ftos(double f,const char * format)144 const char* ftos(double f, const char* format)
145 {
146 sprintf(s_floatToAscii, format, f);
147 return s_floatToAscii;
148 }
149
150 /**
151 * \brief Processs the file in file_list.
152 *
153 * Process the files in file_list. If settings->album is set, the files are
154 * considered to belong to one album.
155 *
156 * \param file_list list of files to process.
157 * \param settings settings and global variables.
158 * \return 0 if successful and -1 if an error occured (in which case a
159 * message has been printed).
160 */
process_files(FILE_LIST * file_list,SETTINGS * settings,const char * dir)161 int process_files(FILE_LIST* file_list, SETTINGS* settings, const char* dir)
162 {
163 FILE_LIST* file;
164 double factor_clip,
165 audiophile_gain,
166 Gain,
167 scale,
168 dB,
169 album_dc_offset[2] = {0., 0.};
170
171 settings->album_peak = NO_PEAK;
172
173 if (file_list == NULL)
174 return 0;
175
176 settings->first_file = 1;
177
178 /* Undo previously applied gain */
179 if (settings->undo) {
180 for (file = file_list; file; file = file->next_file) {
181 if (file->filename == NULL)
182 continue;
183 if (!write_gains(file->filename, 0, 0, 0, 0, 0, settings)) {
184 fprintf(stderr, " Error processing GAIN for file - %s\n", file->filename);
185 continue;
186 }
187 }
188 }
189 else {
190 /* Analyze the files */
191 for (file = file_list; file; file = file->next_file) {
192 int dc_l;
193 int dc_r;
194 if (file->filename == NULL)
195 continue;
196
197 if (!get_gain(file->filename, &file->track_peak, &file->track_gain,
198 file->dc_offset, file->offset, settings)) {
199 file->filename = NULL;
200 continue;
201 }
202 dc_l = (int)(file->dc_offset[0] * 32768 * -1);
203 dc_r = (int)(file->dc_offset[1] * 32768 * -1);
204 if (dc_l < -1 || dc_l > 1 || dc_r < -1 || dc_r > 1)
205 settings->need_to_process = 1;
206 album_dc_offset[0] += file->offset[0];
207 album_dc_offset[1] += file->offset[1];
208 }
209
210 album_dc_offset[0] /= total_samples;
211 album_dc_offset[1] /= total_samples;
212
213 if (!settings->no_offset && settings->adc) {
214 int dc_l = (int)(album_dc_offset[0] * 32768 * -1);
215 int dc_r = (int)(album_dc_offset[1] * 32768 * -1);
216 fprintf(stderr, " ********************* Album DC Offset | %4d | %4d | ***************\n",
217 dc_l, dc_r);
218 if(write_to_log) {
219 write_log(" ********************* Album DC Offset | %4d | %4d | ***************\n",
220 dc_l, dc_r);
221 }
222 }
223
224 fprintf(stderr, "\n");
225 if(write_to_log)
226 write_log("\n");
227
228 if (settings->audiophile) {
229 Gain = GetAlbumGain() + settings->man_gain;
230 scale = pow(10., Gain * 0.05);
231 settings->set_album_gain = 0;
232 if(settings->clip_prev) {
233 factor_clip = (32767./( settings->album_peak + 1));
234 if(scale < factor_clip)
235 factor_clip = 1.0;
236 else
237 factor_clip /= scale;
238 scale *= factor_clip;
239 }
240 if (settings->scale) {
241 fprintf(stdout, "%8.6lf", scale);
242 }
243
244 if (scale > 0.0)
245 dB = 20. * log10(scale);
246 else
247 dB = 0.0;
248 audiophile_gain = dB;
249 if (audiophile_gain < 0.1 && audiophile_gain > -0.1 && !settings->need_to_process) {
250 fprintf(stderr, " No Album Gain adjustment or DC Offset correction required, exiting.\n");
251 if(write_to_log)
252 write_log(" No Album Gain adjustment or DC Offset correction required, exiting.\n");
253 settings->set_album_gain = 1;
254 }
255 else {
256 fprintf(stderr, " Recommended Album Gain: %+6.2f dB\tScale: %6.4f\n\n", audiophile_gain, scale);
257 if(write_to_log)
258 write_log(" Recommended Album Gain: %+6.2f dB\tScale: %6.4f\n\n", audiophile_gain, scale);
259 }
260 }
261
262 if(settings->apply_gain) { /* Write radio and audiophile gains. */
263 total_files = 0.0;
264 for (file = file_list; file; file = file->next_file) {
265 if (file->filename == NULL)
266 continue;
267 if (settings->audiophile && settings->set_album_gain == 1)
268 break;
269 if (settings->radio && (file->track_gain < 0.1 && file->track_gain > -0.1) && !settings->need_to_process) {
270 fprintf(stderr, " No Title Gain adjustment or DC Offset correction required for file: %s, skipping.\n", file->filename);
271 if(write_to_log)
272 write_log(" No Title Gain adjustment or DC Offset correction required for file: %s, skipping.\n", file->filename);
273 }
274 else if (!write_gains(file->filename, file->track_gain, audiophile_gain, file->track_peak,
275 file->dc_offset, album_dc_offset, settings)) {
276 fprintf(stderr, " Error processing GAIN for file - %s\n", file->filename);
277 continue;
278 }
279 }
280 }
281 }
282
283 fprintf(stderr, "\n WaveGain Processing completed normally\n");
284 if(write_to_log)
285 write_log("\n WaveGain Processing completed normally\n\n");
286
287 #ifdef _WIN32
288 if (settings->cmd) { /* execute user command */
289 FILE_LIST* file;
290 char buff[MAX_PATH];
291 char *b, *p, *q;
292 double track_scale = 1.0;
293
294 SetEnvironmentVariable("ALBUM_GAIN", ftos(audiophile_gain, "%.2lf"));
295 SetEnvironmentVariable("ALBUM_SCALE", ftos(scale, "%.5lf"));
296 SetEnvironmentVariable("ALBUM_NEW_PEAK", ftos(settings->album_peak * scale, "%.0lf"));
297 SetEnvironmentVariable("ALBUM_PEAK", ftos(settings->album_peak, "%.0lf"));
298
299 for (file = file_list; file; file = file->next_file) {
300 if (file->filename[0] == NULL)
301 continue;
302
303 if (dir[0] == '.' && dir[1] == '\\') dir += 2;
304
305 strcpy(buff, file->filename);
306 b = (buff[0] == '.' && buff[1] == '\\') ? buff+2 : buff;
307 SetEnvironmentVariable("INPUT_FILE", b);
308
309 p = strrchr(b, '\\');
310 if (p) {
311 p[0] = '\0'; ++p;
312 SetEnvironmentVariable("INPUT_FDIR", b);
313 SetEnvironmentVariable("INPUT_RDIR", b); // assume dir = "."
314 }
315 else {
316 p = b;
317 SetEnvironmentVariable("INPUT_FDIR", ".");
318 SetEnvironmentVariable("INPUT_RDIR", dir);
319 }
320
321 q = strrchr(p, '.');
322 if (q) q[0] = '\0';
323 SetEnvironmentVariable("INPUT_NAME", p);
324
325 track_scale = (pow(10., file->track_gain * 0.05));
326 SetEnvironmentVariable("TRACK_SCALE", ftos(track_scale, "%.5lf"));
327 SetEnvironmentVariable("TRACK_GAIN", ftos(file->track_gain, "%.2lf"));
328 SetEnvironmentVariable("TRACK_NEW_PEAK", ftos(file->track_peak, "%.0lf"));
329 SetEnvironmentVariable("TRACK_PEAK", ftos(file->track_peak / track_scale, "%.0lf"));
330
331 SetEnvironmentVariable("DC_OFFSET_L", ftos((int)(file->dc_offset[0]*(-32768)), "%.0lf"));
332 SetEnvironmentVariable("DC_OFFSET_R", ftos((int)(file->dc_offset[1]*(-32768)), "%.0lf"));
333
334 fprintf(stderr, "\n Executing command on \"%s\":\n\n", file->filename);
335 system(settings->cmd);
336 }
337 }
338 #endif
339
340 return 0;
341 }
342
343
344 /**
345 * Print out a list of options and the command line syntax.
346 */
usage(void)347 static void usage(void)
348 {
349 fprintf(stdout, _("WaveGain v" WAVEGAIN_VERSION " Compiled " __DATE__ ".\n"));
350 fprintf(stdout, _("Copyright (c) 2002-2005 John Edwards <john.edwards33@ntlworld.com>\n"));
351 fprintf(stdout, _("Additional code by Magnus Holmgren, Gian-Carlo Pascutto, and Tycho\n\n"));
352 #ifdef _WIN32
353 fprintf(stdout, " Usage: wavegain [options] input.wav [...] [-e cmd [args]]\n\n");
354 #else
355 fprintf(stdout, " Usage: wavegain [options] input.wav [...]\n\n");
356 #endif
357 fprintf(stdout, " OPTIONS\n");
358 fprintf(stdout, " -h, --help Prints this help information.\n");
359 fprintf(stdout, " -a, --album Use ReplayGain Audiophile/Album gain setting, or\n");
360 fprintf(stdout, " -r, --radio Use ReplayGain Radio/Single Track gain setting(DEFAULT).\n");
361 fprintf(stdout, " -q, --adc Apply Album based DC Offset correction.\n");
362 fprintf(stdout, " DEFAULT is Track based DC Offset correction.\n");
363 fprintf(stdout, " -p, --no_offset Do NOT apply DC Offset correction.\n");
364 fprintf(stdout, " -c, --calculate Calculates and prints gain settings, and DC Offsets BUT\n");
365 fprintf(stdout, " DOES NOT APPLY THEM - This is the DEFAULT.\n");
366 fprintf(stdout, " -x, --scale Writes scale values to stdout in the format: n.nnnnnn\n");
367 fprintf(stdout, " In Album mode it only writes the Album Scale value, and\n");
368 fprintf(stdout, " in Title mode it only writes the Title Scale values.\n");
369 fprintf(stdout, " ONLY works in Calculation mode.\n");
370 fprintf(stdout, " -y, --apply Calculates and APPLIES gain settings, and applies\n");
371 fprintf(stdout, " DC Offset correction.\n");
372 fprintf(stdout, " -w, --write Writes a 'gain' chunk into the Wave Header.\n");
373 fprintf(stdout, " Stores the scalefactor applied to the wave data as a\n");
374 fprintf(stdout, " double floating point number. Only written when gain\n");
375 fprintf(stdout, " is applied. Presence will result in file being skipped\n");
376 fprintf(stdout, " if reprocessed.\n");
377 fprintf(stdout, " (Unless '--force' or '--undo-gain' are specified.)\n");
378 fprintf(stdout, " --force Forces the reprocessing of a file that contains a 'gain'\n");
379 fprintf(stdout, " chunk and will result in the new scalefactor overwriting\n");
380 fprintf(stdout, " the existing value.\n");
381 fprintf(stdout, " --undo-gain Reads the scalefactor in the 'gain' chunk and uses the\n");
382 fprintf(stdout, " value to reverse the previously applied gain. This will NOT\n");
383 fprintf(stdout, " recreate a bit identical version of the original file, but\n");
384 fprintf(stdout, " it will be rescaled to the original level.\n");
385 #ifdef ENABLE_RECURSIVE
386 fprintf(stdout, " -z, --recursive Search for files recursively, each folder as an album\n");
387 #endif
388 fprintf(stdout, " -l, --log Write log file.(Default filename = WGLog.txt)\n");
389 fprintf(stdout, " -f, --logfile Specify log filename. (Assumes -l if present.)\n");
390 fprintf(stdout, " -n, --noclip NO Clipping Prevention.\n");
391 fprintf(stdout, " -d, --dither X Dither output, where X =\n");
392 fprintf(stdout, " 0 for dither OFF (default).\n");
393 fprintf(stdout, " 1 for dither without Noise Shaping.\n");
394 fprintf(stdout, " 2 for dither with Light Noise Shaping.\n");
395 fprintf(stdout, " 3 for dither with Medium Noise Shaping.\n");
396 fprintf(stdout, " 4 for dither with Heavy Noise Shaping.\n");
397 fprintf(stdout, " -t, --limiter Apply 6dB Hard Limiter to output.\n");
398 fprintf(stdout, " -g, --gain X Apply additional Manual Gain adjustment in decibels, where\n");
399 fprintf(stdout, " X = any floating point number between -12.0 and +12.0.\n");
400 fprintf(stdout, " Clipping Prevention WILL be applied UNLESS '-n' is used.\n");
401 fprintf(stdout, " -s, --fast Calculates and prints gain settings - DOES NOT APPLY THEM.\n");
402 fprintf(stdout, " NOTE: This method does NOT process all samples, it only\n");
403 fprintf(stdout, " processes 200 x 16k chunks of samples. Results will\n");
404 fprintf(stdout, " NOT be as accurate as a full analysis but, with most\n");
405 fprintf(stdout, " material, will be within +/- 0.5db. Files of 8,192,000\n");
406 fprintf(stdout, " real samples, or less, will be analysed in full.\n");
407 fprintf(stdout, " DC Offset is neither calculated nor corrected in\n");
408 fprintf(stdout, " FAST mode.\n");
409 fprintf(stdout, " -o, --stdout Write output file to stdout.\n");
410 fprintf(stdout, " FORMAT OPTIONS (One option ONLY may be used)\n");
411 fprintf(stdout, " -b, --bits X Set output sample format, where X =\n");
412 fprintf(stdout, " 1 for 8 bit unsigned PCM data.\n");
413 fprintf(stdout, " 2 for 16 bit signed PCM data.\n");
414 fprintf(stdout, " 3 for 24 bit signed PCM data.\n");
415 fprintf(stdout, " 4 for 32 bit signed PCM data.\n");
416 fprintf(stdout, " 5 for 32 bit floats.\n");
417 fprintf(stdout, " 6 for 16 bit 'aiff' format.\n");
418 fprintf(stdout, " NOTE: By default, the output file will be of the same bitwidth\n");
419 fprintf(stdout, " and type as the input file.\n");
420 #ifdef _WIN32
421 fprintf(stdout, " -e, --exec Cmd Execute a command after wavegain.\n");
422 fprintf(stdout, " The following environment variables are available:\n");
423 fprintf(stdout, " INPUT_FILE, INPUT_FDIR, INPUT_RDIR, INPUT_NAME,\n");
424 fprintf(stdout, " TRACK_GAIN, TRACK_PEAK, TRACK_SCALE, TRACK_NEW_PEAK,\n");
425 fprintf(stdout, " ALBUM_GAIN, ALBUM_PEAK, ALBUM_SCALE, ALBUM_NEW_PEAK,\n");
426 fprintf(stdout, " DC_OFFSET_L, DC_OFFSET_R\n");
427 #endif
428 fprintf(stdout, " INPUT FILES\n");
429 fprintf(stdout, " WaveGain input files may be 8, 16, 24 or 32 bit integer, or floating point\n");
430 fprintf(stdout, " wave files with 1 or 2 channels and a sample rate of 48000Hz, 44100Hz,\n");
431 fprintf(stdout, " 32000Hz, 24000Hz, 22050Hz, 16000Hz, 12000Hz, 11025Hz or 8000Hz.\n");
432 fprintf(stdout, " 16 bit integer 'aiff' files are also supported.\n");
433 fprintf(stdout, " Wildcards (?, *) can be used in the filename, or '-' for stdin.\n");
434
435 return;
436 }
437
438
439 const static struct option long_options[] = {
440 {"help", 0, NULL, 'h'},
441 {"album", 0, NULL, 'a'},
442 {"radio", 0, NULL, 'r'},
443 {"adc", 0, NULL, 'q'},
444 {"no_offset", 0, NULL, 'p'},
445 {"calculate", 0, NULL, 'c'},
446 {"scale", 0, NULL, 'x'},
447 {"apply", 0, NULL, 'y'},
448 {"write", 0, NULL, 'w'},
449 {"force", 0, NULL, 0 },
450 {"undo-gain", 0, NULL, 0 },
451 {"fast", 0, NULL, 's'},
452 {"stdout", 0, NULL, 'o'},
453 #ifdef ENABLE_RECURSIVE
454 {"recursive", 0, NULL, 'z'},
455 #endif
456 {"log", 0, NULL, 'l'},
457 {"logfile", 1, NULL, 'f'},
458 {"noclip", 0, NULL, 'n'},
459 {"dither", 1, NULL, 'd'},
460 {"limiter", 0, NULL, 't'},
461 {"gain", 1, NULL, 'g'},
462 {"bits", 1, NULL, 'b'},
463 #ifdef _WIN32
464 {"exec", 1, NULL, 'e'},
465 #endif
466 {NULL, 0, NULL , 0}
467 };
468
469
470 #ifdef ENABLE_RECURSIVE
471 #define ARG_STRING "harqpcxywsozlf:nd:tg:b:e:"
472 #else
473 #define ARG_STRING "harqpcxywsolf:nd:tg:b:e:"
474 #endif
475
476
main(int argc,char ** argv)477 int main(int argc, char** argv)
478 {
479 SETTINGS settings;
480 int option_index = 1,
481 ret,
482 i,
483 bits;
484
485 char CmdDir[MAX_PATH];
486 char *p;
487
488 memset(&settings, 0, sizeof(settings));
489 settings.first_file = 1;
490 settings.radio = 1;
491 settings.clip_prev = 1;
492 settings.outbitwidth = 16;
493 settings.format = WAV_NO_FMT;
494
495 #ifdef _WIN32
496 /* Is this good enough? Or do we need to consider multi-byte codepages as
497 * well?
498 */
499 SetConsoleOutputCP(GetACP());
500
501 GetModuleFileName(NULL, CmdDir, MAX_PATH);
502 p = strrchr(CmdDir, '\\') + 1;
503 p[0] = '\0';
504 #endif
505
506 while ((ret = getopt_long(argc, argv, ARG_STRING, long_options, &option_index)) != -1) {
507 switch(ret) {
508 case 0:
509 if (!strcmp(long_options[option_index].name, "force")) {
510 settings.force = 1;
511 }
512 else if (!strcmp(long_options[option_index].name, "undo-gain")) {
513 settings.undo = 1;
514 }
515 else {
516 fprintf(stderr, "Internal error parsing command line options\n");
517 exit(1);
518 }
519 break;
520 case 'h':
521 usage();
522 exit(0);
523 break;
524 case 'a':
525 settings.audiophile = 1;
526 break;
527 case 'r':
528 settings.radio = 1;
529 break;
530 case 'q':
531 settings.adc = 1;
532 break;
533 case 'p':
534 settings.no_offset = 1;
535 break;
536 case 'c':
537 settings.apply_gain = 0;
538 break;
539 case 'x':
540 settings.scale = 1;
541 break;
542 case 'y':
543 settings.apply_gain = 1;
544 settings.scale = 0;
545 break;
546 case 'w':
547 settings.write_chunk = 1;
548 break;
549 case 's':
550 settings.fast = 1;
551 break;
552 case 'o':
553 settings.std_out = 1;
554 break;
555 #ifdef ENABLE_RECURSIVE
556 case 'z':
557 settings.recursive = 1;
558 break;
559 #endif
560 case 'l':
561 write_to_log = 1;
562 #ifdef _WIN32
563 strcpy(log_file_name, CmdDir);
564 strcat(log_file_name, LOG_NAME);
565 #else
566 strcpy(log_file_name, LOG_NAME);
567 #endif
568 break;
569 case 'f':
570 write_to_log = 1;
571 strcpy(log_file_name, optarg);
572 break;
573 case 'n':
574 settings.clip_prev = 0;
575 break;
576 case 'd':
577 settings.need_to_process = 1;
578 settings.dithering = 1;
579 if(sscanf(optarg, "%d", &settings.shapingtype) != 1) {
580 fprintf(stderr, "Warning: dither type %s not recognised, using default\n", optarg);
581 break;
582 }
583 if (settings.shapingtype == 0)
584 settings.dithering = 0;
585 else if (settings.shapingtype == 1)
586 settings.shapingtype = 0;
587 else if (settings.shapingtype == 2)
588 settings.shapingtype = 1;
589 else if (settings.shapingtype == 3)
590 settings.shapingtype = 2;
591 else if (settings.shapingtype == 4)
592 settings.shapingtype = 3;
593 break;
594 case 't':
595 settings.limiter = 1;
596 break;
597 case 'g':
598 if(sscanf(optarg, "%lf", &settings.man_gain) != 1) {
599 fprintf(stderr, "Warning: manual gain %s not recognised, ignoring\n", optarg);
600 break;
601 }
602 if(settings.man_gain < -12.0) {
603 fprintf(stderr, "Warning: manual gain %s is out of range, "
604 "applying gain of -12.0dB\n", optarg);
605 settings.man_gain = -12.0;
606 }
607 else if(settings.man_gain > 12.0) {
608 fprintf(stderr, "Warning: manual gain %s is out of range, "
609 "applying gain of +12.0dB\n", optarg);
610 settings.man_gain = -12.0;
611 }
612 break;
613 case 'b':
614 settings.need_to_process = 1;
615 if(sscanf(optarg, "%d", &bits) != 1) {
616 fprintf(stderr, "Warning: output format %s not recognised, using default\n", optarg);
617 break;
618 }
619 if (bits == 1) {
620 settings.outbitwidth = 8;
621 settings.format = WAV_FMT_8BIT;
622 break;
623 }
624 else if (bits == 2) {
625 settings.outbitwidth = 16;
626 settings.format = WAV_FMT_16BIT;
627 break;
628 }
629 else if (bits == 3) {
630 settings.outbitwidth = 24;
631 settings.format = WAV_FMT_24BIT;
632 break;
633 }
634 else if (bits == 4) {
635 settings.outbitwidth = 32;
636 settings.format = WAV_FMT_32BIT;
637 break;
638 }
639 else if (bits == 5) {
640 settings.outbitwidth = 32;
641 settings.format = WAV_FMT_FLOAT;
642 break;
643 }
644 else if (bits == 6) {
645 settings.outbitwidth = 16;
646 settings.format = WAV_FMT_AIFF;
647 break;
648 }
649 else {
650 fprintf(stderr, "Warning: output format %s not recognised, using default\n", optarg);
651 break;
652 }
653 break;
654 #ifdef _WIN32
655 case 'e': {
656 char *p;
657 int k;
658 char * lpPart[MAX_PATH]={NULL};
659
660 settings.cmd = (char *) malloc(1024*8); /* 8Kb is XP's limit */
661 if (settings.cmd == NULL) {
662 fprintf(stderr, "Failed to allocate memory for cmd...\n");
663 return 1;
664 }
665
666 p = settings.cmd;
667
668 k = GetFullPathName(argv[optind - 1], 1024*8, p, lpPart);
669 k = GetShortPathName(p, p, MAX_PATH);
670 if (k == 0) {
671 p += sprintf (p, "%s", argv[optind - 1]);
672 } else {
673 p += k;
674 }
675
676 for (k = optind; k < argc; ++k) {
677 p += sprintf(p, " \"%s\"", argv[k]);
678 }
679
680 argc = optind;
681 break;
682 }
683 #endif
684 }
685 }
686 if (settings.undo == 1) {
687 settings.write_chunk = 0;
688 settings.no_offset = 1;
689 }
690
691 if (optind >= argc) {
692 fprintf(stderr, _("No files specified.\n"));
693 usage();
694 return EXIT_SUCCESS;
695 }
696
697 if (write_to_log) {
698 write_log("Command line:\n\t");
699 for(i = 0; i < argc; i++)
700 write_log("%s ", argv[i]);
701 write_log("\n");
702 }
703
704 if (!strcmp(argv[optind], "-")) {
705 double track_peak, track_gain;
706 double dc_offset;
707 double offset;
708 if (!get_gain("-", &track_peak, &track_gain,
709 &dc_offset, &offset, &settings))
710 return -1;
711 }
712 else {
713 for (i = optind; i < argc; ++i) {
714 #ifdef ENABLE_RECURSIVE
715 if (process_argument(argv[i], &settings) < 0) {
716 free_list(settings.file_list);
717 return EXIT_FAILURE;
718 }
719 #else
720 if (add_to_list(&settings.file_list, argv[i]) < 0)
721 return EXIT_FAILURE;
722 #endif
723 }
724
725 /* Process files (perhaps remaining) in list */
726 ret = process_files(settings.file_list, &settings, ".");
727 free_list(settings.file_list);
728 settings.file_list = NULL;
729 if (settings.cmd) free(settings.cmd);
730 settings.cmd = NULL;
731 }
732
733 return (ret < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
734 }
735