oggenc.c 27.5 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.
 *
Michael Smith's avatar
Michael Smith committed
6
 * Copyright 2000-2002, Michael Smith <msmith@labyrinth.net.au>
Jack Moffitt's avatar
Jack Moffitt committed
7
8
9
10
11
12
13
14
15
16
17
 *
 * Portions from Vorbize, (c) Kenneth Arnold <kcarnold@yahoo.com>
 * 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
#define VERSION_STRING "OggEnc v1.0 (libvorbis 1.0)\n"
Michael Smith's avatar
Michael Smith committed
29
#define COPYRIGHT "(c) 2000-2002 Michael Smith <msmith@labyrinth.net.au>\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},
Michael Smith's avatar
   
Michael Smith committed
63
	{NULL,0,0,0}
Jack Moffitt's avatar
Jack Moffitt committed
64
65
};
	
66
67
static char *generate_name_string(char *format, char *remove_list, 
        char *replace_list, char *artist, char *title, char *album, 
68
        char *track, char *date, char *genre);
69
70
static void parse_options(int argc, char **argv, oe_options *opt);
static void build_comments(vorbis_comment *vc, oe_options *opt, int filenum, 
71
72
		char **artist,char **album, char **title, char **tracknum, char **date,
        char **genre);
73
static void usage(void);
Jack Moffitt's avatar
Jack Moffitt committed
74
75
76

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

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

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

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

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

	if(optind >= argc)
	{
97
		fprintf(stderr, _("%s%s\nERROR: No input files specified. Use -h for help.\n"), VERSION_STRING, COPYRIGHT);
Michael Smith's avatar
   
Michael Smith committed
98
		return 1;
Jack Moffitt's avatar
Jack Moffitt committed
99
100
101
102
103
104
105
106
107
108
109
110
111
	}
	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)
		{
112
			fprintf(stderr, _("ERROR: Multiple files specified when using stdin\n"));
Jack Moffitt's avatar
Jack Moffitt committed
113
114
115
116
117
118
			exit(1);
		}
	}

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

Michael Smith's avatar
   
Michael Smith committed
123
124
125
126
127
128
	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
129
130
131
132
133
134
135
136
137
138
139

	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;
140
141
		char *artist=NULL, *album=NULL, *title=NULL, *track=NULL;
        char *date=NULL, *genre=NULL;
Michael Smith's avatar
   
Michael Smith committed
142
		input_format *format;
Jack Moffitt's avatar
Jack Moffitt committed
143
144
145

		/* Set various encoding defaults */

Michael Smith's avatar
   
Michael Smith committed
146
		enc_opts.serialno = opt.serial++;
Michael Smith's avatar
   
Michael Smith committed
147
		enc_opts.progress_update = update_statistics_full;
148
        enc_opts.start_encode = start_encode_full;
Michael Smith's avatar
   
Michael Smith committed
149
		enc_opts.end_encode = final_statistics;
Michael Smith's avatar
   
Michael Smith committed
150
		enc_opts.error = encode_error;
Jack Moffitt's avatar
Jack Moffitt committed
151
152
		
		/* OK, let's build the vorbis_comments structure */
153
154
		build_comments(&vc, &opt, i, &artist, &album, &title, &track, 
                &date, &genre);
Jack Moffitt's avatar
Jack Moffitt committed
155
156
157
158
159

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

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

			closein = 1;
		}

Michael Smith's avatar
   
