Implemented stereo at the decoder side
[speexdsp.git] / src / speexdec.c
1 /* Copyright (C) 2002 Jean-Marc Valin 
2    File: speexdec.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
40 #include "speex.h"
41 #include "ogg/ogg.h"
42
43 #if defined WIN32 || defined _WIN32
44 #include <windows.h>
45 #include "getopt_win.h"
46 #include "wave_out.h"
47 /* We need the following two to set stdout to binary */
48 #include <io.h>
49 #include <fcntl.h>
50 #endif
51
52 #ifdef HAVE_SYS_SOUNDCARD_H
53 #include <sys/soundcard.h>
54 #include <sys/types.h>
55 #include <sys/stat.h>
56 #include <fcntl.h>
57 #include <sys/ioctl.h>
58 #endif
59
60 #include <string.h>
61 #include "wav_io.h"
62 #include "speex_header.h"
63 #include "speex_stereo.h"
64 #include "speex_callbacks.h"
65 #include "misc.h"
66
67 #define MAX_FRAME_SIZE 2000
68
69 #define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
70                            ((buf[base+2]<<16)&0xff0000)| \
71                            ((buf[base+1]<<8)&0xff00)| \
72                             (buf[base]&0xff))
73
74 static void print_comments(char *comments, int length)
75 {
76    char *c=comments;
77    int len, i, nb_fields;
78
79    len=readint(c, 0);
80    c+=4;
81    fwrite(c, 1, len, stderr);
82    c+=len;
83    fprintf (stderr, "\n");
84    nb_fields=readint(c, 0);
85    c+=4;
86    for (i=0;i<nb_fields;i++)
87    {
88       len=readint(c, 0);
89       c+=4;
90       fwrite(c, 1, len, stderr);
91       c+=len;
92       fprintf (stderr, "\n");
93    }
94 }
95
96 FILE *out_file_open(char *outFile, int rate, int *channels)
97 {
98    FILE *fout=NULL;
99    /*Open output file*/
100    if (strlen(outFile)==0)
101    {
102 #if defined HAVE_SYS_SOUNDCARD_H
103       int audio_fd, format, stereo;
104       audio_fd=open("/dev/dsp", O_WRONLY);
105       
106       format=AFMT_S16_LE;
107       if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format)==-1)
108       {
109          perror("SNDCTL_DSP_SETFMT");
110          close(audio_fd);
111          exit(1);
112       }
113       
114       stereo=0;
115       if (*channels=2)
116          stereo=1;
117       if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo)==-1)
118       {
119          perror("SNDCTL_DSP_STEREO");
120          close(audio_fd);
121          exit(1);
122       }
123       if (stereo!=0)
124       {
125          if (*channels==1)
126             fprintf (stderr, "Cannot set mono mode, will decode in stereo\n");
127          *channels=2;
128          fprintf (stderr, "Cannot set mono mode\n");
129       }
130
131       if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate)==-1)
132       {
133          perror("SNDCTL_DSP_SPEED");
134          close(audio_fd);
135          exit(1);
136       }
137       fout = fdopen(audio_fd, "w");
138 #elif defined WIN32 || defined _WIN32
139       if (Set_WIN_Params (INVALID_FILEDESC, rate, SAMPLE_SIZE, 1))
140       {
141          fprintf (stderr, "Can't access %s\n", "WAVE OUT");
142          exit(1);
143       }
144 #else
145       fprintf (stderr, "No soundcard support\n");
146       exit(1);
147 #endif
148    } else {
149       if (strcmp(outFile,"-")==0)
150       {
151 #if defined WIN32 || defined _WIN32
152          _setmode(_fileno(stdout), _O_BINARY);
153 #endif
154          fout=stdout;
155       }
156       else 
157       {
158 #if defined WIN32 || defined _WIN32
159          fout = fopen(outFile, "wb");
160 #else
161          fout = fopen(outFile, "w");
162 #endif
163          if (!fout)
164          {
165             perror(outFile);
166             exit(1);
167          }
168          if (strcmp(outFile+strlen(outFile)-4,".wav")==0 || strcmp(outFile+strlen(outFile)-4,".WAV")==0)
169             write_wav_header(fout, rate, *channels, 0, 0);
170       }
171    }
172    return fout;
173 }
174
175 void usage()
176 {
177    /*printf ("Speex decoder version " VERSION " (compiled " __DATE__ ")\n");
178    printf ("\n");*/
179    printf ("Usage: speexdec [options] input_file.spx\n");
180    printf ("       speexdec [options] input_file.spx output_file.wav\n");
181    printf ("\n");
182    printf ("Decodes a Speex file and produce a WAV file or raw file\n");
183    printf ("\n");
184    printf ("input_file can be:\n");
185    printf ("  filename.spx          regular Speex file\n");
186    printf ("  -                     stdin\n");
187    printf ("\n");  
188    printf ("output_file can be:\n");
189    printf ("  filename.wav          wav file\n");
190    printf ("  filename.*            raw PCM file (any extension other that .wav)\n");
191    printf ("  -                     stdout\n");
192    printf ("  (nothing)             will be played to soundcard\n");
193    printf ("\n");  
194    printf ("Options:\n");
195    printf (" --enh                 Enable perceptual enhancement\n");
196    printf (" --no-enh              Disable perceptual enhancement (default FOR NOW)\n");
197    printf (" --force-nb            Force decoding in narrowband, even for wideband\n");
198    printf (" --force-wb            Force decoding in wideband, even for narrowband\n");
199    printf (" --mono                Force decoding in mono\n");
200    printf (" --stereo              Force decoding in stereo\n");
201    printf (" --packet-loss n       Simulate n %% random packet loss\n");
202    printf (" -V                    Verbose mode (show bit-rate)\n"); 
203    printf (" -h, --help            This help\n");
204    printf (" -v, --version         Version information\n");
205    printf (" --pf                  Deprecated, use --pf instead\n");
206    printf (" --no-pf               Deprecated, use --no-pf instead\n");
207    printf ("\n");
208    printf ("More information is available from the Speex site: http://www.speex.org\n");
209    printf ("\n");
210    printf ("Please report bugs to the mailing list `speex-dev@xiph.org'.\n");
211 }
212
213 void version()
214 {
215    printf ("Speex decoder version " VERSION " (compiled " __DATE__ ")\n");
216 }
217
218 static void *process_header(ogg_packet *op, int enh_enabled, int *frame_size, int *rate, int *nframes, int forceMode, int *channels, SpeexStereoState *stereo)
219 {
220    void *st;
221    SpeexMode *mode;
222    SpeexHeader *header;
223    int modeID;
224    SpeexCallback callback;
225       
226    header = speex_packet_to_header((char*)op->packet, op->bytes);
227    if (!header)
228    {
229       fprintf (stderr, "Cannot read header\n");
230       return NULL;
231    }
232    if (header->mode >= SPEEX_NB_MODES)
233    {
234       fprintf (stderr, "Mode number %d does not (any longer) exist in this version\n", 
235                header->mode);
236       return NULL;
237    }
238       
239    modeID = header->mode;
240    if (forceMode!=-1)
241       modeID = forceMode;
242    mode = speex_mode_list[modeID];
243    
244    if (mode->bitstream_version < header->mode_bitstream_version)
245    {
246       fprintf (stderr, "The file was encoded with a newer version of Speex. You need to upgrade in order to play it.\n");
247       return NULL;
248    }
249    if (mode->bitstream_version > header->mode_bitstream_version) 
250    {
251       fprintf (stderr, "The file was encoded with an older version of Speex. You would need to downgrade the version in order to play it.\n");
252       return NULL;
253    }
254    
255    st = speex_decoder_init(mode);
256    speex_decoder_ctl(st, SPEEX_SET_ENH, &enh_enabled);
257    speex_decoder_ctl(st, SPEEX_GET_FRAME_SIZE, frame_size);
258
259    callback.callback_id = SPEEX_INBAND_STEREO;
260    callback.func = speex_std_stereo_request_handler;
261    callback.data = stereo;
262    speex_decoder_ctl(st, SPEEX_SET_HANDLER, &callback);
263    
264    /* FIXME: need to adjust in case the forceMode option is set */
265    *rate = header->rate;
266    if (header->mode==1 && forceMode==0)
267       *rate/=2;
268    if (header->mode==0 && forceMode==1)
269       *rate*=2;
270    *nframes = header->frames_per_packet;
271
272    if (*channels==-1)
273       *channels = header->nb_channels;
274    
275    fprintf (stderr, "Decoding %d Hz audio using %s mode", 
276             *rate, mode->modeName);
277
278    if (header->vbr)
279       fprintf (stderr, " (VBR)\n");
280    else
281       fprintf(stderr, "\n");
282    /*fprintf (stderr, "Decoding %d Hz audio at %d bps using %s mode\n", 
283     *rate, mode->bitrate, mode->modeName);*/
284
285    free(header);
286    return st;
287 }
288
289 int main(int argc, char **argv)
290 {
291    int c;
292    int option_index = 0;
293    char *inFile, *outFile;
294    FILE *fin, *fout=NULL;
295    short out[MAX_FRAME_SIZE];
296    float output[MAX_FRAME_SIZE];
297    int frame_size=0;
298    void *st=NULL;
299    SpeexBits bits;
300    int packet_count=0;
301    int stream_init = 0;
302    struct option long_options[] =
303    {
304       {"help", no_argument, NULL, 0},
305       {"version", no_argument, NULL, 0},
306       {"enh", no_argument, NULL, 0},
307       {"no-enh", no_argument, NULL, 0},
308       {"pf", no_argument, NULL, 0},
309       {"no-pf", no_argument, NULL, 0},
310       {"force-nb", no_argument, NULL, 0},
311       {"force-wb", no_argument, NULL, 0},
312       {"mono", no_argument, NULL, 0},
313       {"stereo", no_argument, NULL, 0},
314       {"packet-loss", required_argument, NULL, 0},
315       {0, 0, 0, 0}
316    };
317    ogg_sync_state oy;
318    ogg_page       og;
319    ogg_packet     op;
320    ogg_stream_state os;
321    int enh_enabled;
322    int nframes=2;
323    int print_bitrate=0;
324    int close_in=0;
325    int eos=0;
326    int forceMode=-1;
327    int audio_size=0;
328    float loss_percent=-1;
329    SpeexStereoState stereo = SPEEX_STEREO_STATE_INIT;
330    int channels=-1;
331
332    enh_enabled = 0;
333
334    /*Process options*/
335    while(1)
336    {
337       c = getopt_long (argc, argv, "hvV",
338                        long_options, &option_index);
339       if (c==-1)
340          break;
341       
342       switch(c)
343       {
344       case 0:
345          if (strcmp(long_options[option_index].name,"help")==0)
346          {
347             usage();
348             exit(0);
349          } else if (strcmp(long_options[option_index].name,"version")==0)
350          {
351             version();
352             exit(0);
353          } else if (strcmp(long_options[option_index].name,"enh")==0)
354          {
355             enh_enabled=1;
356          } else if (strcmp(long_options[option_index].name,"no-enh")==0)
357          {
358             enh_enabled=0;
359          } else if (strcmp(long_options[option_index].name,"pf")==0)
360          {
361             fprintf (stderr, "--pf is deprecated, use --enh instead\n");
362             enh_enabled=1;
363          } else if (strcmp(long_options[option_index].name,"no-pf")==0)
364          {
365             fprintf (stderr, "--no-pf is deprecated, use --no-enh instead\n");
366             enh_enabled=0;
367          } else if (strcmp(long_options[option_index].name,"force-nb")==0)
368          {
369             forceMode=0;
370          } else if (strcmp(long_options[option_index].name,"force-wb")==0)
371          {
372             forceMode=1;
373          } else if (strcmp(long_options[option_index].name,"mono")==0)
374          {
375             channels=1;
376          } else if (strcmp(long_options[option_index].name,"stereo")==0)
377          {
378             channels=2;
379          } else if (strcmp(long_options[option_index].name,"packet-loss")==0)
380          {
381             loss_percent = atof(optarg);
382          }
383          break;
384       case 'h':
385          usage();
386          exit(0);
387          break;
388       case 'v':
389          version();
390          exit(0);
391          break;
392       case 'V':
393          print_bitrate=1;
394          break;
395       case '?':
396          usage();
397          exit(1);
398          break;
399       }
400    }
401    if (argc-optind!=2 && argc-optind!=1)
402    {
403       usage();
404       exit(1);
405    }
406    inFile=argv[optind];
407
408    if (argc-optind==2)
409       outFile=argv[optind+1];
410    else
411       outFile = "";
412    /*Open input file*/
413    if (strcmp(inFile, "-")==0)
414    {
415 #if defined WIN32 || defined _WIN32
416       _setmode(_fileno(stdin), _O_BINARY);
417 #endif
418       fin=stdin;
419    }
420    else 
421    {
422 #if defined WIN32 || defined _WIN32
423       fin = fopen(inFile, "rb");
424 #else
425       fin = fopen(inFile, "r");
426 #endif
427       if (!fin)
428       {
429          perror(inFile);
430          exit(1);
431       }
432       close_in=1;
433    }
434
435
436    /*Init Ogg data struct*/
437    ogg_sync_init(&oy);
438    
439    speex_bits_init(&bits);
440    /*Main decoding loop*/
441    while (1)
442    {
443       char *data;
444       int i, j, nb_read;
445       /*Get the ogg buffer for writing*/
446       data = ogg_sync_buffer(&oy, 200);
447       /*Read bitstream from input file*/
448       nb_read = fread(data, sizeof(char), 200, fin);      
449       ogg_sync_wrote(&oy, nb_read);
450
451       /*Loop for all complete pages we got (most likely only one)*/
452       while (ogg_sync_pageout(&oy, &og)==1)
453       {
454          if (stream_init == 0) {
455             ogg_stream_init(&os, ogg_page_serialno(&og));
456             stream_init = 1;
457          }
458          /*Add page to the bitstream*/
459          ogg_stream_pagein(&os, &og);
460          /*Extract all available packets*/
461          while (!eos && ogg_stream_packetout(&os, &op)==1)
462          {
463             /*If first packet, process as Speex header*/
464             if (packet_count==0)
465             {
466                int rate;
467                st = process_header(&op, enh_enabled, &frame_size, &rate, &nframes, forceMode, &channels, &stereo);
468                if (!nframes)
469                   nframes=1;
470                if (!st)
471                   exit(1);
472                fout = out_file_open(outFile, rate, &channels);
473
474             } else if (packet_count==1){
475                print_comments((char*)op.packet, op.bytes);
476                /*
477                fprintf (stderr, "File comments: ");
478                fwrite(op.packet, 1, op.bytes, stderr);
479                fprintf (stderr, "\n");
480                */
481             } else {
482                
483                int lost=0;
484                if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent)
485                   lost=1;
486
487                /*End of stream condition*/
488                if (op.e_o_s)
489                   eos=1;
490
491                /*Copy Ogg packet to Speex bitstream*/
492                speex_bits_read_from(&bits, (char*)op.packet, op.bytes);
493                for (j=0;j<nframes;j++)
494                {
495                   /*Decode frame*/
496                   if (!lost)
497                      speex_decode(st, &bits, output);
498                   else
499                      speex_decode(st, NULL, output);
500
501                   if (channels==2)
502                      speex_decode_stereo(output, frame_size, &stereo);
503
504                   if (print_bitrate) {
505                      int tmp;
506                      char ch=13;
507                      speex_decoder_ctl(st, SPEEX_GET_BITRATE, &tmp);
508                      fputc (ch, stderr);
509                      fprintf (stderr, "Bitrate is use: %d bps     ", tmp);
510                   }
511                   /*PCM saturation (just in case)*/
512                   for (i=0;i<frame_size*channels;i++)
513                   {
514                      if (output[i]>32000.0)
515                         output[i]=32000.0;
516                      else if (output[i]<-32000.0)
517                         output[i]=-32000.0;
518                   }
519                   /*Convert to short and save to output file*/
520                   for (i=0;i<frame_size*channels;i++)
521                      out[i]=(short)le_short((short)output[i]);
522 #if defined WIN32 || defined _WIN32
523                   if (strlen(outFile)==0)
524                       WIN_Play_Samples (out, sizeof(short) * frame_size*channels);
525                   else
526 #endif
527                   fwrite(out, sizeof(short), frame_size*channels, fout);
528                   
529                   /*FIXME: Not sure we should multiply by channels here*/
530                   audio_size+=sizeof(short)*frame_size*channels;
531                }
532             }
533             packet_count++;
534          }
535       }
536       if (feof(fin))
537          break;
538
539    }
540
541    if (strcmp(outFile+strlen(outFile)-4,".wav")==0 || strcmp(inFile+strlen(inFile)-4,".WAV")==0)
542    {
543       if (fseek(fout,4,SEEK_SET)==0)
544       {
545          int tmp;
546          tmp = le_int(audio_size+36);
547          fwrite(&tmp,4,1,fout);
548          if (fseek(fout,32,SEEK_CUR)==0)
549          {
550             tmp = le_int(audio_size);
551             fwrite(&tmp,4,1,fout);
552          } else
553          {
554             fprintf (stderr, "First seek worked, second didn't\n");
555          }
556       } else {
557          fprintf (stderr, "Cannot seek on wave file, size will be incorrect\n");
558       }
559    }
560
561    if (st)
562       speex_decoder_destroy(st);
563    speex_bits_destroy(&bits);
564    ogg_sync_clear(&oy);
565    ogg_stream_clear(&os);
566
567 #if defined WIN32 || defined _WIN32
568    if (strlen(outFile)==0)
569       WIN_Audio_close ();
570 #endif
571
572    if (close_in)
573       fclose(fin);
574    if (fout != NULL)
575       fclose(fout);   
576
577    return 1;
578 }