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