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