oggenc.c 28.8 KB
Newer Older
Jack Moffitt's avatar
Jack Moffitt committed
1
2
3
4
5
/* OggEnc
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
6
 * Copyright 2000-2005, Michael Smith <msmith@xiph.org>
Jack Moffitt's avatar
Jack Moffitt committed
7
 *
8
 * Portions from Vorbize, (c) Kenneth Arnold <kcarnold-xiph@arnoldnet.net>
Jack Moffitt's avatar
Jack Moffitt committed
9
10
11
12
13
14
15
16
17
 * and libvorbis examples, (c) Monty <monty@xiph.org>
 */

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <getopt.h>
#include <string.h>
#include <time.h>
18
#include <locale.h>
Michael Smith's avatar
Michael Smith committed
19
#include <errno.h>
Jack Moffitt's avatar
Jack Moffitt committed
20
21
22
23

#include "platform.h"
#include "encode.h"
#include "audio.h"
24
#include "utf8.h"
25
26
#include "i18n.h"

Jack Moffitt's avatar
Jack Moffitt committed
27

28
29
#define VERSION_STRING "OggEnc v1.0.2\n"
#define COPYRIGHT "(c) 2000-2005 Michael Smith <msmith@xiph.org>\n"
30

Jack Moffitt's avatar
Jack Moffitt committed
31
32
33
#define CHUNK 4096 /* We do reads, etc. in multiples of this */

struct option long_options[] = {
34
	{"quiet",0,0,'Q'},
Jack Moffitt's avatar
Jack Moffitt committed
35
36
37
38
39
	{"help",0,0,'h'},
	{"comment",1,0,'c'},
	{"artist",1,0,'a'},
	{"album",1,0,'l'},
	{"title",1,0,'t'},
40
    {"genre",1,0,'G'},
Jack Moffitt's avatar
Jack Moffitt committed
41
	{"names",1,0,'n'},
42
43
    {"name-remove",1,0,'X'},
    {"name-replace",1,0,'P'},
Jack Moffitt's avatar
Jack Moffitt committed
44
45
46
	{"output",1,0,'o'},
	{"version",0,0,'v'},
	{"raw",0,0,'r'},
47
48
49
	{"raw-bits",1,0,'B'},
	{"raw-chan",1,0,'C'},
	{"raw-rate",1,0,'R'},
Michael Smith's avatar
Michael Smith committed
50
    {"raw-endianness",1,0, 0},
Michael Smith's avatar
   
Michael Smith committed
51
	{"bitrate",1,0,'b'},
52
53
54
	{"min-bitrate",1,0,'m'},
	{"max-bitrate",1,0,'M'},
	{"quality",1,0,'q'},
Michael Smith's avatar
   
Michael Smith committed
55
56
	{"date",1,0,'d'},
	{"tracknum",1,0,'N'},
Michael Smith's avatar
   
Michael Smith committed
57
	{"serial",1,0,'s'},
58
    {"managed", 0, 0, 0},
59
60
    {"resample",1,0,0},
    {"downmix", 0,0,0},
61
    {"scale", 1, 0, 0}, 
62
    {"advanced-encode-option", 1, 0, 0},
63
	{"discard-comments", 0, 0, 0},
Michael Smith's avatar
   
Michael Smith committed
64
	{NULL,0,0,0}
Jack Moffitt's avatar
Jack Moffitt committed
65
66
};
	
67
68
static char *generate_name_string(char *format, char *remove_list, 
        char *replace_list, char *artist, char *title, char *album, 
69
        char *track, char *date, char *genre);
70
71
static void parse_options(int argc, char **argv, oe_options *opt);
static void build_comments(vorbis_comment *vc, oe_options *opt, int filenum, 
72
73
		char **artist,char **album, char **title, char **tracknum, char **date,
        char **genre);
74
static void usage(void);
Jack Moffitt's avatar
Jack Moffitt committed
75
76
77

int main(int argc, char **argv)
{
78
	/* Default values */
Michael Smith's avatar
Michael Smith committed
79
	oe_options opt = {NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 
80
			  0, NULL, 0, NULL, 0, NULL, 0, 1, 0, 0,16,44100,2, 0, NULL,
Monty's avatar
   
Monty committed
81
			  DEFAULT_NAMEFMT_REMOVE, DEFAULT_NAMEFMT_REPLACE, 
82
			  NULL, 0, -1,-1,-1,.3,-1,0, 0,0.f, 0}; 
Monty's avatar
   
Monty committed
83

Jack Moffitt's avatar
Jack Moffitt committed
84
85
86
87
	int i;

	char **infiles;
	int numfiles;
88
	int errors=0;
Jack Moffitt's avatar
Jack Moffitt committed
89

90
	setlocale(LC_ALL, "");
91
92
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);
93

Jack Moffitt's avatar
Jack Moffitt committed
94
95
96
97
	parse_options(argc, argv, &opt);

	if(optind >= argc)
	{
98
		fprintf(stderr, _("%s%s\nERROR: No input files specified. Use -h for help.\n"), VERSION_STRING, COPYRIGHT);
Michael Smith's avatar
   
Michael Smith committed
99
		return 1;
Jack Moffitt's avatar
Jack Moffitt committed
100
101
102
103
104
105
106
107
108
109
110
111
112
	}
	else
	{
		infiles = argv + optind;
		numfiles = argc - optind;
	}

	/* Now, do some checking for illegal argument combinations */

	for(i = 0; i < numfiles; i++)
	{
		if(!strcmp(infiles[i], "-") && numfiles > 1)
		{
113
			fprintf(stderr, _("ERROR: Multiple files specified when using stdin\n"));
Jack Moffitt's avatar
Jack Moffitt committed
114
115
116
117
118
119
			exit(1);
		}
	}

	if(numfiles > 1 && opt.outfile)
	{
120
		fprintf(stderr, _("ERROR: Multiple input files with specified output filename: suggest using -n\n"));
Jack Moffitt's avatar
Jack Moffitt committed
121
122
123
		exit(1);
	}

