Add support for a complexity argument to both testcelt and celtenc.
[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(void)
186 {
187    printf ("celtenc (CELT encoder)\n");
188    printf ("Copyright (C) 2008 Jean-Marc Valin\n");
189 }
190
191 void version_short(void)
192 {
193    printf ("celtenc (CELT encoder)\n");
194    printf ("Copyright (C) 2008 Jean-Marc Valin\n");
195 }
196
197 void usage(void)
198 {
199    printf ("Usage: celtenc [options] input_file output_file.oga\n");
200    printf ("\n");
201    printf ("Encodes input_file using CELT. 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\n"); 
214    printf (" --comp n           Encoding complexity (0-10)\n");
215    printf (" --skeleton         Outputs ogg skeleton metadata (may cause incompatibilities)\n");
216    printf (" --comment          Add the given string as an extra comment. This may be\n");
217    printf ("                     used multiple times\n");
218    printf (" --author           Author of this track\n");
219    printf (" --title            Title for this track\n");
220    printf (" -h, --help         This help\n"); 
221    printf (" -v, --version      Version information\n"); 
222    printf (" -V                 Verbose mode (show bit-rate)\n"); 
223    printf ("Raw input options:\n");
224    printf (" --rate n           Sampling rate for raw input\n"); 
225    printf (" --mono             Consider raw input as mono\n"); 
226    printf (" --stereo           Consider raw input as stereo\n"); 
227    printf (" --le               Raw input is little-endian\n"); 
228    printf (" --be               Raw input is big-endian\n"); 
229    printf (" --8bit             Raw input is 8-bit unsigned\n"); 
230    printf (" --16bit            Raw input is 16-bit signed\n"); 
231    printf ("Default raw PCM input is 16-bit, little-endian, mono\n"); 
232 }
233
234
235 int main(int argc, char **argv)
236 {
237    int nb_samples, total_samples=0, nb_encoded;
238    int c;
239    int option_index = 0;
240    char *inFile, *outFile;
241    FILE *fin, *fout;
242    short input[MAX_FRAME_SIZE];
243    celt_int32_t frame_size;
244    int quiet=0;
245    int nbBytes;
246    CELTMode *mode;
247    void *st;
248    unsigned char bits[MAX_FRAME_BYTES];
249    int with_skeleton = 0;
250    struct option long_options[] =
251    {
252       {"bitrate", required_argument, NULL, 0},
253       {"comp", required_argument, NULL, 0},
254       {"skeleton",no_argument,NULL, 0},
255       {"help", no_argument, NULL, 0},
256       {"quiet", no_argument, NULL, 0},
257       {"le", no_argument, NULL, 0},
258       {"be", no_argument, NULL, 0},
259       {"8bit", no_argument, NULL, 0},
260       {"16bit", no_argument, NULL, 0},
261       {"mono", no_argument, NULL, 0},
262       {"stereo", no_argument, NULL, 0},
263       {"rate", required_argument, NULL, 0},
264       {"version", no_argument, NULL, 0},
265       {"version-short", no_argument, NULL, 0},
266       {"comment", required_argument, NULL, 0},
267       {"author", required_argument, NULL, 0},
268       {"title", required_argument, NULL, 0},
269       {0, 0, 0, 0}
270    };
271    int print_bitrate=0;
272    celt_int32_t rate=44100;
273    celt_int32_t size;
274    int chan=1;
275    int fmt=16;
276    int lsb=1;
277    ogg_stream_state os;
278    ogg_stream_state so; /* ogg stream for skeleton bitstream */
279    ogg_page              og;
280    ogg_packet            op;
281    int bytes_written=0, ret, result;
282    int id=-1;
283    CELTHeader header;
284    char vendor_string[64];
285    char *comments;
286    int comments_length;
287    int close_in=0, close_out=0;
288    int eos=0;
289    celt_int32_t bitrate=-1;
290    char first_bytes[12];
291    int wave_input=0;
292    celt_int32_t lookahead = 0;
293    int bytes_per_packet=48;
294    int complexity=-127;
295    
296    snprintf(vendor_string, sizeof(vendor_string), "Encoded with CELT\n");
297    
298    comment_init(&comments, &comments_length, vendor_string);
299
300    /*Process command-line options*/
301    while(1)
302    {
303       c = getopt_long (argc, argv, "hvV",
304                        long_options, &option_index);
305       if (c==-1)
306          break;
307       
308       switch(c)
309       {
310       case 0:
311          if (strcmp(long_options[option_index].name,"bitrate")==0)
312          {
313             bitrate = atoi (optarg);
314          } else if (strcmp(long_options[option_index].name,"skeleton")==0)
315          {
316             with_skeleton=1;
317          } else if (strcmp(long_options[option_index].name,"help")==0)
318          {
319             usage();
320             exit(0);
321          } else if (strcmp(long_options[option_index].name,"quiet")==0)
322          {
323             quiet = 1;
324          } else if (strcmp(long_options[option_index].name,"version")==0)
325          {
326             version();
327             exit(0);
328          } else if (strcmp(long_options[option_index].name,"version-short")==0)
329          {
330             version_short();
331             exit(0);
332          } else if (strcmp(long_options[option_index].name,"le")==0)
333          {
334             lsb=1;
335          } else if (strcmp(long_options[option_index].name,"be")==0)
336          {
337             lsb=0;
338          } else if (strcmp(long_options[option_index].name,"8bit")==0)
339          {
340             fmt=8;
341          } else if (strcmp(long_options[option_index].name,"16bit")==0)
342          {
343             fmt=16;
344          } else if (strcmp(long_options[option_index].name,"stereo")==0)
345          {
346             chan=2;
347          } else if (strcmp(long_options[option_index].name,"mono")==0)
348          {
349             chan=1;
350          } else if (strcmp(long_options[option_index].name,"rate")==0)
351          {
352             rate=atoi (optarg);
353          } else if (strcmp(long_options[option_index].name,"comp")==0)
354          {
355             complexity=atoi (optarg);
356          } else if (strcmp(long_options[option_index].name,"comment")==0)
357          {
358            if (!strchr(optarg, '='))
359            {
360              fprintf (stderr, "Invalid comment: %s\n", optarg);
361              fprintf (stderr, "Comments must be of the form name=value\n");
362              exit(1);
363            }
364            comment_add(&comments, &comments_length, NULL, optarg); 
365          } else if (strcmp(long_options[option_index].name,"author")==0)
366          {
367            comment_add(&comments, &comments_length, "author=", optarg); 
368          } else if (strcmp(long_options[option_index].name,"title")==0)
369          {
370            comment_add(&comments, &comments_length, "title=", optarg); 
371          }
372
373          break;
374       case 'h':
375          usage();
376          exit(0);
377          break;
378       case 'v':
379          version();
380          exit(0);
381          break;
382       case 'V':
383          print_bitrate=1;
384          break;
385       case '?':
386          usage();
387          exit(1);
388          break;
389       }
390    }
391    if (argc-optind!=2)
392    {
393       usage();
394       exit(1);
395    }
396    inFile=argv[optind];
397    outFile=argv[optind+1];
398
399    /*Initialize Ogg stream struct*/
400    srand(time(NULL));
401    if (ogg_stream_init(&os, rand())==-1)
402    {
403       fprintf(stderr,"Error: stream init failed\n");
404       exit(1);
405    }
406    if (with_skeleton && ogg_stream_init(&so, rand())==-1)
407    {
408       fprintf(stderr,"Error: stream init failed\n");
409       exit(1);
410    }
411
412    if (strcmp(inFile, "-")==0)
413    {
414 #if defined WIN32 || defined _WIN32
415          _setmode(_fileno(stdin), _O_BINARY);
416 #elif defined OS2
417          _fsetmode(stdin,"b");
418 #endif
419       fin=stdin;
420    }
421    else 
422    {
423       fin = fopen(inFile, "rb");
424       if (!fin)
425       {
426          perror(inFile);
427          exit(1);
428       }
429       close_in=1;
430    }
431
432    {
433       fread(first_bytes, 1, 12, fin);
434       if (strncmp(first_bytes,"RIFF",4)==0 && strncmp(first_bytes,"RIFF",4)==0)
435       {
436          if (read_wav_header(fin, &rate, &chan, &fmt, &size)==-1)
437             exit(1);
438          wave_input=1;
439          lsb=1; /* CHECK: exists big-endian .wav ?? */
440       }
441    }
442
443    if (chan == 1)
444    {
445       if (bitrate < 0)
446          bitrate = 64;
447       if (bitrate < 32)
448          bitrate = 32;
449       if (bitrate > 110)
450          bitrate = 110;
451    }
452    else if (chan == 2)
453    {
454       if (bitrate < 0)
455          bitrate = 128;
456       if (bitrate < 64)
457          bitrate = 64;
458       if (bitrate > 150)
459          bitrate = 150;
460    } else {
461       fprintf (stderr, "Only mono and stereo are supported\n");
462       return 1;
463    }
464
465    mode = celt_mode_create(rate, chan, 256, NULL);
466    if (!mode)
467       return 1;
468    celt_mode_info(mode, CELT_GET_FRAME_SIZE, &frame_size);
469    
470    bytes_per_packet = (bitrate*1000*frame_size/rate+4)/8;
471    
472    celt_header_init(&header, mode);
473    header.nb_channels = chan;
474
475    {
476       char *st_string="mono";
477       if (chan==2)
478          st_string="stereo";
479       if (!quiet)
480          fprintf (stderr, "Encoding %d Hz audio using %s (%d bytes per packet)\n", 
481                header.sample_rate, st_string, bytes_per_packet);
482    }
483    /*fprintf (stderr, "Encoding %d Hz audio at %d bps using %s mode\n", 
484      header.rate, mode->bitrate, mode->modeName);*/
485
486    /*Initialize CELT encoder*/
487    st = celt_encoder_create(mode);
488
489    if (complexity!=-127) {
490      if (celt_encoder_ctl(st, CELT_SET_COMPLEXITY(complexity)) != CELT_OK)
491      {
492         fprintf (stderr, "Only complexity 0 through 10 is supported\n");
493         return 1;
494      }
495    }
496
497    if (strcmp(outFile,"-")==0)
498    {
499 #if defined WIN32 || defined _WIN32
500       _setmode(_fileno(stdout), _O_BINARY);
501 #endif
502       fout=stdout;
503    }
504    else 
505    {
506       fout = fopen(outFile, "wb");
507       if (!fout)
508       {
509          perror(outFile);
510          exit(1);
511       }
512       close_out=1;
513    }
514
515    if (with_skeleton) {
516       fprintf (stderr, "Warning: Enabling skeleton output may cause some decoders to fail.\n");
517    }
518
519    /* first packet should be the skeleton header. */
520    if (with_skeleton) {
521       add_fishead_packet(&so);
522       if ((ret = flush_ogg_stream_to_file(&so, fout))) {
523          fprintf (stderr,"Error: failed skeleton (fishead) header to output stream\n");
524          exit(1);
525       } else
526          bytes_written += ret;
527    }
528
529    /*Write header*/
530    {
531       unsigned char header_data[100];
532       int packet_size = celt_header_to_packet(&header, header_data, 100);
533       op.packet = header_data;
534       op.bytes = packet_size;
535       op.b_o_s = 1;
536       op.e_o_s = 0;
537       op.granulepos = 0;
538       op.packetno = 0;
539       ogg_stream_packetin(&os, &op);
540
541       while((result = ogg_stream_flush(&os, &og)))
542       {
543          if(!result) break;
544          ret = oe_write_page(&og, fout);
545          if(ret != og.header_len + og.body_len)
546          {
547             fprintf (stderr,"Error: failed writing header to output stream\n");
548             exit(1);
549          }
550          else
551             bytes_written += ret;
552       }
553
554       op.packet = (unsigned char *)comments;
555       op.bytes = comments_length;
556       op.b_o_s = 0;
557       op.e_o_s = 0;
558       op.granulepos = 0;
559       op.packetno = 1;
560       ogg_stream_packetin(&os, &op);
561    }
562
563    /* fisbone packet should be write after all bos pages */
564    if (with_skeleton) {
565       add_fisbone_packet(&so, os.serialno, &header);
566       if ((ret = flush_ogg_stream_to_file(&so, fout))) {
567          fprintf (stderr,"Error: failed writing skeleton (fisbone )header to output stream\n");
568          exit(1);
569       } else
570          bytes_written += ret;
571    }
572
573    /* writing the rest of the celt header packets */
574    while((result = ogg_stream_flush(&os, &og)))
575    {
576       if(!result) break;
577       ret = oe_write_page(&og, fout);
578       if(ret != og.header_len + og.body_len)
579       {
580          fprintf (stderr,"Error: failed writing header to output stream\n");
581          exit(1);
582       }
583       else
584          bytes_written += ret;
585    }
586
587    free(comments);
588
589    /* write the skeleton eos packet */
590    if (with_skeleton) {
591       add_eos_packet_to_stream(&so);
592       if ((ret = flush_ogg_stream_to_file(&so, fout))) {
593          fprintf (stderr,"Error: failed writing skeleton header to output stream\n");
594          exit(1);
595       } else
596          bytes_written += ret;
597    }
598
599
600    if (!wave_input)
601    {
602       nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, first_bytes, NULL);
603    } else {
604       nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size);
605    }
606    if (nb_samples==0)
607       eos=1;
608    total_samples += nb_samples;
609    nb_encoded = -lookahead;
610    /*Main encoding loop (one frame per iteration)*/
611    while (!eos || total_samples>nb_encoded)
612    {
613       id++;
614       /*Encode current frame*/
615
616       nbBytes = celt_encode(st, input, NULL, bits, bytes_per_packet);
617       if (nbBytes<0)
618       {
619          fprintf(stderr, "Got error %d while encoding. Aborting.\n", nbBytes);
620          break;
621       }
622       nb_encoded += frame_size;
623
624       if (wave_input)
625       {
626          nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size);
627       } else {
628          nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, NULL);
629       }
630       if (nb_samples==0)
631       {
632          eos=1;
633       }
634       if (eos && total_samples<=nb_encoded)
635          op.e_o_s = 1;
636       else
637          op.e_o_s = 0;
638       total_samples += nb_samples;
639
640       op.packet = (unsigned char *)bits;
641       op.bytes = nbBytes;
642       op.b_o_s = 0;
643       /*Is this redundent?*/
644       if (eos && total_samples<=nb_encoded)
645          op.e_o_s = 1;
646       else
647          op.e_o_s = 0;
648       op.granulepos = (id+1)*frame_size-lookahead;
649       if (op.granulepos>total_samples)
650          op.granulepos = total_samples;
651       /*printf ("granulepos: %d %d %d %d %d %d\n", (int)op.granulepos, id, nframes, lookahead, 5, 6);*/
652       op.packetno = 2+id;
653       ogg_stream_packetin(&os, &op);
654
655       /*Write all new pages (most likely 0 or 1)*/
656       while (ogg_stream_pageout(&os,&og))
657       {
658          ret = oe_write_page(&og, fout);
659          if(ret != og.header_len + og.body_len)
660          {
661             fprintf (stderr,"Error: failed writing header to output stream\n");
662             exit(1);
663          }
664          else
665             bytes_written += ret;
666       }
667    }
668    /*Flush all pages left to be written*/
669    while (ogg_stream_flush(&os, &og))
670    {
671       ret = oe_write_page(&og, fout);
672       if(ret != og.header_len + og.body_len)
673       {
674          fprintf (stderr,"Error: failed writing header to output stream\n");
675          exit(1);
676       }
677       else
678          bytes_written += ret;
679    }
680
681    celt_encoder_destroy(st);
682    celt_mode_destroy(mode);
683    ogg_stream_clear(&os);
684
685    if (close_in)
686       fclose(fin);
687    if (close_out)
688       fclose(fout);
689    return 0;
690 }
691
692 /*                 
693  Comments will be stored in the Vorbis style.            
694  It is describled in the "Structure" section of
695     http://www.xiph.org/ogg/vorbis/doc/v-comment.html
696
697 The comment header is decoded as follows:
698   1) [vendor_length] = read an unsigned integer of 32 bits
699   2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
700   3) [user_comment_list_length] = read an unsigned integer of 32 bits
701   4) iterate [user_comment_list_length] times {
702      5) [length] = read an unsigned integer of 32 bits
703      6) this iteration's user comment = read a UTF-8 vector as [length] octets
704      }
705   7) [framing_bit] = read a single bit as boolean
706   8) if ( [framing_bit]  unset or end of packet ) then ERROR
707   9) done.
708
709   If you have troubles, please write to ymnk@jcraft.com.
710  */
711
712 #define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
713                            ((buf[base+2]<<16)&0xff0000)| \
714                            ((buf[base+1]<<8)&0xff00)| \
715                             (buf[base]&0xff))
716 #define writeint(buf, base, val) do{ buf[base+3]=((val)>>24)&0xff; \
717                                      buf[base+2]=((val)>>16)&0xff; \
718                                      buf[base+1]=((val)>>8)&0xff; \
719                                      buf[base]=(val)&0xff; \
720                                  }while(0)
721
722 void comment_init(char **comments, int* length, char *vendor_string)
723 {
724   int vendor_length=strlen(vendor_string);
725   int user_comment_list_length=0;
726   int len=4+vendor_length+4;
727   char *p=(char*)malloc(len);
728   if(p==NULL){
729      fprintf (stderr, "malloc failed in comment_init()\n");
730      exit(1);
731   }
732   writeint(p, 0, vendor_length);
733   memcpy(p+4, vendor_string, vendor_length);
734   writeint(p, 4+vendor_length, user_comment_list_length);
735   *length=len;
736   *comments=p;
737 }
738 void comment_add(char **comments, int* length, char *tag, char *val)
739 {
740   char* p=*comments;
741   int vendor_length=readint(p, 0);
742   int user_comment_list_length=readint(p, 4+vendor_length);
743   int tag_len=(tag?strlen(tag):0);
744   int val_len=strlen(val);
745   int len=(*length)+4+tag_len+val_len;
746
747   p=(char*)realloc(p, len);
748   if(p==NULL){
749      fprintf (stderr, "realloc failed in comment_add()\n");
750      exit(1);
751   }
752
753   writeint(p, *length, tag_len+val_len);      /* length of comment */
754   if(tag) memcpy(p+*length+4, tag, tag_len);  /* comment */
755   memcpy(p+*length+4+tag_len, val, val_len);  /* comment */
756   writeint(p, 4+vendor_length, user_comment_list_length+1);
757
758   *comments=p;
759   *length=len;
760 }
761 #undef readint
762 #undef writeint