7dc9a1426570c65f1a26f58e26f59284b2dd4a33
[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 #if defined WIN32 || defined _WIN32
230          fout = fopen(outFile, "wb");
231 #else
232          fout = fopen(outFile, "w");
233 #endif
234          if (!fout)
235          {
236             perror(outFile);
237             exit(1);
238          }
239          if (strcmp(outFile+strlen(outFile)-4,".wav")==0 || strcmp(outFile+strlen(outFile)-4,".WAV")==0)
240             write_wav_header(fout, rate, *channels, 0, 0);
241       }
242    }
243    return fout;
244 }
245
246 void usage()
247 {
248    printf ("Usage: speexdec [options] input_file.spx [output_file]\n");
249    printf ("\n");
250    printf ("Decodes a Speex file and produce a WAV file or raw file\n");
251    printf ("\n");
252    printf ("input_file can be:\n");
253    printf ("  filename.spx         regular Speex file\n");
254    printf ("  -                    stdin\n");
255    printf ("\n");  
256    printf ("output_file can be:\n");
257    printf ("  filename.wav         Wav file\n");
258    printf ("  filename.*           Raw PCM file (any extension other that .wav)\n");
259    printf ("  -                    stdout\n");
260    printf ("  (nothing)            Will be played to soundcard\n");
261    printf ("\n");  
262    printf ("Options:\n");
263    printf (" --enh                 Enable perceptual enhancement (default)\n");
264    printf (" --no-enh              Disable perceptual enhancement\n");
265    printf (" --force-nb            Force decoding in narrowband\n");
266    printf (" --force-wb            Force decoding in wideband\n");
267    printf (" --force-uwb           Force decoding in ultra-wideband\n");
268    printf (" --mono                Force decoding in mono\n");
269    printf (" --stereo              Force decoding in stereo\n");
270    printf (" --rate n              Force decoding at sampling rate n Hz\n");
271    printf (" --packet-loss n       Simulate n %% random packet loss\n");
272    printf (" -V                    Verbose mode (show bit-rate)\n"); 
273    printf (" -h, --help            This help\n");
274    printf (" -v, --version         Version information\n");
275    printf (" --pf                  Deprecated, use --enh instead\n");
276    printf (" --no-pf               Deprecated, use --no-enh instead\n");
277    printf ("\n");
278    printf ("More information is available from the Speex site: http://www.speex.org\n");
279    printf ("\n");
280    printf ("Please report bugs to the mailing list `speex-dev@xiph.org'.\n");
281 }
282
283 void version()
284 {
285    printf ("speexdec (Speex decoder) version " VERSION " (compiled " __DATE__ ")\n");
286    printf ("Copyright (C) 2002-2003 Jean-Marc Valin\n");
287 }
288
289 void version_short()
290 {
291    printf ("speexdec version " VERSION "\n");
292    printf ("Copyright (C) 2002-2003 Jean-Marc Valin\n");
293 }
294
295 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)
296 {
297    void *st;
298    SpeexMode *mode;
299    SpeexHeader *header;
300    int modeID;
301    SpeexCallback callback;
302       
303    header = speex_packet_to_header((char*)op->packet, op->bytes);
304    if (!header)
305    {
306       fprintf (stderr, "Cannot read header\n");
307       return NULL;
308    }
309    if (header->mode >= SPEEX_NB_MODES)
310    {
311       fprintf (stderr, "Mode number %d does not (yet/any longer) exist in this version\n", 
312                header->mode);
313       return NULL;
314    }
315       
316    modeID = header->mode;
317    if (forceMode!=-1)
318       modeID = forceMode;
319    mode = speex_mode_list[modeID];
320    
321    if (header->speex_version_id > 1)
322    {
323       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);
324       return NULL;
325    }
326
327    if (mode->bitstream_version < header->mode_bitstream_version)
328    {
329       fprintf (stderr, "The file was encoded with a newer version of Speex. You need to upgrade in order to play it.\n");
330       return NULL;
331    }
332    if (mode->bitstream_version > header->mode_bitstream_version) 
333    {
334       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");
335       return NULL;
336    }
337    
338    st = speex_decoder_init(mode);
339    if (!st)
340    {
341       fprintf (stderr, "Decoder initialization failed.\n");
342       return NULL;
343    }
344    speex_decoder_ctl(st, SPEEX_SET_ENH, &enh_enabled);
345    speex_decoder_ctl(st, SPEEX_GET_FRAME_SIZE, frame_size);
346
347    if (!(*channels==1))
348    {
349       callback.callback_id = SPEEX_INBAND_STEREO;
350       callback.func = speex_std_stereo_request_handler;
351       callback.data = stereo;
352       speex_decoder_ctl(st, SPEEX_SET_HANDLER, &callback);
353    }
354    if (!*rate)
355       *rate = header->rate;
356    /* Adjust rate if --force-* options are used */
357    if (forceMode!=-1)
358    {
359       if (header->mode < forceMode)
360          *rate <<= (forceMode - header->mode);
361       if (header->mode > forceMode)
362          *rate >>= (header->mode - forceMode);
363    }
364
365    speex_decoder_ctl(st, SPEEX_SET_SAMPLING_RATE, rate);
366
367    *nframes = header->frames_per_packet;
368
369    if (*channels==-1)
370       *channels = header->nb_channels;
371    
372    fprintf (stderr, "Decoding %d Hz audio using %s mode", 
373             *rate, mode->modeName);
374
375    if (*channels==1)
376       fprintf (stderr, " (mono");
377    else
378       fprintf (stderr, " (stereo");
379       
380    if (header->vbr)
381       fprintf (stderr, ", VBR)\n");
382    else
383       fprintf(stderr, ")\n");
384    /*fprintf (stderr, "Decoding %d Hz audio at %d bps using %s mode\n", 
385     *rate, mode->bitrate, mode->modeName);*/
386
387    *extra_headers = header->extra_headers;
388
389    free(header);
390    return st;
391 }
392
393 int main(int argc, char **argv)
394 {
395    int c;
396    int option_index = 0;
397    char *inFile, *outFile;
398    FILE *fin, *fout=NULL;
399    short out[MAX_FRAME_SIZE];
400    short output[MAX_FRAME_SIZE];
401    int frame_size=0;
402    void *st=NULL;
403    SpeexBits bits;
404    int packet_count=0;
405    int stream_init = 0;
406    struct option long_options[] =
407    {
408       {"help", no_argument, NULL, 0},
409       {"version", no_argument, NULL, 0},
410       {"version-short", no_argument, NULL, 0},
411       {"enh", no_argument, NULL, 0},
412       {"no-enh", no_argument, NULL, 0},
413       {"pf", no_argument, NULL, 0},
414       {"no-pf", no_argument, NULL, 0},
415       {"force-nb", no_argument, NULL, 0},
416       {"force-wb", no_argument, NULL, 0},
417       {"force-uwb", no_argument, NULL, 0},
418       {"rate", required_argument, NULL, 0},
419       {"mono", no_argument, NULL, 0},
420       {"stereo", no_argument, NULL, 0},
421       {"packet-loss", required_argument, NULL, 0},
422       {0, 0, 0, 0}
423    };
424    ogg_sync_state oy;
425    ogg_page       og;
426    ogg_packet     op;
427    ogg_stream_state os;
428    int enh_enabled;
429    int nframes=2;
430    int print_bitrate=0;
431    int close_in=0;
432    int eos=0;
433    int forceMode=-1;
434    int audio_size=0;
435    float loss_percent=-1;
436    SpeexStereoState stereo = SPEEX_STEREO_STATE_INIT;
437    int channels=-1;
438    int rate=0;
439    int extra_headers;
440    int wav_format=0;
441
442    enh_enabled = 1;
443
444    /*Process options*/
445    while(1)
446    {
447       c = getopt_long (argc, argv, "hvV",
448                        long_options, &option_index);
449       if (c==-1)
450          break;
451       
452       switch(c)
453       {
454       case 0:
455          if (strcmp(long_options[option_index].name,"help")==0)
456          {
457             usage();
458             exit(0);
459          } else if (strcmp(long_options[option_index].name,"version")==0)
460          {
461             version();
462             exit(0);
463          } else if (strcmp(long_options[option_index].name,"version-short")==0)
464          {
465             version_short();
466             exit(0);
467          } else if (strcmp(long_options[option_index].name,"enh")==0)
468          {
469             enh_enabled=1;
470          } else if (strcmp(long_options[option_index].name,"no-enh")==0)
471          {
472             enh_enabled=0;
473          } else if (strcmp(long_options[option_index].name,"pf")==0)
474          {
475             fprintf (stderr, "--pf is deprecated, use --enh instead\n");
476             enh_enabled=1;
477          } else if (strcmp(long_options[option_index].name,"no-pf")==0)
478          {
479             fprintf (stderr, "--no-pf is deprecated, use --no-enh instead\n");
480             enh_enabled=0;
481          } else if (strcmp(long_options[option_index].name,"force-nb")==0)
482          {
483             forceMode=0;
484          } else if (strcmp(long_options[option_index].name,"force-wb")==0)
485          {
486             forceMode=1;
487          } else if (strcmp(long_options[option_index].name,"force-uwb")==0)
488          {
489             forceMode=2;
490          } else if (strcmp(long_options[option_index].name,"mono")==0)
491          {
492             channels=1;
493          } else if (strcmp(long_options[option_index].name,"stereo")==0)
494          {
495             channels=2;
496          } else if (strcmp(long_options[option_index].name,"rate")==0)
497          {
498             rate=atoi (optarg);
499          } else if (strcmp(long_options[option_index].name,"packet-loss")==0)
500          {
501             loss_percent = atof(optarg);
502          }
503          break;
504       case 'h':
505          usage();
506          exit(0);
507          break;
508       case 'v':
509          version();
510          exit(0);
511          break;
512       case 'V':
513          print_bitrate=1;
514          break;
515       case '?':
516          usage();
517          exit(1);
518          break;
519       }
520    }
521    if (argc-optind!=2 && argc-optind!=1)
522    {
523       usage();
524       exit(1);
525    }
526    inFile=argv[optind];
527
528    if (argc-optind==2)
529       outFile=argv[optind+1];
530    else
531       outFile = "";
532    wav_format = strlen(outFile)>=4 && (
533                                        strcmp(outFile+strlen(outFile)-4,".wav")==0
534                                        || strcmp(inFile+strlen(inFile)-4,".WAV")==0);
535    /*Open input file*/
536    if (strcmp(inFile, "-")==0)
537    {
538 #if defined WIN32 || defined _WIN32
539       _setmode(_fileno(stdin), _O_BINARY);
540 #endif
541       fin=stdin;
542    }
543    else 
544    {
545 #if defined WIN32 || defined _WIN32
546       fin = fopen(inFile, "rb");
547 #else
548       fin = fopen(inFile, "r");
549 #endif
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);
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                print_comments((char*)op.packet, op.bytes);
599             } else if (packet_count<=1+extra_headers)
600             {
601                /* Ignore extra headers */
602             } else {
603                
604                int lost=0;
605                if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent)
606                   lost=1;
607
608                /*End of stream condition*/
609                if (op.e_o_s)
610                   eos=1;
611
612                /*Copy Ogg packet to Speex bitstream*/
613                speex_bits_read_from(&bits, (char*)op.packet, op.bytes);
614                for (j=0;j!=nframes;j++)
615                {
616                   int ret;
617                   /*Decode frame*/
618                   if (!lost)
619                      ret = speex_decode(st, &bits, output);
620                   else
621                      ret = speex_decode(st, NULL, output);
622
623                   /*for (i=0;i<frame_size*channels;i++)
624                     printf ("%d\n", (int)output[i]);*/
625
626                   if (ret==-1)
627                      break;
628                   if (ret==-2)
629                   {
630                      fprintf (stderr, "Decoding error: corrupted stream?\n");
631                      break;
632                   }
633                   if (speex_bits_remaining(&bits)<0)
634                   {
635                      fprintf (stderr, "Decoding overflow: corrupted stream?\n");
636                      break;
637                   }
638                   if (channels==2)
639                      speex_decode_stereo(output, frame_size, &stereo);
640
641                   if (print_bitrate) {
642                      int tmp;
643                      char ch=13;
644                      speex_decoder_ctl(st, SPEEX_GET_BITRATE, &tmp);
645                      fputc (ch, stderr);
646                      fprintf (stderr, "Bitrate is use: %d bps     ", tmp);
647                   }
648                   /*Convert to short and save to output file*/
649                   if (strlen(outFile)!=0)
650                   {
651                      for (i=0;i<frame_size*channels;i++)
652                         out[i]=(short)le_short((short)floor(.5+output[i]));
653                   } else {
654                      for (i=0;i<frame_size*channels;i++)
655                         out[i]=(short)floor(.5+output[i]);
656                   }
657 #if defined WIN32 || defined _WIN32
658                   if (strlen(outFile)==0)
659                       WIN_Play_Samples (out, sizeof(short) * frame_size*channels);
660                   else
661 #endif
662                   fwrite(out, sizeof(short), frame_size*channels, fout);
663                   
664                   audio_size+=sizeof(short)*frame_size*channels;
665                }
666             }
667             packet_count++;
668          }
669       }
670       if (feof(fin))
671          break;
672
673    }
674
675    if (wav_format)
676    {
677       if (fseek(fout,4,SEEK_SET)==0)
678       {
679          int tmp;
680          tmp = le_int(audio_size+36);
681          fwrite(&tmp,4,1,fout);
682          if (fseek(fout,32,SEEK_CUR)==0)
683          {
684             tmp = le_int(audio_size);
685             fwrite(&tmp,4,1,fout);
686          } else
687          {
688             fprintf (stderr, "First seek worked, second didn't\n");
689          }
690       } else {
691          fprintf (stderr, "Cannot seek on wave file, size will be incorrect\n");
692       }
693    }
694
695    if (st)
696       speex_decoder_destroy(st);
697    else 
698    {
699       fprintf (stderr, "This doesn't look like a Speex file\n");
700    }
701    speex_bits_destroy(&bits);
702    if (stream_init)
703       ogg_stream_clear(&os);
704    ogg_sync_clear(&oy);
705
706 #if defined WIN32 || defined _WIN32
707    if (strlen(outFile)==0)
708       WIN_Audio_close ();
709 #endif
710
711    if (close_in)
712       fclose(fin);
713    if (fout != NULL)
714       fclose(fout);   
715
716    return 0;
717 }