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