OSDN Git Service

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