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