Michael Smith committed
182
183
184
185
186
187
		/* 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)
		{
188
189
190
			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
191
            enc_opts.endianness=opt.raw_endianness;
Michael Smith's avatar
   
Michael Smith committed
192
193
194
195
196
			raw_open(in, &enc_opts);
			foundformat=1;
		}
		else
		{
Michael Smith's avatar
   
Michael Smith committed
197
198
			format = open_audio_file(in, &enc_opts);
			if(format)
Michael Smith's avatar
   
Michael Smith committed
199
			{
200
                if(!opt.quiet)
201
				    fprintf(stderr, _("Opening with %s module: %s\n"), 
202
					    	format->format, format->description);
Michael Smith's avatar
   
Michael Smith committed
203
				foundformat=1;
Michael Smith's avatar
   
Michael Smith committed
204
			}
Michael Smith's avatar
   
Michael Smith committed
205

Michael Smith's avatar
   
Michael Smith committed
206
207
208
209
		}

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

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

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

Michael Smith's avatar
   
Michael Smith committed
250
				start = infiles[i];
Michael Smith's avatar
Michael Smith committed
251
				end = strrchr(infiles[i], '.');
Jack Moffitt's avatar
Jack Moffitt committed
252
253
254
255
256
257
258
				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");
			}
259
            else {
260
                fprintf(stderr, _("WARNING: No filename, defaulting to \"default.ogg\"\n"));
261
262
                out_fn = strdup("default.ogg");
            }
Jack Moffitt's avatar
Jack Moffitt committed
263

264
265
266
267
268
269
270
271
272
            /* 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
273
274
275
276
277
278

			out = fopen(out_fn, "wb");
			if(out == NULL)
			{
				if(closein)
					fclose(in);
Michael Smith's avatar
Michael Smith committed
279
				fprintf(stderr, _("ERROR: Cannot open output file \"%s\": %s\n"), out_fn, strerror(errno));
280
				errors++;
Jack Moffitt's avatar
Jack Moffitt committed
281
282
283
284
285
286
287
288
289
290
				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;
291
		enc_opts.infilename = infiles[i];
292
        enc_opts.managed = opt.managed;
293
294
295
		enc_opts.bitrate = opt.nominal_bitrate; 
		enc_opts.min_bitrate = opt.min_bitrate;
		enc_opts.max_bitrate = opt.max_bitrate;
296
		enc_opts.quality = opt.quality;
Monty's avatar
   
Monty committed
297
		enc_opts.quality_set = opt.quality_set;
298
299
        enc_opts.advopt = opt.advopt;
        enc_opts.advopt_count = opt.advopt_count;
Jack Moffitt's avatar
Jack Moffitt committed
300

301
        if(opt.resamplefreq && opt.resamplefreq != enc_opts.rate) {
302
            int fromrate = enc_opts.rate;
303
304
305
306
307
308
            enc_opts.resamplefreq = opt.resamplefreq;
            if(setup_resample(&enc_opts)) {
                errors++;
                goto clear_all;
            }
            else if(!opt.quiet)
309
                fprintf(stderr, _("Resampling input from %d Hz to %d Hz\n"), fromrate, opt.resamplefreq);
310
311
312
313
314
315
316
317
318
319
320
        }

        if(opt.downmix) {
            if(enc_opts.channels == 2) {
                setup_downmix(&enc_opts);
                if(!opt.quiet)
                    fprintf(stderr, _("Downmixing stereo to mono\n"));
            }
            else {
                fprintf(stderr, _("ERROR: Can't downmix except from stereo to mono\n"));
                errors++;
321
                if(opt.resamplefreq && opt.resamplefreq != enc_opts.rate)
322
323
324
325
326
                    clear_resample(&enc_opts);
                goto clear_all;
            }
        }

327
328
329
330
331
332
        if(opt.scale > 0.f) {
            setup_scaler(&enc_opts, opt.scale);
            if(!opt.quiet)
                fprintf(stderr, _("Scaling input to %f\n"), opt.scale);
        }

333

Michael Smith's avatar
   
Michael Smith committed
334
335
		if(!enc_opts.total_samples_per_channel)
			enc_opts.progress_update = update_statistics_notime;
Jack Moffitt's avatar
Jack Moffitt committed
336

Michael Smith's avatar
   
Michael Smith committed
337
338
		if(opt.quiet)
		{
339
            enc_opts.start_encode = start_encode_null;
Michael Smith's avatar
   
Michael Smith committed
340
341
342
			enc_opts.progress_update = update_statistics_null;
			enc_opts.end_encode = final_statistics_null;
		}
Jack Moffitt's avatar
Jack Moffitt committed
343

344
345
		if(oe_encode(&enc_opts))
			errors++;
Jack Moffitt's avatar
Jack Moffitt committed
346

347
348
        if(opt.scale > 0)
            clear_scaler(&enc_opts);
349
350
        if(opt.downmix)
            clear_downmix(&enc_opts);
351
        if(opt.resamplefreq && opt.resamplefreq != enc_opts.rate)
352
353
354
            clear_resample(&enc_opts);
clear_all:

Jack Moffitt's avatar
Jack Moffitt committed
355
		if(out_fn) free(out_fn);
356
        if(opt.outfile) free(opt.outfile);
Jack Moffitt's avatar
Jack Moffitt committed
357
		vorbis_comment_clear(&vc);
Michael Smith's avatar
   
Michael Smith committed
358
359
		if(!opt.rawmode) 
			format->close_func(enc_opts.readdata);
Jack Moffitt's avatar
Jack Moffitt committed
360
361
362
363
364
365
366

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

367
	return errors?1:0;
Jack Moffitt's avatar
Jack Moffitt committed
368
369
370

}

371
static void usage(void)
Jack Moffitt's avatar
Jack Moffitt committed
372
373
{
	fprintf(stdout, 
374
		_("%s%s\n"
Jack Moffitt's avatar
Jack Moffitt committed
375
376
377
378
		"Usage: oggenc [options] input.wav [...]\n"
		"\n"
		"OPTIONS:\n"
		" General:\n"
379
		" -Q, --quiet          Produce no output to stderr\n"
Jack Moffitt's avatar
Jack Moffitt committed
380
381
		" -h, --help           Print this help text\n"
		" -r, --raw            Raw mode. Input files are read directly as PCM data\n"
382
383
384
		" -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
385
        " --raw-endianness     1 for bigendian, 0 for little (defaults to 0)\n"
386
387
		" -b, --bitrate        Choose a nominal bitrate to encode at. Attempt\n"
		"                      to encode at a bitrate averaging this. Takes an\n"
388
389
390
		"                      argument in kbps. This uses the bitrate management\n"
        "                      engine, and is not recommended for most users.\n"
        "                      See -q, --quality for a better alternative.\n"
391
		" -m, --min-bitrate    Specify a minimum bitrate (in kbps). Useful for\n"
392
		"                      encoding for a fixed-size channel.\n"
Segher Boessenkool's avatar
Segher Boessenkool committed
393
394
		" -M, --max-bitrate    Specify a maximum bitrate in kbps. Useful for\n"
		"                      streaming applications.\n"
395
396
		" -q, --quality        Specify quality between 0 (low) and 10 (high),\n"
		"                      instead of specifying a particular bitrate.\n"
397
		"                      This is the normal mode of operation.\n"
Michael Smith's avatar
Michael Smith committed
398
        "                      Fractional qualities (e.g. 2.75) are permitted\n"
399
400
        "                      Quality -1 is also possible, but may not be of\n"
        "                      acceptable quality.\n"
401
        " --resample n         Resample input data to sampling rate n (Hz)\n"
402
403
        " --downmix            Downmix stereo to mono. Only allowed on stereo\n"
        "                      input.\n"
Michael Smith's avatar
   
Michael Smith committed
404
405
406
		" -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"
Jack Moffitt's avatar
Jack Moffitt committed
407
408
409
		"\n"
		" Naming:\n"
		" -o, --output=fn      Write file to fn (only valid in single-file mode)\n"
Michael Smith's avatar
   
Michael Smith committed
410
		" -n, --names=string   Produce filenames as this string, with %%a, %%t, %%l,\n"
Segher Boessenkool's avatar
Segher Boessenkool committed
411
		"                      %%n, %%d replaced by artist, title, album, track number,\n"
Michael Smith's avatar
   
Michael Smith committed
412
		"                      and date, respectively (see below for specifying these).\n"
Jack Moffitt's avatar
Jack Moffitt committed
413
		"                      %%%% gives a literal %%.\n"
414
415
        " -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
416
417
        " -P, --name-replace=s Replace characters removed by --name-remove with the\n"
        "                      characters specified. If this string is shorter than the\n"
418
419
420
421
        "                      --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
422
		" -c, --comment=c      Add the given string as an extra comment. This may be\n"
423
424
		"                      used multiple times. The argument should be in the\n"
        "                      format \"tag=value\".\n"
Michael Smith's avatar
   
Michael Smith committed
425
426
		" -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
427
428
429
		" -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
430
        " -G, --genre          Genre of track\n"
Jack Moffitt's avatar
Jack Moffitt committed
431
		"                      If multiple input files are given, then multiple\n"
Michael Smith's avatar
   
Michael Smith committed
432
		"                      instances of the previous five arguments will be used,\n"
Jack Moffitt's avatar
Jack Moffitt committed
433
434
		"                      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
435
436
437
438
439
		"                      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
440
441
		"\n"
		"INPUT FILES:\n"
Michael Smith's avatar
Michael Smith committed
442
		" OggEnc input files must currently be 24, 16, or 8 bit PCM WAV, AIFF, or AIFF/C\n"
Michael Smith's avatar
Michael Smith committed
443
444
		" files, or 32 bit IEEE floating point WAV. Files may be mono or stereo\n"
        " (or more channels) and any sample rate.\n"
Michael Smith's avatar
   
Michael Smith committed
445
		" Alternatively, the --raw option may be used to use a raw PCM data file, which\n"
Michael Smith's avatar
Michael Smith committed
446
		" must be 16 bit stereo little-endian PCM ('headerless wav'), unless additional\n"
447
448
		" parameters for raw mode are specified.\n"
		" You can specify taking the file from stdin by using - as the input filename.\n"
449
		" In this mode, output is to stdout unless an output filename is specified\n"
Jack Moffitt's avatar
Jack Moffitt committed
450
		" with -o\n"
451
		"\n"), VERSION_STRING, COPYRIGHT);
Jack Moffitt's avatar
Jack Moffitt committed
452
453
}

454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
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, 
493
        char *track, char *date, char *genre)
Jack Moffitt's avatar
Jack Moffitt committed
494
495
496
{
	char *buffer;
	char next;
497
498
499
	char *string;
	int used=0;
	int buflen;
Jack Moffitt's avatar
Jack Moffitt committed
500

501
502
	buffer = calloc(CHUNK+1,1);
	buflen = CHUNK;
Jack Moffitt's avatar
Jack Moffitt committed
503

504
	while(*format && used < buflen)
Jack Moffitt's avatar
Jack Moffitt committed
505
506
507
508
509
510
511
512
	{
		next = *format++;

		if(next == '%')
		{
			switch(*format++)
			{
				case '%':
513
					*(buffer+(used++)) = '%';
Jack Moffitt's avatar
Jack Moffitt committed
514
515
					break;
				case 'a':
516
					string = artist?artist:_("(none)");
517
518
					used += strncpy_filtered(buffer+used, string, buflen-used, 
                            remove_list, replace_list);
Jack Moffitt's avatar
Jack Moffitt committed
519
					break;
Michael Smith's avatar
   
Michael Smith committed
520
				case 'd':
521
					string = date?date:_("(none)");
522
523
					used += strncpy_filtered(buffer+used, string, buflen-used,
                            remove_list, replace_list);
Michael Smith's avatar
   
Michael Smith committed
524
					break;
525
                case 'g':
526
                    string = genre?genre:_("(none)");
527
528
529
                    used += strncpy_filtered(buffer+used, string, buflen-used,
                            remove_list, replace_list);
                    break;
Jack Moffitt's avatar
Jack Moffitt committed
530
				case 't':
531
					string = title?title:_("(none)");
532
533
					used += strncpy_filtered(buffer+used, string, buflen-used,
                            remove_list, replace_list);
Jack Moffitt's avatar
Jack Moffitt committed
534
535
					break;
				case 'l':
536
					string = album?album:_("(none)");
537
538
					used += strncpy_filtered(buffer+used, string, buflen-used,
                            remove_list, replace_list);
Jack Moffitt's avatar
Jack Moffitt committed
539
					break;
Michael Smith's avatar
   
Michael Smith committed
540
				case 'n':
541
					string = track?track:_("(none)");
542
543
					used += strncpy_filtered(buffer+used, string, buflen-used,
                            remove_list, replace_list);
Michael Smith's avatar
   
Michael Smith committed
544
					break;
Jack Moffitt's avatar
Jack Moffitt committed
545
				default:
546
					fprintf(stderr, _("WARNING: Ignoring illegal escape character '%c' in name format\n"), *(format - 1));
Jack Moffitt's avatar
Jack Moffitt committed
547
548
549
550
					break;
			}
		}
		else
551
			*(buffer + (used++)) = next;
Jack Moffitt's avatar
Jack Moffitt committed
552
553
554
555
556
	}

	return buffer;
}

557
static void parse_options(int argc, char **argv, oe_options *opt)
Jack Moffitt's avatar
Jack Moffitt committed
558
559
560
561
{
	int ret;
	int option_index = 1;

562
	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
563
564
565
566
567
					long_options, &option_index)) != -1)
	{
		switch(ret)
		{
			case 0:
568
                if(!strcmp(long_options[option_index].name, "managed")) {
569
570
571
572
573
574
		            if(!opt->managed){
                        if(!opt->quiet)
            		        fprintf(stderr, 
                                    _("Enabling bitrate management engine\n"));
                        opt->managed = 1;
        		    }
575
                }
Michael Smith's avatar
Michael Smith committed
576
577
578
579
580
581
582
                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"));
			    	}
583
584
585
586
587
588
589
590
591
592
593
				    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;
                    }
594
595
596
597
598
                    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);
599
600
601
                }
                else if(!strcmp(long_options[option_index].name, "downmix")) {
                    opt->downmix = 1;
Michael Smith's avatar
Michael Smith committed
602
                }
603
604
605
606
607
608
609
610
                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);
                    }
                }
611
                else if(!strcmp(long_options[option_index].name, "advanced-encode-option")) {
612
613
614
615
616
617
                    char *arg = strdup(optarg);
                    char *val;

                    val = strchr(arg, '=');
                    if(val == NULL) {
                        fprintf(stderr, _("No value for advanced encoder option found\n"));
618
                        continue;
619
620
621
622
623
624
625
626
                    }
                    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;
                }
627
628
629
630
631
632
                else {
				    fprintf(stderr, _("Internal error parsing command line options\n"));
				    exit(1);
                }

				break;
Jack Moffitt's avatar
Jack Moffitt committed
633
634
635
636
637
			case 'a':
				opt->artist = realloc(opt->artist, (++opt->artist_count)*sizeof(char *));
				opt->artist[opt->artist_count - 1] = strdup(optarg);
				break;
			case 'c':
638
639
640
641
                if(strchr(optarg, '=') == NULL) {
                    fprintf(stderr, _("Warning: Illegal comment used (\"%s\"), ignoring.\n"), optarg);
                    break;
                }
Jack Moffitt's avatar
Jack Moffitt committed
642
643
644
				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
645
646
647
648
			case 'd':
				opt->dates = realloc(opt->dates, (++opt->date_count)*sizeof(char *));
				opt->dates[opt->date_count - 1] = strdup(optarg);
				break;
649
650
651
652
653
654
655
656
            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
657
658
659
660
			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
661
662
663
			case 's':
				/* Would just use atoi(), but that doesn't deal with unsigned
				 * ints. Damn */