Michael Smith's avatar
   
Michael Smith committed
124
125
126
127
128
129
	if(opt.serial == 0)
	{
		/* We randomly pick a serial number. This is then incremented for each file */
		srand(time(NULL));
		opt.serial = rand();
	}
Jack Moffitt's avatar
Jack Moffitt committed
130
131
132
133
134
135
136
137
138
139
140

	for(i = 0; i < numfiles; i++)
	{
		/* Once through the loop for each file */

		oe_enc_opt      enc_opts;
		vorbis_comment  vc;
		char *out_fn = NULL;
		FILE *in, *out = NULL;
		int foundformat = 0;
		int closeout = 0, closein = 0;
141
142
		char *artist=NULL, *album=NULL, *title=NULL, *track=NULL;
        char *date=NULL, *genre=NULL;
Michael Smith's avatar
   
Michael Smith committed
143
		input_format *format;
Jack Moffitt's avatar
Jack Moffitt committed
144
145
146

		/* Set various encoding defaults */

Michael Smith's avatar
   
Michael Smith committed
147
		enc_opts.serialno = opt.serial++;
Michael Smith's avatar
   
Michael Smith committed
148
		enc_opts.progress_update = update_statistics_full;
149
        enc_opts.start_encode = start_encode_full;
Michael Smith's avatar
   
Michael Smith committed
150
		enc_opts.end_encode = final_statistics;
Michael Smith's avatar
   
Michael Smith committed
151
		enc_opts.error = encode_error;
152
153
154
		enc_opts.comments = &vc;
		enc_opts.copy_comments = opt.copy_comments;

Jack Moffitt's avatar
Jack Moffitt committed
155
		/* OK, let's build the vorbis_comments structure */
156
157
		build_comments(&vc, &opt, i, &artist, &album, &title, &track, 
                &date, &genre);
Jack Moffitt's avatar
Jack Moffitt committed
158
159
160
161
162

		if(!strcmp(infiles[i], "-"))
		{
			setbinmode(stdin);
			in = stdin;
163
            infiles[i] = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
164
165
166
167
168
169
170
171
172
173
174
175
			if(!opt.outfile)
			{
				setbinmode(stdout);
				out = stdout;
			}
		}
		else
		{
			in = fopen(infiles[i], "rb");

			if(in == NULL)
			{
Michael Smith's avatar
Michael Smith committed
176
				fprintf(stderr, _("ERROR: Cannot open input file \"%s\": %s\n"), infiles[i], strerror(errno));
Jack Moffitt's avatar
Jack Moffitt committed
177
				free(out_fn);
178
				errors++;
Jack Moffitt's avatar
Jack Moffitt committed
179
180
181
182
183
184
				continue;
			}

			closein = 1;
		}

Michael Smith's avatar
   
Michael Smith committed
185
186
187
188
189
190
		/* Now, we need to select an input audio format - we do this before opening
		   the output file so that we don't end up with a 0-byte file if the input
		   file can't be read */

		if(opt.rawmode)
		{
191
192
193
			enc_opts.rate=opt.raw_samplerate;
			enc_opts.channels=opt.raw_channels;
			enc_opts.samplesize=opt.raw_samplesize;
Michael Smith's avatar
Michael Smith committed
194
            enc_opts.endianness=opt.raw_endianness;
Michael Smith's avatar
   
Michael Smith committed
195
196
197
198
199
			raw_open(in, &enc_opts);
			foundformat=1;
		}
		else
		{
Michael Smith's avatar
   
Michael Smith committed
200
201
			format = open_audio_file(in, &enc_opts);
			if(format)
Michael Smith's avatar
   
Michael Smith committed
202
			{
203
                if(!opt.quiet)
204
				    fprintf(stderr, _("Opening with %s module: %s\n"), 
205
					    	format->format, format->description);
Michael Smith's avatar
   
Michael Smith committed
206
				foundformat=1;
Michael Smith's avatar
   
Michael Smith committed
207
			}
Michael Smith's avatar
   
Michael Smith committed
208

Michael Smith's avatar
   
Michael Smith committed
209
210
211
212
		}

		if(!foundformat)
		{
213
			fprintf(stderr, _("ERROR: Input file \"%s\" is not a supported format\n"), infiles[i]?infiles[i]:"(stdin)");
214
215
    		if(closein)
				fclose(in);
216
			errors++;
Michael Smith's avatar
   
Michael Smith committed
217
218
219
220
221
			continue;
		}

		/* Ok. We can read the file - so now open the output file */

Jack Moffitt's avatar
Jack Moffitt committed
222
223
224
225
226
227
228
229
230
231
232
233
234
		if(opt.outfile && !strcmp(opt.outfile, "-"))
		{
			setbinmode(stdout);
			out = stdout;
		}
		else if(out == NULL)
		{
			if(opt.outfile)
			{
				out_fn = strdup(opt.outfile);
			}
			else if(opt.namefmt)
			{
235
				out_fn = generate_name_string(opt.namefmt, opt.namefmt_remove, 
236
237
                        opt.namefmt_replace, artist, title, album, track,date,
                        genre);
Jack Moffitt's avatar
Jack Moffitt committed
238
			}
239
240
            /* This bit was widely derided in mid-2002, so it's been removed */
            /*
Jack Moffitt's avatar
Jack Moffitt committed
241
242
			else if(opt.title)
			{
Michael Smith's avatar
   
Michael Smith committed
243
244
				out_fn = malloc(strlen(title) + 5);
				strcpy(out_fn, title);
Jack Moffitt's avatar
Jack Moffitt committed
245
246
				strcat(out_fn, ".ogg");
			}
247
            */
248
			else if(infiles[i])
Jack Moffitt's avatar
Jack Moffitt committed
249
250
251
252
			{
				/* Create a filename from existing filename, replacing extension with .ogg */
				char *start, *end;

Michael Smith's avatar
   
Michael Smith committed
253
				start = infiles[i];
Michael Smith's avatar
Michael Smith committed
254
				end = strrchr(infiles[i], '.');
Jack Moffitt's avatar
Jack Moffitt committed
255
256
257
258
259
260
261
				end = end?end:(start + strlen(infiles[i])+1);
			
				out_fn = malloc(end - start + 5);
				strncpy(out_fn, start, end-start);
				out_fn[end-start] = 0;
				strcat(out_fn, ".ogg");
			}
262
            else {
263
                fprintf(stderr, _("WARNING: No filename, defaulting to \"default.ogg\"\n"));
264
265
                out_fn = strdup("default.ogg");
            }
Jack Moffitt's avatar
Jack Moffitt committed
266

267
268
269
270
271
272
273
274
275
            /* Create any missing subdirectories, if possible */
            if(create_directories(out_fn)) {
                if(closein)
                    fclose(in);
				fprintf(stderr, _("ERROR: Could not create required subdirectories for output filename \"%s\"\n"), out_fn);
				errors++;
				free(out_fn);
				continue;
            }
Jack Moffitt's avatar
Jack Moffitt committed
276

277
278
279
280
281
282
283
            if(infiles[i] && !strcmp(infiles[i], out_fn)) {
                fprintf(stderr, _("ERROR: Input filename is the same as output filename \"%s\"\n"), out_fn);
                errors++;
                free(out_fn);
                continue;
            }

Jack Moffitt's avatar
Jack Moffitt committed
284
285
286
287
288
			out = fopen(out_fn, "wb");
			if(out == NULL)
			{
				if(closein)
					fclose(in);
Michael Smith's avatar
Michael Smith committed
289
				fprintf(stderr, _("ERROR: Cannot open output file \"%s\": %s\n"), out_fn, strerror(errno));
290
				errors++;
Jack Moffitt's avatar
Jack Moffitt committed
291
292
293
294
295
296
297
298
299
300
				free(out_fn);
				continue;
			}	
			closeout = 1;
		}

		/* Now, set the rest of the options */
		enc_opts.out = out;
		enc_opts.comments = &vc;
		enc_opts.filename = out_fn;
301
		enc_opts.infilename = infiles[i];
302
        enc_opts.managed = opt.managed;
303
304
305
		enc_opts.bitrate = opt.nominal_bitrate; 
		enc_opts.min_bitrate = opt.min_bitrate;
		enc_opts.max_bitrate = opt.max_bitrate;
306
		enc_opts.quality = opt.quality;
Monty's avatar
   
Monty committed
307
		enc_opts.quality_set = opt.quality_set;
308
309
        enc_opts.advopt = opt.advopt;
        enc_opts.advopt_count = opt.advopt_count;
Jack Moffitt's avatar
Jack Moffitt committed
310

311
        if(opt.resamplefreq && opt.resamplefreq != enc_opts.rate) {
312
            int fromrate = enc_opts.rate;
313
314
315
316
317
318
            enc_opts.resamplefreq = opt.resamplefreq;
            if(setup_resample(&enc_opts)) {
                errors++;
                goto clear_all;
            }
            else if(!opt.quiet)
319
                fprintf(stderr, _("Resampling input from %d Hz to %d Hz\n"), fromrate, opt.resamplefreq);
320
321
322
323
324
325
326
327
328
        }

        if(opt.downmix) {
            if(enc_opts.channels == 2) {
                setup_downmix(&enc_opts);
                if(!opt.quiet)
                    fprintf(stderr, _("Downmixing stereo to mono\n"));
            }
            else {
329
330
                fprintf(stderr, _("WARNING: Can't downmix except from stereo to mono\n"));
                opt.downmix = 0;
331
332
333
            }
        }

334
335
336
337
338
339
        if(opt.scale > 0.f) {
            setup_scaler(&enc_opts, opt.scale);
            if(!opt.quiet)
                fprintf(stderr, _("Scaling input to %f\n"), opt.scale);
        }

340

Michael Smith's avatar
   
Michael Smith committed
341
342
		if(!enc_opts.total_samples_per_channel)
			enc_opts.progress_update = update_statistics_notime;
Jack Moffitt's avatar
Jack Moffitt committed
343

Michael Smith's avatar
   
Michael Smith committed
344
345
		if(opt.quiet)
		{
346
            enc_opts.start_encode = start_encode_null;
Michael Smith's avatar
   
Michael Smith committed
347
348
349
			enc_opts.progress_update = update_statistics_null;
			enc_opts.end_encode = final_statistics_null;
		}
Jack Moffitt's avatar
Jack Moffitt committed
350

351
352
		if(oe_encode(&enc_opts))
			errors++;
Jack Moffitt's avatar
Jack Moffitt committed
353

354
355
        if(opt.scale > 0)
            clear_scaler(&enc_opts);
356
357
        if(opt.downmix)
            clear_downmix(&enc_opts);
358
        if(opt.resamplefreq && opt.resamplefreq != enc_opts.rate)
359
360
361
            clear_resample(&enc_opts);
clear_all:

Jack Moffitt's avatar
Jack Moffitt committed
362
		if(out_fn) free(out_fn);
363
        if(opt.outfile) free(opt.outfile);
Jack Moffitt's avatar
Jack Moffitt committed
364
		vorbis_comment_clear(&vc);
Michael Smith's avatar
   
Michael Smith committed
365
366
		if(!opt.rawmode) 
			format->close_func(enc_opts.readdata);
Jack Moffitt's avatar
Jack Moffitt committed
367
368
369
370
371
372
373

		if(closein)
			fclose(in);
		if(closeout)
			fclose(out);
	}/* Finished this file, loop around to next... */

