fd0a04d4f89dafdc9e63c65c6c8af5dc15f2e3cc
[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 #include <time.h>
25
26 #include "speex.h"
27 #include <ogg/ogg.h>
28 #include "wav_io.h"
29 #include "speex_header.h"
30 #include "misc.h"
31
32 /*Write an Ogg page to a file pointer*/
33 int oe_write_page(ogg_page *page, FILE *fp)
34 {
35    int written;
36    written = fwrite(page->header,1,page->header_len, fp);
37    written += fwrite(page->body,1,page->body_len, fp);
38    
39    return written;
40 }
41
42 #define MAX_FRAME_SIZE 2000
43 #define MAX_FRAME_BYTES 2000
44
45 /* Convert input audio bits, endians and channels */
46 int read_samples(FILE *fin,int frame_size, int bits, int channels, int lsb, float * input)
47 {   
48    unsigned char in[MAX_FRAME_BYTES*2];
49    int i,d;
50    short *s;
51
52    /*Read input audio*/
53    fread(in,bits/8*channels, frame_size, fin);
54    if (feof(fin))
55       return 1;
56    s=(short*)in;
57    if(bits==8)
58    {
59       /* Convert 8->16 bits */
60       for(i=frame_size*channels-1;i>=0;i--)
61       {
62          s[i]=(in[i]<<8)^0x8000;
63       }
64    } else
65    {
66       /* convert to our endian format */
67       for(i=0;i<frame_size*channels;i++)
68       {
69          if(lsb) 
70             s[i]=le_short(s[i]); 
71          else
72             s[i]=be_short(s[i]);
73       }
74    }
75
76    if(channels==2)
77    {
78       /* downmix to mono */
79       for(i=0;i<frame_size;i++)
80       {
81          d=s[i*2]+s[i*2+1];
82          s[i]=d>>1;
83       }
84    }
85
86    /* copy to float input buffer */
87    for (i=0;i<frame_size;i++)
88    {
89       input[i]=(short)s[i];
90    }
91
92    return 0;
93 }
94
95 void usage()
96 {
97    fprintf (stderr, "Speex encoder version " VERSION " (compiled " __DATE__ ")\n");
98    fprintf (stderr, "\n");
99    fprintf (stderr, "usage: speexenc [options] input_file output_file\n");
100    fprintf (stderr, "\n");
101    fprintf (stderr, "input_file can be:\n");
102    fprintf (stderr, "  filename.wav          wav file\n");
103    fprintf (stderr, "  filename.*            raw PCM file (any extension other than .wav)\n");
104    fprintf (stderr, "  -                     stdin\n");
105    fprintf (stderr, "\n");  
106    fprintf (stderr, "output_file can be:\n");
107    fprintf (stderr, "  filename.spx          Speex file\n");
108    fprintf (stderr, "  -                     stdout\n");
109    fprintf (stderr, "\n");  
110    fprintf (stderr, "options:\n");
111    fprintf (stderr, "  --narrowband -n    Narrowband (8 kHz) input file\n"); 
112    fprintf (stderr, "  --wideband   -w    Wideband (16 kHz) input file\n"); 
113    fprintf (stderr, "  --quality n        Encoding quality (0-10), default 3\n"); 
114    fprintf (stderr, "  --lbr              Low bit-rate mode (equivalent to --quality 3)\n"); 
115    fprintf (stderr, "  --vbr              Enable variable bit-rate (VBR)\n"); 
116    fprintf (stderr, "  --comp n           Set encoding complexity (0-10), default 3\n"); 
117    fprintf (stderr, "  --nframes n        Number of frames per Ogg packet (1-10), default 1\n"); 
118    fprintf (stderr, "  --help       -h    This help\n"); 
119    fprintf (stderr, "  --version    -v    Version information\n"); 
120    fprintf (stderr, "  -V                 Verbose mode (show bit-rate)\n"); 
121    fprintf (stderr, "raw input options:\n");
122    fprintf (stderr, "  --le               Raw input is little-endian\n"); 
123    fprintf (stderr, "  --be               Raw input is big-endian\n"); 
124    fprintf (stderr, "  --8bit             Raw input is 8-bit unsigned\n"); 
125    fprintf (stderr, "  --16bit            Raw input is 16-bit signed\n"); 
126    fprintf (stderr, "\n");  
127    fprintf (stderr, "Default Raw PCM input is 16-bit, little-endian, mono\n"); 
128 }
129
130 void version()
131 {
132    fprintf (stderr, "Speex encoder version " VERSION " (compiled " __DATE__ ")\n");
133 }
134
135 int main(int argc, char **argv)
136 {
137    int c;
138    int option_index = 0;
139    int narrowband=0, wideband=0;
140    char *inFile, *outFile;
141    FILE *fin, *fout;
142    float input[MAX_FRAME_SIZE];
143    int frame_size;
144    int vbr_enabled=0;
145    int nbBytes;
146    SpeexMode *mode=NULL;
147    void *st;
148    SpeexBits bits;
149    char cbits[MAX_FRAME_BYTES];
150    struct option long_options[] =
151    {
152       {"wideband", no_argument, NULL, 0},
153       {"narrowband", no_argument, NULL, 0},
154       {"lbr", no_argument, NULL, 0},
155       {"vbr", no_argument, NULL, 0},
156       {"quality", required_argument, NULL, 0},
157       {"nframes", required_argument, NULL, 0},
158       {"comp", required_argument, NULL, 0},
159       {"help", no_argument, NULL, 0},
160       {"le", no_argument, NULL, 0},
161       {"be", no_argument, NULL, 0},
162       {"lin8", no_argument, NULL, 0},
163       {"lin16", no_argument, NULL, 0},
164       {"version", no_argument, NULL, 0},
165       {0, 0, 0, 0}
166    };
167    int print_bitrate=0;
168    int rate, size;
169    int chan=1;
170    int fmt=16;
171    int quality=-1;
172    int lbr=0;
173    int lsb=1;
174    ogg_stream_state os;
175    ogg_page              og;
176    ogg_packet            op;
177    int bytes_written, ret, result;
178    int id=-1;
179    SpeexHeader header;
180    int nframes=1;
181    int complexity=3;
182    char *comments = "Encoded with Speex " VERSION;
183    int close_in=0, close_out=0;
184    int eos=0;
185
186    /*Process command-line options*/
187    while(1)
188    {
189       c = getopt_long (argc, argv, "nwhvV",
190                        long_options, &option_index);
191       if (c==-1)
192          break;
193       
194       switch(c)
195       {
196       case 0:
197          if (strcmp(long_options[option_index].name,"narrowband")==0)
198             narrowband=1;
199          else if (strcmp(long_options[option_index].name,"wideband")==0)
200                wideband=1;
201          else if (strcmp(long_options[option_index].name,"lbr")==0)
202                lbr=1;
203          else if (strcmp(long_options[option_index].name,"vbr")==0)
204                vbr_enabled=1;
205          else if (strcmp(long_options[option_index].name,"quality")==0)
206          {
207             quality = atoi (optarg);
208          } else if (strcmp(long_options[option_index].name,"nframes")==0)
209          {
210             nframes = atoi (optarg);
211             if (nframes<1)
212                nframes=1;
213             if (nframes>10)
214                nframes=10;
215          } else if (strcmp(long_options[option_index].name,"comp")==0)
216          {
217             complexity = atoi (optarg);
218          } else if (strcmp(long_options[option_index].name,"help")==0)
219          {
220             usage();
221             exit(0);
222          } else if (strcmp(long_options[option_index].name,"version")==0)
223          {
224             version();
225             exit(0);
226          } else if (strcmp(long_options[option_index].name,"le")==0)
227          {
228             lsb=1;
229          } else if (strcmp(long_options[option_index].name,"be")==0)
230          {
231             lsb=0;
232          } else if (strcmp(long_options[option_index].name,"lin8")==0)
233          {
234             fmt=8;
235          } else if (strcmp(long_options[option_index].name,"lin16")==0)
236          {
237             fmt=16;
238          }
239          break;
240       case 'n':
241          narrowband=1;
242          break;
243       case 'h':
244          usage();
245          exit(0);
246          break;
247       case 'v':
248          version();
249          exit(0);
250          break;
251       case 'V':
252          print_bitrate=1;
253          break;
254       case 'w':
255          wideband=1;
256          break;
257       case '?':
258          usage();
259          exit(1);
260          break;
261       }
262    }
263    if (argc-optind!=2)
264    {
265       usage();
266       exit(1);
267    }
268    inFile=argv[optind];
269    outFile=argv[optind+1];
270
271    if (wideband && narrowband)
272    {
273       fprintf (stderr,"Cannot specify both wideband and narrowband at the same time\n");
274       exit(1);
275    };
276
277    /*Initialize Ogg stream struct*/
278    srand(time(NULL));
279    if (ogg_stream_init(&os, rand())==-1)
280    {
281       fprintf(stderr,"Stream init failed\n");
282       exit(1);
283    }
284
285    if (strcmp(inFile, "-")==0)
286       fin=stdin;
287    else 
288    {
289       fin = fopen(inFile, "r");
290       if (!fin)
291       {
292          perror(inFile);
293          exit(1);
294       }
295       close_in=1;
296    }
297
298    rate=0;
299    if (strcmp(inFile+strlen(inFile)-4,".wav")==0 || strcmp(inFile+strlen(inFile)-4,".WAV"))
300       {
301          if (read_wav_header(fin, &rate, &chan, &fmt, &size)==-1)
302             exit(1);
303          lsb=1; /* CHECK: exists big-endian .wav ?? */
304       }
305    /*fprintf (stderr, "wave info: %d %d %d %d\n", rate, chan, fmt, size);*/
306
307    if (rate==16000)
308    {
309       wideband=1;
310       if (narrowband)
311          fprintf (stderr,"Warning: encoding a wideband file in narrowband\n");
312    } else if (rate==8000)
313    {
314       narrowband=1;
315       if (wideband)
316          fprintf (stderr,"Warning: encoding a narrowband file in wideband\n");
317    }
318
319    if (!wideband)
320       narrowband=1;
321    if (narrowband)
322    {
323       if (!rate)
324          rate = 8000;
325       mode=&speex_nb_mode;
326    }
327    if (wideband)
328    {
329       if (!rate)
330          rate = 16000;
331       mode=&speex_wb_mode;
332    }
333
334    speex_init_header(&header, rate, 1, mode);
335    header.frames_per_packet=nframes;
336    header.vbr=vbr_enabled;
337
338    fprintf (stderr, "Encoding %d Hz audio using %s mode\n", 
339             header.rate, mode->modeName);
340    /*fprintf (stderr, "Encoding %d Hz audio at %d bps using %s mode\n", 
341      header.rate, mode->bitrate, mode->modeName);*/
342
343    /*Initialize Speex encoder*/
344    st = speex_encoder_init(mode);
345
346    if (strcmp(outFile,"-")==0)
347       fout=stdout;
348    else 
349    {
350       fout = fopen(outFile, "w");
351       if (!fout)
352       {
353          perror(outFile);
354          exit(1);
355       }
356       close_out=1;
357    }
358
359
360    /*Write header (format will change)*/
361    {
362
363       op.packet = (unsigned char *)speex_header_to_packet(&header, (int*)&(op.bytes));
364       op.b_o_s = 1;
365       op.e_o_s = 0;
366       op.granulepos = -1;
367       op.packetno = 0;
368       ogg_stream_packetin(&os, &op);
369       free(op.packet);
370
371       op.packet = (unsigned char *)comments;
372       op.bytes = strlen((char*)op.packet);
373       op.b_o_s = 0;
374       op.e_o_s = 0;
375       op.granulepos = -1;
376       op.packetno = 1;
377       ogg_stream_packetin(&os, &op);
378       
379       while((result = ogg_stream_flush(&os, &og)))
380       {
381          if(!result) break;
382          ret = oe_write_page(&og, fout);
383          if(ret != og.header_len + og.body_len)
384          {
385             fprintf (stderr,"Failed writing header to output stream\n");
386             exit(1);
387          }
388          else
389             bytes_written += ret;
390       }
391    }
392
393    speex_encoder_ctl(st, SPEEX_GET_FRAME_SIZE, &frame_size);
394    speex_encoder_ctl(st, SPEEX_SET_COMPLEXITY, &complexity);
395    if (vbr_enabled)
396    {
397       int tmp;
398       tmp=1;
399       speex_encoder_ctl(st, SPEEX_SET_VBR, &tmp);
400    }
401    if (lbr || quality != -1)
402    {
403       int tmp=quality;
404       if (quality==-1)
405          tmp = 3;
406       speex_encoder_ctl(st, SPEEX_SET_QUALITY, &tmp);
407       if (vbr_enabled)
408          speex_encoder_ctl(st, SPEEX_SET_VBR_QUALITY, &tmp);
409    }
410
411    speex_bits_init(&bits);
412
413    if (read_samples(fin,frame_size,fmt,chan,lsb,input))
414       eos=1;
415
416    /*Main encoding loop (one frame per iteration)*/
417    while (!eos)
418    {
419       id++;
420       /*Encode current frame*/
421       speex_encode(st, input, &bits);
422       
423       if (print_bitrate) {
424          int tmp;
425          char ch=13;
426          speex_encoder_ctl(st, SPEEX_GET_BITRATE, &tmp);
427          fputc (ch, stderr);
428          fprintf (stderr, "Bitrate is use: %d bps     ", tmp);
429       }
430
431       if (read_samples(fin,frame_size,fmt,chan,lsb,input))
432       {
433          eos=1;
434          op.e_o_s = 1;
435       }
436
437       if ((id+1)%nframes!=0)
438          continue;
439       nbBytes = speex_bits_write(&bits, cbits, MAX_FRAME_BYTES);
440       speex_bits_reset(&bits);
441       op.packet = (unsigned char *)cbits;
442       op.bytes = nbBytes;
443       op.b_o_s = 0;
444       if (eos)
445          op.e_o_s = 1;
446       else
447          op.e_o_s = 0;
448       op.granulepos = (id+nframes)*frame_size;
449       op.packetno = 2+id/nframes;
450       ogg_stream_packetin(&os, &op);
451
452       /*Write all new pages (most likely 0 or 1)*/
453       while (ogg_stream_pageout(&os,&og))
454       {
455          ret = oe_write_page(&og, fout);
456          if(ret != og.header_len + og.body_len)
457          {
458             fprintf (stderr,"Failed writing header to output stream\n");
459             exit(1);
460          }
461          else
462             bytes_written += ret;
463       }
464    }
465    if ((id+1)%nframes!=0)
466    {
467       while ((id+1)%nframes!=0)
468       {
469          id++;
470          speex_bits_pack(&bits, 0, 7);
471       }
472       nbBytes = speex_bits_write(&bits, cbits, MAX_FRAME_BYTES);
473       op.packet = (unsigned char *)cbits;
474       op.bytes = nbBytes;
475       op.b_o_s = 0;
476       op.e_o_s = 1;
477       op.granulepos = (id+nframes)*frame_size;
478       op.packetno = 2+id/nframes;
479       ogg_stream_packetin(&os, &op);
480    }
481    /*Flush all pages left to be written*/
482    while (ogg_stream_flush(&os, &og))
483    {
484       ret = oe_write_page(&og, fout);
485       if(ret != og.header_len + og.body_len)
486       {
487          fprintf (stderr,"Failed writing header to output stream\n");
488          exit(1);
489       }
490       else
491          bytes_written += ret;
492    }
493    
494
495    speex_encoder_destroy(st);
496    speex_bits_destroy(&bits);
497    ogg_stream_clear(&os);
498
499    if (close_in)
500       fclose(fin);
501    if (close_out)
502       fclose(fout);
503    return 1;
504 }
505