Updated the header and celtenc/celtdec tools to the new way modes are handled.
[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()
251 {
252    printf ("Usage: celtdec [options] input_file.spx [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()
278 {
279    printf ("celtenc (CELT encoder)\n");
280    printf ("Copyright (C) 2008 Jean-Marc Valin\n");
281 }
282
283 void version_short()
284 {
285    printf ("celtenc (CELT encoder)\n");
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 *extra_headers, int quiet, CELTMode **mode)
290 {
291    CELTDecoder *st;
292    CELTHeader header;
293       
294    celt_header_from_packet((char*)op->packet, op->bytes, &header);
295
296    if (header.nb_channels>2 || header.nb_channels<1)
297    {
298       fprintf (stderr, "Unsupported number of channels: %d\n", header.nb_channels);
299       return NULL;
300    }
301    *mode = celt_mode_create(header.sample_rate, header.nb_channels, header.frame_size, header.overlap, NULL);
302    *channels = header.nb_channels;
303
304    st = celt_decoder_create(*mode);
305    if (!st)
306    {
307       fprintf (stderr, "Decoder initialization failed.\n");
308       return NULL;
309    }
310    
311    celt_mode_info(*mode, CELT_GET_FRAME_SIZE, frame_size);
312    *granule_frame_size = *frame_size;
313
314    if (!*rate)
315       *rate = header.sample_rate;
316
317    *nframes = 1;
318
319    if (!quiet)
320    {
321       fprintf (stderr, "Decoding %d Hz audio in", *rate);
322
323       if (*channels==1)
324          fprintf (stderr, " (mono");
325       else
326          fprintf (stderr, " (stereo");
327       fprintf(stderr, ")\n");
328    }
329
330    *extra_headers = header.extra_headers;
331
332    return st;
333 }
334
335 int main(int argc, char **argv)
336 {
337    int c;
338    int option_index = 0;
339    char *inFile, *outFile;
340    FILE *fin, *fout=NULL;
341    short out[MAX_FRAME_SIZE];
342    short output[MAX_FRAME_SIZE];
343    int frame_size=0, granule_frame_size=0;
344    void *st=NULL;
345    CELTMode *mode=NULL;
346    unsigned char bits[1000];
347    int packet_count=0;
348    int stream_init = 0;
349    int quiet = 0;
350    ogg_int64_t page_granule=0, last_granule=0;
351    int skip_samples=0, page_nb_packets;
352    struct option long_options[] =
353    {
354       {"help", no_argument, NULL, 0},
355       {"quiet", no_argument, NULL, 0},
356       {"version", no_argument, NULL, 0},
357       {"version-short", no_argument, NULL, 0},
358       {"rate", required_argument, NULL, 0},
359       {"mono", no_argument, NULL, 0},
360       {"stereo", no_argument, NULL, 0},
361       {"packet-loss", required_argument, NULL, 0},
362       {0, 0, 0, 0}
363    };
364    ogg_sync_state oy;
365    ogg_page       og;
366    ogg_packet     op;
367    ogg_stream_state os;
368    int enh_enabled;
369    int nframes=2;
370    int print_bitrate=0;
371    int close_in=0;
372    int eos=0;
373    int forceMode=-1;
374    int audio_size=0;
375    float loss_percent=-1;
376    int channels=-1;
377    int rate=0;
378    int extra_headers=0;
379    int wav_format=0;
380    int lookahead;
381    int celt_serialno = -1;
382
383    enh_enabled = 1;
384
385    /*Process options*/
386    while(1)
387    {
388       c = getopt_long (argc, argv, "hvV",
389                        long_options, &option_index);
390       if (c==-1)
391          break;
392       
393       switch(c)
394       {
395       case 0:
396          if (strcmp(long_options[option_index].name,"help")==0)
397          {
398             usage();
399             exit(0);
400          } else if (strcmp(long_options[option_index].name,"quiet")==0)
401          {
402             quiet = 1;
403          } else if (strcmp(long_options[option_index].name,"version")==0)
404          {
405             version();
406             exit(0);
407          } else if (strcmp(long_options[option_index].name,"version-short")==0)
408          {
409             version_short();
410             exit(0);
411          } else if (strcmp(long_options[option_index].name,"mono")==0)
412          {
413             channels=1;
414          } else if (strcmp(long_options[option_index].name,"stereo")==0)
415          {
416             channels=2;
417          } else if (strcmp(long_options[option_index].name,"rate")==0)
418          {
419             rate=atoi (optarg);
420          } else if (strcmp(long_options[option_index].name,"packet-loss")==0)
421          {
422             loss_percent = atof(optarg);
423          }
424          break;
425       case 'h':
426          usage();
427          exit(0);
428          break;
429       case 'v':
430          version();
431          exit(0);
432          break;
433       case 'V':
434          print_bitrate=1;
435          break;
436       case '?':
437          usage();
438          exit(1);
439          break;
440       }
441    }
442    if (argc-optind!=2 && argc-optind!=1)
443    {
444       usage();
445       exit(1);
446    }
447    inFile=argv[optind];
448
449    if (argc-optind==2)
450       outFile=argv[optind+1];
451    else
452       outFile = "";
453    wav_format = strlen(outFile)>=4 && (
454                                        strcmp(outFile+strlen(outFile)-4,".wav")==0
455                                        || strcmp(outFile+strlen(outFile)-4,".WAV")==0);
456    /*Open input file*/
457    if (strcmp(inFile, "-")==0)
458    {
459 #if defined WIN32 || defined _WIN32
460       _setmode(_fileno(stdin), _O_BINARY);
461 #endif
462       fin=stdin;
463    }
464    else 
465    {
466       fin = fopen(inFile, "rb");
467       if (!fin)
468       {
469          perror(inFile);
470          exit(1);
471       }
472       close_in=1;
473    }
474
475
476    /*Init Ogg data struct*/
477    ogg_sync_init(&oy);
478    
479    /*Main decoding loop*/
480    
481    while (1)
482    {
483       char *data;
484       int i, j, nb_read;
485       /*Get the ogg buffer for writing*/
486       data = ogg_sync_buffer(&oy, 200);
487       /*Read bitstream from input file*/
488       nb_read = fread(data, sizeof(char), 200, fin);      
489       ogg_sync_wrote(&oy, nb_read);
490
491       /*Loop for all complete pages we got (most likely only one)*/
492       while (ogg_sync_pageout(&oy, &og)==1)
493       {
494          int packet_no;
495          if (stream_init == 0) {
496             ogg_stream_init(&os, ogg_page_serialno(&og));
497             stream_init = 1;
498          }
499          if (ogg_page_serialno(&og) != os.serialno) {
500             /* so all streams are read. */
501             ogg_stream_reset_serialno(&os, ogg_page_serialno(&og));
502          }
503          /*Add page to the bitstream*/
504          ogg_stream_pagein(&os, &og);
505          page_granule = ogg_page_granulepos(&og);
506          page_nb_packets = ogg_page_packets(&og);
507          if (page_granule>0 && frame_size)
508          {
509             /* FIXME: shift the granule values if --force-* is specified */
510             skip_samples = frame_size*(page_nb_packets*granule_frame_size*nframes - (page_granule-last_granule))/granule_frame_size;
511             if (ogg_page_eos(&og))
512                skip_samples = -skip_samples;
513             /*else if (!ogg_page_bos(&og))
514                skip_samples = 0;*/
515          } else
516          {
517             skip_samples = 0;
518          }
519          /*printf ("page granulepos: %d %d %d\n", skip_samples, page_nb_packets, (int)page_granule);*/
520          last_granule = page_granule;
521          /*Extract all available packets*/
522          packet_no=0;
523          while (!eos && ogg_stream_packetout(&os, &op) == 1 && op.bytes>=8)
524          {
525             if (!memcmp(op.packet, "CELT    ", 8)) {
526                celt_serialno = os.serialno;
527             }
528             if (celt_serialno == -1 || os.serialno != celt_serialno)
529                break;
530             /*If first packet, process as CELT header*/
531             if (packet_count==0)
532             {
533                st = process_header(&op, enh_enabled, &frame_size, &granule_frame_size, &rate, &nframes, forceMode, &channels, &extra_headers, quiet, &mode);
534                if (!st)
535                   exit(1);
536                //FIXME: Do that properly
537                //celt_decoder_ctl(st, SPEEX_GET_LOOKAHEAD, &lookahead);
538                lookahead = 0;
539                if (!nframes)
540                   nframes=1;
541                fout = out_file_open(outFile, rate, &channels);
542
543             } else if (packet_count==1)
544             {
545                if (!quiet)
546                   print_comments((char*)op.packet, op.bytes);
547             } else if (packet_count<=1+extra_headers)
548             {
549                /* Ignore extra headers */
550             } else {
551                int lost=0;
552                packet_no++;
553                if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent)
554                   lost=1;
555
556                /*End of stream condition*/
557                if (op.e_o_s && os.serialno == celt_serialno) /* don't care for anything except celt eos */
558                   eos=1;
559                
560                {
561                   int ret;
562                   /*Decode frame*/
563                   if (!lost)
564                      ret = celt_decode(st, (unsigned char*)op.packet, op.bytes, output);
565                   else
566                      ret = celt_decode(st, NULL, 0, output);
567
568                   /*for (i=0;i<frame_size*channels;i++)
569                     printf ("%d\n", (int)output[i]);*/
570
571                   if (ret!=0)
572                   {
573                      fprintf (stderr, "Decoding error: corrupted stream?\n");
574                      break;
575                   }
576
577                   if (print_bitrate) {
578                      celt_int32_t tmp=op.bytes;
579                      char ch=13;
580                      fputc (ch, stderr);
581                      fprintf (stderr, "Bitrate in use: %d bytes/packet     ", tmp);
582                   }
583                   /*Convert to short and save to output file*/
584                   if (strlen(outFile)!=0)
585                   {
586                      for (i=0;i<frame_size*channels;i++)
587                         out[i]=le_short(output[i]);
588                   } else {
589                      for (i=0;i<frame_size*channels;i++)
590                         out[i]=output[i];
591                   }
592                   {
593                      int frame_offset = 0;
594                      int new_frame_size = frame_size;
595                      /*printf ("packet %d %d\n", packet_no, skip_samples);*/
596                      /*fprintf (stderr, "packet %d %d %d\n", packet_no, skip_samples, lookahead);*/
597                      if (packet_no == 1 && j==0 && skip_samples > 0)
598                      {
599                         /*printf ("chopping first packet\n");*/
600                         new_frame_size -= skip_samples+lookahead;
601                         frame_offset = skip_samples+lookahead;
602                      }
603                      if (packet_no == page_nb_packets && skip_samples < 0)
604                      {
605                         int packet_length = nframes*frame_size+skip_samples+lookahead;
606                         new_frame_size = packet_length - j*frame_size;
607                         if (new_frame_size<0)
608                            new_frame_size = 0;
609                         if (new_frame_size>frame_size)
610                            new_frame_size = frame_size;
611                         /*printf ("chopping end: %d %d %d\n", new_frame_size, packet_length, packet_no);*/
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 }