374
	return errors?1:0;
Jack Moffitt's avatar
Jack Moffitt committed
375
376
377

}

378
static void usage(void)
Jack Moffitt's avatar
Jack Moffitt committed
379
380
{
	fprintf(stdout, 
381
		_("%s%s\n"
Jack Moffitt's avatar
Jack Moffitt committed
382
383
384
385
		"Usage: oggenc [options] input.wav [...]\n"
		"\n"
		"OPTIONS:\n"
		" General:\n"
386
		" -Q, --quiet          Produce no output to stderr\n"
Jack Moffitt's avatar
Jack Moffitt committed
387
		" -h, --help           Print this help text\n"
388
        " -v, --version        Print the version number\n"
Jack Moffitt's avatar
Jack Moffitt committed
389
		" -r, --raw            Raw mode. Input files are read directly as PCM data\n"
390
391
392
		" -B, --raw-bits=n     Set bits/sample for raw input. Default is 16\n"
		" -C, --raw-chan=n     Set number of channels for raw input. Default is 2\n"
		" -R, --raw-rate=n     Set samples/sec for raw input. Default is 44100\n"
Michael Smith's avatar
Michael Smith committed
393
        " --raw-endianness     1 for bigendian, 0 for little (defaults to 0)\n"
394
395
		" -b, --bitrate        Choose a nominal bitrate to encode at. Attempt\n"
		"                      to encode at a bitrate averaging this. Takes an\n"
396
397
398
399
400
401
402
403
404
		"                      argument in kbps. By default, this produces a VBR\n"
        "                      encoding, equivalent to using -q or --quality.\n"
        "                      See the --managed option to use a managed bitrate\n"
        "                      targetting the selected bitrate.\n"
        " --managed            Enable the bitrate management engine. This will allow\n"
        "                      much greater control over the precise bitrate(s) used,\n"
        "                      but encoding will be much slower. Don't use it unless\n"
        "                      you have a strong need for detailed control over\n"
        "                      bitrate, such as for streaming.\n"
405
		" -m, --min-bitrate    Specify a minimum bitrate (in kbps). Useful for\n"
406
407
408
		"                      encoding for a fixed-size channel. Using this will\n"
        "                      automatically enable managed bitrate mode (see\n"
        "                      --managed).\n"
Segher Boessenkool's avatar
Segher Boessenkool committed
409
		" -M, --max-bitrate    Specify a maximum bitrate in kbps. Useful for\n"
410
411
		"                      streaming applications. Using this will automatically\n"
        "                      enable managed bitrate mode (see --managed).\n"
412
413
		" -q, --quality        Specify quality between 0 (low) and 10 (high),\n"
		"                      instead of specifying a particular bitrate.\n"
414
		"                      This is the normal mode of operation.\n"
Michael Smith's avatar
Michael Smith committed
415
        "                      Fractional qualities (e.g. 2.75) are permitted\n"
416
417
        "                      Quality -1 is also possible, but may not be of\n"
        "                      acceptable quality.\n"
418
        " --resample n         Resample input data to sampling rate n (Hz)\n"
419
420
        " --downmix            Downmix stereo to mono. Only allowed on stereo\n"
        "                      input.\n"
Michael Smith's avatar
   
Michael Smith committed
421
422
423
		" -s, --serial         Specify a serial number for the stream. If encoding\n"
		"                      multiple files, this will be incremented for each\n"
		"                      stream after the first.\n"
Stan Seibert's avatar
Stan Seibert committed
424
425
		" --discard-comments   Prevents comments in FLAC and Ogg FLAC files from\n"
		"                      being copied to the output Ogg Vorbis file.\n"
Jack Moffitt's avatar
Jack Moffitt committed
426
427
428
		"\n"
		" Naming:\n"
		" -o, --output=fn      Write file to fn (only valid in single-file mode)\n"
Michael Smith's avatar
   
Michael Smith committed
429
		" -n, --names=string   Produce filenames as this string, with %%a, %%t, %%l,\n"
Segher Boessenkool's avatar
Segher Boessenkool committed
430
		"                      %%n, %%d replaced by artist, title, album, track number,\n"
Michael Smith's avatar
   
Michael Smith committed
431
		"                      and date, respectively (see below for specifying these).\n"
Jack Moffitt's avatar
Jack Moffitt committed
432
		"                      %%%% gives a literal %%.\n"
433
434
        " -X, --name-remove=s  Remove the specified characters from parameters to the\n"
        "                      -n format string. Useful to ensure legal filenames.\n"
Segher Boessenkool's avatar
Segher Boessenkool committed
435
436
        " -P, --name-replace=s Replace characters removed by --name-remove with the\n"
        "                      characters specified. If this string is shorter than the\n"
437
438
439
440
        "                      --name-remove list or is not specified, the extra\n"
        "                      characters are just removed.\n"
        "                      Default settings for the above two arguments are platform\n"
        "                      specific.\n"
Jack Moffitt's avatar
Jack Moffitt committed
441
		" -c, --comment=c      Add the given string as an extra comment. This may be\n"
442
443
		"                      used multiple times. The argument should be in the\n"
        "                      format \"tag=value\".\n"
Michael Smith's avatar
   
Michael Smith committed
444
445
		" -d, --date           Date for track (usually date of performance)\n"
		" -N, --tracknum       Track number for this track\n"
Jack Moffitt's avatar
Jack Moffitt committed
446
447
448
		" -t, --title          Title for this track\n"
		" -l, --album          Name of album\n"
		" -a, --artist         Name of artist\n"
Segher Boessenkool's avatar
Segher Boessenkool committed
449
        " -G, --genre          Genre of track\n"
Jack Moffitt's avatar
Jack Moffitt committed
450
		"                      If multiple input files are given, then multiple\n"
Michael Smith's avatar
   
Michael Smith committed
451
		"                      instances of the previous five arguments will be used,\n"
Jack Moffitt's avatar
Jack Moffitt committed
452
453
		"                      in the order they are given. If fewer titles are\n"
		"                      specified than files, OggEnc will print a warning, and\n"
Michael Smith's avatar
   
Michael Smith committed
454
455
456
457
458
		"                      reuse the final one for the remaining files. If fewer\n"
		"                      track numbers are given, the remaining files will be\n"
		"                      unnumbered. For the others, the final tag will be reused\n"
		"                      for all others without warning (so you can specify a date\n"
		"                      once, for example, and have it used for all the files)\n"
Jack Moffitt's avatar
Jack Moffitt committed
459
460
		"\n"
		"INPUT FILES:\n"
Michael Smith's avatar
Michael Smith committed
461
		" OggEnc input files must currently be 24, 16, or 8 bit PCM WAV, AIFF, or AIFF/C\n"
Stan Seibert's avatar
Stan Seibert committed
462
463
		" files, 32 bit IEEE floating point WAV, and optionally FLAC or Ogg FLAC. Files\n"
                "  may be mono or stereo (or more channels) and any sample rate.\n"
Michael Smith's avatar
   
Michael Smith committed
464
		" Alternatively, the --raw option may be used to use a raw PCM data file, which\n"
Michael Smith's avatar
Michael Smith committed
465
		" must be 16 bit stereo little-endian PCM ('headerless wav'), unless additional\n"
466
467
		" parameters for raw mode are specified.\n"
		" You can specify taking the file from stdin by using - as the input filename.\n"
468
		" In this mode, output is to stdout unless an output filename is specified\n"
Jack Moffitt's avatar
Jack Moffitt committed
469
		" with -o\n"
470
		"\n"), VERSION_STRING, COPYRIGHT);
Jack Moffitt's avatar
Jack Moffitt committed
471
472
}

