689ea4149830219515ad7b912493f82443a34440
[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.channels = *channels;
198       
199       if (ioctl(audio_fd, AUDIO_SETINFO, &info) < 0)
200       {
201          perror ("AUDIO_SETINFO");
202          exit(1);
203       }
204       fout = fdopen(audio_fd, "w");
205 #elif defined WIN32 || defined _WIN32
206       {
207          unsigned int speex_channels = *channels;
208          if (Set_WIN_Params (INVALID_FILEDESC, rate, SAMPLE_SIZE, speex_channels))
209          {
210             fprintf (stderr, "Can't access %s\n", "WAVE OUT");
211             exit(1);
212          }
213       }
214 #else
215       fprintf (stderr, "No soundcard support\n");
216       exit(1);
217 #endif
218    } else {
219       if (strcmp(outFile,"-")==0)
220       {
221 #if defined WIN32 || defined _WIN32
222          _setmode(_fileno(stdout), _O_BINARY);
223 #endif
224          fout=stdout;
225       }
226       else 
227       {
228 #if defined WIN32 || defined _WIN32
229          fout = fopen(outFile, "wb");
230 #else
231          fout = fopen(outFile, "w");
232 #endif
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)
295 {
296    void *st;
297    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    fprintf (stderr, "Decoding %d Hz audio using %s mode", 
372             *rate, mode->modeName);
373
374    if (*channels==1)
375       fprintf (stderr, " (mono");
376    else
377       fprintf (stderr, " (stereo");
378       
379    if (header->vbr)
380       fprintf (stderr, ", VBR)\n");
381    else
382       fprintf(stderr, ")\n");
383    /*fprintf (stderr, "Decoding %d Hz audio at %d bps using %s mode\n", 
384     *rate, mode->bitrate, mode->modeName);*/
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    float 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    struct option long_options[] =
406    {
407       {"help", no_argument, NULL, 0},
408       {"version", no_argument, NULL, 0},
409       {"version-short", no_argument, NULL, 0},
410       {"enh", no_argument, NULL, 0},
411       {"no-enh", no_argument, NULL, 0},
412       {"pf", no_argument, NULL, 0},
413       {"no-pf", no_argument, NULL, 0},
414       {"force-nb", no_argument, NULL, 0},
415       {"force-wb", no_argument, NULL, 0},
416       {"force-uwb", no_argument, NULL, 0},
417       {"rate", required_argument, NULL, 0},
418       {"mono", no_argument, NULL, 0},
419       {"stereo", no_argument, NULL, 0},
420       {"packet-loss", required_argument, NULL, 0},
421       {0, 0, 0, 0}
422    };
423    ogg_sync_state oy;
424    ogg_page       og;
425    ogg_packet     op;
426    ogg_stream_state os;
427    int enh_enabled;
428    int nframes=2;
429    int print_bitrate=0;
430    int close_in=0;
431    int eos=0;
432    int forceMode=-1;
433    int audio_size=0;
434    float loss_percent=-1;
435    SpeexStereoState stereo = SPEEX_STEREO_STATE_INIT;
436    int channels=-1;
437    int rate=0;
438    int extra_headers;
439    int wav_format=0;
440
441    enh_enabled = 1;
442
443    /*Process options*/
444    while(1)
445    {
446       c = getopt_long (argc, argv, "hvV",
447                        long_options, &option_index);
448       if (c==-1)
449          break;
450       
451       switch(c)
452       {
453       case 0:
454          if (strcmp(long_options[option_index].name,"help")==0)
455          {
456             usage();
457             exit(0);
458          } else if (strcmp(long_options[option_index].name,"version")==0)
459          {
460             version();
461             exit(0);
462          } else if (strcmp(long_options[option_index].name,"version-short")==0)
463          {
464             version_short();
465             exit(0);
466          } else if (strcmp(long_options[option_index].name,"enh")==0)
467          {
468             enh_enabled=1;
469          } else if (strcmp(long_options[option_index].name,"no-enh")==0)
470          {
471             enh_enabled=0;
472          } else if (strcmp(long_options[option_index].name,"pf")==0)
473          {
474             fprintf (stderr, "--pf is deprecated, use --enh instead\n");
475             enh_enabled=1;
476          } else if (strcmp(long_options[option_index].name,"no-pf")==0)
477          {
478             fprintf (stderr, "--no-pf is deprecated, use --no-enh instead\n");
479             enh_enabled=0;
480          } else if (strcmp(long_options[option_index].name,"force-nb")==0)
481          {
482             forceMode=0;
483          } else if (strcmp(long_options[option_index].name,"force-wb")==0)
484          {
485             forceMode=1;
486          } else if (strcmp(long_options[option_index].name,"force-uwb")==0)
487          {
488             forceMode=2;
489          } else if (strcmp(long_options[option_index].name,"mono")==0)
490          {
491             channels=1;
492          } else if (strcmp(long_options[option_index].name,"stereo")==0)
493          {
494             channels=2;
495          } else if (strcmp(long_options[option_index].name,"rate")==0)
496          {
497             rate=atoi (optarg);
498          } else if (strcmp(long_options[option_index].name,"packet-loss")==0)
499          {
500             loss_percent = atof(optarg);
501          }
502          break;
503       case 'h':
504          usage();
505          exit(0);
506          break;
507       case 'v':
508          version();
509          exit(0);
510          break;
511       case 'V':
512          print_bitrate=1;
513          break;
514       case '?':
515          usage();
516          exit(1);
517          break;
518       }
519    }
520    if (argc-optind!=2 && argc-optind!=1)
521    {
522       usage();
523       exit(1);
524    }
525    inFile=argv[optind];
526
527    if (argc-optind==2)
528       outFile=argv[optind+1];
529    else
530       outFile = "";
531    wav_format = strlen(outFile)>=4 && (
532                                        strcmp(outFile+strlen(outFile)-4,".wav")==0
533                                        || strcmp(inFile+strlen(inFile)-4,".WAV")==0);
534    /*Open input file*/
535    if (strcmp(inFile, "-")==0)
536    {
537 #if defined WIN32 || defined _WIN32
538       _setmode(_fileno(stdin), _O_BINARY);
539 #endif
540       fin=stdin;
541    }
542    else 
543    {
544 #if defined WIN32 || defined _WIN32
545       fin = fopen(inFile, "rb");
546 #else
547       fin = fopen(inFile, "r");
548 #endif
549       if (!fin)
550       {
551          perror(inFile);
552          exit(1);
553       }
554       close_in=1;
555    }
556
557
558    /*Init Ogg data struct*/
559    ogg_sync_init(&oy);
560    
561    speex_bits_init(&bits);
562    /*Main decoding loop*/
563    while (1)
564    {
565       char *data;
566       int i, j, nb_read;
567       /*Get the ogg buffer for writing*/
568       data = ogg_sync_buffer(&oy, 200);
569       /*Read bitstream from input file*/
570       nb_read = fread(data, sizeof(char), 200, fin);      
571       ogg_sync_wrote(&oy, nb_read);
572
573       /*Loop for all complete pages we got (most likely only one)*/
574       while (ogg_sync_pageout(&oy, &og)==1)
575       {
576          if (stream_init == 0) {
577             ogg_stream_init(&os, ogg_page_serialno(&og));
578             stream_init = 1;
579          }
580          /*Add page to the bitstream*/
581          ogg_stream_pagein(&os, &og);
582          /*Extract all available packets*/
583          while (!eos && ogg_stream_packetout(&os, &op)==1)
584          {
585             /*If first packet, process as Speex header*/
586             if (packet_count==0)
587             {
588                st = process_header(&op, enh_enabled, &frame_size, &rate, &nframes, forceMode, &channels, &stereo, &extra_headers);
589                if (!nframes)
590                   nframes=1;
591                if (!st)
592                   exit(1);
593                fout = out_file_open(outFile, rate, &channels);
594
595             } else if (packet_count==1)
596             {
597                print_comments((char*)op.packet, op.bytes);
598             } else if (packet_count<=1+extra_headers)
599             {
600                /* Ignore extra headers */
601             } else {
602                
603                int lost=0;
604                if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent)
605                   lost=1;
606
607                /*End of stream condition*/
608                if (op.e_o_s)
609                   eos=1;
610
611                /*Copy Ogg packet to Speex bitstream*/
612                speex_bits_read_from(&bits, (char*)op.packet, op.bytes);
613                for (j=0;j!=nframes;j++)
614                {
615                   int ret;
616                   /*Decode frame*/
617                   if (!lost)
618                      ret = speex_decode(st, &bits, output);
619                   else
620                      ret = speex_decode(st, NULL, output);
621
622                   if (ret==-1)
623                      break;
624                   if (ret==-2)
625                   {
626                      fprintf (stderr, "Decoding error: corrupted stream?\n");
627                      break;
628                   }
629                   if (speex_bits_remaining(&bits)<0)
630                   {
631                      fprintf (stderr, "Decoding overflow: corrupted stream?\n");
632                      break;
633                   }
634                   if (channels==2)
635                      speex_decode_stereo(output, frame_size, &stereo);
636
637                   if (print_bitrate) {
638                      int tmp;
639                      char ch=13;
640                      speex_decoder_ctl(st, SPEEX_GET_BITRATE, &tmp);
641                      fputc (ch, stderr);
642                      fprintf (stderr, "Bitrate is use: %d bps     ", tmp);
643                   }
644                   /*PCM saturation (just in case)*/
645                   for (i=0;i<frame_size*channels;i++)
646                   {
647                      if (output[i]>32000.0)
648                         output[i]=32000.0;
649                      else if (output[i]<-32000.0)
650                         output[i]=-32000.0;
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]=(short)le_short((short)floor(.5+output[i]));
657                   } else {
658                      for (i=0;i<frame_size*channels;i++)
659                         out[i]=(short)floor(.5+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 }