Added bit-rate information via speex_*_ctl calls, fixed stupid bug in
[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 2000
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, "  --comp n           Set encoding complexity (0-10)\n"); 
66    fprintf (stderr, "  --nframes n        Number of frames per Ogg packet\n"); 
67    fprintf (stderr, "  --help       -h    This help\n"); 
68    fprintf (stderr, "  --version    -v    Version information\n"); 
69    fprintf (stderr, "  -V                 Verbose mode (show bit-rate)\n"); 
70    fprintf (stderr, "\n");  
71    fprintf (stderr, "Input must be mono\n"); 
72    fprintf (stderr, "Raw PCM needs to be 16-bit little-endian\n"); 
73 }
74
75 void version()
76 {
77    fprintf (stderr, "Speex encoder version " VERSION "\n");
78 }
79
80 int main(int argc, char **argv)
81 {
82    int c;
83    int option_index = 0;
84    int narrowband=0, wideband=0;
85    char *inFile, *outFile;
86    FILE *fin, *fout;
87    short in[MAX_FRAME_SIZE];
88    float input[MAX_FRAME_SIZE];
89    int frame_size;
90    int vbr_enabled=0;
91    int i,nbBytes;
92    SpeexMode *mode=NULL;
93    void *st;
94    SpeexBits bits;
95    char cbits[MAX_FRAME_BYTES];
96    struct option long_options[] =
97    {
98       {"wideband", no_argument, NULL, 0},
99       {"narrowband", no_argument, NULL, 0},
100       {"lbr", no_argument, NULL, 0},
101       {"vbr", no_argument, NULL, 0},
102       {"quality", required_argument, NULL, 0},
103       {"nframes", required_argument, NULL, 0},
104       {"comp", required_argument, NULL, 0},
105       {"help", no_argument, NULL, 0},
106       {"version", no_argument, NULL, 0},
107       {0, 0, 0, 0}
108    };
109    int print_bitrate=0;
110    int rate, chan, fmt, size;
111    int quality=-1;
112    int lbr=0;
113    ogg_stream_state os;
114    ogg_page              og;
115    ogg_packet            op;
116    int bytes_written, ret, result;
117    int id=-1;
118    SpeexHeader header;
119    int nframes=1;
120    int complexity=3;
121    char *comments = "Encoded with Speex " VERSION;
122
123    /*Process command-line options*/
124    while(1)
125    {
126       c = getopt_long (argc, argv, "nwhvV",
127                        long_options, &option_index);
128       if (c==-1)
129          break;
130       
131       switch(c)
132       {
133       case 0:
134          if (strcmp(long_options[option_index].name,"narrowband")==0)
135             narrowband=1;
136          else if (strcmp(long_options[option_index].name,"wideband")==0)
137                wideband=1;
138          else if (strcmp(long_options[option_index].name,"lbr")==0)
139                lbr=1;
140          else if (strcmp(long_options[option_index].name,"vbr")==0)
141                vbr_enabled=1;
142          else if (strcmp(long_options[option_index].name,"quality")==0)
143          {
144             quality = atoi (optarg);
145          } else if (strcmp(long_options[option_index].name,"nframes")==0)
146          {
147             nframes = atoi (optarg);
148             if (nframes<1)
149                nframes=1;
150             if (nframes>10)
151                nframes=10;
152          } else if (strcmp(long_options[option_index].name,"comp")==0)
153          {
154             complexity = atoi (optarg);
155          } else if (strcmp(long_options[option_index].name,"help")==0)
156          {
157             usage();
158             exit(0);
159          } else if (strcmp(long_options[option_index].name,"version")==0)
160          {
161             version();
162             exit(0);
163          }
164          break;
165       case 'n':
166          narrowband=1;
167          break;
168       case 'h':
169          usage();
170          exit(0);
171          break;
172       case 'v':
173          version();
174          exit(0);
175          break;
176       case 'V':
177          print_bitrate=1;
178          break;
179       case 'w':
180          wideband=1;
181          break;
182       case '?':
183          usage();
184          exit(1);
185          break;
186       }
187    }
188    if (argc-optind!=2)
189    {
190       usage();
191       exit(1);
192    }
193    inFile=argv[optind];
194    outFile=argv[optind+1];
195
196    if (wideband && narrowband)
197    {
198       fprintf (stderr,"Cannot specify both wideband and narrowband at the same time\n");
199       exit(1);
200    };
201
202    /*Initialize Ogg stream struct*/
203    srand(time(NULL));
204    if (ogg_stream_init(&os, rand())==-1)
205    {
206       fprintf(stderr,"Stream init failed\n");
207       exit(1);
208    }
209
210    if (strcmp(inFile, "-")==0)
211       fin=stdin;
212    else 
213    {
214       fin = fopen(inFile, "r");
215       if (!fin)
216       {
217          perror(inFile);
218          exit(1);
219       }
220    }
221
222    rate=0;
223    if (strcmp(inFile+strlen(inFile)-4,".wav")==0)
224       if (read_wav_header(fin, &rate, &chan, &fmt, &size)==-1)
225          exit(1);
226    /*fprintf (stderr, "wave info: %d %d %d %d\n", rate, chan, fmt, size);*/
227
228    if (rate==16000)
229    {
230       wideband=1;
231       if (narrowband)
232          fprintf (stderr,"Warning: encoding a wideband file in narrowband\n");
233    } else if (rate==8000)
234    {
235       narrowband=1;
236       if (wideband)
237          fprintf (stderr,"Warning: encoding a narrowband file in wideband\n");
238    }
239
240    if (!wideband)
241       narrowband=1;
242    if (narrowband)
243    {
244       if (!rate)
245          rate = 8000;
246       mode=&speex_nb_mode;
247    }
248    if (wideband)
249    {
250       if (!rate)
251          rate = 16000;
252       mode=&speex_wb_mode;
253    }
254
255    speex_init_header(&header, rate, 1, mode);
256    header.frames_per_packet=nframes;
257    header.vbr=vbr_enabled;
258
259    fprintf (stderr, "Encoding %d Hz audio using %s mode\n", 
260             header.rate, mode->modeName);
261    /*fprintf (stderr, "Encoding %d Hz audio at %d bps using %s mode\n", 
262      header.rate, mode->bitrate, mode->modeName);*/
263
264    /*Initialize Speex encoder*/
265    st = speex_encoder_init(mode);
266
267    if (strcmp(outFile,"-")==0)
268       fout=stdout;
269    else 
270    {
271       fout = fopen(outFile, "w");
272       if (!fout)
273       {
274          perror(outFile);
275          exit(1);
276       }
277    }
278
279
280    /*Write header (format will change)*/
281    {
282
283       op.packet = (unsigned char *)speex_header_to_packet(&header, (int*)&(op.bytes));
284       op.b_o_s = 1;
285       op.e_o_s = 0;
286       op.granulepos = 0;
287       op.packetno = 0;
288       ogg_stream_packetin(&os, &op);
289       free(op.packet);
290
291       op.packet = (unsigned char *)comments;
292       op.bytes = strlen((char*)op.packet);
293       op.b_o_s = 0;
294       op.e_o_s = 0;
295       op.granulepos = 0;
296       op.packetno = 0;
297       ogg_stream_packetin(&os, &op);
298       
299       while((result = ogg_stream_flush(&os, &og)))
300       {
301          if(!result) break;
302          ret = oe_write_page(&og, fout);
303          if(ret != og.header_len + og.body_len)
304          {
305             fprintf (stderr,"Failed writing header to output stream\n");
306             exit(1);
307          }
308          else
309             bytes_written += ret;
310       }
311    }
312
313    speex_encoder_ctl(st, SPEEX_GET_FRAME_SIZE, &frame_size);
314    speex_encoder_ctl(st, SPEEX_SET_COMPLEXITY, &complexity);
315    if (vbr_enabled)
316    {
317       int tmp;
318       tmp=1;
319       speex_encoder_ctl(st, SPEEX_SET_VBR, &tmp);
320    }
321    if (lbr || quality != -1)
322    {
323       int tmp=quality;
324       if (quality==-1)
325          tmp = 3;
326       speex_encoder_ctl(st, SPEEX_SET_QUALITY, &tmp);
327       if (vbr_enabled)
328          speex_encoder_ctl(st, SPEEX_SET_VBR_QUALITY, &tmp);
329    }
330
331    /*Main encoding loop (one frame per iteration)*/
332    while (1)
333    {
334       id++;
335       /*Read input audio*/
336       fread(in, sizeof(short), frame_size, fin);
337       if (feof(fin))
338          break;
339       for (i=0;i<frame_size;i++)
340          input[i]=(short)le_short(in[i]);
341       /*Encode current frame*/
342       speex_encode(st, input, &bits);
343       
344       if (print_bitrate) {
345          int tmp;
346          char ch=13;
347          speex_encoder_ctl(st, SPEEX_GET_BITRATE, &tmp);
348          fputc (ch, stderr);
349          fprintf (stderr, "Bitrate is use: %d bps     ", tmp);
350       }
351       if ((id+1)%nframes!=0)
352          continue;
353       nbBytes = speex_bits_write(&bits, cbits, MAX_FRAME_BYTES);
354       speex_bits_reset(&bits);
355       op.packet = (unsigned char *)cbits;
356       op.bytes = nbBytes;
357       op.b_o_s = 0;
358       op.e_o_s = 0;
359       op.granulepos = id*frame_size;
360       op.packetno = id/nframes;
361       ogg_stream_packetin(&os, &op);
362
363       /*Write all new pages (not likely 0 or 1)*/
364       while (ogg_stream_pageout(&os,&og))
365       {
366          ret = oe_write_page(&og, fout);
367          if(ret != og.header_len + og.body_len)
368          {
369             fprintf (stderr,"Failed writing header to output stream\n");
370             exit(1);
371          }
372          else
373             bytes_written += ret;
374       }
375    }
376    
377    op.packet = (unsigned char *)"END OF STREAM";
378    op.bytes = 13;
379    op.b_o_s = 0;
380    op.e_o_s = 1;
381    op.granulepos = id+1;
382    op.packetno = id+1;
383    ogg_stream_packetin(&os, &op);
384    /*Flush all pages left to be written*/
385    while (ogg_stream_flush(&os, &og))
386    {
387       ret = oe_write_page(&og, fout);
388       if(ret != og.header_len + og.body_len)
389       {
390          fprintf (stderr,"Failed writing header to output stream\n");
391          exit(1);
392       }
393       else
394          bytes_written += ret;
395    }
396    
397
398    speex_encoder_destroy(st);
399    speex_bits_destroy(&bits);
400    ogg_stream_clear(&os);
401
402    exit(0);
403    return 1;
404 }
405