09e7d5ec8b15154f0f2ff70e8ea2f881c6c0b23b
[speexdsp.git] / src / speexdec.c
1 /* Copyright (C) 2002 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
52 #ifdef HAVE_SYS_SOUNDCARD_H
53 #include <sys/soundcard.h>
54 #include <sys/types.h>
55 #include <sys/stat.h>
56 #include <fcntl.h>
57 #include <sys/ioctl.h>
58 #endif
59
60 #include <string.h>
61 #include "wav_io.h"
62 #include "speex_header.h"
63 #include "misc.h"
64
65 #define MAX_FRAME_SIZE 2000
66
67 #define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
68                            ((buf[base+2]<<16)&0xff0000)| \
69                            ((buf[base+1]<<8)&0xff00)| \
70                             (buf[base]&0xff))
71
72 static void print_comments(char *comments, int length)
73 {
74    char *c=comments;
75    int len, i, nb_fields;
76
77    len=readint(c, 0);
78    c+=4;
79    fwrite(c, 1, len, stderr);
80    c+=len;
81    fprintf (stderr, "\n");
82    nb_fields=readint(c, 0);
83    c+=4;
84    for (i=0;i<nb_fields;i++)
85    {
86       len=readint(c, 0);
87       c+=4;
88       fwrite(c, 1, len, stderr);
89       c+=len;
90       fprintf (stderr, "\n");
91    }
92 }
93
94 FILE *out_file_open(char *outFile, int rate)
95 {
96    FILE *fout=NULL;
97    /*Open output file*/
98    if (strlen(outFile)==0)
99    {
100 #if defined HAVE_SYS_SOUNDCARD_H
101       int audio_fd, format, stereo;
102       audio_fd=open("/dev/dsp", O_WRONLY);
103       
104       format=AFMT_S16_LE;
105       if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format)==-1)
106       {
107          perror("SNDCTL_DSP_SETFMT");
108          close(audio_fd);
109          exit(1);
110       }
111       
112       stereo=0;
113       if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo)==-1)
114       {
115          perror("SNDCTL_DSP_STEREO");
116          close(audio_fd);
117          exit(1);
118       }
119       if (stereo!=0)
120       {
121          fprintf (stderr, "Cannot set mono mode\n");
122          exit(1);
123       }
124
125       if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate)==-1)
126       {
127          perror("SNDCTL_DSP_SPEED");
128          close(audio_fd);
129          exit(1);
130       }
131       fout = fdopen(audio_fd, "w");
132 #elif defined WIN32 || defined _WIN32
133       if (Set_WIN_Params (INVALID_FILEDESC, rate, SAMPLE_SIZE, 1))
134       {
135          fprintf (stderr, "Can't access %s\n", "WAVE OUT");
136          exit(1);
137       }
138 #else
139       fprintf (stderr, "No soundcard support\n");
140       exit(1);
141 #endif
142    } else {
143       if (strcmp(outFile,"-")==0)
144       {
145 #if defined WIN32 || defined _WIN32
146          _setmode(_fileno(stdout), _O_BINARY);
147 #endif
148          fout=stdout;
149       }
150       else 
151       {
152 #if defined WIN32 || defined _WIN32
153          fout = fopen(outFile, "wb");
154 #else
155          fout = fopen(outFile, "w");
156 #endif
157          if (!fout)
158          {
159             perror(outFile);
160             exit(1);
161          }
162          if (strcmp(outFile+strlen(outFile)-4,".wav")==0 || strcmp(outFile+strlen(outFile)-4,".WAV")==0)
163             write_wav_header(fout, rate, 1, 0, 0);
164       }
165    }
166    return fout;
167 }
168
169 void usage()
170 {
171    /*printf ("Speex decoder version " VERSION " (compiled " __DATE__ ")\n");
172    printf ("\n");*/
173    printf ("Usage: speexdec [options] input_file.spx\n");
174    printf ("       speexdec [options] input_file.spx output_file.wav\n");
175    printf ("\n");
176    printf ("Decodes a Speex file and produce a WAV file or raw file\n");
177    printf ("\n");
178    printf ("input_file can be:\n");
179    printf ("  filename.spx          regular Speex file\n");
180    printf ("  -                     stdin\n");
181    printf ("\n");  
182    printf ("output_file can be:\n");
183    printf ("  filename.wav          wav file\n");
184    printf ("  filename.*            raw PCM file (any extension other that .wav)\n");
185    printf ("  -                     stdout\n");
186    printf ("  (nothing)             will be played to soundcard\n");
187    printf ("\n");  
188    printf ("Options:\n");
189    printf (" --enh                 Enable perceptual enhancement\n");
190    printf (" --no-enh              Disable perceptual enhancement (default FOR NOW)\n");
191    printf (" --force-nb            Force decoding in narrowband, even for wideband\n");
192    printf (" --force-wb            Force decoding in wideband, even for narrowband\n");
193    printf (" --packet-loss n       Simulate n %% random packet loss\n");
194    printf (" -V                    Verbose mode (show bit-rate)\n"); 
195    printf (" -h, --help            This help\n");
196    printf (" -v, --version         Version information\n");
197    printf (" --pf                  Deprecated, use --pf instead\n");
198    printf (" --no-pf               Deprecated, use --no-pf instead\n");
199    printf ("\n");
200    printf ("More information is available from the Speex site: http://www.speex.org\n");
201    printf ("\n");
202    printf ("Please report bugs to the mailing list `speex-dev@xiph.org'.\n");
203 }
204
205 void version()
206 {
207    printf ("Speex decoder version " VERSION " (compiled " __DATE__ ")\n");
208 }
209
210 static void *process_header(ogg_packet *op, int enh_enabled, int *frame_size, int *rate, int *nframes, int forceMode)
211 {
212    void *st;
213    SpeexMode *mode;
214    SpeexHeader *header;
215    int modeID;
216    
217    header = speex_packet_to_header((char*)op->packet, op->bytes);
218    if (!header)
219    {
220       fprintf (stderr, "Cannot read header\n");
221       return NULL;
222    }
223    if (header->mode >= SPEEX_NB_MODES)
224    {
225       fprintf (stderr, "Mode number %d does not (any longer) exist in this version\n", 
226                header->mode);
227       return NULL;
228    }
229       
230    modeID = header->mode;
231    if (forceMode!=-1)
232       modeID = forceMode;
233    mode = speex_mode_list[modeID];
234    
235    if (mode->bitstream_version < header->mode_bitstream_version)
236    {
237       fprintf (stderr, "The file was encoded with a newer version of Speex. You need to upgrade in order to play it.\n");
238       return NULL;
239    }
240    if (mode->bitstream_version > header->mode_bitstream_version) 
241    {
242       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");
243       return NULL;
244    }
245    
246    st = speex_decoder_init(mode);
247    speex_decoder_ctl(st, SPEEX_SET_ENH, &enh_enabled);
248    speex_decoder_ctl(st, SPEEX_GET_FRAME_SIZE, frame_size);
249    
250    /* FIXME: need to adjust in case the forceMode option is set */
251    *rate = header->rate;
252    if (header->mode==1 && forceMode==0)
253       *rate/=2;
254    if (header->mode==0 && forceMode==1)
255       *rate*=2;
256    *nframes = header->frames_per_packet;
257    
258    fprintf (stderr, "Decoding %d Hz audio using %s mode", 
259             *rate, mode->modeName);
260
261    if (header->vbr)
262       fprintf (stderr, " (VBR)\n");
263    else
264       fprintf(stderr, "\n");
265    /*fprintf (stderr, "Decoding %d Hz audio at %d bps using %s mode\n", 
266     *rate, mode->bitrate, mode->modeName);*/
267
268    free(header);
269    return st;
270 }
271
272 int main(int argc, char **argv)
273 {
274    int c;
275    int option_index = 0;
276    char *inFile, *outFile;
277    FILE *fin, *fout=NULL;
278    short out[MAX_FRAME_SIZE];
279    float output[MAX_FRAME_SIZE];
280    int frame_size=0;
281    void *st=NULL;
282    SpeexBits bits;
283    int packet_count=0;
284    int stream_init = 0;
285    struct option long_options[] =
286    {
287       {"help", no_argument, NULL, 0},
288       {"version", no_argument, NULL, 0},
289       {"enh", no_argument, NULL, 0},
290       {"no-enh", no_argument, NULL, 0},
291       {"pf", no_argument, NULL, 0},
292       {"no-pf", no_argument, NULL, 0},
293       {"force-nb", no_argument, NULL, 0},
294       {"force-wb", no_argument, NULL, 0},
295       {"packet-loss", required_argument, NULL, 0},
296       {0, 0, 0, 0}
297    };
298    ogg_sync_state oy;
299    ogg_page       og;
300    ogg_packet     op;
301    ogg_stream_state os;
302    int enh_enabled;
303    int nframes=2;
304    int print_bitrate=0;
305    int close_in=0;
306    int eos=0;
307    int forceMode=-1;
308    int audio_size=0;
309    float loss_percent=-1;
310
311    enh_enabled = 0;
312
313    /*Process options*/
314    while(1)
315    {
316       c = getopt_long (argc, argv, "hvV",
317                        long_options, &option_index);
318       if (c==-1)
319          break;
320       
321       switch(c)
322       {
323       case 0:
324          if (strcmp(long_options[option_index].name,"help")==0)
325          {
326             usage();
327             exit(0);
328          } else if (strcmp(long_options[option_index].name,"version")==0)
329          {
330             version();
331             exit(0);
332          } else if (strcmp(long_options[option_index].name,"enh")==0)
333          {
334             enh_enabled=1;
335          } else if (strcmp(long_options[option_index].name,"no-enh")==0)
336          {
337             enh_enabled=0;
338          } else if (strcmp(long_options[option_index].name,"pf")==0)
339          {
340             fprintf (stderr, "--pf is deprecated, use --enh instead\n");
341             enh_enabled=1;
342          } else if (strcmp(long_options[option_index].name,"no-pf")==0)
343          {
344             fprintf (stderr, "--no-pf is deprecated, use --no-enh instead\n");
345             enh_enabled=0;
346          } else if (strcmp(long_options[option_index].name,"force-nb")==0)
347          {
348             forceMode=0;
349          } else if (strcmp(long_options[option_index].name,"force-wb")==0)
350          {
351             forceMode=1;
352          } else if (strcmp(long_options[option_index].name,"packet-loss")==0)
353          {
354             loss_percent = atof(optarg);
355          }
356          break;
357       case 'h':
358          usage();
359          exit(0);
360          break;
361       case 'v':
362          version();
363          exit(0);
364          break;
365       case 'V':
366          print_bitrate=1;
367          break;
368       case '?':
369          usage();
370          exit(1);
371          break;
372       }
373    }
374    if (argc-optind!=2 && argc-optind!=1)
375    {
376       usage();
377       exit(1);
378    }
379    inFile=argv[optind];
380
381    if (argc-optind==2)
382       outFile=argv[optind+1];
383    else
384       outFile = "";
385    /*Open input file*/
386    if (strcmp(inFile, "-")==0)
387    {
388 #if defined WIN32 || defined _WIN32
389       _setmode(_fileno(stdin), _O_BINARY);
390 #endif
391       fin=stdin;
392    }
393    else 
394    {
395 #if defined WIN32 || defined _WIN32
396       fin = fopen(inFile, "rb");
397 #else
398       fin = fopen(inFile, "r");
399 #endif
400       if (!fin)
401       {
402          perror(inFile);
403          exit(1);
404       }
405       close_in=1;
406    }
407
408
409    /*Init Ogg data struct*/
410    ogg_sync_init(&oy);
411    
412    speex_bits_init(&bits);
413    /*Main decoding loop*/
414    while (1)
415    {
416       char *data;
417       int i, j, nb_read;
418       /*Get the ogg buffer for writing*/
419       data = ogg_sync_buffer(&oy, 200);
420       /*Read bitstream from input file*/
421       nb_read = fread(data, sizeof(char), 200, fin);      
422       ogg_sync_wrote(&oy, nb_read);
423
424       /*Loop for all complete pages we got (most likely only one)*/
425       while (ogg_sync_pageout(&oy, &og)==1)
426       {
427          if (stream_init == 0) {
428             ogg_stream_init(&os, ogg_page_serialno(&og));
429             stream_init = 1;
430          }
431          /*Add page to the bitstream*/
432          ogg_stream_pagein(&os, &og);
433          /*Extract all available packets*/
434          while (!eos && ogg_stream_packetout(&os, &op)==1)
435          {
436             /*If first packet, process as Speex header*/
437             if (packet_count==0)
438             {
439                int rate;
440                st = process_header(&op, enh_enabled, &frame_size, &rate, &nframes, forceMode);
441                if (!nframes)
442                   nframes=1;
443                if (!st)
444                   exit(1);
445                fout = out_file_open(outFile, rate);
446
447             } else if (packet_count==1){
448                print_comments((char*)op.packet, op.bytes);
449                /*
450                fprintf (stderr, "File comments: ");
451                fwrite(op.packet, 1, op.bytes, stderr);
452                fprintf (stderr, "\n");
453                */
454             } else {
455                
456                int lost=0;
457                if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent)
458                   lost=1;
459
460                /*End of stream condition*/
461                if (op.e_o_s)
462                   eos=1;
463
464                /*Copy Ogg packet to Speex bitstream*/
465                speex_bits_read_from(&bits, (char*)op.packet, op.bytes);
466                for (j=0;j<nframes;j++)
467                {
468                   /*Decode frame*/
469                   if (!lost)
470                      speex_decode(st, &bits, output);
471                   else
472                      speex_decode(st, NULL, output);
473
474                   if (print_bitrate) {
475                      int tmp;
476                      char ch=13;
477                      speex_decoder_ctl(st, SPEEX_GET_BITRATE, &tmp);
478                      fputc (ch, stderr);
479                      fprintf (stderr, "Bitrate is use: %d bps     ", tmp);
480                   }
481                   /*PCM saturation (just in case)*/
482                   for (i=0;i<frame_size;i++)
483                   {
484                      if (output[i]>32000.0)
485                         output[i]=32000.0;
486                      else if (output[i]<-32000.0)
487                         output[i]=-32000.0;
488                   }
489                   /*Convert to short and save to output file*/
490                   for (i=0;i<frame_size;i++)
491                      out[i]=(short)le_short((short)output[i]);
492 #if defined WIN32 || defined _WIN32
493                   if (strlen(outFile)==0)
494                       WIN_Play_Samples (out, sizeof(short) * frame_size);
495                   else
496 #endif
497                   fwrite(out, sizeof(short), frame_size, fout);
498                   audio_size+=sizeof(short)*frame_size;
499                }
500             }
501             packet_count++;
502          }
503       }
504       if (feof(fin))
505          break;
506
507    }
508
509    if (strcmp(outFile+strlen(outFile)-4,".wav")==0 || strcmp(inFile+strlen(inFile)-4,".WAV")==0)
510    {
511       if (fseek(fout,4,SEEK_SET)==0)
512       {
513          int tmp;
514          tmp = le_int(audio_size+36);
515          fwrite(&tmp,4,1,fout);
516          if (fseek(fout,32,SEEK_CUR)==0)
517          {
518             tmp = le_int(audio_size);
519             fwrite(&tmp,4,1,fout);
520          } else
521          {
522             fprintf (stderr, "First seek worked, second didn't\n");
523          }
524       } else {
525          fprintf (stderr, "Cannot seek on wave file, size will be incorrect\n");
526       }
527    }
528
529    if (st)
530       speex_decoder_destroy(st);
531    speex_bits_destroy(&bits);
532    ogg_sync_clear(&oy);
533    ogg_stream_clear(&os);
534
535 #if defined WIN32 || defined _WIN32
536    if (strlen(outFile)==0)
537       WIN_Audio_close ();
538 #endif
539
540    if (close_in)
541       fclose(fin);
542    if (fout != NULL)
543       fclose(fout);   
544
545    return 1;
546 }