473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
static int strncpy_filtered(char *dst, char *src, int len, char *remove_list, 
        char *replace_list)
{
    char *hit, *drop_margin;
    int used=0;

    if(remove_list == NULL || *remove_list == 0)
    {
        strncpy(dst, src, len-1);
        dst[len-1] = 0;
        return strlen(dst);
    }

    drop_margin = remove_list + (replace_list == NULL?0:strlen(replace_list));

    while(*src && used < len-1)
    {
        if((hit = strchr(remove_list, *src)) != NULL)
        {
            if(hit < drop_margin)
            {
                *dst++ = replace_list[hit - remove_list];
                used++;
            }
        }
        else
        {
            *dst++ = *src;
            used++;
        }
        src++;
    }
    *dst = 0;

    return used;
}

static char *generate_name_string(char *format, char *remove_list,
        char *replace_list, char *artist, char *title, char *album, 
512
        char *track, char *date, char *genre)
Jack Moffitt's avatar
Jack Moffitt committed
513
514
515
{
	char *buffer;
	char next;
516
517
518
	char *string;
	int used=0;
	int buflen;
Jack Moffitt's avatar
Jack Moffitt committed
519

520
521
	buffer = calloc(CHUNK+1,1);
	buflen = CHUNK;
Jack Moffitt's avatar
Jack Moffitt committed
522

523
	while(*format && used < buflen)
Jack Moffitt's avatar
Jack Moffitt committed
524
525
526
527
528
529
530
531
	{
		next = *format++;

		if(next == '%')
		{
			switch(*format++)
			{
				case '%':
532
					*(buffer+(used++)) = '%';
Jack Moffitt's avatar
Jack Moffitt committed
533
534
					break;
				case 'a':
535
					string = artist?artist:_("(none)");
536
537
					used += strncpy_filtered(buffer+used, string, buflen-used, 
                            remove_list, replace_list);
Jack Moffitt's avatar
Jack Moffitt committed
538
					break;
Michael Smith's avatar
   
Michael Smith committed
539
				case 'd':
540
					string = date?date:_("(none)");
541
542
					used += strncpy_filtered(buffer+used, string, buflen-used,
                            remove_list, replace_list);
Michael Smith's avatar
   
Michael Smith committed
543
					break;
544
                case 'g':
545
                    string = genre?genre:_("(none)");
546
547
548
                    used += strncpy_filtered(buffer+used, string, buflen-used,
                            remove_list, replace_list);
                    break;
Jack Moffitt's avatar
Jack Moffitt committed
549
				case 't':
550
					string = title?title:_("(none)");
551
552
					used += strncpy_filtered(buffer+used, string, buflen-used,
                            remove_list, replace_list);
Jack Moffitt's avatar
Jack Moffitt committed
553
554
					break;
				case 'l':
555
					string = album?album:_("(none)");
556
557
					used += strncpy_filtered(buffer+used, string, buflen-used,
                            remove_list, replace_list);
Jack Moffitt's avatar
Jack Moffitt committed
558
					break;
Michael Smith's avatar
   
Michael Smith committed
559
				case 'n':
560
					string = track?track:_("(none)");
561
562
					used += strncpy_filtered(buffer+used, string, buflen-used,
                            remove_list, replace_list);
Michael Smith's avatar
   
Michael Smith committed
563
					break;
Jack Moffitt's avatar
Jack Moffitt committed
564
				default:
565
					fprintf(stderr, _("WARNING: Ignoring illegal escape character '%c' in name format\n"), *(format - 1));
Jack Moffitt's avatar
Jack Moffitt committed
566
567
568
569
					break;
			}
		}
		else
570
			*(buffer + (used++)) = next;
Jack Moffitt's avatar
Jack Moffitt committed
571
572
573
574
575
	}

	return buffer;
}