Michael Smith's avatar
   
Michael Smith committed
664
				if(sscanf(optarg, "%u", &opt->serial) != 1)
Michael Smith's avatar
   
Michael Smith committed
665
666
					opt->serial = 0; /* Failed, so just set to zero */
				break;
Jack Moffitt's avatar
Jack Moffitt committed
667
668
669
670
			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
671
			case 'b':
672
673
674
675
   				if(sscanf(optarg, "%d", &opt->nominal_bitrate)
    					!= 1) {
	    			fprintf(stderr, _("Warning: nominal bitrate \"%s\" not recognised\n"), optarg);
		    		opt->nominal_bitrate = -1;
676
				}
677

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

Jack Moffitt's avatar
Jack Moffitt committed
815
816
}

817
static void add_tag(vorbis_comment *vc, oe_options *opt,char *name, char *value)
818
819
{
	char *utf8;
820
	if(utf8_encode(value, &utf8) >= 0)
821
822
823
824
825
826
827
828
	{
		if(name == NULL)
			vorbis_comment_add(vc, utf8);
		else
			vorbis_comment_add_tag(vc, name, utf8);
		free(utf8);
	}
	else
829
		fprintf(stderr, _("Couldn't convert comment to UTF-8, cannot add\n"));
830
831
}

832
static void build_comments(vorbis_comment *vc, oe_options *opt, int filenum, 
833
834
		char **artist, char **album, char **title, char **tracknum, 
        char **date, char **genre)
