VBR support. VBR API and VBR support in celtenc.
[opus.git] / tools / celtenc.c
1 /* Copyright (C) 2002-2008 Jean-Marc Valin 
2    Copyright (C) 2008-2009 Gregory Maxwell
3    File: celtenc.c
4
5    Redistribution and use in source and binary forms, with or without
6    modification, are permitted provided that the following conditions
7    are met:
8    
9    - Redistributions of source code must retain the above copyright
10    notice, this list of conditions and the following disclaimer.
11    
12    - Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in the
14    documentation and/or other materials provided with the distribution.
15    
16    - Neither the name of the Xiph.org Foundation nor the names of its
17    contributors may be used to endorse or promote products derived from
18    this software without specific prior written permission.
19    
20    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
24    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 #include <stdio.h>
38 #if !defined WIN32 && !defined _WIN32
39 #include <unistd.h>
40 #endif
41
42 #ifdef HAVE_GETOPT_H
43 #include <getopt.h>
44 #endif
45
46 #ifndef HAVE_GETOPT_LONG
47 #include "getopt_win.h"
48 #endif
49
50 #include <stdlib.h>
51 #include <string.h>
52 #include <time.h>
53
54 #include "celt.h"
55 #include "celt_header.h"
56 #include <ogg/ogg.h>
57 #include "wav_io.h"
58
59 #if defined WIN32 || defined _WIN32
60 /* We need the following two to set stdout to binary */
61 #include <io.h>
62 #include <fcntl.h>
63 #endif
64
65 #include "skeleton.h"
66
67
68 void comment_init(char **comments, int* length, char *vendor_string);
69 void comment_add(char **comments, int* length, char *tag, char *val);
70
71
72 /*Write an Ogg page to a file pointer*/
73 int oe_write_page(ogg_page *page, FILE *fp)
74 {
75    int written;
76    written = fwrite(page->header,1,page->header_len, fp);
77    written += fwrite(page->body,1,page->body_len, fp);
78    
79    return written;
80 }
81
82 #define MAX_FRAME_SIZE 2000
83 #define MAX_FRAME_BYTES 300
84 #define IMIN(a,b) ((a) < (b) ? (a) : (b))   /**< Minimum int value.   */
85 #define IMAX(a,b) ((a) > (b) ? (a) : (b))   /**< Maximum int value.   */
86
87 /* Convert input audio bits, endians and channels */
88 static int read_samples(FILE *fin,int frame_size, int bits, int channels, int lsb, short * input, char *buff, celt_int32_t *size)
89 {   
90    unsigned char in[MAX_FRAME_SIZE*2];
91    int i;
92    short *s;
93    int nb_read;
94
95    if (size && *size<=0)
96    {
97       return 0;
98    }
99    /*Read input audio*/
100    if (size)
101       *size -= bits/8*channels*frame_size;
102    if (buff)
103    {
104       for (i=0;i<12;i++)
105          in[i]=buff[i];
106       nb_read = fread(in+12,1,bits/8*channels*frame_size-12, fin) + 12;
107       if (size)
108          *size += 12;
109    } else {
110       nb_read = fread(in,1,bits/8*channels* frame_size, fin);
111    }
112    nb_read /= bits/8*channels;
113
114    /*fprintf (stderr, "%d\n", nb_read);*/
115    if (nb_read==0)
116       return 0;
117
118    s=(short*)in;
119    if(bits==8)
120    {
121       /* Convert 8->16 bits */
122       for(i=frame_size*channels-1;i>=0;i--)
123       {
124          s[i]=(in[i]<<8)^0x8000;
125       }
126    } else
127    {
128       /* convert to our endian format */
129       for(i=0;i<frame_size*channels;i++)
130       {
131          if(lsb) 
132             s[i]=le_short(s[i]); 
133          else
134             s[i]=be_short(s[i]);
135       }
136    }
137
138    /* FIXME: This is probably redundent now */
139    /* copy to float input buffer */
140    for (i=0;i<frame_size*channels;i++)
141    {
142       input[i]=(short)s[i];
143    }
144
145    for (i=nb_read*channels;i<frame_size*channels;i++)
146    {
147       input[i]=0;
148    }
149
150
151    return nb_read;
152 }
153
154 void add_fishead_packet (ogg_stream_state *os) {
155
156    fishead_packet fp;
157
158    memset(&fp, 0, sizeof(fp));
159    fp.ptime_n = 0;
160    fp.ptime_d = 1000;
161    fp.btime_n = 0;
162    fp.btime_d = 1000;
163
164    add_fishead_to_stream(os, &fp);
165 }
166
167 /*
168  * Adds the fishead packets in the skeleton output stream along with the e_o_s packet
169  */
170 void add_fisbone_packet (ogg_stream_state *os, celt_int32_t serialno, CELTHeader *header) {
171
172    fisbone_packet fp;
173
174    memset(&fp, 0, sizeof(fp));
175    fp.serial_no = serialno;
176    fp.nr_header_packet = 2 + header->extra_headers;
177    fp.granule_rate_n = header->sample_rate;
178    fp.granule_rate_d = 1;
179    fp.start_granule = 0;
180    fp.preroll = 3;
181    fp.granule_shift = 0;
182
183    add_message_header_field(&fp, "Content-Type", "audio/x-celt");
184
185    add_fisbone_to_stream(os, &fp);
186 }
187
188 void version(void)
189 {
190    printf ("celtenc (CELT %s encoder)\n",CELT_VERSION);
191    printf ("Copyright (C) 2008 Jean-Marc Valin\n");
192 }
193
194 void version_short(void)
195 {
196    printf ("celtenc (CELT %s encoder)\n",CELT_VERSION);
197    printf ("Copyright (C) 2008 Jean-Marc Valin\n");
198 }
199
200 void usage(void)
201 {
202    printf ("Usage: celtenc [options] input_file output_file.oga\n");
203    printf ("\n");
204    printf ("Encodes input_file using CELT. It can read the WAV or raw files.\n");
205    printf ("\n");
206    printf ("input_file can be:\n");
207    printf ("  filename.wav      wav file\n");
208    printf ("  filename.*        Raw PCM file (any extension other than .wav)\n");
209    printf ("  -                 stdin\n");
210    printf ("\n");  
211    printf ("output_file can be:\n");
212    printf ("  filename.oga      compressed file\n");
213    printf ("  -                 stdout\n");
214    printf ("\n");  
215    printf ("Options:\n");
216    printf (" --bitrate n        Encoding bit-rate in kbit/sec\n"); 
217    printf (" --vbr              Use variable bitrate encoding\n"); 
218    printf (" --comp n           Encoding complexity (0-10)\n");
219    printf (" --framesize n      Frame size (Default: 256)\n");
220    printf (" --skeleton         Outputs ogg skeleton metadata (may cause incompatibilities)\n");
221    printf (" --comment          Add the given string as an extra comment. This may be\n");
222    printf ("                     used multiple times\n");
223    printf (" --author           Author of this track\n");
224    printf (" --title            Title for this track\n");
225    printf (" -h, --help         This help\n"); 
226    printf (" -v, --version      Version information\n"); 
227    printf (" -V                 Verbose mode (show bit-rate)\n"); 
228    printf ("Raw input options:\n");
229    printf (" --rate n           Sampling rate for raw input\n"); 
230    printf (" --mono             Consider raw input as mono\n"); 
231    printf (" --stereo           Consider raw input as stereo\n"); 
232    printf (" --le               Raw input is little-endian\n"); 
233    printf (" --be               Raw input is big-endian\n"); 
234    printf (" --8bit             Raw input is 8-bit unsigned\n"); 
235    printf (" --16bit            Raw input is 16-bit signed\n"); 
236    printf ("Default raw PCM input is 16-bit, little-endian, mono\n"); 
237 }
238
239
240 int main(int argc, char **argv)
241 {
242    int nb_samples, total_samples=0, nb_encoded;
243    int c;
244    int option_index = 0;
245    char *inFile, *outFile;
246    FILE *fin, *fout;
247    short input[MAX_FRAME_SIZE];
248    celt_int32_t frame_size = 256;
249    int quiet=0;
250    int nbBytes;
251    CELTMode *mode;
252    void *st;
253    unsigned char bits[MAX_FRAME_BYTES];
254    int with_vbr = 0;
255    int with_skeleton = 0;
256    int total_bytes = 0;
257    int peak_bytes = 0;
258    struct option long_options[] =
259    {
260       {"bitrate", required_argument, NULL, 0},
261       {"vbr",no_argument,NULL, 0},
262       {"comp", required_argument, NULL, 0},
263       {"framesize", required_argument, NULL, 0},
264       {"skeleton",no_argument,NULL, 0},
265       {"help", no_argument, NULL, 0},
266       {"quiet", no_argument, NULL, 0},
267       {"le", no_argument, NULL, 0},
268       {"be", no_argument, NULL, 0},
269       {"8bit", no_argument, NULL, 0},
270       {"16bit", no_argument, NULL, 0},
271       {"mono", no_argument, NULL, 0},
272       {"stereo", no_argument, NULL, 0},
273       {"rate", required_argument, NULL, 0},
274       {"version", no_argument, NULL, 0},
275       {"version-short", no_argument, NULL, 0},
276       {"comment", required_argument, NULL, 0},
277       {"author", required_argument, NULL, 0},
278       {"title", required_argument, NULL, 0},
279       {0, 0, 0, 0}
280    };
281    int print_bitrate=0;
282    celt_int32_t rate=44100;
283    celt_int32_t size;
284    int chan=1;
285    int fmt=16;
286    int lsb=1;
287    ogg_stream_state os;
288    ogg_stream_state so; /* ogg stream for skeleton bitstream */
289    ogg_page              og;
290    ogg_packet            op;
291    int bytes_written=0, ret, result;
292    int id=-1;
293    CELTHeader header;
294    char vendor_string[64];
295    char *comments;
296    int comments_length;
297    int close_in=0, close_out=0;
298    int eos=0;
299    float bitrate=-1;
300    char first_bytes[12];
301    int wave_input=0;
302    celt_int32_t lookahead = 0;
303    int bytes_per_packet=48;
304    int complexity=-127;
305    int bitstream;
306
307
308    /*Process command-line options*/
309    while(1)
310    {
311       c = getopt_long (argc, argv, "hvV",
312                        long_options, &option_index);
313       if (c==-1)
314          break;
315       
316       switch(c)
317       {
318       case 0:
319          if (strcmp(long_options[option_index].name,"bitrate")==0)
320          {
321             bitrate = atof (optarg);
322          } else if (strcmp(long_options[option_index].name,"vbr")==0)
323          {
324             with_vbr=1;
325          } else if (strcmp(long_options[option_index].name,"skeleton")==0)
326          {
327             with_skeleton=1;
328          } else if (strcmp(long_options[option_index].name,"help")==0)
329          {
330             usage();
331             exit(0);
332          } else if (strcmp(long_options[option_index].name,"quiet")==0)
333          {
334             quiet = 1;
335          } else if (strcmp(long_options[option_index].name,"version")==0)
336          {
337             version();
338             exit(0);
339          } else if (strcmp(long_options[option_index].name,"version-short")==0)
340          {
341             version_short();
342             exit(0);
343          } else if (strcmp(long_options[option_index].name,"le")==0)
344          {
345             lsb=1;
346          } else if (strcmp(long_options[option_index].name,"be")==0)
347          {
348             lsb=0;
349          } else if (strcmp(long_options[option_index].name,"8bit")==0)
350          {
351             fmt=8;
352          } else if (strcmp(long_options[option_index].name,"16bit")==0)
353          {
354             fmt=16;
355          } else if (strcmp(long_options[option_index].name,"stereo")==0)
356          {
357             chan=2;
358          } else if (strcmp(long_options[option_index].name,"mono")==0)
359          {
360             chan=1;
361          } else if (strcmp(long_options[option_index].name,"rate")==0)
362          {
363             rate=atoi (optarg);
364          } else if (strcmp(long_options[option_index].name,"comp")==0)
365          {
366             complexity=atoi (optarg);
367          } else if (strcmp(long_options[option_index].name,"framesize")==0)
368          {
369             frame_size=atoi (optarg);
370          } else if (strcmp(long_options[option_index].name,"comment")==0)
371          {
372            if (!strchr(optarg, '='))
373            {
374              fprintf (stderr, "Invalid comment: %s\n", optarg);
375              fprintf (stderr, "Comments must be of the form name=value\n");
376              exit(1);
377            }
378            comment_add(&comments, &comments_length, NULL, optarg); 
379          } else if (strcmp(long_options[option_index].name,"author")==0)
380          {
381            comment_add(&comments, &comments_length, "author=", optarg); 
382          } else if (strcmp(long_options[option_index].name,"title")==0)
383          {
384            comment_add(&comments, &comments_length, "title=", optarg); 
385          }
386
387          break;
388       case 'h':
389          usage();
390          exit(0);
391          break;
392       case 'v':
393          version();
394          exit(0);
395          break;
396       case 'V':
397          print_bitrate=1;
398          break;
399       case '?':
400          usage();
401          exit(1);
402          break;
403       }
404    }
405    if (argc-optind!=2)
406    {
407       usage();
408       exit(1);
409    }
410    inFile=argv[optind];
411    outFile=argv[optind+1];
412
413    /*Initialize Ogg stream struct*/
414    srand(time(NULL));
415    if (ogg_stream_init(&os, rand())==-1)
416    {
417       fprintf(stderr,"Error: stream init failed\n");
418       exit(1);
419    }
420    if (with_skeleton && ogg_stream_init(&so, rand())==-1)
421    {
422       fprintf(stderr,"Error: stream init failed\n");
423       exit(1);
424    }
425
426    if (strcmp(inFile, "-")==0)
427    {
428 #if defined WIN32 || defined _WIN32
429          _setmode(_fileno(stdin), _O_BINARY);
430 #elif defined OS2
431          _fsetmode(stdin,"b");
432 #endif
433       fin=stdin;
434    }
435    else 
436    {
437       fin = fopen(inFile, "rb");
438       if (!fin)
439       {
440          perror(inFile);
441          exit(1);
442       }
443       close_in=1;
444    }
445
446    {
447       fread(first_bytes, 1, 12, fin);
448       if (strncmp(first_bytes,"RIFF",4)==0 && strncmp(first_bytes,"RIFF",4)==0)
449       {
450          if (read_wav_header(fin, &rate, &chan, &fmt, &size)==-1)
451             exit(1);
452          wave_input=1;
453          lsb=1; /* CHECK: exists big-endian .wav ?? */
454       }
455    }
456
457    if (bitrate<=0.005)
458      if (chan==1)
459        bitrate=64.0;
460      else
461        bitrate=128.0;
462      
463    bytes_per_packet = (bitrate*1000*frame_size/rate+4)/8;
464    
465    if (bytes_per_packet < 8) {
466       bytes_per_packet=8;
467       fprintf (stderr, "Warning: Requested bitrate (%0.3fkbit/sec) is too low. Setting CELT to 8 bytes/frame.\n",bitrate);
468    } else if (bytes_per_packet > MAX_FRAME_BYTES) {
469       bytes_per_packet=MAX_FRAME_BYTES;
470       fprintf (stderr, "Warning: Requested bitrate (%0.3fkbit/sec) is too high. Setting CELT to %d bytes/frame.\n",bitrate,MAX_FRAME_BYTES);      
471    }
472
473    if (with_vbr)
474    {
475      /*In VBR mode the bytes_per_packet argument becomes a hard maximum. 3x the average rate is just a random choice.*/
476      bytes_per_packet=IMIN(bytes_per_packet*3,MAX_FRAME_BYTES);
477    } else { 
478      bitrate = ((rate/(float)frame_size)*8*bytes_per_packet)/1000.0;
479    }
480    
481    mode = celt_mode_create(rate, chan, frame_size, NULL);
482    if (!mode)
483       return 1;
484
485   celt_mode_info(mode,CELT_GET_BITSTREAM_VERSION,&bitstream);      
486
487    snprintf(vendor_string, sizeof(vendor_string), "Encoded with CELT %s (bitstream: %d)\n",CELT_VERSION,bitstream);
488    comment_init(&comments, &comments_length, vendor_string);
489
490    celt_mode_info(mode, CELT_GET_FRAME_SIZE, &frame_size);   
491    
492    celt_header_init(&header, mode);
493    header.nb_channels = chan;
494
495    {
496       char *st_string="mono";
497       if (chan==2)
498          st_string="stereo";
499       if (!quiet)
500          if (with_vbr)
501            fprintf (stderr, "Encoding %d Hz %s audio in %d sample packets at %0.3fkbit/sec (%d maximum bytes per packet) with bitstream version %d\n", 
502                header.sample_rate, st_string, frame_size, bitrate, bytes_per_packet,bitstream);
503          else      
504            fprintf (stderr, "Encoding %d Hz %s audio in %d sample packets at %0.3fkbit/sec (%d bytes per packet) with bitstream version %d\n", 
505                header.sample_rate, st_string, frame_size, bitrate, bytes_per_packet,bitstream);
506    }
507
508    /*Initialize CELT encoder*/
509    st = celt_encoder_create(mode);
510
511    if (with_vbr)
512    {
513      int tmp = (bitrate*1000);
514      if (celt_encoder_ctl(st, CELT_SET_VBR_RATE(tmp)) != CELT_OK)
515      {
516         fprintf (stderr, "VBR request failed\n");
517         return 1;
518      }
519    }     
520
521    if (complexity!=-127) {
522      if (celt_encoder_ctl(st, CELT_SET_COMPLEXITY(complexity)) != CELT_OK)
523      {
524         fprintf (stderr, "Only complexity 0 through 10 is supported\n");
525         return 1;
526      }
527    }
528
529    if (strcmp(outFile,"-")==0)
530    {
531 #if defined WIN32 || defined _WIN32
532       _setmode(_fileno(stdout), _O_BINARY);
533 #endif
534       fout=stdout;
535    }
536    else 
537    {
538       fout = fopen(outFile, "wb");
539       if (!fout)
540       {
541          perror(outFile);
542          exit(1);
543       }
544       close_out=1;
545    }
546
547    if (with_skeleton) {
548       fprintf (stderr, "Warning: Enabling skeleton output may cause some decoders to fail.\n");
549    }
550
551    /* first packet should be the skeleton header. */
552    if (with_skeleton) {
553       add_fishead_packet(&so);
554       if ((ret = flush_ogg_stream_to_file(&so, fout))) {
555          fprintf (stderr,"Error: failed skeleton (fishead) header to output stream\n");
556          exit(1);
557       } else
558          bytes_written += ret;
559    }
560
561    /*Write header*/
562    {
563       unsigned char header_data[100];
564       int packet_size = celt_header_to_packet(&header, header_data, 100);
565       op.packet = header_data;
566       op.bytes = packet_size;
567       op.b_o_s = 1;
568       op.e_o_s = 0;
569       op.granulepos = 0;
570       op.packetno = 0;
571       ogg_stream_packetin(&os, &op);
572
573       while((result = ogg_stream_flush(&os, &og)))
574       {
575          if(!result) break;
576          ret = oe_write_page(&og, fout);
577          if(ret != og.header_len + og.body_len)
578          {
579             fprintf (stderr,"Error: failed writing header to output stream\n");
580             exit(1);
581          }
582          else
583             bytes_written += ret;
584       }
585
586       op.packet = (unsigned char *)comments;
587       op.bytes = comments_length;
588       op.b_o_s = 0;
589       op.e_o_s = 0;
590       op.granulepos = 0;
591       op.packetno = 1;
592       ogg_stream_packetin(&os, &op);
593    }
594
595    /* fisbone packet should be write after all bos pages */
596    if (with_skeleton) {
597       add_fisbone_packet(&so, os.serialno, &header);
598       if ((ret = flush_ogg_stream_to_file(&so, fout))) {
599          fprintf (stderr,"Error: failed writing skeleton (fisbone )header to output stream\n");
600          exit(1);
601       } else
602          bytes_written += ret;
603    }
604
605    /* writing the rest of the celt header packets */
606    while((result = ogg_stream_flush(&os, &og)))
607    {
608       if(!result) break;
609       ret = oe_write_page(&og, fout);
610       if(ret != og.header_len + og.body_len)
611       {
612          fprintf (stderr,"Error: failed writing header to output stream\n");
613          exit(1);
614       }
615       else
616          bytes_written += ret;
617    }
618
619    free(comments);
620
621    /* write the skeleton eos packet */
622    if (with_skeleton) {
623       add_eos_packet_to_stream(&so);
624       if ((ret = flush_ogg_stream_to_file(&so, fout))) {
625          fprintf (stderr,"Error: failed writing skeleton header to output stream\n");
626          exit(1);
627       } else
628          bytes_written += ret;
629    }
630
631
632    if (!wave_input)
633    {
634       nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, first_bytes, NULL);
635    } else {
636       nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size);
637    }
638    if (nb_samples==0)
639       eos=1;
640    total_samples += nb_samples;
641    nb_encoded = -lookahead;
642    /*Main encoding loop (one frame per iteration)*/
643    while (!eos || total_samples>nb_encoded)
644    {
645       id++;
646       /*Encode current frame*/
647
648       nbBytes = celt_encode(st, input, NULL, bits, bytes_per_packet);
649       if (nbBytes<0)
650       {
651          fprintf(stderr, "Got error %d while encoding. Aborting.\n", nbBytes);
652          break;
653       }
654       nb_encoded += frame_size;
655       total_bytes += nbBytes;
656       peak_bytes=IMAX(nbBytes,peak_bytes);
657
658       if (wave_input)
659       {
660          nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size);
661       } else {
662          nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, NULL);
663       }
664       if (nb_samples==0)
665       {
666          eos=1;
667       }
668       if (eos && total_samples<=nb_encoded)
669          op.e_o_s = 1;
670       else
671          op.e_o_s = 0;
672       total_samples += nb_samples;
673
674       op.packet = (unsigned char *)bits;
675       op.bytes = nbBytes;
676       op.b_o_s = 0;
677       /*Is this redundent?*/
678       if (eos && total_samples<=nb_encoded)
679          op.e_o_s = 1;
680       else
681          op.e_o_s = 0;
682       op.granulepos = (id+1)*frame_size-lookahead;
683       if (op.granulepos>total_samples)
684          op.granulepos = total_samples;
685       /*printf ("granulepos: %d %d %d %d %d %d\n", (int)op.granulepos, id, nframes, lookahead, 5, 6);*/
686       op.packetno = 2+id;
687       ogg_stream_packetin(&os, &op);
688
689       /*Write all new pages (most likely 0 or 1)*/
690       while (ogg_stream_pageout(&os,&og))
691       {
692          ret = oe_write_page(&og, fout);
693          if(ret != og.header_len + og.body_len)
694          {
695             fprintf (stderr,"Error: failed writing header to output stream\n");
696             exit(1);
697          }
698          else
699             bytes_written += ret;
700       }
701    }
702    /*Flush all pages left to be written*/
703    while (ogg_stream_flush(&os, &og))
704    {
705       ret = oe_write_page(&og, fout);
706       if(ret != og.header_len + og.body_len)
707       {
708          fprintf (stderr,"Error: failed writing header to output stream\n");
709          exit(1);
710       }
711       else
712          bytes_written += ret;
713    }
714
715    if (with_vbr && !quiet)
716      fprintf (stderr, "Average rate %0.3fkbit/sec, %d peak bytes per packet\n", (total_bytes*8.0/((float)nb_encoded/header.sample_rate))/1000.0, peak_bytes);
717
718    celt_encoder_destroy(st);
719    celt_mode_destroy(mode);
720    ogg_stream_clear(&os);
721
722    if (close_in)
723       fclose(fin);
724    if (close_out)
725       fclose(fout);
726    return 0;
727 }
728
729 /*                 
730  Comments will be stored in the Vorbis style.            
731  It is describled in the "Structure" section of
732     http://www.xiph.org/ogg/vorbis/doc/v-comment.html
733
734 The comment header is decoded as follows:
735   1) [vendor_length] = read an unsigned integer of 32 bits
736   2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
737   3) [user_comment_list_length] = read an unsigned integer of 32 bits
738   4) iterate [user_comment_list_length] times {
739      5) [length] = read an unsigned integer of 32 bits
740      6) this iteration's user comment = read a UTF-8 vector as [length] octets
741      }
742   7) [framing_bit] = read a single bit as boolean
743   8) if ( [framing_bit]  unset or end of packet ) then ERROR
744   9) done.
745
746   If you have troubles, please write to ymnk@jcraft.com.
747  */
748
749 #define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
750                            ((buf[base+2]<<16)&0xff0000)| \
751                            ((buf[base+1]<<8)&0xff00)| \
752                             (buf[base]&0xff))
753 #define writeint(buf, base, val) do{ buf[base+3]=((val)>>24)&0xff; \
754                                      buf[base+2]=((val)>>16)&0xff; \
755                                      buf[base+1]=((val)>>8)&0xff; \
756                                      buf[base]=(val)&0xff; \
757                                  }while(0)
758
759 void comment_init(char **comments, int* length, char *vendor_string)
760 {
761   int vendor_length=strlen(vendor_string);
762   int user_comment_list_length=0;
763   int len=4+vendor_length+4;
764   char *p=(char*)malloc(len);
765   if(p==NULL){
766      fprintf (stderr, "malloc failed in comment_init()\n");
767      exit(1);
768   }
769   writeint(p, 0, vendor_length);
770   memcpy(p+4, vendor_string, vendor_length);
771   writeint(p, 4+vendor_length, user_comment_list_length);
772   *length=len;
773   *comments=p;
774 }
775 void comment_add(char **comments, int* length, char *tag, char *val)
776 {
777   char* p=*comments;
778   int vendor_length=readint(p, 0);
779   int user_comment_list_length=readint(p, 4+vendor_length);
780   int tag_len=(tag?strlen(tag):0);
781   int val_len=strlen(val);
782   int len=(*length)+4+tag_len+val_len;
783
784   p=(char*)realloc(p, len);
785   if(p==NULL){
786      fprintf (stderr, "realloc failed in comment_add()\n");
787      exit(1);
788   }
789
790   writeint(p, *length, tag_len+val_len);      /* length of comment */
791   if(tag) memcpy(p+*length+4, tag, tag_len);  /* comment */
792   memcpy(p+*length+4+tag_len, val, val_len);  /* comment */
793   writeint(p, 4+vendor_length, user_comment_list_length+1);
794
795   *comments=p;
796   *length=len;
797 }
798 #undef readint
799 #undef writeint