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