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