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