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