Completed VBR for 0.5.0 release
[speexdsp.git] / src / speexenc.c
1 /* Copyright (C) 2002 Jean-Marc Valin 
2    File: speexenc.c
3
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8    
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13    
14    You should have received a copy of the GNU Lesser General Public
15    License along with this library; if not, write to the Free Software
16    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <getopt.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "speex.h"
26 #include <ogg/ogg.h>
27 #include "wav_io.h"
28 #include "speex_header.h"
29 #include "misc.h"
30
31 /*Write an Ogg page to a file pointer*/
32 int oe_write_page(ogg_page *page, FILE *fp)
33 {
34    int written;
35    written = fwrite(page->header,1,page->header_len, fp);
36    written += fwrite(page->body,1,page->body_len, fp);
37    
38    return written;
39 }
40
41 #define MAX_FRAME_SIZE 2000
42 #define MAX_FRAME_BYTES 1000
43
44 void usage()
45 {
46    fprintf (stderr, "Speex encoder version " VERSION "\n");
47    fprintf (stderr, "\n");
48    fprintf (stderr, "usage: speexenc [options] input_file output_file\n");
49    fprintf (stderr, "\n");
50    fprintf (stderr, "input_file can be:\n");
51    fprintf (stderr, "  filename.wav          wav file\n");
52    fprintf (stderr, "  filename.*            raw PCM file (any extension other than .wav)\n");
53    fprintf (stderr, "  -                     stdin\n");
54    fprintf (stderr, "\n");  
55    fprintf (stderr, "output_file can be:\n");
56    fprintf (stderr, "  filename.spx          Speex file\n");
57    fprintf (stderr, "  -                     stdout\n");
58    fprintf (stderr, "\n");  
59    fprintf (stderr, "options:\n");
60    fprintf (stderr, "  --narrowband -n    Narrowband (8 kHz) input file\n"); 
61    fprintf (stderr, "  --wideband   -w    Wideband (16 kHz) input file\n"); 
62    fprintf (stderr, "  --quality n        Encoding quality setting from 0 to 10\n"); 
63    fprintf (stderr, "  --lbr              Low bit-rate mode (equivalent to --quality 3)\n"); 
64    fprintf (stderr, "  --vbr              Enable variable bit-rate (VBR)\n"); 
65    fprintf (stderr, "  --help       -h    This help\n"); 
66    fprintf (stderr, "  --version    -v    Version information\n"); 
67    fprintf (stderr, "\n");  
68    fprintf (stderr, "Input must be mono\n"); 
69    fprintf (stderr, "Raw PCM needs to be 16-bit little-endian\n"); 
70 }
71
72 void version()
73 {
74    fprintf (stderr, "Speex encoder version " VERSION "\n");
75 }
76
77 int main(int argc, char **argv)
78 {
79    int c;
80    int option_index = 0;
81    int narrowband=0, wideband=0;
82    char *inFile, *outFile;
83    FILE *fin, *fout;
84    short in[MAX_FRAME_SIZE];
85    float input[MAX_FRAME_SIZE];
86    int frame_size;
87    int vbr_enabled;
88    int i,nbBytes;
89    SpeexMode *mode=NULL;
90    void *st;
91    SpeexBits bits;
92    char cbits[MAX_FRAME_BYTES];
93    struct option long_options[] =
94    {
95       {"wideband", no_argument, NULL, 0},
96       {"narrowband", no_argument, NULL, 0},
97       {"lbr", no_argument, NULL, 0},
98       {"vbr", no_argument, NULL, 0},
99       {"quality", required_argument, NULL, 0},
100       {"help", no_argument, NULL, 0},
101       {"version", no_argument, NULL, 0},
102       {0, 0, 0, 0}
103    };
104    int rate, chan, fmt, size;
105    int quality=-1;
106    int lbr=0;
107    ogg_stream_state os;
108    ogg_page              og;
109    ogg_packet            op;
110    int bytes_written, ret, result;
111    int id=0;
112    SpeexHeader header;
113    char *comments = "Encoded with Speex " VERSION;
114
115    /*Process command-line options*/
116    while(1)
117    {
118       c = getopt_long (argc, argv, "nwhv",
119                        long_options, &option_index);
120       if (c==-1)
121          break;
122       
123       switch(c)
124       {
125       case 0:
126          if (strcmp(long_options[option_index].name,"narrowband")==0)
127             narrowband=1;
128          else if (strcmp(long_options[option_index].name,"wideband")==0)
129                wideband=1;
130          else if (strcmp(long_options[option_index].name,"lbr")==0)
131                lbr=1;
132          else if (strcmp(long_options[option_index].name,"vbr")==0)
133                vbr_enabled=1;
134          else if (strcmp(long_options[option_index].name,"quality")==0)
135          {
136             quality = atoi (optarg);
137          } else if (strcmp(long_options[option_index].name,"help")==0)
138          {
139             usage();
140             exit(0);
141          } else if (strcmp(long_options[option_index].name,"version")==0)
142          {
143             version();
144             exit(0);
145          }
146          break;
147       case 'n':
148          narrowband=1;
149          break;
150       case 'h':
151          usage();
152          exit(0);
153          break;
154       case 'v':
155          version();
156          exit(0);
157          break;
158       case 'w':
159          wideband=1;
160          break;
161       case '?':
162          usage();
163          exit(1);
164          break;
165       }
166    }
167    if (argc-optind!=2)
168    {
169       usage();
170       exit(1);
171    }
172    inFile=argv[optind];
173    outFile=argv[optind+1];
174
175    if (wideband && narrowband)
176    {
177       fprintf (stderr,"Cannot specify both wideband and narrowband at the same time\n");
178       exit(1);
179    };
180
181    /*Initialize Ogg stream struct*/
182    if (ogg_stream_init(&os, 0)==-1)
183    {
184       fprintf(stderr,"Stream init failed\n");
185       exit(1);
186    }
187
188    if (strcmp(inFile, "-")==0)
189       fin=stdin;
190    else 
191    {
192       fin = fopen(inFile, "r");
193       if (!fin)
194       {
195          perror(inFile);
196          exit(1);
197       }
198    }
199
200    rate=0;
201    if (strcmp(inFile+strlen(inFile)-4,".wav")==0)
202       if (read_wav_header(fin, &rate, &chan, &fmt, &size)==-1)
203          exit(1);
204    /*fprintf (stderr, "wave info: %d %d %d %d\n", rate, chan, fmt, size);*/
205
206    if (rate==16000)
207    {
208       wideband=1;
209       if (narrowband)
210          fprintf (stderr,"Warning: encoding a wideband file in narrowband\n");
211    } else if (rate==8000)
212    {
213       narrowband=1;
214       if (wideband)
215          fprintf (stderr,"Warning: encoding a narrowband file in wideband\n");
216    }
217
218    if (!wideband)
219       narrowband=1;
220    if (narrowband)
221    {
222       if (!rate)
223          rate = 8000;
224       mode=&speex_nb_mode;
225    }
226    if (wideband)
227    {
228       if (!rate)
229          rate = 16000;
230       mode=&speex_wb_mode;
231    }
232
233    speex_init_header(&header, rate, 1, mode);
234
235    fprintf (stderr, "Encoding %d Hz audio using %s mode\n", 
236             header.rate, mode->modeName);
237    /*fprintf (stderr, "Encoding %d Hz audio at %d bps using %s mode\n", 
238      header.rate, mode->bitrate, mode->modeName);*/
239
240    /*Initialize Speex encoder*/
241    st = speex_encoder_init(mode);
242
243    if (strcmp(outFile,"-")==0)
244       fout=stdout;
245    else 
246    {
247       fout = fopen(outFile, "w");
248       if (!fout)
249       {
250          perror(outFile);
251          exit(1);
252       }
253    }
254
255
256    /*Write header (format will change)*/
257    {
258
259       op.packet = (unsigned char *)speex_header_to_packet(&header, (int*)&(op.bytes));
260       op.b_o_s = 1;
261       op.e_o_s = 0;
262       op.granulepos = 0;
263       op.packetno = 0;
264       ogg_stream_packetin(&os, &op);
265       free(op.packet);
266
267       op.packet = (unsigned char *)comments;
268       op.bytes = strlen((char*)op.packet);
269       op.b_o_s = 0;
270       op.e_o_s = 0;
271       op.granulepos = 0;
272       op.packetno = 0;
273       ogg_stream_packetin(&os, &op);
274       
275       while((result = ogg_stream_flush(&os, &og)))
276       {
277          if(!result) break;
278          ret = oe_write_page(&og, fout);
279          if(ret != og.header_len + og.body_len)
280          {
281             fprintf (stderr,"Failed writing header to output stream\n");
282             exit(1);
283          }
284          else
285             bytes_written += ret;
286       }
287    }
288
289    speex_encoder_ctl(st, SPEEX_GET_FRAME_SIZE, &frame_size);
290    if (vbr_enabled)
291    {
292       int tmp;
293       tmp=1;
294       speex_encoder_ctl(st, SPEEX_SET_VBR, &tmp);
295    }
296    if (lbr || quality != -1)
297    {
298       int tmp=quality;
299       if (quality==-1)
300          tmp = 3;
301       speex_encoder_ctl(st, SPEEX_SET_QUALITY, &tmp);
302       if (vbr_enabled)
303          speex_encoder_ctl(st, SPEEX_SET_VBR_QUALITY, &tmp);
304    }
305
306    /*Main encoding loop (one frame per iteration)*/
307    while (1)
308    {
309       id++;
310       /*Read input audio*/
311       fread(in, sizeof(short), frame_size, fin);
312       if (feof(fin))
313          break;
314       for (i=0;i<frame_size;i++)
315          input[i]=(short)le_short(in[i]);
316       /*Encode current frame*/
317       speex_encode(st, input, &bits);
318
319       /*if (id%5!=0)
320         continue;*/
321       nbBytes = speex_bits_write(&bits, cbits, 500);
322       speex_bits_reset(&bits);
323       op.packet = (unsigned char *)cbits;
324       op.bytes = nbBytes;
325       op.b_o_s = 0;
326       op.e_o_s = 0;
327       op.granulepos = id;
328       op.packetno = id;
329       ogg_stream_packetin(&os, &op);
330
331       /*Write all new pages (not likely 0 or 1)*/
332       while (ogg_stream_pageout(&os,&og))
333       {
334          ret = oe_write_page(&og, fout);
335          if(ret != og.header_len + og.body_len)
336          {
337             fprintf (stderr,"Failed writing header to output stream\n");
338             exit(1);
339          }
340          else
341             bytes_written += ret;
342       }
343    }
344    
345    op.packet = (unsigned char *)"END OF STREAM";
346    op.bytes = 13;
347    op.b_o_s = 0;
348    op.e_o_s = 1;
349    op.granulepos = id+1;
350    op.packetno = id+1;
351    ogg_stream_packetin(&os, &op);
352    /*Flush all pages left to be written*/
353    while (ogg_stream_flush(&os, &og))
354    {
355       ret = oe_write_page(&og, fout);
356       if(ret != og.header_len + og.body_len)
357       {
358          fprintf (stderr,"Failed writing header to output stream\n");
359          exit(1);
360       }
361       else
362          bytes_written += ret;
363    }
364    
365
366    speex_encoder_destroy(st);
367    speex_bits_destroy(&bits);
368    ogg_stream_clear(&os);
369
370    exit(0);
371    return 1;
372 }
373