Jack Moffitt's avatar
Jack Moffitt committed
835
836
837
838
839
840
{
	int i;

	vorbis_comment_init(vc);

	for(i = 0; i < opt->comment_count; i++)
841
		add_tag(vc, opt, NULL, opt->comments[i]);
Jack Moffitt's avatar
Jack Moffitt committed
842
843
844
845
846
847

	if(opt->title_count)
	{
		if(filenum >= opt->title_count)
		{
			if(!opt->quiet)
848
				fprintf(stderr, _("WARNING: Insufficient titles specified, defaulting to final title.\n"));
Jack Moffitt's avatar
Jack Moffitt committed
849
850
851
852
853
			i = opt->title_count-1;
		}
		else
			i = filenum;

Michael Smith's avatar
   
Michael Smith committed
854
		*title = opt->title[i];
855
		add_tag(vc, opt, "title", opt->title[i]);
Jack Moffitt's avatar
Jack Moffitt committed
856
857
858
859
860
861
862
863
864
	}

	if(opt->artist_count)
	{
		if(filenum >= opt->artist_count)
			i = opt->artist_count-1;
		else
			i = filenum;
	
Michael Smith's avatar
   
Michael Smith committed
865
		*artist = opt->artist[i];
866
		add_tag(vc, opt, "artist", opt->artist[i]);
Jack Moffitt's avatar
Jack Moffitt committed
867
	}
Michael Smith's avatar
   
Michael Smith committed
868

869
870
871
872
873
874
875
876
877
878
879
    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
880
881
882
883
884
885
886
887
	if(opt->date_count)
	{
		if(filenum >= opt->date_count)
			i = opt->date_count-1;
		else
			i = filenum;
	
		*date = opt->dates[i];
888
		add_tag(vc, opt, "date", opt->dates[i]);
Michael Smith's avatar
   
Michael Smith committed
889
	}
Jack Moffitt's avatar
Jack Moffitt committed
890
891
892
893
894
895
896
897
898
899
	
	if(opt->album_count)
	{
		if(filenum >= opt->album_count)
		{
			i = opt->album_count-1;
		}
		else
			i = filenum;

Michael Smith's avatar
   
Michael Smith committed
900
		*album = opt->album[i];	
901
		add_tag(vc, opt, "album", opt->album[i]);
Jack Moffitt's avatar
Jack Moffitt committed
902
	}
Michael Smith's avatar
   
Michael Smith committed
903
904
905
906
907

	if(filenum < opt->track_count)
	{
		i = filenum;
		*tracknum = opt->tracknum[i];
908
		add_tag(vc, opt, "tracknumber", opt->tracknum[i]);
Michael Smith's avatar
   
Michael Smith committed
909
	}
Jack Moffitt's avatar
Jack Moffitt committed
910
911
}