1 /* OggDec
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 2002, Michael Smith <msmith@xiph.org>
7 *
8 */
9
10 #ifdef HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <getopt.h>
17 #include <errno.h>
18 #include <string.h>
19 #include <locale.h>
20
21 #if defined(_WIN32) || defined(__EMX__) || defined(__WATCOMC__)
getTheWebAssemblyTarget32()22 #include <fcntl.h>
23 #include <io.h>
24 #endif
25
26 #include <vorbis/vorbisfile.h>
27
28 #include "i18n.h"
29
30 struct option long_options[] = {
31 {"quiet", no_argument, NULL, 'Q'},
32 {"help", no_argument, NULL, 'h'},
33 {"version", no_argument, NULL, 'V'},
34 {"bits", required_argument, NULL, 'b'},
35 {"endianness", required_argument, NULL, 'e'},
36 {"raw", no_argument, NULL, 'R'},
37 {"sign", required_argument, NULL, 's'},
38 {"output", required_argument, NULL, 'o'},
39 {NULL,0,NULL,0}
40 };
41
42 static int quiet = 0;
43 static int bits = 16;
44 static int endian = 0;
45 static int raw = 0;
46 static int sign = 1;
47 unsigned char headbuf[44]; /* The whole buffer */
48 char *outfilename = NULL;
49
50 static void version (void) {
51 fprintf(stderr, _("oggdec from %s %s\n"), PACKAGE, VERSION);
52 }
53
54 static void usage(void)
55 {
56 version ();
57 fprintf(stdout, _(" by the Xiph.Org Foundation (https://www.xiph.org/)\n"));
58 fprintf(stdout, _(" using decoder %s.\n\n"), vorbis_version_string());
59 fprintf(stdout, _("Usage: oggdec [options] file1.ogg [file2.ogg ... fileN.ogg]\n\n"));
60 fprintf(stdout, _("Supported options:\n"));
61 fprintf(stdout, _(" --quiet, -Q Quiet mode. No console output.\n"));
62 fprintf(stdout, _(" --help, -h Produce this help message.\n"));
63 fprintf(stdout, _(" --version, -V Print out version number.\n"));
64 fprintf(stdout, _(" --bits, -b Bit depth for output (8 and 16 supported)\n"));
65 fprintf(stdout, _(" --endianness, -e Output endianness for 16-bit output; 0 for\n"
66 " little endian (default), 1 for big endian.\n"));
67 fprintf(stdout, _(" --sign, -s Sign for output PCM; 0 for unsigned, 1 for\n"
68 " signed (default 1).\n"));
69 fprintf(stdout, _(" --raw, -R Raw (headerless) output.\n"));
70 fprintf(stdout, _(" --output, -o Output to given filename. May only be used\n"
71 " if there is only one input file, except in\n"
72 " raw mode.\n"));
73 }
74
75 static void parse_options(int argc, char **argv)
76 {
77 int option_index = 1;
78 int ret;
79
80 while((ret = getopt_long(argc, argv, "QhVb:e:Rs:o:",
81 long_options, &option_index)) != -1)
82 {
83 switch(ret)
84 {
85 case 'Q':
86 quiet = 1;
87 break;
88 case 'h':
89 usage();
90 exit(0);
91 break;
92 case 'V':
93 version();
94 exit(0);
95 break;
96 case 's':
97 sign = atoi(optarg);
98 break;
99 case 'b':
100 bits = atoi(optarg);
101 if(bits <= 8)
102 bits = 8;
103 else
104 bits = 16;
105 break;
106 case 'e':
107 endian = atoi(optarg);
108 break;
109 case 'o':
110 outfilename = strdup(optarg);
111 break;
112 case 'R':
113 raw = 1;
114 break;
115 default:
116 fprintf(stderr, _("Internal error: Unrecognised argument\n"));
117 break;
118 }
119 }
120 }
121
122 #define WRITE_U32(buf, x) *(buf) = (unsigned char)((x)&0xff);\
123 *((buf)+1) = (unsigned char)(((x)>>8)&0xff);\
124 *((buf)+2) = (unsigned char)(((x)>>16)&0xff);\
125 *((buf)+3) = (unsigned char)(((x)>>24)&0xff);
126
127 #define WRITE_U16(buf, x) *(buf) = (unsigned char)((x)&0xff);\
128 *((buf)+1) = (unsigned char)(((x)>>8)&0xff);
129
130 /* Some of this based on ao/src/ao_wav.c */
131 int write_prelim_header(OggVorbis_File *vf, FILE *out, ogg_int64_t knownlength) {
132 unsigned int size = 0x7fffffff;
133 int channels = ov_info(vf,0)->channels;
134 int samplerate = ov_info(vf,0)->rate;
135 int bytespersec = channels*samplerate*bits/8;
136 int align = channels*bits/8;
137 int samplesize = bits;
138
139 if(knownlength && knownlength*bits/8*channels < size)
140 size = (unsigned int)(knownlength*bits/8*channels+44) ;
141
142 memcpy(headbuf, "RIFF", 4);
143 WRITE_U32(headbuf+4, size-8);
144 memcpy(headbuf+8, "WAVE", 4);
145 memcpy(headbuf+12, "fmt ", 4);
146 WRITE_U32(headbuf+16, 16);
147 WRITE_U16(headbuf+20, 1); /* format */
148 WRITE_U16(headbuf+22, channels);
149 WRITE_U32(headbuf+24, samplerate);
150 WRITE_U32(headbuf+28, bytespersec);
151 WRITE_U16(headbuf+32, align);
152 WRITE_U16(headbuf+34, samplesize);
153 memcpy(headbuf+36, "data", 4);
154 WRITE_U32(headbuf+40, size - 44);
155
156 if(fwrite(headbuf, 1, 44, out) != 44) {
157 fprintf(stderr, _("ERROR: Failed to write Wave header: %s\n"), strerror(errno));
158 return 1;
159 }
160
161 return 0;
162 }
163
164 int rewrite_header(FILE *out, unsigned int written)
165 {
166 unsigned int length = written;
167
168 length += 44;
169
170 WRITE_U32(headbuf+4, length-8);
171 WRITE_U32(headbuf+40, length-44);
172 if(fseek(out, 0, SEEK_SET) != 0)
173 return 1;
174
175 if(fwrite(headbuf, 1, 44, out) != 44) {
176 fprintf(stderr, _("ERROR: Failed to write Wave header: %s\n"), strerror(errno));
177 return 1;
178 }
179 return 0;
180 }
181
182 static FILE *open_input(char *infile)
183 {
184 FILE *in;
185
186 if(!infile) {
187 #ifdef __BORLANDC__
188 setmode(fileno(stdin), O_BINARY);
189 #elif _WIN32
190 _setmode(_fileno(stdin), _O_BINARY);
191 #endif
192 in = stdin;
193 }
194 else {
195 in = fopen(infile, "rb");
196 if(!in) {
197 fprintf(stderr, _("ERROR: Failed to open input file: %s\n"), strerror(errno));
198 return NULL;
199 }
200 }
201
202 return in;
203 }
204
205 static FILE *open_output(char *outfile)
206 {
207 FILE *out;
208 if(!outfile) {
209 #ifdef __BORLANDC__
210 setmode(fileno(stdout), O_BINARY);
211 #elif _WIN32
212 _setmode(_fileno(stdout), _O_BINARY);
213 #endif
214 out = stdout;
215 }
216 else {
217 out = fopen(outfile, "wb");
218 if(!out) {
219 fprintf(stderr, _("ERROR: Failed to open output file: %s\n"), strerror(errno));
220 return NULL;
221 }
222 }
223
224 return out;
225 }
226
227 static void
228 permute_channels(char *in, char *out, int len, int channels, int bytespersample)
229 {
230 int permute[6][6] = {{0}, {0,1}, {0,2,1}, {0,1,2,3}, {0,1,2,3,4},
231 {0,2,1,5,3,4}};
232 int i,j,k;
233 int samples = len/channels/bytespersample;
234
235 /* Can't handle, don't try */
236 if (channels > 6)
237 return;
238
239 for (i=0; i < samples; i++) {
240 for (j=0; j < channels; j++) {
241 for (k=0; k < bytespersample; k++) {
242 out[i*bytespersample*channels +
243 bytespersample*permute[channels-1][j] + k] =
244 in[i*bytespersample*channels + bytespersample*j + k];
245 }
246 }
247 }
248 }
249
250 static int decode_file(FILE *in, FILE *out, char *infile, char *outfile)
251 {
252 OggVorbis_File vf;
253 int bs = 0;
254 char buf[8192], outbuf[8192];
255 char *p_outbuf;
256 int buflen = 8192;
257 unsigned int written = 0;
258 int ret;
259 ogg_int64_t length = 0;
260 ogg_int64_t done = 0;
261 int size = 0;
262 int seekable = 0;
263 int percent = 0;
264 int channels;
265 int samplerate;
266
267 if (ov_open_callbacks(in, &vf, NULL, 0, OV_CALLBACKS_DEFAULT) < 0) {
268 fprintf(stderr, _("ERROR: Failed to open input as Vorbis\n"));
269 fclose(in);
270 return 1;
271 }
272
273 channels = ov_info(&vf,0)->channels;
274 samplerate = ov_info(&vf,0)->rate;
275
276 if(ov_seekable(&vf)) {
277 int link;
278 int chainsallowed = 0;
279 for(link = 0; link < ov_streams(&vf); link++) {
280 if(ov_info(&vf, link)->channels == channels &&
281 ov_info(&vf, link)->rate == samplerate)
282 {
283 chainsallowed = 1;
284 }
285 }
286
287 seekable = 1;
288 if(chainsallowed)
289 length = ov_pcm_total(&vf, -1);
290 else
291 length = ov_pcm_total(&vf, 0);
292 size = bits/8 * channels;
293 if(!quiet)
294 fprintf(stderr, _("Decoding \"%s\" to \"%s\"\n"),
295 infile?infile:_("standard input"),
296 outfile?outfile:_("standard output"));
297 }
298
299 if(!raw) {
300 if(write_prelim_header(&vf, out, length)) {
301 ov_clear(&vf);
302 return 1;
303 }
304 }
305
306 while((ret = ov_read(&vf, buf, buflen, endian, bits/8, sign, &bs)) != 0) {
307 if(bs != 0) {
308 vorbis_info *vi = ov_info(&vf, -1);
309 if(channels != vi->channels || samplerate != vi->rate) {
310 fprintf(stderr, _("Logical bitstreams with changing parameters are not supported\n"));
311 break;
312 }
313 }
314
315 if(ret == OV_HOLE) {
316 if(!quiet) {
317 fprintf(stderr, _("WARNING: hole in data (%d)\n"), ret);
318 }
319 continue;
320 }
321 else if(ret < 0) {
322 if(!quiet) {
323 fprintf(stderr, _("=== Vorbis library reported a stream error.\n"));
324 }
325 ov_clear(&vf);
326 return 1;
327 }
328
329 if(channels > 2 && !raw) {
330 /* Then permute! */
331 permute_channels(buf, outbuf, ret, channels, bits/8);
332 p_outbuf = outbuf;
333 }
334 else {
335 p_outbuf = buf;
336 }
337
338 if(fwrite(p_outbuf, 1, ret, out) != ret) {
339 fprintf(stderr, _("Error writing to file: %s\n"), strerror(errno));
340 ov_clear(&vf);
341 return 1;
342 }
343
344 written += ret;
345 if(!quiet && seekable) {
346 done += ret/size;
347 if((double)done/(double)length * 200. > (double)percent) {
348 percent = (int)((double)done/(double)length *200);
349 fprintf(stderr, "\r\t[%5.1f%%]", (double)percent/2.);
350 }
351 }
352 }
353
354 if(seekable && !quiet)
355 fprintf(stderr, "\n");
356
357 if(!raw)
358 rewrite_header(out, written); /* We don't care if it fails, too late */
359
360 ov_clear(&vf);
361
362 return 0;
363 }
364
365 int main(int argc, char **argv)
366 {
367 int i;
368
369 setlocale(LC_ALL, "");
370 bindtextdomain(PACKAGE, LOCALEDIR);
371 textdomain(PACKAGE);
372
373 if(argc == 1) {
374 usage();
375 return 1;
376 }
377
378 parse_options(argc,argv);
379
380 if(!quiet)
381 version();
382
383 if(optind >= argc) {
384 fprintf(stderr, _("ERROR: No input files specified. Use -h for help\n"));
385 return 1;
386 }
387
388 if(argc - optind > 1 && outfilename && !raw) {
389 fprintf(stderr, _("ERROR: Can only specify one input file if output filename is specified\n"));
390 return 1;
391 }
392
393 if(outfilename && raw) {
394 FILE *infile, *outfile;
395 char *infilename;
396
397 if(!strcmp(outfilename, "-")) {
398 free(outfilename);
399 outfilename = NULL;
400 }
401 outfile = open_output(outfilename);
402
403 if(!outfile)
404 return 1;
405
406 for(i=optind; i < argc; i++) {
407 if(!strcmp(argv[i], "-")) {
408 infilename = NULL;
409 infile = open_input(NULL);
410 }
411 else {
412 infilename = argv[i];
413 infile = open_input(argv[i]);
414 }
415
416 if(!infile) {
417 fclose(outfile);
418 free(outfilename);
419 return 1;
420 }
421 if(decode_file(infile, outfile, infilename, outfilename)) {
422 fclose(outfile);
423 return 1;
424 }
425
426 }
427
428 fclose(outfile);
429 }
430 else {
431 for(i=optind; i < argc; i++) {
432 char *in, *out;
433 FILE *infile, *outfile;
434
435 if(!strcmp(argv[i], "-"))
436 in = NULL;
437 else
438 in = argv[i];
439
440 if(outfilename) {
441 if(!strcmp(outfilename, "-"))
442 out = NULL;
443 else
444 out = outfilename;
445 }
446 else {
447 if(!strcmp(argv[i], "-")) {
448 out = NULL;
449 }
450 else {
451 char *end = strrchr(argv[i], '.');
452 end = end?end:(argv[i] + strlen(argv[i]) + 1);
453
454 out = malloc(strlen(argv[i]) + 10);
455 strncpy(out, argv[i], end-argv[i]);
456 out[end-argv[i]] = 0;
457 if(raw)
458 strcat(out, ".raw");
459 else
460 strcat(out, ".wav");
461 }
462 }
463
464 infile = open_input(in);
465 if(!infile) {
466 if(outfilename)
467 free(outfilename);
468 return 1;
469 }
470 outfile = open_output(out);
471 if(!outfile) {
472 fclose(infile);
473 return 1;
474 }
475
476 if(decode_file(infile, outfile, in, out)) {
477 fclose(outfile);
478 return 1;
479 }
480
481 if(!outfilename && out)
482 free(out);
483
484 fclose(outfile);
485 }
486 }
487
488 if(outfilename)
489 free(outfilename);
490
491 return 0;
492 }
493