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