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