bit of cleaning up, default sampling rate
[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 (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 (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 (" --enh                 Enable perceptual enhancement (default)\n");
268    printf (" --no-enh              Disable perceptual enhancement\n");
269    printf (" --force-nb            Force decoding in narrowband\n");
270    printf (" --force-wb            Force decoding in wideband\n");
271    printf (" --force-uwb           Force decoding in ultra-wideband\n");
272    printf (" --mono                Force decoding in mono\n");
273    printf (" --stereo              Force decoding in stereo\n");
274    printf (" --rate n              Force decoding at sampling rate n Hz\n");
275    printf (" --packet-loss n       Simulate n %% random packet loss\n");
276    printf (" -V                    Verbose mode (show bit-rate)\n"); 
277    printf (" -h, --help            This help\n");
278    printf (" -v, --version         Version information\n");
279    printf (" --pf                  Deprecated, use --enh instead\n");
280    printf (" --no-pf               Deprecated, use --no-enh instead\n");
281    printf ("\n");
282 }
283
284 void version()
285 {
286    printf ("celtenc (CELT encoder)\n");
287    printf ("Copyright (C) 2008 Jean-Marc Valin\n");
288 }
289
290 void version_short()
291 {
292    printf ("celtenc (CELT encoder)\n");
293    printf ("Copyright (C) 2008 Jean-Marc Valin\n");
294 }
295
296 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)
297 {
298    CELTDecoder *st;
299    const CELTMode *mode;
300    CELTHeader header;
301    int modeID;
302       
303    celt_header_from_packet((char*)op->packet, op->bytes, &header);
304       
305    if (header.mode==0)
306       mode = celt_mono;
307    else if (header.mode==1)
308       mode = celt_stereo;
309    else
310    {
311       fprintf (stderr, "Invalid mode: %d\n", header.mode);
312       return NULL;
313    }
314
315    st = celt_decoder_new(mode);
316    if (!st)
317    {
318       fprintf (stderr, "Decoder initialization failed.\n");
319       return NULL;
320    }
321    
322    celt_mode_info(mode, CELT_GET_FRAME_SIZE, frame_size);
323    *granule_frame_size = *frame_size;
324
325    if (!*rate)
326       *rate = header.sample_rate;
327    /* Adjust rate if --force-* options are used */
328
329    *nframes = 1;
330
331    if (*channels==-1)
332       *channels = header.nb_channels;
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    unsigned char bits[1000];
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;
395    int celt_serialno = -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, j, 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          int packet_no;
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          packet_no=0;
537          while (!eos && ogg_stream_packetout(&os, &op) == 1)
538          {
539             if (!memcmp(op.packet, "CELT    ", 8)) {
540                celt_serialno = os.serialno;
541             }
542             if (celt_serialno == -1 || os.serialno != celt_serialno)
543                break;
544             /*If first packet, process as CELT header*/
545             if (packet_count==0)
546             {
547                st = process_header(&op, enh_enabled, &frame_size, &granule_frame_size, &rate, &nframes, forceMode, &channels, &extra_headers, quiet);
548                if (!st)
549                   exit(1);
550                //FIXME: Do that properly
551                //celt_decoder_ctl(st, SPEEX_GET_LOOKAHEAD, &lookahead);
552                lookahead = 0;
553                if (!nframes)
554                   nframes=1;
555                fout = out_file_open(outFile, rate, &channels);
556
557             } else if (packet_count==1)
558             {
559                if (!quiet)
560                   print_comments((char*)op.packet, op.bytes);
561             } else if (packet_count<=1+extra_headers)
562             {
563                /* Ignore extra headers */
564             } else {
565                int lost=0;
566                packet_no++;
567                if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent)
568                   lost=1;
569
570                /*End of stream condition*/
571                if (op.e_o_s && os.serialno == celt_serialno) /* don't care for anything except celt eos */
572                   eos=1;
573                
574                {
575                   int ret;
576                   /*Decode frame*/
577                   if (!lost)
578                      ret = celt_decode(st, (unsigned char*)op.packet, op.bytes, output);
579                   else
580                      ret = celt_decode(st, NULL, 0, output);
581
582                   /*for (i=0;i<frame_size*channels;i++)
583                     printf ("%d\n", (int)output[i]);*/
584
585                   if (ret!=0)
586                   {
587                      fprintf (stderr, "Decoding error: corrupted stream?\n");
588                      break;
589                   }
590
591                   if (print_bitrate) {
592                      celt_int32_t tmp=40;
593                      char ch=13;
594                      fputc (ch, stderr);
595                      fprintf (stderr, "Bitrate is use: %d bytes/packet     ", tmp);
596                   }
597                   /*Convert to short and save to output file*/
598                   if (strlen(outFile)!=0)
599                   {
600                      for (i=0;i<frame_size*channels;i++)
601                         out[i]=le_short(output[i]);
602                   } else {
603                      for (i=0;i<frame_size*channels;i++)
604                         out[i]=output[i];
605                   }
606                   {
607                      int frame_offset = 0;
608                      int new_frame_size = frame_size;
609                      /*printf ("packet %d %d\n", packet_no, skip_samples);*/
610                      /*fprintf (stderr, "packet %d %d %d\n", packet_no, skip_samples, lookahead);*/
611                      if (packet_no == 1 && j==0 && skip_samples > 0)
612                      {
613                         /*printf ("chopping first packet\n");*/
614                         new_frame_size -= skip_samples+lookahead;
615                         frame_offset = skip_samples+lookahead;
616                      }
617                      if (packet_no == page_nb_packets && skip_samples < 0)
618                      {
619                         int packet_length = nframes*frame_size+skip_samples+lookahead;
620                         new_frame_size = packet_length - j*frame_size;
621                         if (new_frame_size<0)
622                            new_frame_size = 0;
623                         if (new_frame_size>frame_size)
624                            new_frame_size = frame_size;
625                         /*printf ("chopping end: %d %d %d\n", new_frame_size, packet_length, packet_no);*/
626                      }
627                      if (new_frame_size>0)
628                      {  
629 #if defined WIN32 || defined _WIN32
630                         if (strlen(outFile)==0)
631                            WIN_Play_Samples (out+frame_offset*channels, sizeof(short) * new_frame_size*channels);
632                         else
633 #endif
634                            fwrite(out+frame_offset*channels, sizeof(short), new_frame_size*channels, fout);
635                   
636                         audio_size+=sizeof(short)*new_frame_size*channels;
637                      }
638                   }
639                }
640             }
641             packet_count++;
642          }
643       }
644       if (feof(fin))
645          break;
646
647    }
648
649    if (fout && wav_format)
650    {
651       if (fseek(fout,4,SEEK_SET)==0)
652       {
653          int tmp;
654          tmp = le_int(audio_size+36);
655          fwrite(&tmp,4,1,fout);
656          if (fseek(fout,32,SEEK_CUR)==0)
657          {
658             tmp = le_int(audio_size);
659             fwrite(&tmp,4,1,fout);
660          } else
661          {
662             fprintf (stderr, "First seek worked, second didn't\n");
663          }
664       } else {
665          fprintf (stderr, "Cannot seek on wave file, size will be incorrect\n");
666       }
667    }
668
669    if (st)
670       celt_decoder_destroy(st);
671    else 
672    {
673       fprintf (stderr, "This doesn't look like a CELT file\n");
674    }
675    if (stream_init)
676       ogg_stream_clear(&os);
677    ogg_sync_clear(&oy);
678
679 #if defined WIN32 || defined _WIN32
680    if (strlen(outFile)==0)
681       WIN_Audio_close ();
682 #endif
683
684    if (close_in)
685       fclose(fin);
686    if (fout != NULL)
687       fclose(fout);   
688
689    return 0;
690 }