]> 4ch.mooo.com Git - 16.git/blob - src/lib/dl/ext/vorbtool/oggenc.c
refresh wwww
[16.git] / src / lib / dl / ext / vorbtool / oggenc.c
1 /* OggEnc
2  *
3  * This program is distributed under the GNU General Public License, version 2.
4  * A copy of this license is included with this source.
5  *
6  * Copyright 2000-2005, Michael Smith <msmith@xiph.org>
7  *
8  * Portions from Vorbize, (c) Kenneth Arnold <kcarnold-xiph@arnoldnet.net>
9  * and libvorbis examples, (c) Monty <monty@xiph.org>
10  */
11
12 #ifdef HAVE_CONFIG_H
13 #include "config.h"
14 #endif
15
16 /* Watcom C unistd.h defines the vars? */
17 #define DONT_DEFINE_GETOPT_VARS
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <math.h>
22 #include "getopt.h"
23 #include <string.h>
24 #include <time.h>
25 #include <locale.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 #if defined WIN32 || defined _WIN32
30 #include <process.h>
31 #endif
32
33 #include "platform.h"
34 #include "encode.h"
35 #include "audio.h"
36 #include "utf8.h"
37 #include "i18n.h"
38
39 #define CHUNK 4096 /* We do reads, etc. in multiples of this */
40
41 struct option long_options[] = {
42     {"quiet",0,0,'Q'},
43     {"help",0,0,'h'},
44     {"skeleton",no_argument,NULL, 'k'},
45     {"comment",1,0,'c'},
46     {"artist",1,0,'a'},
47     {"album",1,0,'l'},
48     {"title",1,0,'t'},
49     {"genre",1,0,'G'},
50     {"names",1,0,'n'},
51     {"name-remove",1,0,'X'},
52     {"name-replace",1,0,'P'},
53     {"output",1,0,'o'},
54     {"version",0,0,'V'},
55     {"raw",0,0,'r'},
56     {"raw-bits",1,0,'B'},
57     {"raw-chan",1,0,'C'},
58     {"raw-rate",1,0,'R'},
59     {"raw-endianness",1,0, 0},
60     {"bitrate",1,0,'b'},
61     {"min-bitrate",1,0,'m'},
62     {"max-bitrate",1,0,'M'},
63     {"quality",1,0,'q'},
64     {"date",1,0,'d'},
65     {"tracknum",1,0,'N'},
66     {"serial",1,0,'s'},
67     {"managed", 0, 0, 0},
68     {"resample",1,0,0},
69     {"downmix", 0,0,0},
70     {"scale", 1, 0, 0},
71     {"advanced-encode-option", 1, 0, 0},
72     {"discard-comments", 0, 0, 0},
73     {"utf8", 0,0,0},
74     {"ignorelength", 0, 0, 0},
75     {"lyrics",1,0,'L'},
76     {"lyrics-language",1,0,'Y'},
77     {NULL,0,0,0}
78 };
79
80 static char *generate_name_string(char *format, char *remove_list,
81         char *replace_list, char *artist, char *title, char *album,
82         char *track, char *date, char *genre);
83 static void parse_options(int argc, char **argv, oe_options *opt);
84 static void build_comments(vorbis_comment *vc, oe_options *opt, int filenum,
85         char **artist,char **album, char **title, char **tracknum, char **date,
86         char **genre);
87 static void usage(void);
88
89 int main(int argc, char **argv)
90 {
91     /* Default values */
92     oe_options opt = {
93               NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0,
94               NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0,
95               1, 0, 0, 0,
96               16,44100,2, 0,
97               NULL, DEFAULT_NAMEFMT_REMOVE, DEFAULT_NAMEFMT_REPLACE,
98               NULL,
99               0, -1,-1,-1,
100               .3,-1,
101               0,0,0.f,
102               0, 0, 0, 0, 0};
103
104     int i;
105
106     char **infiles;
107     int numfiles;
108     int errors=0;
109
110     get_args_from_ucs16(&argc, &argv);
111
112     setlocale(LC_ALL, "");
113     bindtextdomain(PACKAGE, LOCALEDIR);
114     textdomain(PACKAGE);
115
116     parse_options(argc, argv, &opt);
117
118     if(optind >= argc)
119     {
120         fprintf(stderr, _("ERROR: No input files specified. Use -h for help.\n"));
121         return 1;
122     }
123     else
124     {
125         infiles = argv + optind;
126         numfiles = argc - optind;
127     }
128
129     /* Now, do some checking for illegal argument combinations */
130
131     for(i = 0; i < numfiles; i++)
132     {
133         if(!strcmp(infiles[i], "-") && numfiles > 1)
134         {
135             fprintf(stderr, _("ERROR: Multiple files specified when using stdin\n"));
136             exit(1);
137         }
138     }
139
140     if(numfiles > 1 && opt.outfile)
141     {
142         fprintf(stderr, _("ERROR: Multiple input files with specified output filename: suggest using -n\n"));
143         exit(1);
144     }
145
146     if(!opt.fixedserial)
147     {
148                 /* We randomly pick a serial number. This is then incremented for each
149                    file. The random seed includes the PID so two copies of oggenc that
150                    start in the same second will generate different serial numbers. */
151             /* Jonathan C: MS-DOS does not have getpid() nor can it run multiple instances concurrently */
152                 srand(time(NULL));
153         opt.serial = rand();
154     }
155     opt.skeleton_serial = opt.serial + numfiles;
156     opt.kate_serial = opt.skeleton_serial + numfiles;
157
158     for(i = 0; i < numfiles; i++)
159     {
160         /* Once through the loop for each file */
161
162         oe_enc_opt      enc_opts;
163         vorbis_comment  vc;
164         char *out_fn = NULL;
165         FILE *in, *out = NULL;
166         int foundformat = 0;
167         int closeout = 0, closein = 0;
168         char *artist=NULL, *album=NULL, *title=NULL, *track=NULL;
169         char *date=NULL, *genre=NULL;
170         char *lyrics=NULL, *lyrics_language=NULL;
171         input_format *format;
172         int resampled = 0;
173
174         /* Set various encoding defaults */
175
176         enc_opts.serialno = opt.serial++;
177         enc_opts.skeleton_serialno = opt.skeleton_serial++;
178         enc_opts.kate_serialno = opt.kate_serial++;
179         enc_opts.progress_update = update_statistics_full;
180         enc_opts.start_encode = start_encode_full;
181         enc_opts.end_encode = final_statistics;
182         enc_opts.error = encode_error;
183         enc_opts.comments = &vc;
184         enc_opts.copy_comments = opt.copy_comments;
185         enc_opts.with_skeleton = opt.with_skeleton;
186         enc_opts.ignorelength = opt.ignorelength;
187
188         /* OK, let's build the vorbis_comments structure */
189         build_comments(&vc, &opt, i, &artist, &album, &title, &track,
190                 &date, &genre);
191
192         if(opt.lyrics_count)
193         {
194             if(i >= opt.lyrics_count)
195             {
196                 lyrics = NULL;
197             }
198             else
199                 lyrics = opt.lyrics[i];
200         }
201
202         if(opt.lyrics_language_count)
203         {
204             if(i >= opt.lyrics_language_count)
205             {
206                 if(!opt.quiet)
207                     fprintf(stderr, _("WARNING: Insufficient lyrics languages specified, defaulting to final lyrics language.\n"));
208                 lyrics_language = opt.lyrics_language[opt.lyrics_language_count-1];
209             }
210             else
211                 lyrics_language = opt.lyrics_language[i];
212         }
213
214         if(!strcmp(infiles[i], "-"))
215         {
216             setbinmode(stdin);
217             in = stdin;
218             infiles[i] = NULL;
219             if(!opt.outfile)
220             {
221                 setbinmode(stdout);
222                 out = stdout;
223             }
224         }
225         else
226         {
227             in = oggenc_fopen(infiles[i], "rb", opt.isutf8);
228
229             if(in == NULL)
230             {
231                 fprintf(stderr, _("ERROR: Cannot open input file \"%s\": %s\n"), infiles[i], strerror(errno));
232                 free(out_fn);
233                 errors++;
234                 continue;
235             }
236
237             closein = 1;
238         }
239
240         /* Now, we need to select an input audio format - we do this before opening
241            the output file so that we don't end up with a 0-byte file if the input
242            file can't be read */
243
244         if(opt.rawmode)
245         {
246             input_format raw_format = {NULL, 0, raw_open, wav_close, "raw", 
247                 N_("RAW file reader")};
248
249             enc_opts.rate=opt.raw_samplerate;
250             enc_opts.channels=opt.raw_channels;
251             enc_opts.samplesize=opt.raw_samplesize;
252             enc_opts.endianness=opt.raw_endianness;
253
254             format = &raw_format;
255             format->open_func(in, &enc_opts, NULL, 0);
256             foundformat=1;
257         }
258         else
259         {
260             format = open_audio_file(in, &enc_opts);
261             if(format)
262             {
263                 if(!opt.quiet)
264                     fprintf(stderr, _("Opening with %s module: %s\n"),
265                             format->format, format->description);
266                 foundformat=1;
267             }
268
269         }
270
271         if(!foundformat)
272         {
273             fprintf(stderr, _("ERROR: Input file \"%s\" is not a supported format\n"), infiles[i]?infiles[i]:"(stdin)");
274             if(closein)
275                 fclose(in);
276             errors++;
277             continue;
278         }
279
280         /* Ok. We can read the file - so now open the output file */
281
282         if(opt.outfile && !strcmp(opt.outfile, "-"))
283         {
284             setbinmode(stdout);
285             out = stdout;
286         }
287         else if(out == NULL)
288         {
289             if(opt.outfile)
290             {
291                 out_fn = strdup(opt.outfile);
292             }
293             else if(opt.namefmt)
294             {
295                 out_fn = generate_name_string(opt.namefmt, opt.namefmt_remove, 
296                         opt.namefmt_replace, artist, title, album, track,date,
297                         genre);
298             }
299             /* This bit was widely derided in mid-2002, so it's been removed */
300             /*
301             else if(opt.title)
302             {
303                 out_fn = malloc(strlen(title) + 5);
304                 strcpy(out_fn, title);
305                 strcat(out_fn, ".ogg");
306             }
307             */
308             else if(infiles[i])
309             {
310                 /* Create a filename from existing filename, replacing extension with .ogg or .oga */
311                 char *start, *end;
312                 char *extension;
313
314                 /* if adding Skeleton or Kate, we're not Vorbis I anymore */
315                 extension = (opt.with_skeleton || opt.lyrics_count>0) ? ".oga" : ".ogg";
316
317                 start = infiles[i];
318                 end = strrchr(infiles[i], '.');
319                 end = end?end:(start + strlen(infiles[i])+1);
320
321                 /* NTS: Looking over this code carefully, it either appends ".ogg" or replaces the extension with ".ogg"
322                  *      which is perfectly fine for this MS-DOS port knowing that DOS 8.3 limitations would not like
323                  *      ".wav.ogg". If the user is running us under FreeDOS with the LFN driver enabled, or in a Windows 9x
324                  *      DOS Box, then the limit does not apply. ---Jonathan C */
325                 out_fn = malloc(end - start + 5);
326                 strncpy(out_fn, start, end-start);
327                 out_fn[end-start] = 0;
328                 strcat(out_fn, extension);
329             }
330             else {
331                 /* if adding skeleton or kate, we're not Vorbis I anymore */
332                 if (opt.with_skeleton || opt.lyrics_count>0)
333                     out_fn = strdup("default.oga");
334                 else
335                     out_fn = strdup("default.ogg");
336                 fprintf(stderr, _("WARNING: No filename, defaulting to \"%s\"\n"), out_fn);
337             }
338
339             /* Create any missing subdirectories, if possible */
340             if(create_directories(out_fn, opt.isutf8)) {
341                 if(closein)
342                     fclose(in);
343                 fprintf(stderr, _("ERROR: Could not create required subdirectories for output filename \"%s\"\n"), out_fn);
344                 errors++;
345                 free(out_fn);
346                 continue;
347             }
348
349             if(infiles[i] && !strcmp(infiles[i], out_fn)) {
350                 fprintf(stderr, _("ERROR: Input filename is the same as output filename \"%s\"\n"), out_fn);
351                 errors++;
352                 free(out_fn);
353                 continue;
354             }
355
356             out = oggenc_fopen(out_fn, "wb", opt.isutf8);
357             if(out == NULL)
358             {
359                 if(closein)
360                     fclose(in);
361                 fprintf(stderr, _("ERROR: Cannot open output file \"%s\": %s\n"), out_fn, strerror(errno));
362                 errors++;
363                 free(out_fn);
364                 continue;
365             }
366             closeout = 1;
367         }
368
369         /* Now, set the rest of the options */
370         enc_opts.out = out;
371         enc_opts.comments = &vc;
372 #ifdef _WIN32
373         if (opt.isutf8) {
374             enc_opts.filename = NULL;
375             enc_opts.infilename = NULL;
376             utf8_decode(out_fn, &enc_opts.filename);
377             utf8_decode(infiles[i], &enc_opts.infilename);
378         } else {
379             enc_opts.filename = strdup(out_fn);
380             enc_opts.infilename = strdup(infiles[i]);
381         }
382 #else
383         enc_opts.filename = out_fn;
384         enc_opts.infilename = infiles[i];
385 #endif
386         enc_opts.managed = opt.managed;
387         enc_opts.bitrate = opt.nominal_bitrate; 
388         enc_opts.min_bitrate = opt.min_bitrate;
389         enc_opts.max_bitrate = opt.max_bitrate;
390         enc_opts.quality = opt.quality;
391         enc_opts.quality_set = opt.quality_set;
392         enc_opts.advopt = opt.advopt;
393         enc_opts.advopt_count = opt.advopt_count;
394         enc_opts.lyrics = lyrics;
395         enc_opts.lyrics_language = lyrics_language;
396
397         if(opt.resamplefreq && opt.resamplefreq != enc_opts.rate) {
398             int fromrate = enc_opts.rate;
399
400             resampled = 1;
401             enc_opts.resamplefreq = opt.resamplefreq;
402             if(setup_resample(&enc_opts)) {
403                 errors++;
404                 goto clear_all;
405             }
406             else if(!opt.quiet)
407                 fprintf(stderr, _("Resampling input from %d Hz to %d Hz\n"), fromrate, opt.resamplefreq);
408         }
409
410         if(opt.downmix) {
411             if(enc_opts.channels == 2) {
412                 setup_downmix(&enc_opts);
413                 if(!opt.quiet)
414                     fprintf(stderr, _("Downmixing stereo to mono\n"));
415             }
416             else {
417                 fprintf(stderr, _("WARNING: Can't downmix except from stereo to mono\n"));
418                 opt.downmix = 0;
419             }
420         }
421
422         if(opt.scale > 0.f) {
423             setup_scaler(&enc_opts, opt.scale);
424             if(!opt.quiet)
425                 fprintf(stderr, _("Scaling input to %f\n"), opt.scale);
426         }
427
428
429         if(!enc_opts.total_samples_per_channel)
430             enc_opts.progress_update = update_statistics_notime;
431
432         if(opt.quiet)
433         {
434             enc_opts.start_encode = start_encode_null;
435             enc_opts.progress_update = update_statistics_null;
436             enc_opts.end_encode = final_statistics_null;
437         }
438
439         if(oe_encode(&enc_opts))
440             errors++;
441
442         if(opt.scale > 0)
443             clear_scaler(&enc_opts);
444         if(opt.downmix)
445             clear_downmix(&enc_opts);
446         if(resampled)
447             clear_resample(&enc_opts);
448 clear_all:
449
450         if(out_fn) free(out_fn);
451         if(opt.outfile) free(opt.outfile);
452 #ifdef _WIN32
453         if(enc_opts.filename) free(enc_opts.filename);
454         if(enc_opts.infilename) free(enc_opts.infilename);
455 #endif
456         vorbis_comment_clear(&vc);
457         format->close_func(enc_opts.readdata);
458
459         if(closein)
460             fclose(in);
461         if(closeout)
462             fclose(out);
463     }/* Finished this file, loop around to next... */
464
465     return errors?1:0;
466
467 }
468
469 static void usage(void)
470 {
471     fprintf(stdout, _("oggenc from %s %s"), PACKAGE, VERSION);
472     fprintf(stdout, _(" by the Xiph.Org Foundation (http://www.xiph.org/)\n\n"));
473     fprintf(stdout, _("Usage: oggenc [options] inputfile [...]\n\n"));
474     fprintf(stdout, _("OPTIONS:\n"
475         " General:\n"
476         " -Q, --quiet          Produce no output to stderr\n"
477         " -h, --help           Print this help text\n"
478         " -V, --version        Print the version number\n"));
479     fprintf(stdout, _(
480         " -k, --skeleton       Adds an Ogg Skeleton bitstream\n"
481         " -r, --raw            Raw mode. Input files are read directly as PCM data\n"
482         " -B, --raw-bits=n     Set bits/sample for raw input; default is 16\n"
483         " -C, --raw-chan=n     Set number of channels for raw input; default is 2\n"
484         " -R, --raw-rate=n     Set samples/sec for raw input; default is 44100\n"
485         " --raw-endianness     1 for bigendian, 0 for little (defaults to 0)\n"));
486     fprintf(stdout, _(
487         " -b, --bitrate        Choose a nominal bitrate to encode at. Attempt\n"
488         "                      to encode at a bitrate averaging this. Takes an\n"
489         "                      argument in kbps. By default, this produces a VBR\n"
490         "                      encoding, equivalent to using -q or --quality.\n"
491         "                      See the --managed option to use a managed bitrate\n"
492         "                      targetting the selected bitrate.\n"));
493     fprintf(stdout, _(
494         " --managed            Enable the bitrate management engine. This will allow\n"
495         "                      much greater control over the precise bitrate(s) used,\n"
496         "                      but encoding will be much slower. Don't use it unless\n"
497         "                      you have a strong need for detailed control over\n"
498         "                      bitrate, such as for streaming.\n"));
499     fprintf(stdout, _(
500         " -m, --min-bitrate    Specify a minimum bitrate (in kbps). Useful for\n"
501         "                      encoding for a fixed-size channel. Using this will\n"
502         "                      automatically enable managed bitrate mode (see\n"
503         "                      --managed).\n"
504         " -M, --max-bitrate    Specify a maximum bitrate in kbps. Useful for\n"
505         "                      streaming applications. Using this will automatically\n"
506         "                      enable managed bitrate mode (see --managed).\n"));
507     fprintf(stdout, _(
508         " --advanced-encode-option option=value\n"
509         "                      Sets an advanced encoder option to the given value.\n"
510         "                      The valid options (and their values) are documented\n"
511         "                      in the man page supplied with this program. They are\n"
512         "                      for advanced users only, and should be used with\n"
513         "                      caution.\n"));
514     fprintf(stdout, _(
515         " -q, --quality        Specify quality, between -1 (very low) and 10 (very\n"
516         "                      high), instead of specifying a particular bitrate.\n"
517         "                      This is the normal mode of operation.\n"
518         "                      Fractional qualities (e.g. 2.75) are permitted\n"
519         "                      The default quality level is 3.\n"));
520     fprintf(stdout, _(
521         " --resample n         Resample input data to sampling rate n (Hz)\n"
522         " --downmix            Downmix stereo to mono. Only allowed on stereo\n"
523         "                      input.\n"
524         " -s, --serial         Specify a serial number for the stream. If encoding\n"
525         "                      multiple files, this will be incremented for each\n"
526         "                      stream after the first.\n"));
527     fprintf(stdout, _(
528         " --discard-comments   Prevents comments in FLAC and Ogg FLAC files from\n"
529         "                      being copied to the output Ogg Vorbis file.\n"
530         " --ignorelength       Ignore the datalength in Wave headers. This allows\n"
531         "                      support for files > 4GB and STDIN data streams. \n"
532         "\n"));
533     fprintf(stdout, _(
534         " Naming:\n"
535         " -o, --output=fn      Write file to fn (only valid in single-file mode)\n"
536         " -n, --names=string   Produce filenames as this string, with %%a, %%t, %%l,\n"
537         "                      %%n, %%d replaced by artist, title, album, track number,\n"
538         "                      and date, respectively (see below for specifying these).\n"
539         "                      %%%% gives a literal %%.\n"));
540     fprintf(stdout, _(
541         " -X, --name-remove=s  Remove the specified characters from parameters to the\n"
542         "                      -n format string. Useful to ensure legal filenames.\n"
543         " -P, --name-replace=s Replace characters removed by --name-remove with the\n"
544         "                      characters specified. If this string is shorter than the\n"
545         "                      --name-remove list or is not specified, the extra\n"
546         "                      characters are just removed.\n"
547         "                      Default settings for the above two arguments are platform\n"
548         "                      specific.\n"));
549     fprintf(stdout, _(
550         " --utf8               Tells oggenc that the command line parameters date, title,\n"
551         "                      album, artist, genre, and comment are already in UTF-8.\n"
552         "                      On Windows, this switch applies to file names too.\n"
553         " -c, --comment=c      Add the given string as an extra comment. This may be\n"
554         "                      used multiple times. The argument should be in the\n"
555         "                      format \"tag=value\".\n"
556         " -d, --date           Date for track (usually date of performance)\n"));
557     fprintf(stdout, _(
558         " -N, --tracknum       Track number for this track\n"
559         " -t, --title          Title for this track\n"
560         " -l, --album          Name of album\n"
561         " -a, --artist         Name of artist\n"
562         " -G, --genre          Genre of track\n"));
563     fprintf(stdout, _(
564         " -L, --lyrics         Include lyrics from given file (.srt or .lrc format)\n"
565         " -Y, --lyrics-language  Sets the language for the lyrics\n"));
566     fprintf(stdout, _(
567         "                      If multiple input files are given, then multiple\n"
568         "                      instances of the previous eight arguments will be used,\n"
569         "                      in the order they are given. If fewer titles are\n"
570         "                      specified than files, OggEnc will print a warning, and\n"
571         "                      reuse the final one for the remaining files. If fewer\n"
572         "                      track numbers are given, the remaining files will be\n"
573         "                      unnumbered. If fewer lyrics are given, the remaining\n"
574         "                      files will not have lyrics added. For the others, the\n"
575         "                      final tag will be reused for all others without warning\n"
576         "                      (so you can specify a date once, for example, and have\n"
577         "                      it used for all the files)\n"
578         "\n"));
579     fprintf(stdout, _(
580         "INPUT FILES:\n"
581         " OggEnc input files must currently be 24, 16, or 8 bit PCM Wave, AIFF, or AIFF/C\n"
582         " files, 32 bit IEEE floating point Wave, and optionally FLAC or Ogg FLAC. Files\n"
583                 "  may be mono or stereo (or more channels) and any sample rate.\n"
584         " Alternatively, the --raw option may be used to use a raw PCM data file, which\n"
585         " must be 16 bit stereo little-endian PCM ('headerless Wave'), unless additional\n"
586         " parameters for raw mode are specified.\n"
587         " You can specify taking the file from stdin by using - as the input filename.\n"
588         " In this mode, output is to stdout unless an output filename is specified\n"
589         " with -o\n"
590         " Lyrics files may be in SubRip (.srt) or LRC (.lrc) format\n"
591         "\n"));
592 }
593
594 static int strncpy_filtered(char *dst, char *src, int len, char *remove_list,
595         char *replace_list)
596 {
597     char *hit, *drop_margin;
598     int used=0;
599
600     if(remove_list == NULL || *remove_list == 0)
601     {
602         strncpy(dst, src, len-1);
603         dst[len-1] = 0;
604         return strlen(dst);
605     }
606
607     drop_margin = remove_list + (replace_list == NULL?0:strlen(replace_list));
608
609     while(*src && used < len-1)
610     {
611         if((hit = strchr(remove_list, *src)) != NULL)
612         {
613             if(hit < drop_margin)
614             {
615                 *dst++ = replace_list[hit - remove_list];
616                 used++;
617             }
618         }
619         else
620         {
621             *dst++ = *src;
622             used++;
623         }
624         src++;
625     }
626     *dst = 0;
627
628     return used;
629 }
630
631 static char *generate_name_string(char *format, char *remove_list,
632         char *replace_list, char *artist, char *title, char *album, 
633         char *track, char *date, char *genre)
634 {
635     char *buffer;
636     char next;
637     char *string;
638     int used=0;
639     int buflen;
640
641     buffer = calloc(CHUNK+1,1);
642     buflen = CHUNK;
643
644     while(*format && used < buflen)
645     {
646         next = *format++;
647
648         if(next == '%')
649         {
650             switch(*format++)
651             {
652                 case '%':
653                     *(buffer+(used++)) = '%';
654                     break;
655                 case 'a':
656                     string = artist?artist:_("(none)");
657                     used += strncpy_filtered(buffer+used, string, buflen-used, 
658                             remove_list, replace_list);
659                     break;
660                 case 'd':
661                     string = date?date:_("(none)");
662                     used += strncpy_filtered(buffer+used, string, buflen-used,
663                             remove_list, replace_list);
664                     break;
665                 case 'g':
666                     string = genre?genre:_("(none)");
667                     used += strncpy_filtered(buffer+used, string, buflen-used,
668                             remove_list, replace_list);
669                     break;
670                 case 't':
671                     string = title?title:_("(none)");
672                     used += strncpy_filtered(buffer+used, string, buflen-used,
673                             remove_list, replace_list);
674                     break;
675                 case 'l':
676                     string = album?album:_("(none)");
677                     used += strncpy_filtered(buffer+used, string, buflen-used,
678                             remove_list, replace_list);
679                     break;
680                 case 'n':
681                     string = track?track:_("(none)");
682                     used += strncpy_filtered(buffer+used, string, buflen-used,
683                             remove_list, replace_list);
684                     break;
685                 default:
686                     fprintf(stderr, _("WARNING: Ignoring illegal escape character '%c' in name format\n"), *(format - 1));
687                     break;
688             }
689         }
690         else
691             *(buffer + (used++)) = next;
692     }
693
694     return buffer;
695 }
696
697 static void parse_options(int argc, char **argv, oe_options *opt)
698 {
699     int ret;
700     int option_index = 1;
701
702     while((ret = getopt_long(argc, argv, "a:b:B:c:C:d:G:hkl:L:m:M:n:N:o:P:q:QrR:s:t:VX:Y:",
703                     long_options, &option_index)) != -1)
704     {
705         switch(ret)
706         {
707             case 0:
708                 if(!strcmp(long_options[option_index].name, "skeleton")) {
709                     opt->with_skeleton = 1;
710                 }
711                 else if(!strcmp(long_options[option_index].name, "managed")) {
712                     if(!opt->managed){
713                         if(!opt->quiet)
714                             fprintf(stderr, 
715                                     _("Enabling bitrate management engine\n"));
716                         opt->managed = 1;
717                     }
718                 }
719                 else if(!strcmp(long_options[option_index].name, 
720                             "raw-endianness")) {
721                     if (opt->rawmode != 1)
722                     {
723                         opt->rawmode = 1;
724                         fprintf(stderr, _("WARNING: Raw endianness specified for non-raw data. Assuming input is raw.\n"));
725                     }
726                     if(sscanf(optarg, "%d", &opt->raw_endianness) != 1) {
727                         fprintf(stderr, _("WARNING: Couldn't read endianness argument \"%s\"\n"), optarg);
728                         opt->raw_endianness = 0;
729                     }
730                 }
731                 else if(!strcmp(long_options[option_index].name,
732                             "resample")) {
733                     if(sscanf(optarg, "%d", &opt->resamplefreq) != 1) {
734                         fprintf(stderr, _("WARNING: Couldn't read resampling frequency \"%s\"\n"), optarg);
735                         opt->resamplefreq = 0;
736                     }
737                     if(opt->resamplefreq < 100) /* User probably specified it
738                                                    in kHz accidently */
739                         fprintf(stderr, 
740                                 _("WARNING: Resample rate specified as %d Hz. Did you mean %d Hz?\n"), 
741                                 opt->resamplefreq, opt->resamplefreq*1000);
742                 }
743                 else if(!strcmp(long_options[option_index].name, "downmix")) {
744                     opt->downmix = 1;
745                 }
746                 else if(!strcmp(long_options[option_index].name, "scale")) {
747                     opt->scale = atof(optarg);
748                     if(sscanf(optarg, "%f", &opt->scale) != 1) {
749                         opt->scale = 0;
750                         fprintf(stderr, _("WARNING: Couldn't parse scaling factor \"%s\"\n"), 
751                                 optarg);
752                     }
753                 }
754                 else if(!strcmp(long_options[option_index].name, "utf8")) {
755                     opt->isutf8 = 1;
756                 }
757                 else if(!strcmp(long_options[option_index].name, "advanced-encode-option")) {
758                     char *arg = strdup(optarg);
759                     char *val;
760
761                     if(strcmp("disable_coupling",arg)){
762                       val = strchr(arg, '=');
763                       if(val == NULL) {
764                         fprintf(stderr, _("No value for advanced encoder option found\n"));
765                         continue;
766                       }
767                       else
768                         *val++=0;
769                     }else
770                       val=0;
771
772                     opt->advopt = realloc(opt->advopt, (++opt->advopt_count)*sizeof(adv_opt));
773                     opt->advopt[opt->advopt_count - 1].arg = arg;
774                     opt->advopt[opt->advopt_count - 1].val = val;
775                 }
776                 else if(!strcmp(long_options[option_index].name, "discard-comments")) {
777                     opt->copy_comments = 0;
778                 }
779                 else if(!strcmp(long_options[option_index].name, "ignorelength")) {
780                     opt->ignorelength = 1;
781                 }
782
783                 else {
784                     fprintf(stderr, _("Internal error parsing command line options\n"));
785                     exit(1);
786                 }
787
788                 break;
789             case 'a':
790                 opt->artist = realloc(opt->artist, (++opt->artist_count)*sizeof(char *));
791                 opt->artist[opt->artist_count - 1] = strdup(optarg);
792                 break;
793             case 'c':
794                 if(strchr(optarg, '=') == NULL) {
795                     fprintf(stderr, _("WARNING: Illegal comment used (\"%s\"), ignoring.\n"), optarg);
796                     break;
797                 }
798                 opt->comments = realloc(opt->comments, (++opt->comment_count)*sizeof(char *));
799                 opt->comments[opt->comment_count - 1] = strdup(optarg);
800                 break;
801             case 'd':
802                 opt->dates = realloc(opt->dates, (++opt->date_count)*sizeof(char *));
803                 opt->dates[opt->date_count - 1] = strdup(optarg);
804                 break;
805             case 'G':
806                 opt->genre = realloc(opt->genre, (++opt->genre_count)*sizeof(char *));
807                 opt->genre[opt->genre_count - 1] = strdup(optarg);
808                 break;
809             case 'h':
810                 usage();
811                 exit(0);
812                 break;
813             case 'l':
814                 opt->album = realloc(opt->album, (++opt->album_count)*sizeof(char *));
815                 opt->album[opt->album_count - 1] = strdup(optarg);
816                 break;
817             case 's':
818                 /* Would just use atoi(), but that doesn't deal with unsigned
819                  * ints. Damn */
820                 if(sscanf(optarg, "%u", &opt->serial) != 1)
821                     opt->serial = 0; /* Failed, so just set to zero */
822                                 else
823                                     opt->fixedserial = 1;
824                 break;
825             case 't':
826                 opt->title = realloc(opt->title, (++opt->title_count)*sizeof(char *));
827                 opt->title[opt->title_count - 1] = strdup(optarg);
828                 break;
829             case 'b':
830                    if(sscanf(optarg, "%d", &opt->nominal_bitrate)
831                         != 1) {
832                     fprintf(stderr, _("WARNING: nominal bitrate \"%s\" not recognised\n"), optarg);
833                     opt->nominal_bitrate = -1;
834                 }
835
836                 break;
837             case 'm':
838                 if(sscanf(optarg, "%d", &opt->min_bitrate)
839                         != 1) {
840                     fprintf(stderr, _("WARNING: minimum bitrate \"%s\" not recognised\n"), optarg);
841                     opt->min_bitrate = -1;
842                 }
843                 if(!opt->managed){
844                   if(!opt->quiet)
845                     fprintf(stderr, 
846                         _("Enabling bitrate management engine\n"));
847                   opt->managed = 1;
848                 }
849                 break;
850             case 'M':
851                 if(sscanf(optarg, "%d", &opt->max_bitrate)
852                         != 1) {
853                     fprintf(stderr, _("WARNING: maximum bitrate \"%s\" not recognised\n"), optarg);
854                     opt->max_bitrate = -1;
855                 }
856                 if(!opt->managed){
857                   if(!opt->quiet)
858                     fprintf(stderr, 
859                         _("Enabling bitrate management engine\n"));
860                   opt->managed = 1;
861                 }
862                 break;
863             case 'q':
864                 if(sscanf(optarg, "%f", &opt->quality) != 1) {
865                     fprintf(stderr, _("Quality option \"%s\" not recognised, ignoring\n"), optarg);
866                     break;
867                 }
868                 opt->quality_set=1;
869                 opt->quality *= 0.1;
870                 if(opt->quality > 1.0f)
871                 {
872                     opt->quality = 1.0f;
873                     fprintf(stderr, _("WARNING: quality setting too high, setting to maximum quality.\n"));
874                 }
875                 break;
876             case 'n':
877                 if(opt->namefmt)
878                 {
879                     fprintf(stderr, _("WARNING: Multiple name formats specified, using final\n"));
880                     free(opt->namefmt);
881                 }
882                 opt->namefmt = strdup(optarg);
883                 break;
884             case 'X':
885                 if(opt->namefmt_remove &&
886                         strcmp(opt->namefmt_remove, DEFAULT_NAMEFMT_REMOVE))
887                 {
888                     fprintf(stderr, _("WARNING: Multiple name format filters specified, using final\n"));
889                     free(opt->namefmt_remove);
890                 }
891                 opt->namefmt_remove = strdup(optarg);
892                 break;
893             case 'P':
894                 if(opt->namefmt_replace &&
895                         strcmp(opt->namefmt_replace, DEFAULT_NAMEFMT_REPLACE))
896                 {
897                     fprintf(stderr, _("WARNING: Multiple name format filter replacements specified, using final\n"));
898                     free(opt->namefmt_replace);
899                 }
900                 opt->namefmt_replace = strdup(optarg);
901                 break;
902             case 'o':
903                 if(opt->outfile)
904                 {
905                     fprintf(stderr, _("WARNING: Multiple output files specified, suggest using -n\n"));
906                     free(opt->outfile);
907                 }
908                 opt->outfile = strdup(optarg);
909                 break;
910             case 'Q':
911                 opt->quiet = 1;
912                 break;
913             case 'r':
914                 opt->rawmode = 1;
915                 break;
916             case 'V':
917                 fprintf(stdout, _("oggenc from %s %s\n"), PACKAGE, VERSION);
918                 exit(0);
919                 break;
920             case 'B':
921                 if (opt->rawmode != 1)
922                 {
923                     opt->rawmode = 1;
924                     fprintf(stderr, _("WARNING: Raw bits/sample specified for non-raw data. Assuming input is raw.\n"));
925                 }
926                 if(sscanf(optarg, "%u", &opt->raw_samplesize) != 1)
927                 {
928                     opt->raw_samplesize = 16; /* Failed, so just set to 16 */
929                     fprintf(stderr, _("WARNING: Invalid bits/sample specified, assuming 16.\n"));
930                 }
931                 if((opt->raw_samplesize != 8) && (opt->raw_samplesize != 16))
932                 {
933                     fprintf(stderr, _("WARNING: Invalid bits/sample specified, assuming 16.\n"));
934                 }
935                 break;
936             case 'C':
937                 if (opt->rawmode != 1)
938                 {
939                     opt->rawmode = 1;
940                     fprintf(stderr, _("WARNING: Raw channel count specified for non-raw data. Assuming input is raw.\n"));
941                 }
942                 if(sscanf(optarg, "%u", &opt->raw_channels) != 1)
943                 {
944                     opt->raw_channels = 2; /* Failed, so just set to 2 */
945                     fprintf(stderr, _("WARNING: Invalid channel count specified, assuming 2.\n"));
946                 }
947                 break;
948             case 'N':
949                 opt->tracknum = realloc(opt->tracknum, (++opt->track_count)*sizeof(char *));
950                 opt->tracknum[opt->track_count - 1] = strdup(optarg);
951                 break;
952             case 'R':
953                 if (opt->rawmode != 1)
954                 {
955                     opt->rawmode = 1;
956                     fprintf(stderr, _("WARNING: Raw sample rate specified for non-raw data. Assuming input is raw.\n"));
957                 }
958                 if(sscanf(optarg, "%u", &opt->raw_samplerate) != 1)
959                 {
960                     opt->raw_samplerate = 44100; /* Failed, so just set to 44100 */
961                     fprintf(stderr, _("WARNING: Invalid sample rate specified, assuming 44100.\n"));
962                 }
963                 break;
964             case 'k':
965                 opt->with_skeleton = 1;
966                 break;
967             case 'L':
968 #ifdef HAVE_KATE
969                 opt->lyrics = realloc(opt->lyrics, (++opt->lyrics_count)*sizeof(char *));
970                 opt->lyrics[opt->lyrics_count - 1] = strdup(optarg);
971                 opt->with_skeleton = 1;
972 #else
973                 fprintf(stderr, _("WARNING: Kate support not compiled in; lyrics will not be included.\n"));
974 #endif
975                 break;
976             case 'Y':
977 #ifdef HAVE_KATE
978                 opt->lyrics_language = realloc(opt->lyrics_language, (++opt->lyrics_language_count)*sizeof(char *));
979                 opt->lyrics_language[opt->lyrics_language_count - 1] = strdup(optarg);
980                 if (strlen(opt->lyrics_language[opt->lyrics_language_count - 1]) > 15) {
981                   fprintf(stderr, _("WARNING: language can not be longer than 15 characters; truncated.\n"));
982                   opt->lyrics_language[opt->lyrics_language_count - 1][15] = 0;
983                 }
984 #else
985                 fprintf(stderr, _("WARNING: Kate support not compiled in; lyrics will not be included.\n"));
986 #endif
987                 break;
988             case '?':
989                 fprintf(stderr, _("WARNING: Unknown option specified, ignoring->\n"));
990                 break;
991             default:
992                 usage();
993                 exit(0);
994         }
995     }
996
997 }
998
999 static void add_tag(vorbis_comment *vc, oe_options *opt,char *name, char *value)
1000 {
1001     if (opt->isutf8) {
1002         fprintf(stderr,"WARNING: This build does not accept UTF-8 tags\n");
1003     }
1004     else {
1005         if(name == NULL)
1006             vorbis_comment_add(vc, value);
1007         else
1008             vorbis_comment_add_tag(vc, name, value);
1009     }
1010 }
1011
1012 static void build_comments(vorbis_comment *vc, oe_options *opt, int filenum, 
1013         char **artist, char **album, char **title, char **tracknum, 
1014         char **date, char **genre)
1015 {
1016     int i;
1017
1018     vorbis_comment_init(vc);
1019
1020     for(i = 0; i < opt->comment_count; i++)
1021         add_tag(vc, opt, NULL, opt->comments[i]);
1022
1023     if(opt->title_count)
1024     {
1025         if(filenum >= opt->title_count)
1026         {
1027             if(!opt->quiet)
1028                 fprintf(stderr, _("WARNING: Insufficient titles specified, defaulting to final title.\n"));
1029             i = opt->title_count-1;
1030         }
1031         else
1032             i = filenum;
1033
1034         *title = opt->title[i];
1035         add_tag(vc, opt, "title", opt->title[i]);
1036     }
1037
1038     if(opt->artist_count)
1039     {
1040         if(filenum >= opt->artist_count)
1041             i = opt->artist_count-1;
1042         else
1043             i = filenum;
1044
1045         *artist = opt->artist[i];
1046         add_tag(vc, opt, "artist", opt->artist[i]);
1047     }
1048
1049     if(opt->genre_count)
1050     {
1051         if(filenum >= opt->genre_count)
1052             i = opt->genre_count-1;
1053         else
1054             i = filenum;
1055
1056         *genre = opt->genre[i];
1057         add_tag(vc, opt, "genre", opt->genre[i]);
1058     }
1059
1060     if(opt->date_count)
1061     {
1062         if(filenum >= opt->date_count)
1063             i = opt->date_count-1;
1064         else
1065             i = filenum;
1066
1067         *date = opt->dates[i];
1068         add_tag(vc, opt, "date", opt->dates[i]);
1069     }
1070
1071     if(opt->album_count)
1072     {
1073         if(filenum >= opt->album_count)
1074         {
1075             i = opt->album_count-1;
1076         }
1077         else
1078             i = filenum;
1079
1080         *album = opt->album[i];
1081         add_tag(vc, opt, "album", opt->album[i]);
1082     }
1083
1084     if(filenum < opt->track_count)
1085     {
1086         i = filenum;
1087         *tracknum = opt->tracknum[i];
1088         add_tag(vc, opt, "tracknumber", opt->tracknum[i]);
1089     }
1090 }