added --quiet switch
[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, int quiet)
296 {
297    void *st;
298    const 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    if (!quiet)
373    {
374       fprintf (stderr, "Decoding %d Hz audio using %s mode", 
375                *rate, mode->modeName);
376
377       if (*channels==1)
378          fprintf (stderr, " (mono");
379       else
380          fprintf (stderr, " (stereo");
381       
382       if (header->vbr)
383          fprintf (stderr, ", VBR)\n");
384       else
385          fprintf(stderr, ")\n");
386       /*fprintf (stderr, "Decoding %d Hz audio at %d bps using %s mode\n", 
387        *rate, mode->bitrate, mode->modeName);*/
388    }
389
390    *extra_headers = header->extra_headers;
391
392    free(header);
393    return st;
394 }
395
396 int main(int argc, char **argv)
397 {
398    int c;
399    int option_index = 0;
400    char *inFile, *outFile;
401    FILE *fin, *fout=NULL;
402    short out[MAX_FRAME_SIZE];
403    short output[MAX_FRAME_SIZE];
404    int frame_size=0;
405    void *st=NULL;
406    SpeexBits bits;
407    int packet_count=0;
408    int stream_init = 0;
409    int quiet = 0;
410    struct option long_options[] =
411    {
412       {"help", no_argument, NULL, 0},
413       {"quiet", no_argument, NULL, 0},
414       {"version", no_argument, NULL, 0},
415       {"version-short", no_argument, NULL, 0},
416       {"enh", no_argument, NULL, 0},
417       {"no-enh", no_argument, NULL, 0},
418       {"pf", no_argument, NULL, 0},
419       {"no-pf", no_argument, NULL, 0},
420       {"force-nb", no_argument, NULL, 0},
421       {"force-wb", no_argument, NULL, 0},
422       {"force-uwb", no_argument, NULL, 0},
423       {"rate", required_argument, NULL, 0},
424       {"mono", no_argument, NULL, 0},
425       {"stereo", no_argument, NULL, 0},
426       {"packet-loss", required_argument, NULL, 0},
427       {0, 0, 0, 0}
428    };
429    ogg_sync_state oy;
430    ogg_page       og;
431    ogg_packet     op;
432    ogg_stream_state os;
433    int enh_enabled;
434    int nframes=2;
435    int print_bitrate=0;
436    int close_in=0;
437    int eos=0;
438    int forceMode=-1;
439    int audio_size=0;
440    float loss_percent=-1;
441    SpeexStereoState stereo = SPEEX_STEREO_STATE_INIT;
442    int channels=-1;
443    int rate=0;
444    int extra_headers;
445    int wav_format=0;
446
447    enh_enabled = 1;
448
449    /*Process options*/
450    while(1)
451    {
452       c = getopt_long (argc, argv, "hvV",
453                        long_options, &option_index);
454       if (c==-1)
455          break;
456       
457       switch(c)
458       {
459       case 0:
460          if (strcmp(long_options[option_index].name,"help")==0)
461          {
462             usage();
463             exit(0);
464          } else if (strcmp(long_options[option_index].name,"quiet")==0)
465          {
466             quiet = 1;
467          } else if (strcmp(long_options[option_index].name,"version")==0)
468          {
469             version();
470             exit(0);
471          } else if (strcmp(long_options[option_index].name,"version-short")==0)
472          {
473             version_short();
474             exit(0);
475          } else if (strcmp(long_options[option_index].name,"enh")==0)
476          {
477             enh_enabled=1;
478          } else if (strcmp(long_options[option_index].name,"no-enh")==0)
479          {
480             enh_enabled=0;
481          } else if (strcmp(long_options[option_index].name,"pf")==0)
482          {
483             fprintf (stderr, "--pf is deprecated, use --enh instead\n");
484             enh_enabled=1;
485          } else if (strcmp(long_options[option_index].name,"no-pf")==0)
486          {
487             fprintf (stderr, "--no-pf is deprecated, use --no-enh instead\n");
488             enh_enabled=0;
489          } else if (strcmp(long_options[option_index].name,"force-nb")==0)
490          {
491             forceMode=0;
492          } else if (strcmp(long_options[option_index].name,"force-wb")==0)
493          {
494             forceMode=1;
495          } else if (strcmp(long_options[option_index].name,"force-uwb")==0)
496          {
497             forceMode=2;
498          } else if (strcmp(long_options[option_index].name,"mono")==0)
499          {
500             channels=1;
501          } else if (strcmp(long_options[option_index].name,"stereo")==0)
502          {
503             channels=2;
504          } else if (strcmp(long_options[option_index].name,"rate")==0)
505          {
506             rate=atoi (optarg);
507          } else if (strcmp(long_options[option_index].name,"packet-loss")==0)
508          {
509             loss_percent = atof(optarg);
510          }
511          break;
512       case 'h':
513          usage();
514          exit(0);
515          break;
516       case 'v':
517          version();
518          exit(0);
519          break;
520       case 'V':
521          print_bitrate=1;
522          break;
523       case '?':
524          usage();
525          exit(1);
526          break;
527       }
528    }
529    if (argc-optind!=2 && argc-optind!=1)
530    {
531       usage();
532       exit(1);
533    }
534    inFile=argv[optind];
535
536    if (argc-optind==2)
537       outFile=argv[optind+1];
538    else
539       outFile = "";
540    wav_format = strlen(outFile)>=4 && (
541                                        strcmp(outFile+strlen(outFile)-4,".wav")==0
542                                        || strcmp(inFile+strlen(inFile)-4,".WAV")==0);
543    /*Open input file*/
544    if (strcmp(inFile, "-")==0)
545    {
546 #if defined WIN32 || defined _WIN32
547       _setmode(_fileno(stdin), _O_BINARY);
548 #endif
549       fin=stdin;
550    }
551    else 
552    {
553 #if defined WIN32 || defined _WIN32
554       fin = fopen(inFile, "rb");
555 #else
556       fin = fopen(inFile, "r");
557 #endif
558       if (!fin)
559       {
560          perror(inFile);
561          exit(1);
562       }
563       close_in=1;
564    }
565
566
567    /*Init Ogg data struct*/
568    ogg_sync_init(&oy);
569    
570    speex_bits_init(&bits);
571    /*Main decoding loop*/
572    while (1)
573    {
574       char *data;
575       int i, j, nb_read;
576       /*Get the ogg buffer for writing*/
577       data = ogg_sync_buffer(&oy, 200);
578       /*Read bitstream from input file*/
579       nb_read = fread(data, sizeof(char), 200, fin);      
580       ogg_sync_wrote(&oy, nb_read);
581
582       /*Loop for all complete pages we got (most likely only one)*/
583       while (ogg_sync_pageout(&oy, &og)==1)
584       {
585          if (stream_init == 0) {
586             ogg_stream_init(&os, ogg_page_serialno(&og));
587             stream_init = 1;
588          }
589          /*Add page to the bitstream*/
590          ogg_stream_pagein(&os, &og);
591          /*Extract all available packets*/
592          while (!eos && ogg_stream_packetout(&os, &op)==1)
593          {
594             /*If first packet, process as Speex header*/
595             if (packet_count==0)
596             {
597                st = process_header(&op, enh_enabled, &frame_size, &rate, &nframes, forceMode, &channels, &stereo, &extra_headers, quiet);
598                if (!nframes)
599                   nframes=1;
600                if (!st)
601                   exit(1);
602                fout = out_file_open(outFile, rate, &channels);
603
604             } else if (packet_count==1)
605             {
606                if (!quiet)
607                   print_comments((char*)op.packet, op.bytes);
608             } else if (packet_count<=1+extra_headers)
609             {
610                /* Ignore extra headers */
611             } else {
612                
613                int lost=0;
614                if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent)
615                   lost=1;
616
617                /*End of stream condition*/
618                if (op.e_o_s)
619                   eos=1;
620
621                /*Copy Ogg packet to Speex bitstream*/
622                speex_bits_read_from(&bits, (char*)op.packet, op.bytes);
623                for (j=0;j!=nframes;j++)
624                {
625                   int ret;
626                   /*Decode frame*/
627                   if (!lost)
628                      ret = speex_decode(st, &bits, output);
629                   else
630                      ret = speex_decode(st, NULL, output);
631
632                   /*for (i=0;i<frame_size*channels;i++)
633                     printf ("%d\n", (int)output[i]);*/
634
635                   if (ret==-1)
636                      break;
637                   if (ret==-2)
638                   {
639                      fprintf (stderr, "Decoding error: corrupted stream?\n");
640                      break;
641                   }
642                   if (speex_bits_remaining(&bits)<0)
643                   {
644                      fprintf (stderr, "Decoding overflow: corrupted stream?\n");
645                      break;
646                   }
647                   if (channels==2)
648                      speex_decode_stereo(output, frame_size, &stereo);
649
650                   if (print_bitrate) {
651                      int tmp;
652                      char ch=13;
653                      speex_decoder_ctl(st, SPEEX_GET_BITRATE, &tmp);
654                      fputc (ch, stderr);
655                      fprintf (stderr, "Bitrate is use: %d bps     ", tmp);
656                   }
657                   /*Convert to short and save to output file*/
658                   if (strlen(outFile)!=0)
659                   {
660                      for (i=0;i<frame_size*channels;i++)
661                         out[i]=le_short(output[i]);
662                   } else {
663                      for (i=0;i<frame_size*channels;i++)
664                         out[i]=output[i];
665                   }
666 #if defined WIN32 || defined _WIN32
667                   if (strlen(outFile)==0)
668                       WIN_Play_Samples (out, sizeof(short) * frame_size*channels);
669                   else
670 #endif
671                   fwrite(out, sizeof(short), frame_size*channels, fout);
672                   
673                   audio_size+=sizeof(short)*frame_size*channels;
674                }
675             }
676             packet_count++;
677          }
678       }
679       if (feof(fin))
680          break;
681
682    }
683
684    if (wav_format)
685    {
686       if (fseek(fout,4,SEEK_SET)==0)
687       {
688          int tmp;
689          tmp = le_int(audio_size+36);
690          fwrite(&tmp,4,1,fout);
691          if (fseek(fout,32,SEEK_CUR)==0)
692          {
693             tmp = le_int(audio_size);
694             fwrite(&tmp,4,1,fout);
695          } else
696          {
697             fprintf (stderr, "First seek worked, second didn't\n");
698          }
699       } else {
700          fprintf (stderr, "Cannot seek on wave file, size will be incorrect\n");
701       }
702    }
703
704    if (st)
705       speex_decoder_destroy(st);
706    else 
707    {
708       fprintf (stderr, "This doesn't look like a Speex file\n");
709    }
710    speex_bits_destroy(&bits);
711    if (stream_init)
712       ogg_stream_clear(&os);
713    ogg_sync_clear(&oy);
714
715 #if defined WIN32 || defined _WIN32
716    if (strlen(outFile)==0)
717       WIN_Audio_close ();
718 #endif
719
720    if (close_in)
721       fclose(fin);
722    if (fout != NULL)
723       fclose(fout);   
724
725    return 0;
726 }