Add version info to celtenc/celtdec output as suggested on the
[opus.git] / tools / celtdec.c
1 /* Copyright (C) 2008 Jean-Marc Valin, CSIRO
2    File: celtdec.c
3
4    Redistribution and use in source and binary forms, with or without
5    modification, are permitted provided that the following conditions
6    are met:
7    
8    - Redistributions of source code must retain the above copyright
9    notice, this list of conditions and the following disclaimer.
10    
11    - Redistributions in binary form must reproduce the above copyright
12    notice, this list of conditions and the following disclaimer in the
13    documentation and/or other materials provided with the distribution.
14    
15    - Neither the name of the Xiph.org Foundation nor the names of its
16    contributors may be used to endorse or promote products derived from
17    this software without specific prior written permission.
18    
19    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
23    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include <stdio.h>
37 #if !defined WIN32 && !defined _WIN32
38 #include <unistd.h>
39 #endif
40 #ifdef HAVE_GETOPT_H
41 #include <getopt.h>
42 #endif
43 #ifndef HAVE_GETOPT_LONG
44 #include "getopt_win.h"
45 #endif
46 #include <stdlib.h>
47 #include <string.h>
48
49 #include <celt.h>
50 #include <ogg/ogg.h>
51
52 #if defined WIN32 || defined _WIN32
53 #include "wave_out.h"
54 /* We need the following two to set stdout to binary */
55 #include <io.h>
56 #include <fcntl.h>
57 #endif
58 #include <math.h>
59
60 #ifdef __MINGW32__
61 #include "wave_out.c"
62 #endif
63
64 #ifdef HAVE_SYS_SOUNDCARD_H
65 #include <sys/soundcard.h>
66 #include <sys/types.h>
67 #include <sys/stat.h>
68 #include <fcntl.h>
69 #include <sys/ioctl.h>
70
71 #elif defined HAVE_SYS_AUDIOIO_H
72 #include <sys/types.h>
73 #include <fcntl.h>
74 #include <sys/ioctl.h>
75 #include <sys/audioio.h>
76 #ifndef AUDIO_ENCODING_SLINEAR
77 #define AUDIO_ENCODING_SLINEAR AUDIO_ENCODING_LINEAR /* Solaris */
78 #endif
79
80 #endif
81
82 #include <string.h>
83 #include "wav_io.h"
84 #include <celt_header.h>
85
86 #define MAX_FRAME_SIZE 2000
87
88 #define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
89                            ((buf[base+2]<<16)&0xff0000)| \
90                            ((buf[base+1]<<8)&0xff00)| \
91                             (buf[base]&0xff))
92
93 static void print_comments(char *comments, int length)
94 {
95    char *c=comments;
96    int len, i, nb_fields;
97    char *end;
98    
99    if (length<8)
100    {
101       fprintf (stderr, "Invalid/corrupted comments\n");
102       return;
103    }
104    end = c+length;
105    len=readint(c, 0);
106    c+=4;
107    if (len < 0 || c+len>end)
108    {
109       fprintf (stderr, "Invalid/corrupted comments\n");
110       return;
111    }
112    fwrite(c, 1, len, stderr);
113    c+=len;
114    fprintf (stderr, "\n");
115    if (c+4>end)
116    {
117       fprintf (stderr, "Invalid/corrupted comments\n");
118       return;
119    }
120    nb_fields=readint(c, 0);
121    c+=4;
122    for (i=0;i<nb_fields;i++)
123    {
124       if (c+4>end)
125       {
126          fprintf (stderr, "Invalid/corrupted comments\n");
127          return;
128       }
129       len=readint(c, 0);
130       c+=4;
131       if (len < 0 || c+len>end)
132       {
133          fprintf (stderr, "Invalid/corrupted comments\n");
134          return;
135       }
136       fwrite(c, 1, len, stderr);
137       c+=len;
138       fprintf (stderr, "\n");
139    }
140 }
141
142 FILE *out_file_open(char *outFile, int rate, int *channels)
143 {
144    FILE *fout=NULL;
145    /*Open output file*/
146    if (strlen(outFile)==0)
147    {
148 #if defined HAVE_SYS_SOUNDCARD_H
149       int audio_fd, format, stereo;
150       audio_fd=open("/dev/dsp", O_WRONLY);
151       if (audio_fd<0)
152       {
153          perror("Cannot open /dev/dsp");
154          exit(1);         
155       }
156
157       format=AFMT_S16_NE;
158       if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format)==-1)
159       {
160          perror("SNDCTL_DSP_SETFMT");
161          close(audio_fd);
162          exit(1);
163       }
164
165       stereo=0;
166       if (*channels==2)
167          stereo=1;
168       if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo)==-1)
169       {
170          perror("SNDCTL_DSP_STEREO");
171          close(audio_fd);
172          exit(1);
173       }
174       if (stereo!=0)
175       {
176          if (*channels==1)
177             fprintf (stderr, "Cannot set mono mode, will decode in stereo\n");
178          *channels=2;
179       }
180
181       if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate)==-1)
182       {
183          perror("SNDCTL_DSP_SPEED");
184          close(audio_fd);
185          exit(1);
186       }
187       fout = fdopen(audio_fd, "w");
188 #elif defined HAVE_SYS_AUDIOIO_H
189       audio_info_t info;
190       int audio_fd;
191       
192       audio_fd = open("/dev/audio", O_WRONLY);
193       if (audio_fd<0)
194       {
195          perror("Cannot open /dev/audio");
196          exit(1);
197       }
198
199       AUDIO_INITINFO(&info);
200 #ifdef AUMODE_PLAY    /* NetBSD/OpenBSD */
201       info.mode = AUMODE_PLAY;
202 #endif
203       info.play.encoding = AUDIO_ENCODING_SLINEAR;
204       info.play.precision = 16;
205       info.play.sample_rate = rate;
206       info.play.channels = *channels;
207       
208       if (ioctl(audio_fd, AUDIO_SETINFO, &info) < 0)
209       {
210          perror ("AUDIO_SETINFO");
211          exit(1);
212       }
213       fout = fdopen(audio_fd, "w");
214 #elif defined WIN32 || defined _WIN32
215       {
216          unsigned int celt_channels = *channels;
217          if (Set_WIN_Params (INVALID_FILEDESC, rate, SAMPLE_SIZE, celt_channels))
218          {
219             fprintf (stderr, "Can't access %s\n", "WAVE OUT");
220             exit(1);
221          }
222       }
223 #else
224       fprintf (stderr, "No soundcard support\n");
225       exit(1);
226 #endif
227    } else {
228       if (strcmp(outFile,"-")==0)
229       {
230 #if defined WIN32 || defined _WIN32
231          _setmode(_fileno(stdout), _O_BINARY);
232 #endif
233          fout=stdout;
234       }
235       else 
236       {
237          fout = fopen(outFile, "wb");
238          if (!fout)
239          {
240             perror(outFile);
241             exit(1);
242          }
243          if (strcmp(outFile+strlen(outFile)-4,".wav")==0 || strcmp(outFile+strlen(outFile)-4,".WAV")==0)
244             write_wav_header(fout, rate, *channels, 0, 0);
245       }
246    }
247    return fout;
248 }
249
250 void usage(void)
251 {
252    printf ("Usage: celtdec [options] input_file.oga [output_file]\n");
253    printf ("\n");
254    printf ("Decodes a CELT file and produce a WAV file or raw file\n");
255    printf ("\n");
256    printf ("input_file can be:\n");
257    printf ("  filename.oga         regular CELT file\n");
258    printf ("  -                    stdin\n");
259    printf ("\n");  
260    printf ("output_file can be:\n");
261    printf ("  filename.wav         Wav file\n");
262    printf ("  filename.*           Raw PCM file (any extension other that .wav)\n");
263    printf ("  -                    stdout\n");
264    printf ("  (nothing)            Will be played to soundcard\n");
265    printf ("\n");  
266    printf ("Options:\n");
267    printf (" --mono                Force decoding in mono\n");
268    printf (" --stereo              Force decoding in stereo\n");
269    printf (" --rate n              Force decoding at sampling rate n Hz\n");
270    printf (" --packet-loss n       Simulate n %% random packet loss\n");
271    printf (" -V                    Verbose mode (show bit-rate)\n"); 
272    printf (" -h, --help            This help\n");
273    printf (" -v, --version         Version information\n");
274    printf ("\n");
275 }
276
277 void version(void)
278 {
279    printf ("celtenc (CELT %s encoder)\n",CELT_VERSION);
280    printf ("Copyright (C) 2008 Jean-Marc Valin\n");
281 }
282
283 void version_short(void)
284 {
285    printf ("celtenc (CELT %s encoder)\n",CELT_VERSION);
286    printf ("Copyright (C) 2008 Jean-Marc Valin\n");
287 }
288
289 static CELTDecoder *process_header(ogg_packet *op, celt_int32_t enh_enabled, celt_int32_t *frame_size, int *granule_frame_size, celt_int32_t *rate, int *nframes, int forceMode, int *channels, int *overlap, int *extra_headers, int quiet, CELTMode **mode)
290 {
291    CELTDecoder *st;
292    CELTHeader header;
293    int bitstream;
294       
295    celt_header_from_packet(op->packet, op->bytes, &header);
296
297    if (header.nb_channels>2 || header.nb_channels<1)
298    {
299       fprintf (stderr, "Unsupported number of channels: %d\n", header.nb_channels);
300       return NULL;
301    }
302    *mode = celt_mode_create(header.sample_rate, header.nb_channels, header.frame_size, NULL);
303    if (*mode == NULL)
304    {
305       fprintf (stderr, "Mode initialization failed.\n");
306       return NULL;
307    }
308
309    
310    celt_mode_info(*mode, CELT_GET_BITSTREAM_VERSION, &bitstream);
311    if (bitstream!=header.version_id)
312      fprintf(stderr, "WARNING: Input was encoded with a CELT bitstream version %d. This decoder uses %d. Output will probably be corrupted.\n",header.version_id,bitstream);
313    
314    *channels = header.nb_channels;
315    *overlap=header.overlap;
316    st = celt_decoder_create(*mode);
317    if (!st)
318    {
319       fprintf (stderr, "Decoder initialization failed.\n");
320       return NULL;
321    }
322    
323    celt_mode_info(*mode, CELT_GET_FRAME_SIZE, frame_size);
324    *granule_frame_size = *frame_size;
325
326    if (!*rate)
327       *rate = header.sample_rate;
328
329    *nframes = 1;
330
331    if (!quiet)
332    {
333       fprintf (stderr, "Decoding %d Hz audio in", *rate);
334
335       if (*channels==1)
336          fprintf (stderr, " (mono");
337       else
338          fprintf (stderr, " (stereo");
339       fprintf(stderr, ")\n");
340    }
341
342    *extra_headers = header.extra_headers;
343
344    return st;
345 }
346
347 int main(int argc, char **argv)
348 {
349    int c;
350    int option_index = 0;
351    char *inFile, *outFile;
352    FILE *fin, *fout=NULL;
353    short out[MAX_FRAME_SIZE];
354    short output[MAX_FRAME_SIZE];
355    int frame_size=0, granule_frame_size=0;
356    void *st=NULL;
357    CELTMode *mode=NULL;
358    int packet_count=0;
359    int stream_init = 0;
360    int quiet = 0;
361    ogg_int64_t page_granule=0, last_granule=0;
362    int skip_samples=0, page_nb_packets;
363    struct option long_options[] =
364    {
365       {"help", no_argument, NULL, 0},
366       {"quiet", no_argument, NULL, 0},
367       {"version", no_argument, NULL, 0},
368       {"version-short", no_argument, NULL, 0},
369       {"rate", required_argument, NULL, 0},
370       {"mono", no_argument, NULL, 0},
371       {"stereo", no_argument, NULL, 0},
372       {"packet-loss", required_argument, NULL, 0},
373       {0, 0, 0, 0}
374    };
375    ogg_sync_state oy;
376    ogg_page       og;
377    ogg_packet     op;
378    ogg_stream_state os;
379    int enh_enabled;
380    int nframes=2;
381    int print_bitrate=0;
382    int close_in=0;
383    int eos=0;
384    int forceMode=-1;
385    int audio_size=0;
386    float loss_percent=-1;
387    int channels=-1;
388    int rate=0;
389    int extra_headers=0;
390    int wav_format=0;
391    int lookahead=0;
392    int celt_serialno = -1;
393    int firstpacket = 1;
394
395    enh_enabled = 1;
396
397    /*Process options*/
398    while(1)
399    {
400       c = getopt_long (argc, argv, "hvV",
401                        long_options, &option_index);
402       if (c==-1)
403          break;
404       
405       switch(c)
406       {
407       case 0:
408          if (strcmp(long_options[option_index].name,"help")==0)
409          {
410             usage();
411             exit(0);
412          } else if (strcmp(long_options[option_index].name,"quiet")==0)
413          {
414             quiet = 1;
415          } else if (strcmp(long_options[option_index].name,"version")==0)
416          {
417             version();
418             exit(0);
419          } else if (strcmp(long_options[option_index].name,"version-short")==0)
420          {
421             version_short();
422             exit(0);
423          } else if (strcmp(long_options[option_index].name,"mono")==0)
424          {
425             channels=1;
426          } else if (strcmp(long_options[option_index].name,"stereo")==0)
427          {
428             channels=2;
429          } else if (strcmp(long_options[option_index].name,"rate")==0)
430          {
431             rate=atoi (optarg);
432          } else if (strcmp(long_options[option_index].name,"packet-loss")==0)
433          {
434             loss_percent = atof(optarg);
435          }
436          break;
437       case 'h':
438          usage();
439          exit(0);
440          break;
441       case 'v':
442          version();
443          exit(0);
444          break;
445       case 'V':
446          print_bitrate=1;
447          break;
448       case '?':
449          usage();
450          exit(1);
451          break;
452       }
453    }
454    if (argc-optind!=2 && argc-optind!=1)
455    {
456       usage();
457       exit(1);
458    }
459    inFile=argv[optind];
460
461    if (argc-optind==2)
462       outFile=argv[optind+1];
463    else
464       outFile = "";
465    wav_format = strlen(outFile)>=4 && (
466                                        strcmp(outFile+strlen(outFile)-4,".wav")==0
467                                        || strcmp(outFile+strlen(outFile)-4,".WAV")==0);
468    /*Open input file*/
469    if (strcmp(inFile, "-")==0)
470    {
471 #if defined WIN32 || defined _WIN32
472       _setmode(_fileno(stdin), _O_BINARY);
473 #endif
474       fin=stdin;
475    }
476    else 
477    {
478       fin = fopen(inFile, "rb");
479       if (!fin)
480       {
481          perror(inFile);
482          exit(1);
483       }
484       close_in=1;
485    }
486
487
488    /*Init Ogg data struct*/
489    ogg_sync_init(&oy);
490    
491    /*Main decoding loop*/
492    
493    while (1)
494    {
495       char *data;
496       int i, nb_read;
497       /*Get the ogg buffer for writing*/
498       data = ogg_sync_buffer(&oy, 200);
499       /*Read bitstream from input file*/
500       nb_read = fread(data, sizeof(char), 200, fin);      
501       ogg_sync_wrote(&oy, nb_read);
502
503       /*Loop for all complete pages we got (most likely only one)*/
504       while (ogg_sync_pageout(&oy, &og)==1)
505       {
506          if (stream_init == 0) {
507             ogg_stream_init(&os, ogg_page_serialno(&og));
508             stream_init = 1;
509          }
510          if (ogg_page_serialno(&og) != os.serialno) {
511             /* so all streams are read. */
512             ogg_stream_reset_serialno(&os, ogg_page_serialno(&og));
513          }
514          /*Add page to the bitstream*/
515          ogg_stream_pagein(&os, &og);
516          page_granule = ogg_page_granulepos(&og);
517          page_nb_packets = ogg_page_packets(&og);
518          if (page_granule>0 && frame_size)
519          {
520             /* FIXME: shift the granule values if --force-* is specified */
521             skip_samples = frame_size*(page_nb_packets*granule_frame_size*nframes - (page_granule-last_granule))/granule_frame_size;
522             if (ogg_page_eos(&og))
523                skip_samples = -skip_samples;
524             /*else if (!ogg_page_bos(&og))
525                skip_samples = 0;*/
526          } else
527          {
528             skip_samples = 0;
529          }
530          /*printf ("page granulepos: %d %d %d\n", skip_samples, page_nb_packets, (int)page_granule);*/
531          last_granule = page_granule;
532          /*Extract all available packets*/
533          while (!eos && ogg_stream_packetout(&os, &op) == 1 && op.bytes>=8)
534          {
535             if (!memcmp(op.packet, "CELT    ", 8)) {
536                celt_serialno = os.serialno;
537             }
538             if (celt_serialno == -1 || os.serialno != celt_serialno)
539                break;
540             /*If first packet, process as CELT header*/
541             if (packet_count==0)
542             {
543                st = process_header(&op, enh_enabled, &frame_size, &granule_frame_size, &rate, &nframes, forceMode, &channels, &lookahead, &extra_headers, quiet, &mode);
544                if (!st)
545                   exit(1);
546                if (!nframes)
547                   nframes=1;
548                fout = out_file_open(outFile, rate, &channels);
549
550             } else if (packet_count==1)
551             {
552                if (!quiet)
553                   print_comments((char*)op.packet, op.bytes);
554             } else if (packet_count<=1+extra_headers)
555             {
556                /* Ignore extra headers */
557             } else {
558                int lost=0;
559                if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent)
560                   lost=1;
561
562                /*End of stream condition*/
563                if (op.e_o_s && os.serialno == celt_serialno) /* don't care for anything except celt eos */
564                   eos=1;
565                
566                {
567                   int ret;
568                   /*Decode frame*/
569                   if (!lost)
570                      ret = celt_decode(st, (unsigned char*)op.packet, op.bytes, output);
571                   else
572                      ret = celt_decode(st, NULL, 0, output);
573
574                   /*for (i=0;i<frame_size*channels;i++)
575                     printf ("%d\n", (int)output[i]);*/
576
577                   if (ret!=0)
578                   {
579                      fprintf (stderr, "Decoding error: corrupted stream?\n");
580                      break;
581                   }
582
583                   if (print_bitrate) {
584                      celt_int32_t tmp=op.bytes;
585                      char ch=13;
586                      fputc (ch, stderr);
587                      fprintf (stderr, "Bitrate in use: %d bytes/packet     ", tmp);
588                   }
589                   /*Convert to short and save to output file*/
590                   if (strlen(outFile)!=0)
591                   {
592                      for (i=0;i<frame_size*channels;i++)
593                         out[i]=le_short(output[i]);
594                   } else {
595                      for (i=0;i<frame_size*channels;i++)
596                         out[i]=output[i];
597                   }
598                   {
599                      int frame_offset = 0;
600                      int new_frame_size = frame_size;
601                      /*printf ("packet %d %d\n", packet_no, skip_samples);*/
602                      /*fprintf (stderr, "packet %d %d %d\n", packet_no, skip_samples, lookahead);*/
603                      if (firstpacket == 1)
604                      {
605                         /*printf ("chopping first packet\n");*/
606                         new_frame_size -= lookahead;
607                         frame_offset = lookahead;
608                         firstpacket = 0;
609                      }
610                      if (new_frame_size>0)
611                      {  
612 #if defined WIN32 || defined _WIN32
613                         if (strlen(outFile)==0)
614                            WIN_Play_Samples (out+frame_offset*channels, sizeof(short) * new_frame_size*channels);
615                         else
616 #endif
617                            fwrite(out+frame_offset*channels, sizeof(short), new_frame_size*channels, fout);
618                   
619                         audio_size+=sizeof(short)*new_frame_size*channels;
620                      }
621                   }
622                }
623             }
624             packet_count++;
625          }
626       }
627       if (feof(fin))
628          break;
629
630    }
631
632    if (fout && wav_format)
633    {
634       if (fseek(fout,4,SEEK_SET)==0)
635       {
636          int tmp;
637          tmp = le_int(audio_size+36);
638          fwrite(&tmp,4,1,fout);
639          if (fseek(fout,32,SEEK_CUR)==0)
640          {
641             tmp = le_int(audio_size);
642             fwrite(&tmp,4,1,fout);
643          } else
644          {
645             fprintf (stderr, "First seek worked, second didn't\n");
646          }
647       } else {
648          fprintf (stderr, "Cannot seek on wave file, size will be incorrect\n");
649       }
650    }
651
652    if (st)
653    {
654       celt_decoder_destroy(st);
655       celt_mode_destroy(mode);
656    } else {
657       fprintf (stderr, "This doesn't look like a CELT file\n");
658    }
659    if (stream_init)
660       ogg_stream_clear(&os);
661    ogg_sync_clear(&oy);
662
663 #if defined WIN32 || defined _WIN32
664    if (strlen(outFile)==0)
665       WIN_Audio_close ();
666 #endif
667
668    if (close_in)
669       fclose(fin);
670    if (fout != NULL)
671       fclose(fout);   
672
673    return 0;
674 }