73ed9679c398b5d3071bcae0a64d0385f45293ee
[speexdsp.git] / src / speexenc.c
1 /* Copyright (C) 2002 Jean-Marc Valin 
2    File: speexenc.c
3
4    Redistribution and use in source and binary forms, with or without
5    modification, are permitted provided that the following conditions
6    are met:
7    
8    - Redistributions of source code must retain the above copyright
9    notice, this list of conditions and the following disclaimer.
10    
11    - Redistributions in binary form must reproduce the above copyright
12    notice, this list of conditions and the following disclaimer in the
13    documentation and/or other materials provided with the distribution.
14    
15    - Neither the name of the Xiph.org Foundation nor the names of its
16    contributors may be used to endorse or promote products derived from
17    this software without specific prior written permission.
18    
19    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
23    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <stdio.h>
33 #if !defined WIN32 && !defined _WIN32
34 #include <unistd.h>
35 #include <getopt.h>
36 #endif
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40
41 #include "speex.h"
42 #include <ogg/ogg.h>
43 #include "wav_io.h"
44 #include "speex_header.h"
45 #include "misc.h"
46
47 #if defined WIN32 || defined _WIN32
48 #include "getopt_win.h"
49 /* We need the following two to set stdout to binary */
50 #include <io.h>
51 #include <fcntl.h>
52 #endif
53
54
55 void comment_init(char **comments, int* length, char *vendor_string);
56 void comment_add(char **comments, int* length, char *tag, char *val);
57
58
59 /*Write an Ogg page to a file pointer*/
60 int oe_write_page(ogg_page *page, FILE *fp)
61 {
62    int written;
63    written = fwrite(page->header,1,page->header_len, fp);
64    written += fwrite(page->body,1,page->body_len, fp);
65    
66    return written;
67 }
68
69 #define MAX_FRAME_SIZE 2000
70 #define MAX_FRAME_BYTES 2000
71
72 /* Convert input audio bits, endians and channels */
73 int read_samples(FILE *fin,int frame_size, int bits, int channels, int lsb, float * input)
74 {   
75    unsigned char in[MAX_FRAME_BYTES*2];
76    int i,d;
77    short *s;
78
79    /*Read input audio*/
80    fread(in,bits/8*channels, frame_size, fin);
81    if (feof(fin))
82       return 1;
83    s=(short*)in;
84    if(bits==8)
85    {
86       /* Convert 8->16 bits */
87       for(i=frame_size*channels-1;i>=0;i--)
88       {
89          s[i]=(in[i]<<8)^0x8000;
90       }
91    } else
92    {
93       /* convert to our endian format */
94       for(i=0;i<frame_size*channels;i++)
95       {
96          if(lsb) 
97             s[i]=le_short(s[i]); 
98          else
99             s[i]=be_short(s[i]);
100       }
101    }
102
103 #if 0
104    if(channels==2)
105    {
106       /* downmix to mono */
107       for(i=0;i<frame_size;i++)
108       {
109          d=s[i*2]+s[i*2+1];
110          s[i]=d>>1;
111       }
112    }
113 #endif
114
115    /* copy to float input buffer */
116    for (i=0;i<frame_size*channels;i++)
117    {
118       input[i]=(short)s[i];
119    }
120
121    return 0;
122 }
123
124 void usage()
125 {
126    /*printf ("Speex encoder version " VERSION " (compiled " __DATE__ ")\n");
127    printf ("\n");*/
128    printf ("Usage: speexenc [options] input_file output_file\n");
129    printf ("\n");
130    printf ("Encodes input_file using Speex. It can read the WAV or raw files.\n");
131    printf ("\n");
132    printf ("input_file can be:\n");
133    printf ("  filename.wav          wav file\n");
134    printf ("  filename.*            raw PCM file (any extension other than .wav)\n");
135    printf ("  -                     stdin\n");
136    printf ("\n");  
137    printf ("output_file can be:\n");
138    printf ("  filename.spx          Speex file\n");
139    printf ("  -                     stdout\n");
140    printf ("\n");  
141    printf ("Options:\n");
142    printf (" -n, --narrowband   Narrowband (8 kHz) input file\n"); 
143    printf (" -w, --wideband     Wideband (16 kHz) input file\n"); 
144    printf (" -u, --ultra-wideband \"Ultra-Wideband\" (32 kHz) input file\n"); 
145    printf (" --quality n        Encoding quality (0-10), default 3\n"); 
146    printf (" --vbr              Enable variable bit-rate (VBR)\n"); 
147    printf (" --comp n           Set encoding complexity (0-10), default 3\n"); 
148    printf (" --nframes n        Number of frames per Ogg packet (1-10), default 1\n"); 
149    printf (" --comment          Add the given string as an extra comment. This may be\n                     used multiple times.\n");
150    printf (" --author           Author of this track.\n");
151    printf (" --title            Title for this track.\n");
152    printf (" -h, --help         This help\n"); 
153    printf (" -v, --version      Version information\n"); 
154    printf (" -V                 Verbose mode (show bit-rate)\n"); 
155    printf ("Raw input options:\n");
156    printf (" --le               Raw input is little-endian\n"); 
157    printf (" --be               Raw input is big-endian\n"); 
158    printf (" --8bit             Raw input is 8-bit unsigned\n"); 
159    printf (" --16bit            Raw input is 16-bit signed\n"); 
160    printf (" --stereo           Consider input as stereo\n"); 
161    printf ("Default raw PCM input is 16-bit, little-endian, mono\n"); 
162    printf ("\n");
163    printf ("More information is available from the Speex site: http://www.speex.org\n");
164    printf ("\n");
165    printf ("Please report bugs to the mailing list `speex-dev@xiph.org'.\n");
166 }
167
168 void version()
169 {
170    printf ("Speex encoder version " VERSION " (compiled " __DATE__ ")\n");
171 }
172
173 int main(int argc, char **argv)
174 {
175    int c;
176    int option_index = 0;
177    int narrowband=0, wideband=0, ultrawide=0;
178    char *inFile, *outFile;
179    FILE *fin, *fout;
180    float input[MAX_FRAME_SIZE];
181    int frame_size;
182    int vbr_enabled=0;
183    int nbBytes;
184    SpeexMode *mode=NULL;
185    void *st;
186    SpeexBits bits;
187    char cbits[MAX_FRAME_BYTES];
188    struct option long_options[] =
189    {
190       {"wideband", no_argument, NULL, 0},
191       {"ultra-wideband", no_argument, NULL, 0},
192       {"narrowband", no_argument, NULL, 0},
193       {"vbr", no_argument, NULL, 0},
194       {"quality", required_argument, NULL, 0},
195       {"nframes", required_argument, NULL, 0},
196       {"comp", required_argument, NULL, 0},
197       {"help", no_argument, NULL, 0},
198       {"le", no_argument, NULL, 0},
199       {"be", no_argument, NULL, 0},
200       {"lin8", no_argument, NULL, 0},
201       {"lin16", no_argument, NULL, 0},
202       {"stereo", no_argument, NULL, 0},
203       {"version", no_argument, NULL, 0},
204       {"comment", required_argument, NULL, 0},
205       {"author", required_argument, NULL, 0},
206       {"title", required_argument, NULL, 0},
207       {0, 0, 0, 0}
208    };
209    int print_bitrate=0;
210    int rate, size;
211    int chan=1;
212    int fmt=16;
213    int quality=-1;
214    float vbr_quality=-1;
215    int lsb=1;
216    ogg_stream_state os;
217    ogg_page              og;
218    ogg_packet            op;
219    int bytes_written, ret, result;
220    int id=-1;
221    SpeexHeader header;
222    int nframes=1;
223    int complexity=3;
224    char *vendor_string = "Encoded with Speex " VERSION;
225    char *comments;
226    int comments_length;
227    int close_in=0, close_out=0;
228    int eos=0;
229
230    comment_init(&comments, &comments_length, vendor_string);
231
232    /*Process command-line options*/
233    while(1)
234    {
235       c = getopt_long (argc, argv, "nwhvV",
236                        long_options, &option_index);
237       if (c==-1)
238          break;
239       
240       switch(c)
241       {
242       case 0:
243          if (strcmp(long_options[option_index].name,"narrowband")==0)
244             narrowband=1;
245          else if (strcmp(long_options[option_index].name,"wideband")==0)
246                wideband=1;
247          else if (strcmp(long_options[option_index].name,"ultra-wideband")==0)
248                ultrawide=1;
249          else if (strcmp(long_options[option_index].name,"vbr")==0)
250                vbr_enabled=1;
251          else if (strcmp(long_options[option_index].name,"quality")==0)
252          {
253             quality = atoi (optarg);
254             vbr_quality=atof(optarg);
255          } else if (strcmp(long_options[option_index].name,"nframes")==0)
256          {
257             nframes = atoi (optarg);
258             if (nframes<1)
259                nframes=1;
260             if (nframes>10)
261                nframes=10;
262          } else if (strcmp(long_options[option_index].name,"comp")==0)
263          {
264             complexity = atoi (optarg);
265          } else if (strcmp(long_options[option_index].name,"help")==0)
266          {
267             usage();
268             exit(0);
269          } else if (strcmp(long_options[option_index].name,"version")==0)
270          {
271             version();
272             exit(0);
273          } else if (strcmp(long_options[option_index].name,"le")==0)
274          {
275             lsb=1;
276          } else if (strcmp(long_options[option_index].name,"be")==0)
277          {
278             lsb=0;
279          } else if (strcmp(long_options[option_index].name,"lin8")==0)
280          {
281             fmt=8;
282          } else if (strcmp(long_options[option_index].name,"lin16")==0)
283          {
284             fmt=16;
285          } else if (strcmp(long_options[option_index].name,"stereo")==0)
286          {
287             chan=2;
288          } else if (strcmp(long_options[option_index].name,"comment")==0)
289          {
290            comment_add(&comments, &comments_length, NULL, optarg); 
291          } else if (strcmp(long_options[option_index].name,"author")==0)
292          {
293            comment_add(&comments, &comments_length, "author=", optarg); 
294          } else if (strcmp(long_options[option_index].name,"title")==0)
295          {
296            comment_add(&comments, &comments_length, "title=", optarg); 
297          }
298
299          break;
300       case 'n':
301          narrowband=1;
302          break;
303       case 'h':
304          usage();
305          exit(0);
306          break;
307       case 'v':
308          version();
309          exit(0);
310          break;
311       case 'V':
312          print_bitrate=1;
313          break;
314       case 'w':
315          wideband=1;
316          break;
317       case 'u':
318          ultrawide=1;
319          break;
320       case '?':
321          usage();
322          exit(1);
323          break;
324       }
325    }
326    if (argc-optind!=2)
327    {
328       usage();
329       exit(1);
330    }
331    inFile=argv[optind];
332    outFile=argv[optind+1];
333
334    if ((wideband && narrowband) || (wideband && ultrawide) || (ultrawide && narrowband))
335    {
336       fprintf (stderr,"Cannot specify two modes at the same time\n");
337       exit(1);
338    };
339
340    /*Initialize Ogg stream struct*/
341    srand(time(NULL));
342    if (ogg_stream_init(&os, rand())==-1)
343    {
344       fprintf(stderr,"Stream init failed\n");
345       exit(1);
346    }
347
348    if (strcmp(inFile, "-")==0)
349    {
350 #if defined WIN32 || defined _WIN32
351          _setmode(_fileno(stdin), _O_BINARY);
352 #endif
353       fin=stdin;
354    }
355    else 
356    {
357 #if defined WIN32 || defined _WIN32
358       fin = fopen(inFile, "rb");
359 #else
360       fin = fopen(inFile, "r");
361 #endif
362       if (!fin)
363       {
364          perror(inFile);
365          exit(1);
366       }
367       close_in=1;
368    }
369
370    rate=0;
371    if (strcmp(inFile+strlen(inFile)-4,".wav")==0 || strcmp(inFile+strlen(inFile)-4,".WAV")==0)
372       {
373          if (read_wav_header(fin, &rate, &chan, &fmt, &size)==-1)
374             exit(1);
375          lsb=1; /* CHECK: exists big-endian .wav ?? */
376       }
377    /*fprintf (stderr, "wave info: %d %d %d %d\n", rate, chan, fmt, size);*/
378
379    if (rate==16000)
380    {
381       wideband=1;
382       if (narrowband)
383          fprintf (stderr,"Warning: encoding a wideband file in narrowband\n");
384    } else if (rate==8000)
385    {
386       narrowband=1;
387       if (wideband)
388          fprintf (stderr,"Warning: encoding a narrowband file in wideband\n");
389    } else if (rate==22050)
390    {
391       wideband=1;
392       fprintf (stderr,"Warning: Speex is not optimized for 22.05 kHz sampling rate. Your mileage may vary\n");
393       if (narrowband)
394          fprintf (stderr,"Warning: encoding a wideband file in narrowband\n");
395    } else if (rate==32000)
396    {
397       ultrawide=1;
398       if (wideband)
399          fprintf (stderr,"Warning: encoding a narrowband file in wideband\n");
400    } else if (rate==44100)
401    {
402       fprintf (stderr,"Warning: Speex is not optimized for 44.1 kHz sampling rate. Your mileage may vary\n");
403       ultrawide=1;
404       if (wideband)
405          fprintf (stderr,"Warning: encoding a narrowband file in wideband\n");
406    } else if (rate==48000)
407    {
408       fprintf (stderr,"Warning: Speex is not optimized for 48 kHz sampling rate. Your mileage may vary\n");
409       ultrawide=1;
410       if (wideband)
411          fprintf (stderr,"Warning: encoding a narrowband file in wideband\n");
412    } else if (rate==11025)
413    {
414       fprintf (stderr,"Warning: Speex is not optimized for 11.025 kHz sampling rate. Your mileage may vary\n");
415       narrowband=1;
416       if (wideband)
417          fprintf (stderr,"Warning: encoding a narrowband file in wideband\n");
418    }
419
420    if (!wideband)
421       narrowband=1;
422    if (narrowband)
423    {
424       if (!rate)
425          rate = 8000;
426       mode=&speex_nb_mode;
427    }
428    if (wideband)
429    {
430       if (!rate)
431          rate = 16000;
432       mode=&speex_wb_mode;
433    }
434    if (ultrawide)
435    {
436       if (!rate)
437          rate = 32000;
438       mode=&speex_uwb_mode;
439    }
440
441    speex_init_header(&header, rate, 1, mode);
442    header.frames_per_packet=nframes;
443    header.vbr=vbr_enabled;
444
445    fprintf (stderr, "Encoding %d Hz audio using %s mode\n", 
446             header.rate, mode->modeName);
447    /*fprintf (stderr, "Encoding %d Hz audio at %d bps using %s mode\n", 
448      header.rate, mode->bitrate, mode->modeName);*/
449
450    /*Initialize Speex encoder*/
451    st = speex_encoder_init(mode);
452
453    if (strcmp(outFile,"-")==0)
454    {
455 #if defined WIN32 || defined _WIN32
456       _setmode(_fileno(stdout), _O_BINARY);
457 #endif
458       fout=stdout;
459    }
460    else 
461    {
462 #if defined WIN32 || defined _WIN32
463       fout = fopen(outFile, "wb");
464 #else
465       fout = fopen(outFile, "w");
466 #endif
467       if (!fout)
468       {
469          perror(outFile);
470          exit(1);
471       }
472       close_out=1;
473    }
474
475
476    /*Write header (format will change)*/
477    {
478
479       op.packet = (unsigned char *)speex_header_to_packet(&header, (int*)&(op.bytes));
480       op.b_o_s = 1;
481       op.e_o_s = 0;
482       op.granulepos = 0;
483       op.packetno = 0;
484       ogg_stream_packetin(&os, &op);
485       free(op.packet);
486
487       op.packet = (unsigned char *)comments;
488       op.bytes = comments_length;
489       op.b_o_s = 0;
490       op.e_o_s = 0;
491       op.granulepos = 0;
492       op.packetno = 1;
493       ogg_stream_packetin(&os, &op);
494       
495       while((result = ogg_stream_flush(&os, &og)))
496       {
497          if(!result) break;
498          ret = oe_write_page(&og, fout);
499          if(ret != og.header_len + og.body_len)
500          {
501             fprintf (stderr,"Failed writing header to output stream\n");
502             exit(1);
503          }
504          else
505             bytes_written += ret;
506       }
507    }
508
509    free(comments);
510
511    speex_encoder_ctl(st, SPEEX_GET_FRAME_SIZE, &frame_size);
512    speex_encoder_ctl(st, SPEEX_SET_COMPLEXITY, &complexity);
513    if (vbr_enabled)
514    {
515       int tmp;
516       tmp=1;
517       speex_encoder_ctl(st, SPEEX_SET_VBR, &tmp);
518    }
519    if (quality >= 0)
520    {
521       if (vbr_enabled)
522          speex_encoder_ctl(st, SPEEX_SET_VBR_QUALITY, &vbr_quality);
523       else
524          speex_encoder_ctl(st, SPEEX_SET_QUALITY, &quality);
525    }
526
527    speex_bits_init(&bits);
528
529    if (read_samples(fin,frame_size,fmt,chan,lsb,input))
530       eos=1;
531
532    /*Main encoding loop (one frame per iteration)*/
533    while (!eos)
534    {
535       id++;
536       /*Encode current frame*/
537       if (chan==2)
538          speex_encode_stereo(input, frame_size, &bits);
539       speex_encode(st, input, &bits);
540       
541       if (print_bitrate) {
542          int tmp;
543          char ch=13;
544          speex_encoder_ctl(st, SPEEX_GET_BITRATE, &tmp);
545          fputc (ch, stderr);
546          fprintf (stderr, "Bitrate is use: %d bps     ", tmp);
547       }
548
549       if (read_samples(fin,frame_size,fmt,chan,lsb,input))
550       {
551          eos=1;
552          op.e_o_s = 1;
553       }
554
555       if ((id+1)%nframes!=0)
556          continue;
557       nbBytes = speex_bits_write(&bits, cbits, MAX_FRAME_BYTES);
558       speex_bits_reset(&bits);
559       op.packet = (unsigned char *)cbits;
560       op.bytes = nbBytes;
561       op.b_o_s = 0;
562       if (eos)
563          op.e_o_s = 1;
564       else
565          op.e_o_s = 0;
566       op.granulepos = (id+nframes)*frame_size;
567       op.packetno = 2+id/nframes;
568       ogg_stream_packetin(&os, &op);
569
570       /*Write all new pages (most likely 0 or 1)*/
571       while (ogg_stream_pageout(&os,&og))
572       {
573          ret = oe_write_page(&og, fout);
574          if(ret != og.header_len + og.body_len)
575          {
576             fprintf (stderr,"Failed writing header to output stream\n");
577             exit(1);
578          }
579          else
580             bytes_written += ret;
581       }
582    }
583    if ((id+1)%nframes!=0)
584    {
585       while ((id+1)%nframes!=0)
586       {
587          id++;
588          speex_bits_pack(&bits, 0, 7);
589       }
590       nbBytes = speex_bits_write(&bits, cbits, MAX_FRAME_BYTES);
591       op.packet = (unsigned char *)cbits;
592       op.bytes = nbBytes;
593       op.b_o_s = 0;
594       op.e_o_s = 1;
595       op.granulepos = (id+nframes)*frame_size;
596       op.packetno = 2+id/nframes;
597       ogg_stream_packetin(&os, &op);
598    }
599    /*Flush all pages left to be written*/
600    while (ogg_stream_flush(&os, &og))
601    {
602       ret = oe_write_page(&og, fout);
603       if(ret != og.header_len + og.body_len)
604       {
605          fprintf (stderr,"Failed writing header to output stream\n");
606          exit(1);
607       }
608       else
609          bytes_written += ret;
610    }
611    
612
613    speex_encoder_destroy(st);
614    speex_bits_destroy(&bits);
615    ogg_stream_clear(&os);
616
617    if (close_in)
618       fclose(fin);
619    if (close_out)
620       fclose(fout);
621    return 1;
622 }
623
624 /*                 
625  Comments will be stored in the Vorbis style.            
626  It is describled in the "Structure" section of
627     http://www.xiph.org/ogg/vorbis/doc/v-comment.html
628
629 The comment header is decoded as follows:
630   1) [vendor_length] = read an unsigned integer of 32 bits
631   2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
632   3) [user_comment_list_length] = read an unsigned integer of 32 bits
633   4) iterate [user_comment_list_length] times {
634      5) [length] = read an unsigned integer of 32 bits
635      6) this iteration's user comment = read a UTF-8 vector as [length] octets
636      }
637   7) [framing_bit] = read a single bit as boolean
638   8) if ( [framing_bit]  unset or end of packet ) then ERROR
639   9) done.
640
641   If you have troubles, please write to ymnk@jcraft.com.
642  */
643
644 #define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
645                            ((buf[base+2]<<16)&0xff0000)| \
646                            ((buf[base+1]<<8)&0xff00)| \
647                             (buf[base]&0xff))
648 #define writeint(buf, base, val) do{ buf[base+3]=((val)>>24)&0xff; \
649                                      buf[base+2]=((val)>>16)&0xff; \
650                                      buf[base+1]=((val)>>8)&0xff; \
651                                      buf[base]=(val)&0xff; \
652                                  }while(0)
653
654 void comment_init(char **comments, int* length, char *vendor_string)
655 {
656   int vendor_length=strlen(vendor_string);
657   int user_comment_list_length=0;
658   int len=4+vendor_length+4;
659   char *p=(char*)malloc(len);
660   if(p==NULL){
661   }
662   writeint(p, 0, vendor_length);
663   memcpy(p+4, vendor_string, vendor_length);
664   writeint(p, 4+vendor_length, user_comment_list_length);
665   *length=len;
666   *comments=p;
667 }
668 void comment_add(char **comments, int* length, char *tag, char *val)
669 {
670   char* p=*comments;
671   int vendor_length=readint(p, 0);
672   int user_comment_list_length=readint(p, 4+vendor_length);
673   int tag_len=(tag?strlen(tag):0);
674   int val_len=strlen(val);
675   int len=(*length)+4+tag_len+val_len;
676
677   p=(char*)realloc(p, len);
678   if(p==NULL){
679   }
680
681   writeint(p, *length, tag_len+val_len);      /* length of comment */
682   if(tag) memcpy(p+*length+4, tag, tag_len);  /* comment */
683   memcpy(p+*length+4+tag_len, val, val_len);  /* comment */
684   writeint(p, 4+vendor_length, user_comment_list_length+1);
685
686   *comments=p;
687   *length=len;
688 }
689 #undef readint
690 #undef writeint