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