20a479a8aab626750b0879f564dd94fa21063211
[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    comment_init(&comments, &comments_length, vendor_string);
269
270    /*Process command-line options*/
271    while(1)
272    {
273       c = getopt_long (argc, argv, "nwuhvV",
274                        long_options, &option_index);
275       if (c==-1)
276          break;
277       
278       switch(c)
279       {
280       case 0:
281          if (strcmp(long_options[option_index].name,"narrowband")==0)
282          {
283             mode=&speex_nb_mode;
284          } else if (strcmp(long_options[option_index].name,"wideband")==0)
285          {
286             mode=&speex_wb_mode;
287          } else if (strcmp(long_options[option_index].name,"ultra-wideband")==0)
288          {
289             mode=&speex_uwb_mode;
290          } else if (strcmp(long_options[option_index].name,"vbr")==0)
291          {
292             vbr_enabled=1;
293          } else if (strcmp(long_options[option_index].name,"abr")==0)
294          {
295             abr_enabled=atoi(optarg);
296             if (!abr_enabled)
297             {
298                fprintf (stderr, "Invalid ABR value: %d\n", abr_enabled);
299                exit(1);
300             }
301          } else if (strcmp(long_options[option_index].name,"vad")==0)
302          {
303             vad_enabled=1;
304          } else if (strcmp(long_options[option_index].name,"dtx")==0)
305          {
306             dtx_enabled=1;
307          } else if (strcmp(long_options[option_index].name,"quality")==0)
308          {
309             quality = atoi (optarg);
310             vbr_quality=atof(optarg);
311          } else if (strcmp(long_options[option_index].name,"bitrate")==0)
312          {
313             bitrate = atoi (optarg);
314          } else if (strcmp(long_options[option_index].name,"nframes")==0)
315          {
316             nframes = atoi (optarg);
317             if (nframes<1)
318                nframes=1;
319             if (nframes>10)
320                nframes=10;
321          } else if (strcmp(long_options[option_index].name,"comp")==0)
322          {
323             complexity = atoi (optarg);
324          } else if (strcmp(long_options[option_index].name,"help")==0)
325          {
326             usage();
327             exit(0);
328          } else if (strcmp(long_options[option_index].name,"version")==0)
329          {
330             version();
331             exit(0);
332          } else if (strcmp(long_options[option_index].name,"version-short")==0)
333          {
334             version_short();
335             exit(0);
336          } else if (strcmp(long_options[option_index].name,"le")==0)
337          {
338             lsb=1;
339          } else if (strcmp(long_options[option_index].name,"be")==0)
340          {
341             lsb=0;
342          } else if (strcmp(long_options[option_index].name,"lin8")==0)
343          {
344             fmt=8;
345          } else if (strcmp(long_options[option_index].name,"lin16")==0)
346          {
347             fmt=16;
348          } else if (strcmp(long_options[option_index].name,"stereo")==0)
349          {
350             chan=2;
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            comment_add(&comments, &comments_length, NULL, optarg); 
357          } else if (strcmp(long_options[option_index].name,"author")==0)
358          {
359            comment_add(&comments, &comments_length, "author=", optarg); 
360          } else if (strcmp(long_options[option_index].name,"title")==0)
361          {
362            comment_add(&comments, &comments_length, "title=", optarg); 
363          }
364
365          break;
366       case 'n':
367          mode=&speex_nb_mode;
368          break;
369       case 'h':
370          usage();
371          exit(0);
372          break;
373       case 'v':
374          version();
375          exit(0);
376          break;
377       case 'V':
378          print_bitrate=1;
379          break;
380       case 'w':
381          mode=&speex_wb_mode;
382          break;
383       case 'u':
384          mode=&speex_uwb_mode;
385          break;
386       case '?':
387          usage();
388          exit(1);
389          break;
390       }
391    }
392    if (argc-optind!=2)
393    {
394       usage();
395       exit(1);
396    }
397    inFile=argv[optind];
398    outFile=argv[optind+1];
399
400    /*Initialize Ogg stream struct*/
401    srand(time(NULL));
402    if (ogg_stream_init(&os, rand())==-1)
403    {
404       fprintf(stderr,"Error: stream init failed\n");
405       exit(1);
406    }
407
408    if (strcmp(inFile, "-")==0)
409    {
410 #if defined WIN32 || defined _WIN32
411          _setmode(_fileno(stdin), _O_BINARY);
412 #endif
413       fin=stdin;
414    }
415    else 
416    {
417 #if defined WIN32 || defined _WIN32
418       fin = fopen(inFile, "rb");
419 #else
420       fin = fopen(inFile, "r");
421 #endif
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    if (!mode && !rate)
442    {
443       /* By default, use narrowband/8 kHz */
444       mode=&speex_nb_mode;
445       rate=8000;
446    } else if (mode && rate)
447    {
448       if (rate>48000)
449       {
450          fprintf (stderr, "Error: sampling rate too high: %d Hz, try down-sampling\n", rate);
451          exit(1);
452       } else if (rate>25000)
453       {
454          if (mode!=&speex_uwb_mode)
455          {
456             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);
457          }
458       } else if (rate>12500)
459       {
460          if (mode!=&speex_wb_mode)
461          {
462             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);
463          }
464       } else if (rate>=6000)
465       {
466          if (mode!=&speex_nb_mode)
467          {
468             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);
469          }
470       } else {
471          fprintf (stderr, "Error: sampling rate too low: %d Hz\n", rate);
472          exit(1);
473       }
474    } else if (!mode)
475    {
476       if (rate>48000)
477       {
478          fprintf (stderr, "Error: sampling rate too high: %d Hz, try down-sampling\n", rate);
479          exit(1);
480       } else if (rate>25000)
481       {
482          mode=&speex_uwb_mode;
483       } else if (rate>12500)
484       {
485          mode=&speex_wb_mode;
486       } else if (rate>=6000)
487       {
488          mode=&speex_nb_mode;
489       } else {
490          fprintf (stderr, "Error: Sampling rate too low: %d Hz\n", rate);
491          exit(1);
492       }
493    } else if (!rate)
494    {
495       if (mode==&speex_nb_mode)
496          rate=8000;
497       else if (mode==&speex_wb_mode)
498          rate=16000;
499       else if (mode==&speex_uwb_mode)
500          rate=32000;
501    }
502
503    if (rate!=8000 && rate!=16000 && rate!=32000)
504       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); 
505
506    speex_init_header(&header, rate, 1, mode);
507    header.frames_per_packet=nframes;
508    header.vbr=vbr_enabled;
509    header.nb_channels = chan;
510
511    {
512       char *st_string="mono";
513       if (chan==2)
514          st_string="stereo";
515       fprintf (stderr, "Encoding %d Hz audio using %s mode (%s)\n", 
516                header.rate, mode->modeName, st_string);
517    }
518    /*fprintf (stderr, "Encoding %d Hz audio at %d bps using %s mode\n", 
519      header.rate, mode->bitrate, mode->modeName);*/
520
521    /*Initialize Speex encoder*/
522    st = speex_encoder_init(mode);
523
524    if (strcmp(outFile,"-")==0)
525    {
526 #if defined WIN32 || defined _WIN32
527       _setmode(_fileno(stdout), _O_BINARY);
528 #endif
529       fout=stdout;
530    }
531    else 
532    {
533 #if defined WIN32 || defined _WIN32
534       fout = fopen(outFile, "wb");
535 #else
536       fout = fopen(outFile, "w");
537 #endif
538       if (!fout)
539       {
540          perror(outFile);
541          exit(1);
542       }
543       close_out=1;
544    }
545
546
547    /*Write header (format will change)*/
548    {
549
550       op.packet = (unsigned char *)speex_header_to_packet(&header, (int*)&(op.bytes));
551       op.b_o_s = 1;
552       op.e_o_s = 0;
553       op.granulepos = 0;
554       op.packetno = 0;
555       ogg_stream_packetin(&os, &op);
556       free(op.packet);
557
558       op.packet = (unsigned char *)comments;
559       op.bytes = comments_length;
560       op.b_o_s = 0;
561       op.e_o_s = 0;
562       op.granulepos = 0;
563       op.packetno = 1;
564       ogg_stream_packetin(&os, &op);
565       
566       while((result = ogg_stream_flush(&os, &og)))
567       {
568          if(!result) break;
569          ret = oe_write_page(&og, fout);
570          if(ret != og.header_len + og.body_len)
571          {
572             fprintf (stderr,"Error: failed writing header to output stream\n");
573             exit(1);
574          }
575          else
576             bytes_written += ret;
577       }
578    }
579
580    free(comments);
581
582    speex_encoder_ctl(st, SPEEX_GET_FRAME_SIZE, &frame_size);
583    speex_encoder_ctl(st, SPEEX_SET_COMPLEXITY, &complexity);
584    speex_encoder_ctl(st, SPEEX_SET_SAMPLING_RATE, &rate);
585
586    if (quality >= 0)
587    {
588       if (vbr_enabled)
589          speex_encoder_ctl(st, SPEEX_SET_VBR_QUALITY, &vbr_quality);
590       else
591          speex_encoder_ctl(st, SPEEX_SET_QUALITY, &quality);
592    }
593    if (bitrate)
594    {
595       if (quality >= 0 && vbr_enabled)
596          fprintf (stderr, "Warning: --bitrate option is overriding --quality\n");
597       speex_encoder_ctl(st, SPEEX_SET_BITRATE, &bitrate);
598    }
599    if (vbr_enabled)
600    {
601       int tmp;
602       tmp=1;
603       speex_encoder_ctl(st, SPEEX_SET_VBR, &tmp);
604    } else if (vad_enabled)
605    {
606       int tmp;
607       tmp=1;
608       speex_encoder_ctl(st, SPEEX_SET_VAD, &tmp);
609       if (dtx_enabled)
610          speex_encoder_ctl(st, SPEEX_SET_DTX, &tmp);
611    }
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