576
static void parse_options(int argc, char **argv, oe_options *opt)
Jack Moffitt's avatar
Jack Moffitt committed
577
578
579
580
{
	int ret;
	int option_index = 1;

581
	while((ret = getopt_long(argc, argv, "A:a:b:B:c:C:d:G:hl:m:M:n:N:o:P:q:QrR:s:t:vX:", 
Jack Moffitt's avatar
Jack Moffitt committed
582
583
584
585
586
					long_options, &option_index)) != -1)
	{
		switch(ret)
		{
			case 0:
587
                if(!strcmp(long_options[option_index].name, "managed")) {
588
589
590
591
592
593
		            if(!opt->managed){
                        if(!opt->quiet)
            		        fprintf(stderr, 
                                    _("Enabling bitrate management engine\n"));
                        opt->managed = 1;
        		    }
594
                }
Michael Smith's avatar
Michael Smith committed
595
596
597
598
599
600
601
                else if(!strcmp(long_options[option_index].name, 
                            "raw-endianness")) {
				    if (opt->rawmode != 1)
    				{
	    				opt->rawmode = 1;
		    			fprintf(stderr, _("WARNING: Raw endianness specified for non-raw data. Assuming input is raw.\n"));
			    	}
602
603
604
605
606
607
608
609
610
611
612
				    if(sscanf(optarg, "%d", &opt->raw_endianness) != 1) {
                        fprintf(stderr, _("WARNING: Couldn't read endianness argument \"%s\"\n"), optarg);
    					opt->raw_endianness = 0;
                    }
                }
                else if(!strcmp(long_options[option_index].name,
                            "resample")) {
				    if(sscanf(optarg, "%d", &opt->resamplefreq) != 1) {
                        fprintf(stderr, _("WARNING: Couldn't read resampling frequency \"%s\"\n"), optarg);
    					opt->resamplefreq = 0;
                    }
613
614
615
616
617
                    if(opt->resamplefreq < 100) /* User probably specified it
                                                   in kHz accidently */
                        fprintf(stderr, 
                                _("Warning: Resample rate specified as %d Hz. Did you mean %d Hz?\n"), 
                                opt->resamplefreq, opt->resamplefreq*1000);
618
619
620
                }
                else if(!strcmp(long_options[option_index].name, "downmix")) {
                    opt->downmix = 1;
Michael Smith's avatar
Michael Smith committed
621
                }
622
623
624
625
626
627
628
629
                else if(!strcmp(long_options[option_index].name, "scale")) {
                    opt->scale = atof(optarg);
				    if(sscanf(optarg, "%f", &opt->scale) != 1) {
                        opt->scale = 0;
                        fprintf(stderr, _("Warning: Couldn't parse scaling factor \"%s\"\n"), 
                                optarg);
                    }
                }
630
                else if(!strcmp(long_options[option_index].name, "advanced-encode-option")) {
631
632
633
634
635
636
                    char *arg = strdup(optarg);
                    char *val;

                    val = strchr(arg, '=');
                    if(val == NULL) {
                        fprintf(stderr, _("No value for advanced encoder option found\n"));
637
                        continue;
638
639
640
641
642
643
644
645
                    }
                    else
                        *val++=0;

                    opt->advopt = realloc(opt->advopt, (++opt->advopt_count)*sizeof(adv_opt));
                    opt->advopt[opt->advopt_count - 1].arg = arg;
                    opt->advopt[opt->advopt_count - 1].val = val;
                }
646
647
648
649
                else if(!strcmp(long_options[option_index].name, "discard-comments")) {
		  opt->copy_comments = 0;
		}

650
651
652
653
654
655
                else {
				    fprintf(stderr, _("Internal error parsing command line options\n"));
				    exit(1);
                }

				break;
Jack Moffitt's avatar
Jack Moffitt committed
656
657
658
659
660
			case 'a':
				opt->artist = realloc(opt->artist, (++opt->artist_count)*sizeof(char *));
				opt->artist[opt->artist_count - 1] = strdup(optarg);
				break;
			case 'c':
661
662
663
664
                if(strchr(optarg, '=') == NULL) {
                    fprintf(stderr, _("Warning: Illegal comment used (\"%s\"), ignoring.\n"), optarg);
                    break;
                }
Jack Moffitt's avatar
Jack Moffitt committed
665
666
667
				opt->comments = realloc(opt->comments, (++opt->comment_count)*sizeof(char *));
				opt->comments[opt->comment_count - 1] = strdup(optarg);
				break;
Michael Smith's avatar
   
Michael Smith committed
668
669
670
671
			case 'd':
				opt->dates = realloc(opt->dates, (++opt->date_count)*sizeof(char *));
				opt->dates[opt->date_count - 1] = strdup(optarg);
				break;
672
673
674
675
676
677
678
679
            case 'G':
                opt->genre = realloc(opt->genre, (++opt->genre_count)*sizeof(char *));
                opt->genre[opt->genre_count - 1] = strdup(optarg);
                break;
			case 'h':
				usage();
				exit(0);
				break;
Jack Moffitt's avatar
Jack Moffitt committed
680
681
682
683
			case 'l':
				opt->album = realloc(opt->album, (++opt->album_count)*sizeof(char *));
				opt->album[opt->album_count - 1] = strdup(optarg);
				break;
Michael Smith's avatar
   
Michael Smith committed
684
685
686
			case 's':
				/* Would just use atoi(), but that doesn't deal with unsigned
				 * ints. Damn */
Michael Smith's avatar
   
Michael Smith committed
687
				if(sscanf(optarg, "%u", &opt->serial) != 1)
Michael Smith's avatar
   
Michael Smith committed
688
689
					opt->serial = 0; /* Failed, so just set to zero */
				break;
Jack Moffitt's avatar
Jack Moffitt committed
690
691
692
693
			case 't':
				opt->title = realloc(opt->title, (++opt->title_count)*sizeof(char *));
				opt->title[opt->title_count - 1] = strdup(optarg);
				break;
Michael Smith's avatar
   
Michael Smith committed
694
			case 'b':
695
696
697
698
   				if(sscanf(optarg, "%d", &opt->nominal_bitrate)
    					!= 1) {
	    			fprintf(stderr, _("Warning: nominal bitrate \"%s\" not recognised\n"), optarg);
		    		opt->nominal_bitrate = -1;
699
				}
700

701
702
				break;
			case 'm':
703
704
				if(sscanf(optarg, "%d", &opt->min_bitrate)
						!= 1) {
705
					fprintf(stderr, _("Warning: minimum bitrate \"%s\" not recognised\n"), optarg);
706
707
					opt->min_bitrate = -1;
				}
Monty's avatar
   
Monty committed
708
709
710
711
712
713
				if(!opt->managed){
				  if(!opt->quiet)
				    fprintf(stderr, 
					    _("Enabling bitrate management engine\n"));
				  opt->managed = 1;
				}
714
715
				break;
			case 'M':
716
717
				if(sscanf(optarg, "%d", &opt->max_bitrate)
						!= 1) {
718
					fprintf(stderr, _("Warning: maximum bitrate \"%s\" not recognised\n"), optarg);
719
720
					opt->max_bitrate = -1;
				}
Monty's avatar
   
Monty committed
721
722
723
724
725
726
				if(!opt->managed){
				  if(!opt->quiet)
				    fprintf(stderr, 
					    _("Enabling bitrate management engine\n"));
				  opt->managed = 1;
				}
727
728
				break;
			case 'q':
729
				if(sscanf(optarg, "%f", &opt->quality) != 1) {
730
					fprintf(stderr, _("Quality option \"%s\" not recognised, ignoring\n"), optarg);
731
732
					break;
				}
Monty's avatar
   
Monty committed
733
				opt->quality_set=1;
Jack Moffitt's avatar
Jack Moffitt committed
734
				opt->quality *= 0.1;
735
736
737
				if(opt->quality > 1.0f)
				{
					opt->quality = 1.0f;
738
					fprintf(stderr, _("WARNING: quality setting too high, setting to maximum quality.\n"));
739
				}
Jack Moffitt's avatar
Jack Moffitt committed
740
741
742
743
				break;
			case 'n':
				if(opt->namefmt)
				{
744
					fprintf(stderr, _("WARNING: Multiple name formats specified, using final\n"));
Jack Moffitt's avatar
Jack Moffitt committed
745
746
747
748
					free(opt->namefmt);
				}
				opt->namefmt = strdup(optarg);
				break;
749
750
751
752
            case 'X':
				if(opt->namefmt_remove && opt->namefmt_remove != 
                        DEFAULT_NAMEFMT_REMOVE)
				{
753
					fprintf(stderr, _("WARNING: Multiple name format filters specified, using final\n"));
754
755
756
757
758
759
760
761
					free(opt->namefmt_remove);
				}
				opt->namefmt_remove = strdup(optarg);
				break;
            case 'P':
				if(opt->namefmt_replace && opt->namefmt_replace != 
                        DEFAULT_NAMEFMT_REPLACE)
                {
762
					fprintf(stderr, _("WARNING: Multiple name format filter replacements specified, using final\n"));
763
764
765
766
					free(opt->namefmt_replace);
				}
				opt->namefmt_replace = strdup(optarg);
				break;
Jack Moffitt's avatar
Jack Moffitt committed
767
768
769
			case 'o':
				if(opt->outfile)
				{
770
					fprintf(stderr, _("WARNING: Multiple output files specified, suggest using -n\n"));
Jack Moffitt's avatar
Jack Moffitt committed
771
772
773
774
					free(opt->outfile);
				}
				opt->outfile = strdup(optarg);
				break;
775
			case 'Q':
Jack Moffitt's avatar
Jack Moffitt committed
776
777
778
779
780
781
				opt->quiet = 1;
				break;
			case 'r':
				opt->rawmode = 1;
				break;
			case 'v':
782
				fprintf(stdout, VERSION_STRING);
Jack Moffitt's avatar
Jack Moffitt committed
783
784
				exit(0);
				break;
785
786
787
788
			case 'B':
				if (opt->rawmode != 1)
				{
					opt->rawmode = 1;
789
					fprintf(stderr, _("WARNING: Raw bits/sample specified for non-raw data. Assuming input is raw.\n"));
790
791
792
793
				}
				if(sscanf(optarg, "%u", &opt->raw_samplesize) != 1)
				{
					opt->raw_samplesize = 16; /* Failed, so just set to 16 */
794
					fprintf(stderr, _("WARNING: Invalid bits/sample specified, assuming 16.\n"));
795
796
797
				}
				if((opt->raw_samplesize != 8) && (opt->raw_samplesize != 16))
				{
798
					fprintf(stderr, _("WARNING: Invalid bits/sample specified, assuming 16.\n"));
799
800
801
802
803
804
				}
				break;
			case 'C':
				if (opt->rawmode != 1)
				{
					opt->rawmode = 1;
805
					fprintf(stderr, _("WARNING: Raw channel count specified for non-raw data. Assuming input is raw.\n"));
806
807
808
809
				}
				if(sscanf(optarg, "%u", &opt->raw_channels) != 1)
				{
					opt->raw_channels = 2; /* Failed, so just set to 2 */
810
					fprintf(stderr, _("WARNING: Invalid channel count specified, assuming 2.\n"));
811
812
				}
				break;
Michael Smith's avatar
   
Michael Smith committed
813
814
815
816
			case 'N':
				opt->tracknum = realloc(opt->tracknum, (++opt->track_count)*sizeof(char *));
				opt->tracknum[opt->track_count - 1] = strdup(optarg);
				break;
817
818
819
820
			case 'R':
				if (opt->rawmode != 1)
				{
					opt->rawmode = 1;
821
					fprintf(stderr, _("WARNING: Raw sample rate specified for non-raw data. Assuming input is raw.\n"));
822
823
824
825
				}
				if(sscanf(optarg, "%u", &opt->raw_samplerate) != 1)
				{
					opt->raw_samplerate = 44100; /* Failed, so just set to 44100 */
826
					fprintf(stderr, _("WARNING: Invalid sample rate specified, assuming 44100.\n"));
827
828
				}
				break;
Jack Moffitt's avatar
Jack Moffitt committed
829
			case '?':
830
				fprintf(stderr, _("WARNING: Unknown option specified, ignoring->\n"));
Jack Moffitt's avatar
Jack Moffitt committed
831
832
833
834
835
836
				break;
			default:
				usage();
				exit(0);
		}
	}
837

Jack Moffitt's avatar
Jack Moffitt committed
838
839
}

