mostly wideband tuning...
[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 (yet/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    if (!st)
298    {
299       fprintf (stderr, "Decoder initialization failed.\n");
300       return NULL;
301    }
302    speex_decoder_ctl(st, SPEEX_SET_ENH, &enh_enabled);
303    speex_decoder_ctl(st, SPEEX_GET_FRAME_SIZE, frame_size);
304
305    if (!(*channels==1))
306    {
307       callback.callback_id = SPEEX_INBAND_STEREO;
308       callback.func = speex_std_stereo_request_handler;
309       callback.data = stereo;
310       speex_decoder_ctl(st, SPEEX_SET_HANDLER, &callback);
311    }
312    if (!*rate)
313       *rate = header->rate;
314    /* Adjust rate if --force-* options are used */
315    if (forceMode!=-1)
316    {
317       if (header->mode < forceMode)
318          *rate <<= (forceMode - header->mode);
319       if (header->mode > forceMode)
320          *rate >>= (header->mode - forceMode);
321    }
322
323    speex_decoder_ctl(st, SPEEX_SET_SAMPLING_RATE, rate);
324
325    *nframes = header->frames_per_packet;
326
327    if (*channels==-1)
328       *channels = header->nb_channels;
329    
330    fprintf (stderr, "Decoding %d Hz audio using %s mode", 
331             *rate, mode->modeName);
332
333    if (*channels==1)
334       fprintf (stderr, " (mono");
335    else
336       fprintf (stderr, " (stereo");
337       
338    if (header->vbr)
339       fprintf (stderr, ", VBR)\n");
340    else
341       fprintf(stderr, ")\n");
342    /*fprintf (stderr, "Decoding %d Hz audio at %d bps using %s mode\n", 
343     *rate, mode->bitrate, mode->modeName);*/
344
345    free(header);
346    return st;
347 }
348
349 int main(int argc, char **argv)
350 {
351    int c;
352    int option_index = 0;
353    char *inFile, *outFile;
354    FILE *fin, *fout=NULL;
355    short out[MAX_FRAME_SIZE];
356    float output[MAX_FRAME_SIZE];
357    int frame_size=0;
358    void *st=NULL;
359    SpeexBits bits;
360    int packet_count=0;
361    int stream_init = 0;
362    struct option long_options[] =
363    {
364       {"help", no_argument, NULL, 0},
365       {"version", no_argument, NULL, 0},
366       {"version-short", no_argument, NULL, 0},
367       {"enh", no_argument, NULL, 0},
368       {"no-enh", no_argument, NULL, 0},
369       {"pf", no_argument, NULL, 0},
370       {"no-pf", no_argument, NULL, 0},
371       {"force-nb", no_argument, NULL, 0},
372       {"force-wb", no_argument, NULL, 0},
373       {"force-uwb", no_argument, NULL, 0},
374       {"rate", required_argument, NULL, 0},
375       {"mono", no_argument, NULL, 0},
376       {"stereo", no_argument, NULL, 0},
377       {"packet-loss", required_argument, NULL, 0},
378       {0, 0, 0, 0}
379    };
380    ogg_sync_state oy;
381    ogg_page       og;
382    ogg_packet     op;
383    ogg_stream_state os;
384    int enh_enabled;
385    int nframes=2;
386    int print_bitrate=0;
387    int close_in=0;
388    int eos=0;
389    int forceMode=-1;
390    int audio_size=0;
391    float loss_percent=-1;
392    SpeexStereoState stereo = SPEEX_STEREO_STATE_INIT;
393    int channels=-1;
394    int rate=0;
395
396    enh_enabled = 1;
397
398    /*Process options*/
399    while(1)
400    {
401       c = getopt_long (argc, argv, "hvV",
402                        long_options, &option_index);
403       if (c==-1)
404          break;
405       
406       switch(c)
407       {
408       case 0:
409          if (strcmp(long_options[option_index].name,"help")==0)
410          {
411             usage();
412             exit(0);
413          } else if (strcmp(long_options[option_index].name,"version")==0)
414          {
415             version();
416             exit(0);
417          } else if (strcmp(long_options[option_index].name,"version-short")==0)
418          {
419             version_short();
420             exit(0);
421          } else if (strcmp(long_options[option_index].name,"enh")==0)
422          {
423             enh_enabled=1;
424          } else if (strcmp(long_options[option_index].name,"no-enh")==0)
425          {
426             enh_enabled=0;
427          } else if (strcmp(long_options[option_index].name,"pf")==0)
428          {
429             fprintf (stderr, "--pf is deprecated, use --enh instead\n");
430             enh_enabled=1;
431          } else if (strcmp(long_options[option_index].name,"no-pf")==0)
432          {
433             fprintf (stderr, "--no-pf is deprecated, use --no-enh instead\n");
434             enh_enabled=0;
435          } else if (strcmp(long_options[option_index].name,"force-nb")==0)
436          {
437             forceMode=0;
438          } else if (strcmp(long_options[option_index].name,"force-wb")==0)
439          {
440             forceMode=1;
441          } else if (strcmp(long_options[option_index].name,"force-uwb")==0)
442          {
443             forceMode=2;
444          } else if (strcmp(long_options[option_index].name,"mono")==0)
445          {
446             channels=1;
447          } else if (strcmp(long_options[option_index].name,"stereo")==0)
448          {
449             channels=2;
450          } else if (strcmp(long_options[option_index].name,"rate")==0)
451          {
452             rate=atoi (optarg);
453          } else if (strcmp(long_options[option_index].name,"packet-loss")==0)
454          {
455             loss_percent = atof(optarg);
456          }
457          break;
458       case 'h':
459          usage();
460          exit(0);
461          break;
462       case 'v':
463          version();
464          exit(0);
465          break;
466       case 'V':
467          print_bitrate=1;
468          break;
469       case '?':
470          usage();
471          exit(1);
472          break;
473       }
474    }
475    if (argc-optind!=2 && argc-optind!=1)
476    {
477       usage();
478       exit(1);
479    }
480    inFile=argv[optind];
481
482    if (argc-optind==2)
483       outFile=argv[optind+1];
484    else
485       outFile = "";
486    /*Open input file*/
487    if (strcmp(inFile, "-")==0)
488    {
489 #if defined WIN32 || defined _WIN32
490       _setmode(_fileno(stdin), _O_BINARY);
491 #endif
492       fin=stdin;
493    }
494    else 
495    {
496 #if defined WIN32 || defined _WIN32
497       fin = fopen(inFile, "rb");
498 #else
499       fin = fopen(inFile, "r");
500 #endif
501       if (!fin)
502       {
503          perror(inFile);
504          exit(1);
505       }
506       close_in=1;
507    }
508
509
510    /*Init Ogg data struct*/
511    ogg_sync_init(&oy);
512    
513    speex_bits_init(&bits);
514    /*Main decoding loop*/
515    while (1)
516    {
517       char *data;
518       int i, j, nb_read;
519       /*Get the ogg buffer for writing*/
520       data = ogg_sync_buffer(&oy, 200);
521       /*Read bitstream from input file*/
522       nb_read = fread(data, sizeof(char), 200, fin);      
523       ogg_sync_wrote(&oy, nb_read);
524
525       /*Loop for all complete pages we got (most likely only one)*/
526       while (ogg_sync_pageout(&oy, &og)==1)
527       {
528          if (stream_init == 0) {
529             ogg_stream_init(&os, ogg_page_serialno(&og));
530             stream_init = 1;
531          }
532          /*Add page to the bitstream*/
533          ogg_stream_pagein(&os, &og);
534          /*Extract all available packets*/
535          while (!eos && ogg_stream_packetout(&os, &op)==1)
536          {
537             /*If first packet, process as Speex header*/
538             if (packet_count==0)
539             {
540                st = process_header(&op, enh_enabled, &frame_size, &rate, &nframes, forceMode, &channels, &stereo);
541                if (!nframes)
542                   nframes=1;
543                if (!st)
544                   exit(1);
545                fout = out_file_open(outFile, rate, &channels);
546
547             } else if (packet_count==1){
548                print_comments((char*)op.packet, op.bytes);
549                /*
550                fprintf (stderr, "File comments: ");
551                fwrite(op.packet, 1, op.bytes, stderr);
552                fprintf (stderr, "\n");
553                */
554             } else {
555                
556                int lost=0;
557                if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent)
558                   lost=1;
559
560                /*End of stream condition*/
561                if (op.e_o_s)
562                   eos=1;
563
564                /*Copy Ogg packet to Speex bitstream*/
565                speex_bits_read_from(&bits, (char*)op.packet, op.bytes);
566                for (j=0;j<nframes;j++)
567                {
568                   int ret;
569                   /*Decode frame*/
570                   if (!lost)
571                      ret = speex_decode(st, &bits, output);
572                   else
573                      ret = speex_decode(st, NULL, output);
574
575                   if (ret==-1)
576                      break;
577                   if (ret==-2)
578                   {
579                      fprintf (stderr, "Decoding error: corrupted stream?\n");
580                      break;
581                   }
582                   if (speex_bits_remaining(&bits)<0)
583                   {
584                      fprintf (stderr, "Decoding overflow: corrupted stream?\n");
585                      break;
586                   }
587                   if (channels==2)
588                      speex_decode_stereo(output, frame_size, &stereo);
589
590                   if (print_bitrate) {
591                      int tmp;
592                      char ch=13;
593                      speex_decoder_ctl(st, SPEEX_GET_BITRATE, &tmp);
594                      fputc (ch, stderr);
595                      fprintf (stderr, "Bitrate is use: %d bps     ", tmp);
596                   }
597                   /*PCM saturation (just in case)*/
598                   for (i=0;i<frame_size*channels;i++)
599                   {
600                      if (output[i]>32000.0)
601                         output[i]=32000.0;
602                      else if (output[i]<-32000.0)
603                         output[i]=-32000.0;
604                   }
605                   /*Convert to short and save to output file*/
606                   for (i=0;i<frame_size*channels;i++)
607                      out[i]=(short)le_short((short)floor(.5+output[i]));
608 #if defined WIN32 || defined _WIN32
609                   if (strlen(outFile)==0)
610                       WIN_Play_Samples (out, sizeof(short) * frame_size*channels);
611                   else
612 #endif
613                   fwrite(out, sizeof(short), frame_size*channels, fout);
614                   
615                   audio_size+=sizeof(short)*frame_size*channels;
616                }
617             }
618             packet_count++;
619          }
620       }
621       if (feof(fin))
622          break;
623
624    }
625
626    if (strcmp(outFile+strlen(outFile)-4,".wav")==0 || strcmp(inFile+strlen(inFile)-4,".WAV")==0)
627    {
628       if (fseek(fout,4,SEEK_SET)==0)
629       {
630          int tmp;
631          tmp = le_int(audio_size+36);
632          fwrite(&tmp,4,1,fout);
633          if (fseek(fout,32,SEEK_CUR)==0)
634          {
635             tmp = le_int(audio_size);
636             fwrite(&tmp,4,1,fout);
637          } else
638          {
639             fprintf (stderr, "First seek worked, second didn't\n");
640          }
641       } else {
642          fprintf (stderr, "Cannot seek on wave file, size will be incorrect\n");
643       }
644    }
645
646    if (st)
647       speex_decoder_destroy(st);
648    else 
649    {
650       fprintf (stderr, "This doesn't look like a Speex file\n");
651    }
652    speex_bits_destroy(&bits);
653    ogg_sync_clear(&oy);
654    ogg_stream_clear(&os);
655
656 #if defined WIN32 || defined _WIN32
657    if (fout && strlen(outFile)==0)
658       WIN_Audio_close ();
659 #endif
660
661    if (close_in)
662       fclose(fin);
663    if (fout != NULL)
664       fclose(fout);   
665
666    return 0;
667 }