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