840
static void add_tag(vorbis_comment *vc, oe_options *opt,char *name, char *value)
841
842
{
	char *utf8;
843
	if(utf8_encode(value, &utf8) >= 0)
844
845
846
847
848
849
850
851
	{
		if(name == NULL)
			vorbis_comment_add(vc, utf8);
		else
			vorbis_comment_add_tag(vc, name, utf8);
		free(utf8);
	}
	else
852
		fprintf(stderr, _("Couldn't convert comment to UTF-8, cannot add\n"));
853
854
}

855
static void build_comments(vorbis_comment *vc, oe_options *opt, int filenum, 
856
857
		char **artist, char **album, char **title, char **tracknum, 
        char **date, char **genre)
Jack Moffitt's avatar
Jack Moffitt committed
858
859
860
861
862
863
{
	int i;

	vorbis_comment_init(vc);

	for(i = 0; i < opt->comment_count; i++)
864
		add_tag(vc, opt, NULL, opt->comments[i]);
Jack Moffitt's avatar
Jack Moffitt committed
865
866
867
868
869
870

	if(opt->title_count)
	{
		if(filenum >= opt->title_count)
		{
			if(!opt->quiet)
871
				fprintf(stderr, _("WARNING: Insufficient titles specified, defaulting to final title.\n"));
Jack Moffitt's avatar
Jack Moffitt committed
872
873
874
875
876
			i = opt->title_count-1;
		}
		else
			i = filenum;

Michael Smith's avatar
   
Michael Smith committed
877
		*title = opt->title[i];
878
		add_tag(vc, opt, "title", opt->title[i]);
Jack Moffitt's avatar
Jack Moffitt committed
879
880
881
882
883
884
885
886
887
	}

	if(opt->artist_count)
	{
		if(filenum >= opt->artist_count)
			i = opt->artist_count-1;
		else
			i = filenum;
	
Michael Smith's avatar
   
Michael Smith committed
888
		*artist = opt->artist[i];
889
		add_tag(vc, opt, "artist", opt->artist[i]);
Jack Moffitt's avatar
Jack Moffitt committed
890
	}
Michael Smith's avatar
   
Michael Smith committed
891

892
893
894
895
896
897
898
899
900
901
902
    if(opt->genre_count)
    {
        if(filenum >= opt->genre_count)
            i = opt->genre_count-1;
        else
            i = filenum;

        *genre = opt->genre[i];
        add_tag(vc, opt, "genre", opt->genre[i]);
    }

Michael Smith's avatar
   
Michael Smith committed
903
904
905
906
907
908
909
910
	if(opt->date_count)
	{
		if(filenum >= opt->date_count)
			i = opt->date_count-1;
		else
			i = filenum;
	
		*date = opt->dates[i];
911
		add_tag(vc, opt, "date", opt->dates[i]);
Michael Smith's avatar
   
Michael Smith committed
912
	}
Jack Moffitt's avatar
Jack Moffitt committed
913
914
915
916
917
918
919
920
921
922
	
	if(opt->album_count)
	{
		if(filenum >= opt->album_count)
		{
			i = opt->album_count-1;
		}
		else
			i = filenum;

Michael Smith's avatar
   
Michael Smith committed
923
		*album = opt->album[i];	
924
		add_tag(vc, opt, "album", opt->album[i]);
Jack Moffitt's avatar
Jack Moffitt committed
925
	}
Michael Smith's avatar
   
Michael Smith committed
926
927
928
929
930

	if(filenum < opt->track_count)
	{
		i = filenum;
		*tracknum = opt->tracknum[i];
931
		add_tag(vc, opt, "tracknumber", opt->tracknum[i]);
Michael Smith's avatar
   
Michael Smith committed
932
	}
Jack Moffitt's avatar
Jack Moffitt committed
933
934
}