This fixes the frame_size check on the encoder and decoder and removes some unused...
[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_custom(*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    *frame_size = header.frame_size;
327    *granule_frame_size = *frame_size;
328
329    if (!*rate)
330       *rate = header.sample_rate;
331
332    *nframes = 1;
333
334    if (!quiet)
335    {
336       fprintf (stderr, "Decoding %d Hz audio in", *rate);
337
338       if (*channels==1)
339          fprintf (stderr, " (mono");
340       else
341          fprintf (stderr, " (stereo");
342       fprintf(stderr, ")\n");
343    }
344
345    *extra_headers = header.extra_headers;
346
347    return st;
348 }
349
350 int main(int argc, char **argv)
351 {
352    int c;
353    int option_index = 0;
354    char *inFile, *outFile;
355    FILE *fin, *fout=NULL;
356    short out[MAX_FRAME_SIZE];
357    short output[MAX_FRAME_SIZE];
358    int frame_size=0, granule_frame_size=0;
359    void *st=NULL;
360    CELTMode *mode=NULL;
361    int packet_count=0;
362    int stream_init = 0;
363    int quiet = 0;
364    ogg_int64_t page_granule=0, last_granule=0;
365    int skip_samples=0, page_nb_packets;
366    struct option long_options[] =
367    {
368       {"help", no_argument, NULL, 0},
369       {"quiet", no_argument, NULL, 0},
370       {"version", no_argument, NULL, 0},
371       {"version-short", no_argument, NULL, 0},
372       {"rate", required_argument, NULL, 0},
373       {"mono", no_argument, NULL, 0},
374       {"stereo", no_argument, NULL, 0},
375       {"packet-loss", required_argument, NULL, 0},
376       {0, 0, 0, 0}
377    };
378    ogg_sync_state oy;
379    ogg_page       og;
380    ogg_packet     op;
381    ogg_stream_state os;
382    int enh_enabled;
383    int nframes=2;
384    int print_bitrate=0;
385    int close_in=0;
386    int eos=0;
387    int forceMode=-1;
388    int audio_size=0;
389    float loss_percent=-1;
390    int channels=-1;
391    int rate=0;
392    int extra_headers=0;
393    int wav_format=0;
394    int lookahead=0;
395    int celt_serialno = -1;
396    int firstpacket = 1;
397
398    enh_enabled = 1;
399
400    /*Process options*/
401    while(1)
402    {
403       c = getopt_long (argc, argv, "hvV",
404                        long_options, &option_index);
405       if (c==-1)
406          break;
407       
408       switch(c)
409       {
410       case 0:
411          if (strcmp(long_options[option_index].name,"help")==0)
412          {
413             usage();
414             exit(0);
415          } else if (strcmp(long_options[option_index].name,"quiet")==0)
416          {
417             quiet = 1;
418          } else if (strcmp(long_options[option_index].name,"version")==0)
419          {
420             version();
421             exit(0);
422          } else if (strcmp(long_options[option_index].name,"version-short")==0)
423          {
424             version_short();
425             exit(0);
426          } else if (strcmp(long_options[option_index].name,"mono")==0)
427          {
428             channels=1;
429          } else if (strcmp(long_options[option_index].name,"stereo")==0)
430          {
431             channels=2;
432          } else if (strcmp(long_options[option_index].name,"rate")==0)
433          {
434             rate=atoi (optarg);
435          } else if (strcmp(long_options[option_index].name,"packet-loss")==0)
436          {
437             loss_percent = atof(optarg);
438          }
439          break;
440       case 'h':
441          usage();
442          exit(0);
443          break;
444       case 'v':
445          version();
446          exit(0);
447          break;
448       case 'V':
449          print_bitrate=1;
450          break;
451       case '?':
452          usage();
453          exit(1);
454          break;
455       }
456    }
457    if (argc-optind!=2 && argc-optind!=1)
458    {
459       usage();
460       exit(1);
461    }
462    inFile=argv[optind];
463
464    if (argc-optind==2)
465       outFile=argv[optind+1];
466    else
467       outFile = "";
468    wav_format = strlen(outFile)>=4 && (
469                                        strcmp(outFile+strlen(outFile)-4,".wav")==0
470                                        || strcmp(outFile+strlen(outFile)-4,".WAV")==0);
471    /*Open input file*/
472    if (strcmp(inFile, "-")==0)
473    {
474 #if defined WIN32 || defined _WIN32
475       _setmode(_fileno(stdin), _O_BINARY);
476 #endif
477       fin=stdin;
478    }
479    else 
480    {
481       fin = fopen(inFile, "rb");
482       if (!fin)
483       {
484          perror(inFile);
485          exit(1);
486       }
487       close_in=1;
488    }
489
490
491    /*Init Ogg data struct*/
492    ogg_sync_init(&oy);
493    
494    /*Main decoding loop*/
495    
496    while (1)
497    {
498       char *data;
499       int i, nb_read;
500       /*Get the ogg buffer for writing*/
501       data = ogg_sync_buffer(&oy, 200);
502       /*Read bitstream from input file*/
503       nb_read = fread(data, sizeof(char), 200, fin);      
504       ogg_sync_wrote(&oy, nb_read);
505
506       /*Loop for all complete pages we got (most likely only one)*/
507       while (ogg_sync_pageout(&oy, &og)==1)
508       {
509          if (stream_init == 0) {
510             ogg_stream_init(&os, ogg_page_serialno(&og));
511             stream_init = 1;
512          }
513          if (ogg_page_serialno(&og) != os.serialno) {
514             /* so all streams are read. */
515             ogg_stream_reset_serialno(&os, ogg_page_serialno(&og));
516          }
517          /*Add page to the bitstream*/
518          ogg_stream_pagein(&os, &og);
519          page_granule = ogg_page_granulepos(&og);
520          page_nb_packets = ogg_page_packets(&og);
521          if (page_granule>0 && frame_size)
522          {
523             /* FIXME: shift the granule values if --force-* is specified */
524             skip_samples = frame_size*(page_nb_packets*granule_frame_size*nframes - (page_granule-last_granule))/granule_frame_size;
525             if (ogg_page_eos(&og))
526                skip_samples = -skip_samples;
527             /*else if (!ogg_page_bos(&og))
528                skip_samples = 0;*/
529          } else
530          {
531             skip_samples = 0;
532          }
533          /*printf ("page granulepos: %d %d %d\n", skip_samples, page_nb_packets, (int)page_granule);*/
534          last_granule = page_granule;
535          /*Extract all available packets*/
536          while (!eos && ogg_stream_packetout(&os, &op) == 1)
537          {
538             if (op.bytes>=8 && !memcmp(op.packet, "CELT    ", 8)) {
539                celt_serialno = os.serialno;
540             }
541             if (celt_serialno == -1 || os.serialno != celt_serialno)
542                break;
543             /*If first packet, process as CELT header*/
544             if (packet_count==0)
545             {
546                st = process_header(&op, enh_enabled, &frame_size, &granule_frame_size, &rate, &nframes, forceMode, &channels, &lookahead, &extra_headers, quiet, &mode);
547                if (!st)
548                   exit(1);
549                if (!nframes)
550                   nframes=1;
551                fout = out_file_open(outFile, rate, &channels);
552
553             } else if (packet_count==1)
554             {
555                if (!quiet)
556                   print_comments((char*)op.packet, op.bytes);
557             } else if (packet_count<=1+extra_headers)
558             {
559                /* Ignore extra headers */
560             } else {
561                int lost=0;
562                if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent)
563                   lost=1;
564
565                /*End of stream condition*/
566                if (op.e_o_s && os.serialno == celt_serialno) /* don't care for anything except celt eos */
567                   eos=1;
568                
569                {
570                   int ret;
571                   /*Decode frame*/
572                   if (!lost)
573                      ret = celt_decode(st, (unsigned char*)op.packet, op.bytes, output, frame_size);
574                   else
575                      ret = celt_decode(st, NULL, 0, output, frame_size);
576
577                   /*for (i=0;i<frame_size*channels;i++)
578                     printf ("%d\n", (int)output[i]);*/
579
580                   if (ret!=0)
581                   {
582                      fprintf (stderr, "Decoding error: corrupted stream?\n");
583                      break;
584                   }
585
586                   if (print_bitrate) {
587                      celt_int32 tmp=op.bytes;
588                      char ch=13;
589                      fputc (ch, stderr);
590                      fprintf (stderr, "Bitrate in use: %d bytes/packet     ", tmp);
591                   }
592                   /*Convert to short and save to output file*/
593                   if (strlen(outFile)!=0)
594                   {
595                      for (i=0;i<frame_size*channels;i++)
596                         out[i]=le_short(output[i]);
597                   } else {
598                      for (i=0;i<frame_size*channels;i++)
599                         out[i]=output[i];
600                   }
601                   {
602                      int frame_offset = 0;
603                      int new_frame_size = frame_size;
604                      /*printf ("packet %d %d\n", packet_no, skip_samples);*/
605                      /*fprintf (stderr, "packet %d %d %d\n", packet_no, skip_samples, lookahead);*/
606                      if (firstpacket == 1)
607                      {
608                         /*printf ("chopping first packet\n");*/
609                         new_frame_size -= lookahead;
610                         frame_offset = lookahead;
611                         firstpacket = 0;
612                      }
613                      if (new_frame_size>0)
614                      {  
615 #if defined WIN32 || defined _WIN32
616                         if (strlen(outFile)==0)
617                            WIN_Play_Samples (out+frame_offset*channels, sizeof(short) * new_frame_size*channels);
618                         else
619 #endif
620                            fwrite(out+frame_offset*channels, sizeof(short), new_frame_size*channels, fout);
621                   
622                         audio_size+=sizeof(short)*new_frame_size*channels;
623                      }
624                   }
625                }
626             }
627             packet_count++;
628          }
629       }
630       if (feof(fin))
631          break;
632
633    }
634
635    if (fout && wav_format)
636    {
637       if (fseek(fout,4,SEEK_SET)==0)
638       {
639          int tmp;
640          tmp = le_int(audio_size+36);
641          fwrite(&tmp,4,1,fout);
642          if (fseek(fout,32,SEEK_CUR)==0)
643          {
644             tmp = le_int(audio_size);
645             fwrite(&tmp,4,1,fout);
646          } else
647          {
648             fprintf (stderr, "First seek worked, second didn't\n");
649          }
650       } else {
651          fprintf (stderr, "Cannot seek on wave file, size will be incorrect\n");
652       }
653    }
654
655    if (st)
656    {
657       celt_decoder_destroy(st);
658       celt_mode_destroy(mode);
659    } else {
660       fprintf (stderr, "This doesn't look like a CELT file\n");
661    }
662    if (stream_init)
663       ogg_stream_clear(&os);
664    ogg_sync_clear(&oy);
665
666 #if defined WIN32 || defined _WIN32
667    if (strlen(outFile)==0)
668       WIN_Audio_close ();
669 #endif
670
671    if (close_in)
672       fclose(fin);
673    if (fout != NULL)
674       fclose(fout);   
675
676    return 0;
677 }