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