Fixed the default int32 type which was wrong on amd64 (and added testcase).
[opus.git] / tools / celtenc.c
1 /* Copyright (C) 2002-2008 Jean-Marc Valin 
2    File: celtenc.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
41 #ifdef HAVE_GETOPT_H
42 #include <getopt.h>
43 #endif
44
45 #ifndef HAVE_GETOPT_LONG
46 #include "getopt_win.h"
47 #endif
48
49 #include <stdlib.h>
50 #include <string.h>
51 #include <time.h>
52
53 #include "celt.h"
54 #include "celt_header.h"
55 #include <ogg/ogg.h>
56 #include "wav_io.h"
57
58 #if defined WIN32 || defined _WIN32
59 /* We need the following two to set stdout to binary */
60 #include <io.h>
61 #include <fcntl.h>
62 #endif
63
64 #include "skeleton.h"
65
66
67 void comment_init(char **comments, int* length, char *vendor_string);
68 void comment_add(char **comments, int* length, char *tag, char *val);
69
70
71 /*Write an Ogg page to a file pointer*/
72 int oe_write_page(ogg_page *page, FILE *fp)
73 {
74    int written;
75    written = fwrite(page->header,1,page->header_len, fp);
76    written += fwrite(page->body,1,page->body_len, fp);
77    
78    return written;
79 }
80
81 #define MAX_FRAME_SIZE 2000
82 #define MAX_FRAME_BYTES 2000
83
84 /* Convert input audio bits, endians and channels */
85 static int read_samples(FILE *fin,int frame_size, int bits, int channels, int lsb, short * input, char *buff, celt_int32_t *size)
86 {   
87    unsigned char in[MAX_FRAME_BYTES*2];
88    int i;
89    short *s;
90    int nb_read;
91
92    if (size && *size<=0)
93    {
94       return 0;
95    }
96    /*Read input audio*/
97    if (size)
98       *size -= bits/8*channels*frame_size;
99    if (buff)
100    {
101       for (i=0;i<12;i++)
102          in[i]=buff[i];
103       nb_read = fread(in+12,1,bits/8*channels*frame_size-12, fin) + 12;
104       if (size)
105          *size += 12;
106    } else {
107       nb_read = fread(in,1,bits/8*channels* frame_size, fin);
108    }
109    nb_read /= bits/8*channels;
110
111    /*fprintf (stderr, "%d\n", nb_read);*/
112    if (nb_read==0)
113       return 0;
114
115    s=(short*)in;
116    if(bits==8)
117    {
118       /* Convert 8->16 bits */
119       for(i=frame_size*channels-1;i>=0;i--)
120       {
121          s[i]=(in[i]<<8)^0x8000;
122       }
123    } else
124    {
125       /* convert to our endian format */
126       for(i=0;i<frame_size*channels;i++)
127       {
128          if(lsb) 
129             s[i]=le_short(s[i]); 
130          else
131             s[i]=be_short(s[i]);
132       }
133    }
134
135    /* FIXME: This is probably redundent now */
136    /* copy to float input buffer */
137    for (i=0;i<frame_size*channels;i++)
138    {
139       input[i]=(short)s[i];
140    }
141
142    for (i=nb_read*channels;i<frame_size*channels;i++)
143    {
144       input[i]=0;
145    }
146
147
148    return nb_read;
149 }
150
151 void add_fishead_packet (ogg_stream_state *os) {
152
153    fishead_packet fp;
154
155    memset(&fp, 0, sizeof(fp));
156    fp.ptime_n = 0;
157    fp.ptime_d = 1000;
158    fp.btime_n = 0;
159    fp.btime_d = 1000;
160
161    add_fishead_to_stream(os, &fp);
162 }
163
164 /*
165  * Adds the fishead packets in the skeleton output stream along with the e_o_s packet
166  */
167 void add_fisbone_packet (ogg_stream_state *os, celt_int32_t serialno, CELTHeader *header) {
168
169    fisbone_packet fp;
170
171    memset(&fp, 0, sizeof(fp));
172    fp.serial_no = serialno;
173    fp.nr_header_packet = 2 + header->extra_headers;
174    fp.granule_rate_n = header->sample_rate;
175    fp.granule_rate_d = 1;
176    fp.start_granule = 0;
177    fp.preroll = 3;
178    fp.granule_shift = 0;
179
180    add_message_header_field(&fp, "Content-Type", "audio/x-celt");
181
182    add_fisbone_to_stream(os, &fp);
183 }
184
185 void version()
186 {
187    printf ("celtenc (CELT encoder)\n");
188    printf ("Copyright (C) 2008 Jean-Marc Valin\n");
189 }
190
191 void version_short()
192 {
193    printf ("celtenc (CELT encoder)\n");
194    printf ("Copyright (C) 2008 Jean-Marc Valin\n");
195 }
196
197 void usage()
198 {
199    printf ("Usage: celtenc [options] input_file output_file\n");
200    printf ("\n");
201    printf ("Encodes input_file using Speex. It can read the WAV or raw files.\n");
202    printf ("\n");
203    printf ("input_file can be:\n");
204    printf ("  filename.wav      wav file\n");
205    printf ("  filename.*        Raw PCM file (any extension other than .wav)\n");
206    printf ("  -                 stdin\n");
207    printf ("\n");  
208    printf ("output_file can be:\n");
209    printf ("  filename.oga      compressed file\n");
210    printf ("  -                 stdout\n");
211    printf ("\n");  
212    printf ("Options:\n");
213    printf (" --bitrate n        Encoding bit-rate (use bit-rate n or lower)\n"); 
214    printf (" --skeleton         Outputs ogg skeleton metadata (may cause incompatibilities)\n");
215    printf (" --comment          Add the given string as an extra comment. This may be\n");
216    printf ("                     used multiple times\n");
217    printf (" --author           Author of this track\n");
218    printf (" --title            Title for this track\n");
219    printf (" -h, --help         This help\n"); 
220    printf (" -v, --version      Version information\n"); 
221    printf (" -V                 Verbose mode (show bit-rate)\n"); 
222    printf ("Raw input options:\n");
223    printf (" --rate n           Sampling rate for raw input\n"); 
224    printf (" --stereo           Consider raw input as stereo\n"); 
225    printf (" --le               Raw input is little-endian\n"); 
226    printf (" --be               Raw input is big-endian\n"); 
227    printf (" --8bit             Raw input is 8-bit unsigned\n"); 
228    printf (" --16bit            Raw input is 16-bit signed\n"); 
229    printf ("Default raw PCM input is 16-bit, little-endian, mono\n"); 
230    printf ("\n");
231    printf ("More information is available from the Speex site: http://www.speex.org\n");
232    printf ("\n");
233    printf ("Please report bugs to the mailing list `speex-dev@xiph.org'.\n");
234 }
235
236
237 int main(int argc, char **argv)
238 {
239    int nb_samples, total_samples=0, nb_encoded;
240    int c;
241    int option_index = 0;
242    char *inFile, *outFile;
243    FILE *fin, *fout;
244    short input[MAX_FRAME_SIZE];
245    celt_int32_t frame_size;
246    int quiet=0;
247    int nbBytes;
248    const CELTMode *mode=celt_mono;
249    int modeID = -1;
250    void *st;
251    char bits[MAX_FRAME_BYTES];
252    int with_skeleton = 0;
253    struct option long_options[] =
254    {
255       {"bitrate", required_argument, NULL, 0},
256       {"skeleton",no_argument,NULL, 0},
257       {"help", no_argument, NULL, 0},
258       {"quiet", no_argument, NULL, 0},
259       {"le", no_argument, NULL, 0},
260       {"be", no_argument, NULL, 0},
261       {"8bit", no_argument, NULL, 0},
262       {"16bit", no_argument, NULL, 0},
263       {"stereo", no_argument, NULL, 0},
264       {"rate", required_argument, NULL, 0},
265       {"version", no_argument, NULL, 0},
266       {"version-short", no_argument, NULL, 0},
267       {"comment", required_argument, NULL, 0},
268       {"author", required_argument, NULL, 0},
269       {"title", required_argument, NULL, 0},
270       {0, 0, 0, 0}
271    };
272    int print_bitrate=0;
273    celt_int32_t rate=0;
274    celt_int32_t size;
275    int chan=1;
276    int fmt=16;
277    int lsb=1;
278    ogg_stream_state os;
279    ogg_stream_state so; /* ogg stream for skeleton bitstream */
280    ogg_page              og;
281    ogg_packet            op;
282    int bytes_written=0, ret, result;
283    int id=-1;
284    CELTHeader header;
285    celt_int32_t complexity=3;
286    char vendor_string[64];
287    char *comments;
288    int comments_length;
289    int close_in=0, close_out=0;
290    int eos=0;
291    celt_int32_t bitrate=0;
292    double cumul_bits=0, enc_frames=0;
293    char first_bytes[12];
294    int wave_input=0;
295    celt_int32_t tmp;
296    celt_int32_t lookahead = 0;
297    int bytes_per_packet=48;
298    
299    snprintf(vendor_string, sizeof(vendor_string), "Encoded with CELT\n");
300    
301    comment_init(&comments, &comments_length, vendor_string);
302
303    /*Process command-line options*/
304    while(1)
305    {
306       c = getopt_long (argc, argv, "hvV",
307                        long_options, &option_index);
308       if (c==-1)
309          break;
310       
311       switch(c)
312       {
313       case 0:
314          if (strcmp(long_options[option_index].name,"bitrate")==0)
315          {
316             bitrate = atoi (optarg);
317          } else if (strcmp(long_options[option_index].name,"skeleton")==0)
318          {
319             with_skeleton=1;
320          } else if (strcmp(long_options[option_index].name,"help")==0)
321          {
322             usage();
323             exit(0);
324          } else if (strcmp(long_options[option_index].name,"quiet")==0)
325          {
326             quiet = 1;
327          } else if (strcmp(long_options[option_index].name,"version")==0)
328          {
329             version();
330             exit(0);
331          } else if (strcmp(long_options[option_index].name,"version-short")==0)
332          {
333             version_short();
334             exit(0);
335          } else if (strcmp(long_options[option_index].name,"le")==0)
336          {
337             lsb=1;
338          } else if (strcmp(long_options[option_index].name,"be")==0)
339          {
340             lsb=0;
341          } else if (strcmp(long_options[option_index].name,"8bit")==0)
342          {
343             fmt=8;
344          } else if (strcmp(long_options[option_index].name,"16bit")==0)
345          {
346             fmt=16;
347          } else if (strcmp(long_options[option_index].name,"stereo")==0)
348          {
349             chan=2;
350             mode = celt_stereo;
351          } else if (strcmp(long_options[option_index].name,"rate")==0)
352          {
353             rate=atoi (optarg);
354          } else if (strcmp(long_options[option_index].name,"comment")==0)
355          {
356            if (!strchr(optarg, '='))
357            {
358              fprintf (stderr, "Invalid comment: %s\n", optarg);
359              fprintf (stderr, "Comments must be of the form name=value\n");
360              exit(1);
361            }
362            comment_add(&comments, &comments_length, NULL, optarg); 
363          } else if (strcmp(long_options[option_index].name,"author")==0)
364          {
365            comment_add(&comments, &comments_length, "author=", optarg); 
366          } else if (strcmp(long_options[option_index].name,"title")==0)
367          {
368            comment_add(&comments, &comments_length, "title=", optarg); 
369          }
370
371          break;
372       case 'h':
373          usage();
374          exit(0);
375          break;
376       case 'v':
377          version();
378          exit(0);
379          break;
380       case 'V':
381          print_bitrate=1;
382          break;
383       case '?':
384          usage();
385          exit(1);
386          break;
387       }
388    }
389    if (argc-optind!=2)
390    {
391       usage();
392       exit(1);
393    }
394    inFile=argv[optind];
395    outFile=argv[optind+1];
396
397    /*Initialize Ogg stream struct*/
398    srand(time(NULL));
399    if (ogg_stream_init(&os, rand())==-1)
400    {
401       fprintf(stderr,"Error: stream init failed\n");
402       exit(1);
403    }
404    if (with_skeleton && ogg_stream_init(&so, rand())==-1)
405    {
406       fprintf(stderr,"Error: stream init failed\n");
407       exit(1);
408    }
409
410    if (strcmp(inFile, "-")==0)
411    {
412 #if defined WIN32 || defined _WIN32
413          _setmode(_fileno(stdin), _O_BINARY);
414 #elif defined OS2
415          _fsetmode(stdin,"b");
416 #endif
417       fin=stdin;
418    }
419    else 
420    {
421       fin = fopen(inFile, "rb");
422       if (!fin)
423       {
424          perror(inFile);
425          exit(1);
426       }
427       close_in=1;
428    }
429
430    {
431       fread(first_bytes, 1, 12, fin);
432       if (strncmp(first_bytes,"RIFF",4)==0 && strncmp(first_bytes,"RIFF",4)==0)
433       {
434          if (read_wav_header(fin, &rate, &chan, &fmt, &size)==-1)
435             exit(1);
436          wave_input=1;
437          lsb=1; /* CHECK: exists big-endian .wav ?? */
438       }
439    }
440
441    celt_mode_info(mode, CELT_GET_FRAME_SIZE, &frame_size);
442    celt_init_header(&header, rate, 1, mode);
443    header.nb_channels = chan;
444
445    {
446       char *st_string="mono";
447       if (chan==2)
448          st_string="stereo";
449       if (!quiet)
450          fprintf (stderr, "Encoding %d Hz audio using %s\n", 
451                header.sample_rate, st_string);
452    }
453    /*fprintf (stderr, "Encoding %d Hz audio at %d bps using %s mode\n", 
454      header.rate, mode->bitrate, mode->modeName);*/
455
456    /*Initialize Speex encoder*/
457    st = celt_encoder_new(mode);
458
459    if (strcmp(outFile,"-")==0)
460    {
461 #if defined WIN32 || defined _WIN32
462       _setmode(_fileno(stdout), _O_BINARY);
463 #endif
464       fout=stdout;
465    }
466    else 
467    {
468       fout = fopen(outFile, "wb");
469       if (!fout)
470       {
471          perror(outFile);
472          exit(1);
473       }
474       close_out=1;
475    }
476
477    if (with_skeleton) {
478       fprintf (stderr, "Warning: Enabling skeleton output may cause some decoders to fail.\n");
479    }
480
481    /* first packet should be the skeleton header. */
482    if (with_skeleton) {
483       add_fishead_packet(&so);
484       if ((ret = flush_ogg_stream_to_file(&so, fout))) {
485          fprintf (stderr,"Error: failed skeleton (fishead) header to output stream\n");
486          exit(1);
487       } else
488          bytes_written += ret;
489    }
490
491    /*Write header*/
492    {
493       char header_data[100];
494       int packet_size = celt_header_to_packet(&header, header_data, 100);
495       op.packet = header_data;
496       op.bytes = packet_size;
497       fprintf(stderr, "header size is %d\n", op.bytes);
498       op.b_o_s = 1;
499       op.e_o_s = 0;
500       op.granulepos = 0;
501       op.packetno = 0;
502       ogg_stream_packetin(&os, &op);
503
504       while((result = ogg_stream_flush(&os, &og)))
505       {
506          if(!result) break;
507          ret = oe_write_page(&og, fout);
508          if(ret != og.header_len + og.body_len)
509          {
510             fprintf (stderr,"Error: failed writing header to output stream\n");
511             exit(1);
512          }
513          else
514             bytes_written += ret;
515       }
516
517       op.packet = (unsigned char *)comments;
518       op.bytes = comments_length;
519       op.b_o_s = 0;
520       op.e_o_s = 0;
521       op.granulepos = 0;
522       op.packetno = 1;
523       ogg_stream_packetin(&os, &op);
524    }
525
526    /* fisbone packet should be write after all bos pages */
527    if (with_skeleton) {
528       add_fisbone_packet(&so, os.serialno, &header);
529       if ((ret = flush_ogg_stream_to_file(&so, fout))) {
530          fprintf (stderr,"Error: failed writing skeleton (fisbone )header to output stream\n");
531          exit(1);
532       } else
533          bytes_written += ret;
534    }
535
536    /* writing the rest of the speex header packets */
537    while((result = ogg_stream_flush(&os, &og)))
538    {
539       if(!result) break;
540       ret = oe_write_page(&og, fout);
541       if(ret != og.header_len + og.body_len)
542       {
543          fprintf (stderr,"Error: failed writing header to output stream\n");
544          exit(1);
545       }
546       else
547          bytes_written += ret;
548    }
549
550    free(comments);
551
552    /* write the skeleton eos packet */
553    if (with_skeleton) {
554       add_eos_packet_to_stream(&so);
555       if ((ret = flush_ogg_stream_to_file(&so, fout))) {
556          fprintf (stderr,"Error: failed writing skeleton header to output stream\n");
557          exit(1);
558       } else
559          bytes_written += ret;
560    }
561
562
563    if (!wave_input)
564    {
565       nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, first_bytes, NULL);
566    } else {
567       nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size);
568    }
569    if (nb_samples==0)
570       eos=1;
571    total_samples += nb_samples;
572    nb_encoded = -lookahead;
573    /*Main encoding loop (one frame per iteration)*/
574    while (!eos || total_samples>nb_encoded)
575    {
576       id++;
577       /*Encode current frame*/
578
579       nbBytes = celt_encode(st, input, bits, bytes_per_packet);
580       
581       nb_encoded += frame_size;
582
583       if (wave_input)
584       {
585          nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size);
586       } else {
587          nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, NULL);
588       }
589       if (nb_samples==0)
590       {
591          eos=1;
592       }
593       if (eos && total_samples<=nb_encoded)
594          op.e_o_s = 1;
595       else
596          op.e_o_s = 0;
597       total_samples += nb_samples;
598
599       op.packet = (unsigned char *)bits;
600       op.bytes = nbBytes;
601       op.b_o_s = 0;
602       /*Is this redundent?*/
603       if (eos && total_samples<=nb_encoded)
604          op.e_o_s = 1;
605       else
606          op.e_o_s = 0;
607       op.granulepos = (id+1)*frame_size-lookahead;
608       if (op.granulepos>total_samples)
609          op.granulepos = total_samples;
610       /*printf ("granulepos: %d %d %d %d %d %d\n", (int)op.granulepos, id, nframes, lookahead, 5, 6);*/
611       op.packetno = 2+id;
612       ogg_stream_packetin(&os, &op);
613
614       /*Write all new pages (most likely 0 or 1)*/
615       while (ogg_stream_pageout(&os,&og))
616       {
617          ret = oe_write_page(&og, fout);
618          if(ret != og.header_len + og.body_len)
619          {
620             fprintf (stderr,"Error: failed writing header to output stream\n");
621             exit(1);
622          }
623          else
624             bytes_written += ret;
625       }
626    }
627    /*Flush all pages left to be written*/
628    while (ogg_stream_flush(&os, &og))
629    {
630       ret = oe_write_page(&og, fout);
631       if(ret != og.header_len + og.body_len)
632       {
633          fprintf (stderr,"Error: failed writing header to output stream\n");
634          exit(1);
635       }
636       else
637          bytes_written += ret;
638    }
639
640    celt_encoder_destroy(st);
641    ogg_stream_clear(&os);
642
643    if (close_in)
644       fclose(fin);
645    if (close_out)
646       fclose(fout);
647    return 0;
648 }
649
650 /*                 
651  Comments will be stored in the Vorbis style.            
652  It is describled in the "Structure" section of
653     http://www.xiph.org/ogg/vorbis/doc/v-comment.html
654
655 The comment header is decoded as follows:
656   1) [vendor_length] = read an unsigned integer of 32 bits
657   2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
658   3) [user_comment_list_length] = read an unsigned integer of 32 bits
659   4) iterate [user_comment_list_length] times {
660      5) [length] = read an unsigned integer of 32 bits
661      6) this iteration's user comment = read a UTF-8 vector as [length] octets
662      }
663   7) [framing_bit] = read a single bit as boolean
664   8) if ( [framing_bit]  unset or end of packet ) then ERROR
665   9) done.
666
667   If you have troubles, please write to ymnk@jcraft.com.
668  */
669
670 #define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
671                            ((buf[base+2]<<16)&0xff0000)| \
672                            ((buf[base+1]<<8)&0xff00)| \
673                             (buf[base]&0xff))
674 #define writeint(buf, base, val) do{ buf[base+3]=((val)>>24)&0xff; \
675                                      buf[base+2]=((val)>>16)&0xff; \
676                                      buf[base+1]=((val)>>8)&0xff; \
677                                      buf[base]=(val)&0xff; \
678                                  }while(0)
679
680 void comment_init(char **comments, int* length, char *vendor_string)
681 {
682   int vendor_length=strlen(vendor_string);
683   int user_comment_list_length=0;
684   int len=4+vendor_length+4;
685   char *p=(char*)malloc(len);
686   if(p==NULL){
687      fprintf (stderr, "malloc failed in comment_init()\n");
688      exit(1);
689   }
690   writeint(p, 0, vendor_length);
691   memcpy(p+4, vendor_string, vendor_length);
692   writeint(p, 4+vendor_length, user_comment_list_length);
693   *length=len;
694   *comments=p;
695 }
696 void comment_add(char **comments, int* length, char *tag, char *val)
697 {
698   char* p=*comments;
699   int vendor_length=readint(p, 0);
700   int user_comment_list_length=readint(p, 4+vendor_length);
701   int tag_len=(tag?strlen(tag):0);
702   int val_len=strlen(val);
703   int len=(*length)+4+tag_len+val_len;
704
705   p=(char*)realloc(p, len);
706   if(p==NULL){
707      fprintf (stderr, "realloc failed in comment_add()\n");
708      exit(1);
709   }
710
711   writeint(p, *length, tag_len+val_len);      /* length of comment */
712   if(tag) memcpy(p+*length+4, tag, tag_len);  /* comment */
713   memcpy(p+*length+4+tag_len, val, val_len);  /* comment */
714   writeint(p, 4+vendor_length, user_comment_list_length+1);
715
716   *comments=p;
717   *length=len;
718 }
719 #undef readint
720 #undef writeint