build fix for FreeBSD (gnugetopt), allow VBR without DTX
[speexdsp.git] / src / speexenc.c
1 /* Copyright (C) 2002 Jean-Marc Valin 
2    File: speexenc.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 #include <stdio.h>
33 #if !defined WIN32 && !defined _WIN32
34 #include <unistd.h>
35 #include <getopt.h>
36 #endif
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40
41 #include "speex.h"
42 #include <ogg/ogg.h>
43 #include "wav_io.h"
44 #include "speex_header.h"
45 #include "speex_stereo.h"
46 #include "misc.h"
47
48 #if defined WIN32 || defined _WIN32
49 #include "getopt_win.h"
50 /* We need the following two to set stdout to binary */
51 #include <io.h>
52 #include <fcntl.h>
53 #endif
54
55
56 void comment_init(char **comments, int* length, char *vendor_string);
57 void comment_add(char **comments, int* length, char *tag, char *val);
58
59
60 /*Write an Ogg page to a file pointer*/
61 int oe_write_page(ogg_page *page, FILE *fp)
62 {
63    int written;
64    written = fwrite(page->header,1,page->header_len, fp);
65    written += fwrite(page->body,1,page->body_len, fp);
66    
67    return written;
68 }
69
70 #define MAX_FRAME_SIZE 2000
71 #define MAX_FRAME_BYTES 2000
72
73 /* Convert input audio bits, endians and channels */
74 static int read_samples(FILE *fin,int frame_size, int bits, int channels, int lsb, float * input, char *buff, int *size)
75 {   
76    unsigned char in[MAX_FRAME_BYTES*2];
77    int i;
78    short *s;
79    int nb_read;
80
81    if (size && *size<=0)
82    {
83       return 1;
84    }
85    /*Read input audio*/
86    if (size)
87       *size -= bits/8*channels*frame_size;
88    if (buff)
89    {
90       for (i=0;i<12;i++)
91          in[i]=buff[i];
92       nb_read = fread(in+12,1,bits/8*channels*frame_size-12, fin) + 12;
93       if (size)
94          *size += 12;
95    } else {
96       nb_read = fread(in,1,bits/8*channels* frame_size, fin);
97    }
98    nb_read /= bits/8*channels;
99
100    /*fprintf (stderr, "%d\n", nb_read);*/
101    if (nb_read==0)
102       return 1;
103
104    s=(short*)in;
105    if(bits==8)
106    {
107       /* Convert 8->16 bits */
108       for(i=frame_size*channels-1;i>=0;i--)
109       {
110          s[i]=(in[i]<<8)^0x8000;
111       }
112    } else
113    {
114       /* convert to our endian format */
115       for(i=0;i<frame_size*channels;i++)
116       {
117          if(lsb) 
118             s[i]=le_short(s[i]); 
119          else
120             s[i]=be_short(s[i]);
121       }
122    }
123
124    /* copy to float input buffer */
125    for (i=0;i<frame_size*channels;i++)
126    {
127       input[i]=(short)s[i];
128    }
129
130    for (i=nb_read*channels;i<frame_size*channels;i++)
131    {
132       input[i]=0;
133    }
134
135
136    return 0;
137 }
138
139 void version()
140 {
141    printf ("speexenc (Speex encoder) version " VERSION " (compiled " __DATE__ ")\n");
142    printf ("Copyright (C) 2002 Jean-Marc Valin\n");
143 }
144
145 void version_short()
146 {
147    printf ("speexenc version " VERSION "\n");
148    printf ("Copyright (C) 2002 Jean-Marc Valin\n");
149 }
150
151 void usage()
152 {
153    printf ("Usage: speexenc [options] input_file output_file\n");
154    printf ("\n");
155    printf ("Encodes input_file using Speex. It can read the WAV or raw files.\n");
156    printf ("\n");
157    printf ("input_file can be:\n");
158    printf ("  filename.wav      wav file\n");
159    printf ("  filename.*        Raw PCM file (any extension other than .wav)\n");
160    printf ("  -                 stdin\n");
161    printf ("\n");  
162    printf ("output_file can be:\n");
163    printf ("  filename.spx      Speex file\n");
164    printf ("  -                 stdout\n");
165    printf ("\n");  
166    printf ("Options:\n");
167    printf (" -n, --narrowband   Narrowband (8 kHz) input file\n"); 
168    printf (" -w, --wideband     Wideband (16 kHz) input file\n"); 
169    printf (" -u, --ultra-wideband \"Ultra-wideband\" (32 kHz) input file\n"); 
170    printf (" --quality n        Encoding quality (0-10), default 8\n"); 
171    printf (" --bitrate n        Encoding bit-rate (use bit-rate n or lower)\n"); 
172    printf (" --vbr              Enable variable bit-rate (VBR)\n"); 
173    printf (" --abr rate         Enable average bit-rate (ABR) at rate bps\n"); 
174    printf (" --vad              Enable voice activity detection (VAD)\n"); 
175    printf (" --dtx              Enable file-based discontinuous transmission (DTX)\n"); 
176    printf (" --comp n           Set encoding complexity (0-10), default 3\n"); 
177    printf (" --nframes n        Number of frames per Ogg packet (1-10), default 1\n"); 
178    printf (" --comment          Add the given string as an extra comment. This may be\n");
179    printf ("                     used multiple times.\n");
180    printf (" --author           Author of this track.\n");
181    printf (" --title            Title for this track.\n");
182    printf (" -h, --help         This help\n"); 
183    printf (" -v, --version      Version information\n"); 
184    printf (" -V                 Verbose mode (show bit-rate)\n"); 
185    printf ("Raw input options:\n");
186    printf (" --rate n           Sampling rate for raw input\n"); 
187    printf (" --stereo           Consider raw input as stereo\n"); 
188    printf (" --le               Raw input is little-endian\n"); 
189    printf (" --be               Raw input is big-endian\n"); 
190    printf (" --8bit             Raw input is 8-bit unsigned\n"); 
191    printf (" --16bit            Raw input is 16-bit signed\n"); 
192    printf ("Default raw PCM input is 16-bit, little-endian, mono\n"); 
193    printf ("\n");
194    printf ("More information is available from the Speex site: http://www.speex.org\n");
195    printf ("\n");
196    printf ("Please report bugs to the mailing list `speex-dev@xiph.org'.\n");
197 }
198
199
200 int main(int argc, char **argv)
201 {
202    int c;
203    int option_index = 0;
204    char *inFile, *outFile;
205    FILE *fin, *fout;
206    float input[MAX_FRAME_SIZE];
207    int frame_size;
208    int vbr_enabled=0;
209    int abr_enabled=0;
210    int vad_enabled=0;
211    int dtx_enabled=0;
212    int nbBytes;
213    SpeexMode *mode=NULL;
214    void *st;
215    SpeexBits bits;
216    char cbits[MAX_FRAME_BYTES];
217    struct option long_options[] =
218    {
219       {"wideband", no_argument, NULL, 0},
220       {"ultra-wideband", no_argument, NULL, 0},
221       {"narrowband", no_argument, NULL, 0},
222       {"vbr", no_argument, NULL, 0},
223       {"abr", required_argument, NULL, 0},
224       {"vad", no_argument, NULL, 0},
225       {"dtx", no_argument, NULL, 0},
226       {"quality", required_argument, NULL, 0},
227       {"bitrate", required_argument, NULL, 0},
228       {"nframes", required_argument, NULL, 0},
229       {"comp", required_argument, NULL, 0},
230       {"help", no_argument, NULL, 0},
231       {"le", no_argument, NULL, 0},
232       {"be", no_argument, NULL, 0},
233       {"lin8", no_argument, NULL, 0},
234       {"lin16", no_argument, NULL, 0},
235       {"stereo", no_argument, NULL, 0},
236       {"rate", required_argument, NULL, 0},
237       {"version", no_argument, NULL, 0},
238       {"version-short", no_argument, NULL, 0},
239       {"comment", required_argument, NULL, 0},
240       {"author", required_argument, NULL, 0},
241       {"title", required_argument, NULL, 0},
242       {0, 0, 0, 0}
243    };
244    int print_bitrate=0;
245    int rate=0, size;
246    int chan=1;
247    int fmt=16;
248    int quality=-1;
249    float vbr_quality=-1;
250    int lsb=1;
251    ogg_stream_state os;
252    ogg_page              og;
253    ogg_packet            op;
254    int bytes_written, ret, result;
255    int id=-1;
256    SpeexHeader header;
257    int nframes=1;
258    int complexity=3;
259    char *vendor_string = "Encoded with Speex " VERSION;
260    char *comments;
261    int comments_length;
262    int close_in=0, close_out=0;
263    int eos=0;
264    int bitrate=0;
265    double cumul_bits=0, enc_frames=0;
266    char first_bytes[12];
267    int wave_input=0;
268    int tmp;
269
270    comment_init(&comments, &comments_length, vendor_string);
271
272    /*Process command-line options*/
273    while(1)
274    {
275       c = getopt_long (argc, argv, "nwuhvV",
276                        long_options, &option_index);
277       if (c==-1)
278          break;
279       
280       switch(c)
281       {
282       case 0:
283          if (strcmp(long_options[option_index].name,"narrowband")==0)
284          {
285             mode=&speex_nb_mode;
286          } else if (strcmp(long_options[option_index].name,"wideband")==0)
287          {
288             mode=&speex_wb_mode;
289          } else if (strcmp(long_options[option_index].name,"ultra-wideband")==0)
290          {
291             mode=&speex_uwb_mode;
292          } else if (strcmp(long_options[option_index].name,"vbr")==0)
293          {
294             vbr_enabled=1;
295          } else if (strcmp(long_options[option_index].name,"abr")==0)
296          {
297             abr_enabled=atoi(optarg);
298             if (!abr_enabled)
299             {
300                fprintf (stderr, "Invalid ABR value: %d\n", abr_enabled);
301                exit(1);
302             }
303          } else if (strcmp(long_options[option_index].name,"vad")==0)
304          {
305             vad_enabled=1;
306          } else if (strcmp(long_options[option_index].name,"dtx")==0)
307          {
308             dtx_enabled=1;
309          } else if (strcmp(long_options[option_index].name,"quality")==0)
310          {
311             quality = atoi (optarg);
312             vbr_quality=atof(optarg);
313          } else if (strcmp(long_options[option_index].name,"bitrate")==0)
314          {
315             bitrate = atoi (optarg);
316          } else if (strcmp(long_options[option_index].name,"nframes")==0)
317          {
318             nframes = atoi (optarg);
319             if (nframes<1)
320                nframes=1;
321             if (nframes>10)
322                nframes=10;
323          } else if (strcmp(long_options[option_index].name,"comp")==0)
324          {
325             complexity = atoi (optarg);
326          } else if (strcmp(long_options[option_index].name,"help")==0)
327          {
328             usage();
329             exit(0);
330          } else if (strcmp(long_options[option_index].name,"version")==0)
331          {
332             version();
333             exit(0);
334          } else if (strcmp(long_options[option_index].name,"version-short")==0)
335          {
336             version_short();
337             exit(0);
338          } else if (strcmp(long_options[option_index].name,"le")==0)
339          {
340             lsb=1;
341          } else if (strcmp(long_options[option_index].name,"be")==0)
342          {
343             lsb=0;
344          } else if (strcmp(long_options[option_index].name,"lin8")==0)
345          {
346             fmt=8;
347          } else if (strcmp(long_options[option_index].name,"lin16")==0)
348          {
349             fmt=16;
350          } else if (strcmp(long_options[option_index].name,"stereo")==0)
351          {
352             chan=2;
353          } else if (strcmp(long_options[option_index].name,"rate")==0)
354          {
355             rate=atoi (optarg);
356          } else if (strcmp(long_options[option_index].name,"comment")==0)
357          {
358            comment_add(&comments, &comments_length, NULL, optarg); 
359          } else if (strcmp(long_options[option_index].name,"author")==0)
360          {
361            comment_add(&comments, &comments_length, "author=", optarg); 
362          } else if (strcmp(long_options[option_index].name,"title")==0)
363          {
364            comment_add(&comments, &comments_length, "title=", optarg); 
365          }
366
367          break;
368       case 'n':
369          mode=&speex_nb_mode;
370          break;
371       case 'h':
372          usage();
373          exit(0);
374          break;
375       case 'v':
376          version();
377          exit(0);
378          break;
379       case 'V':
380          print_bitrate=1;
381          break;
382       case 'w':
383          mode=&speex_wb_mode;
384          break;
385       case 'u':
386          mode=&speex_uwb_mode;
387          break;
388       case '?':
389          usage();
390          exit(1);
391          break;
392       }
393    }
394    if (argc-optind!=2)
395    {
396       usage();
397       exit(1);
398    }
399    inFile=argv[optind];
400    outFile=argv[optind+1];
401
402    /*Initialize Ogg stream struct*/
403    srand(time(NULL));
404    if (ogg_stream_init(&os, 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 #endif
415       fin=stdin;
416    }
417    else 
418    {
419 #if defined WIN32 || defined _WIN32
420       fin = fopen(inFile, "rb");
421 #else
422       fin = fopen(inFile, "r");
423 #endif
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 (!mode && !rate)
444    {
445       /* By default, use narrowband/8 kHz */
446       mode=&speex_nb_mode;
447       rate=8000;
448    } else if (mode && rate)
449    {
450       if (rate>48000)
451       {
452          fprintf (stderr, "Error: sampling rate too high: %d Hz, try down-sampling\n", rate);
453          exit(1);
454       } else if (rate>25000)
455       {
456          if (mode!=&speex_uwb_mode)
457          {
458             fprintf (stderr, "Warning: Trying to encode in %s at %d Hz. I'll do it but I suggest you try ultra-wideband instead\n", mode->modeName , rate);
459          }
460       } else if (rate>12500)
461       {
462          if (mode!=&speex_wb_mode)
463          {
464             fprintf (stderr, "Warning: Trying to encode in %s at %d Hz. I'll do it but I suggest you try wideband instead\n", mode->modeName , rate);
465          }
466       } else if (rate>=6000)
467       {
468          if (mode!=&speex_nb_mode)
469          {
470             fprintf (stderr, "Warning: Trying to encode in %s at %d Hz. I'll do it but I suggest you try narrowband instead\n", mode->modeName , rate);
471          }
472       } else {
473          fprintf (stderr, "Error: sampling rate too low: %d Hz\n", rate);
474          exit(1);
475       }
476    } else if (!mode)
477    {
478       if (rate>48000)
479       {
480          fprintf (stderr, "Error: sampling rate too high: %d Hz, try down-sampling\n", rate);
481          exit(1);
482       } else if (rate>25000)
483       {
484          mode=&speex_uwb_mode;
485       } else if (rate>12500)
486       {
487          mode=&speex_wb_mode;
488       } else if (rate>=6000)
489       {
490          mode=&speex_nb_mode;
491       } else {
492          fprintf (stderr, "Error: Sampling rate too low: %d Hz\n", rate);
493          exit(1);
494       }
495    } else if (!rate)
496    {
497       if (mode==&speex_nb_mode)
498          rate=8000;
499       else if (mode==&speex_wb_mode)
500          rate=16000;
501       else if (mode==&speex_uwb_mode)
502          rate=32000;
503    }
504
505    if (rate!=8000 && rate!=16000 && rate!=32000)
506       fprintf (stderr, "Warning: Speex is only optimized for 8, 16 and 32 kHz. It will still work at %d Hz but your mileage may vary\n", rate); 
507
508    speex_init_header(&header, rate, 1, mode);
509    header.frames_per_packet=nframes;
510    header.vbr=vbr_enabled;
511    header.nb_channels = chan;
512
513    {
514       char *st_string="mono";
515       if (chan==2)
516          st_string="stereo";
517       fprintf (stderr, "Encoding %d Hz audio using %s mode (%s)\n", 
518                header.rate, mode->modeName, st_string);
519    }
520    /*fprintf (stderr, "Encoding %d Hz audio at %d bps using %s mode\n", 
521      header.rate, mode->bitrate, mode->modeName);*/
522
523    /*Initialize Speex encoder*/
524    st = speex_encoder_init(mode);
525
526    if (strcmp(outFile,"-")==0)
527    {
528 #if defined WIN32 || defined _WIN32
529       _setmode(_fileno(stdout), _O_BINARY);
530 #endif
531       fout=stdout;
532    }
533    else 
534    {
535 #if defined WIN32 || defined _WIN32
536       fout = fopen(outFile, "wb");
537 #else
538       fout = fopen(outFile, "w");
539 #endif
540       if (!fout)
541       {
542          perror(outFile);
543          exit(1);
544       }
545       close_out=1;
546    }
547
548
549    /*Write header (format will change)*/
550    {
551
552       op.packet = (unsigned char *)speex_header_to_packet(&header, (int*)&(op.bytes));
553       op.b_o_s = 1;
554       op.e_o_s = 0;
555       op.granulepos = 0;
556       op.packetno = 0;
557       ogg_stream_packetin(&os, &op);
558       free(op.packet);
559
560       op.packet = (unsigned char *)comments;
561       op.bytes = comments_length;
562       op.b_o_s = 0;
563       op.e_o_s = 0;
564       op.granulepos = 0;
565       op.packetno = 1;
566       ogg_stream_packetin(&os, &op);
567       
568       while((result = ogg_stream_flush(&os, &og)))
569       {
570          if(!result) break;
571          ret = oe_write_page(&og, fout);
572          if(ret != og.header_len + og.body_len)
573          {
574             fprintf (stderr,"Error: failed writing header to output stream\n");
575             exit(1);
576          }
577          else
578             bytes_written += ret;
579       }
580    }
581
582    free(comments);
583
584    speex_encoder_ctl(st, SPEEX_GET_FRAME_SIZE, &frame_size);
585    speex_encoder_ctl(st, SPEEX_SET_COMPLEXITY, &complexity);
586    speex_encoder_ctl(st, SPEEX_SET_SAMPLING_RATE, &rate);
587
588    if (quality >= 0)
589    {
590       if (vbr_enabled)
591          speex_encoder_ctl(st, SPEEX_SET_VBR_QUALITY, &vbr_quality);
592       else
593          speex_encoder_ctl(st, SPEEX_SET_QUALITY, &quality);
594    }
595    if (bitrate)
596    {
597       if (quality >= 0 && vbr_enabled)
598          fprintf (stderr, "Warning: --bitrate option is overriding --quality\n");
599       speex_encoder_ctl(st, SPEEX_SET_BITRATE, &bitrate);
600    }
601    if (vbr_enabled)
602    {
603       tmp=1;
604       speex_encoder_ctl(st, SPEEX_SET_VBR, &tmp);
605    } else if (vad_enabled)
606    {
607       tmp=1;
608       speex_encoder_ctl(st, SPEEX_SET_VAD, &tmp);
609    }
610    if (dtx_enabled)
611       speex_encoder_ctl(st, SPEEX_SET_DTX, &tmp);
612    if (dtx_enabled && !(vbr_enabled || abr_enabled || vad_enabled))
613    {
614       fprintf (stderr, "Warning: --dtx is useless without --vad\n");
615    } else if ((vbr_enabled || abr_enabled) && (vad_enabled || dtx_enabled))
616    {
617       fprintf (stderr, "Warning: --vad and --dtx are already implied by --vbr or --abr\n");
618    }
619
620    if (abr_enabled)
621    {
622       speex_encoder_ctl(st, SPEEX_SET_ABR, &abr_enabled);
623    }
624
625    speex_bits_init(&bits);
626
627    if (!wave_input)
628    {
629       if (read_samples(fin,frame_size,fmt,chan,lsb,input, first_bytes, NULL))
630          eos=1;
631    } else {
632       if (read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size))
633          eos=1;
634    }
635    /*Main encoding loop (one frame per iteration)*/
636    while (!eos)
637    {
638       id++;
639       /*Encode current frame*/
640       if (chan==2)
641          speex_encode_stereo(input, frame_size, &bits);
642       speex_encode(st, input, &bits);
643       
644       if (print_bitrate) {
645          int tmp;
646          char ch=13;
647          speex_encoder_ctl(st, SPEEX_GET_BITRATE, &tmp);
648          fputc (ch, stderr);
649          cumul_bits += tmp;
650          enc_frames += 1;
651          if (vad_enabled || vbr_enabled || abr_enabled)
652             fprintf (stderr, "Bitrate is use: %d bps  (average %d bps)   ", tmp, (int)(cumul_bits/enc_frames));
653          else
654             fprintf (stderr, "Bitrate is use: %d bps     ", tmp);
655          
656       }
657
658       if (wave_input)
659       {
660          if (read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size))
661          {
662             eos=1;
663             op.e_o_s = 1;
664          }
665       } else {
666          if (read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, NULL))
667          {
668             eos=1;
669             op.e_o_s = 1;
670          }
671       }
672
673       if ((id+1)%nframes!=0)
674          continue;
675       nbBytes = speex_bits_write(&bits, cbits, MAX_FRAME_BYTES);
676       speex_bits_reset(&bits);
677       op.packet = (unsigned char *)cbits;
678       op.bytes = nbBytes;
679       op.b_o_s = 0;
680       if (eos)
681          op.e_o_s = 1;
682       else
683          op.e_o_s = 0;
684       op.granulepos = (id+nframes)*frame_size;
685       op.packetno = 2+id/nframes;
686       ogg_stream_packetin(&os, &op);
687
688       /*Write all new pages (most likely 0 or 1)*/
689       while (ogg_stream_pageout(&os,&og))
690       {
691          ret = oe_write_page(&og, fout);
692          if(ret != og.header_len + og.body_len)
693          {
694             fprintf (stderr,"Error: failed writing header to output stream\n");
695             exit(1);
696          }
697          else
698             bytes_written += ret;
699       }
700    }
701    if ((id+1)%nframes!=0)
702    {
703       while ((id+1)%nframes!=0)
704       {
705          id++;
706          speex_bits_pack(&bits, 15, 5);
707       }
708       nbBytes = speex_bits_write(&bits, cbits, MAX_FRAME_BYTES);
709       op.packet = (unsigned char *)cbits;
710       op.bytes = nbBytes;
711       op.b_o_s = 0;
712       op.e_o_s = 1;
713       op.granulepos = (id+nframes)*frame_size;
714       op.packetno = 2+id/nframes;
715       ogg_stream_packetin(&os, &op);
716    }
717    /*Flush all pages left to be written*/
718    while (ogg_stream_flush(&os, &og))
719    {
720       ret = oe_write_page(&og, fout);
721       if(ret != og.header_len + og.body_len)
722       {
723          fprintf (stderr,"Error: failed writing header to output stream\n");
724          exit(1);
725       }
726       else
727          bytes_written += ret;
728    }
729    
730
731    speex_encoder_destroy(st);
732    speex_bits_destroy(&bits);
733    ogg_stream_clear(&os);
734
735    if (close_in)
736       fclose(fin);
737    if (close_out)
738       fclose(fout);
739    return 0;
740 }
741
742 /*                 
743  Comments will be stored in the Vorbis style.            
744  It is describled in the "Structure" section of
745     http://www.xiph.org/ogg/vorbis/doc/v-comment.html
746
747 The comment header is decoded as follows:
748   1) [vendor_length] = read an unsigned integer of 32 bits
749   2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
750   3) [user_comment_list_length] = read an unsigned integer of 32 bits
751   4) iterate [user_comment_list_length] times {
752      5) [length] = read an unsigned integer of 32 bits
753      6) this iteration's user comment = read a UTF-8 vector as [length] octets
754      }
755   7) [framing_bit] = read a single bit as boolean
756   8) if ( [framing_bit]  unset or end of packet ) then ERROR
757   9) done.
758
759   If you have troubles, please write to ymnk@jcraft.com.
760  */
761
762 #define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
763                            ((buf[base+2]<<16)&0xff0000)| \
764                            ((buf[base+1]<<8)&0xff00)| \
765                             (buf[base]&0xff))
766 #define writeint(buf, base, val) do{ buf[base+3]=((val)>>24)&0xff; \
767                                      buf[base+2]=((val)>>16)&0xff; \
768                                      buf[base+1]=((val)>>8)&0xff; \
769                                      buf[base]=(val)&0xff; \
770                                  }while(0)
771
772 void comment_init(char **comments, int* length, char *vendor_string)
773 {
774   int vendor_length=strlen(vendor_string);
775   int user_comment_list_length=0;
776   int len=4+vendor_length+4;
777   char *p=(char*)malloc(len);
778   if(p==NULL){
779   }
780   writeint(p, 0, vendor_length);
781   memcpy(p+4, vendor_string, vendor_length);
782   writeint(p, 4+vendor_length, user_comment_list_length);
783   *length=len;
784   *comments=p;
785 }
786 void comment_add(char **comments, int* length, char *tag, char *val)
787 {
788   char* p=*comments;
789   int vendor_length=readint(p, 0);
790   int user_comment_list_length=readint(p, 4+vendor_length);
791   int tag_len=(tag?strlen(tag):0);
792   int val_len=strlen(val);
793   int len=(*length)+4+tag_len+val_len;
794
795   p=(char*)realloc(p, len);
796   if(p==NULL){
797   }
798
799   writeint(p, *length, tag_len+val_len);      /* length of comment */
800   if(tag) memcpy(p+*length+4, tag, tag_len);  /* comment */
801   memcpy(p+*length+4+tag_len, val, val_len);  /* comment */
802   writeint(p, 4+vendor_length, user_comment_list_length+1);
803
804   *comments=p;
805   *length=len;
806 }
807 #undef readint
808 #undef writeint