Getting ready for beta2
[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;
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 }
200
201 void version()
202 {
203    printf ("Speex decoder version " VERSION " (compiled " __DATE__ ")\n");
204 }
205
206 static void *process_header(ogg_packet *op, int enh_enabled, int *frame_size, int *rate, int *nframes, int forceMode)
207 {
208    void *st;
209    SpeexMode *mode;
210    SpeexHeader *header;
211    int modeID;
212    
213    header = speex_packet_to_header((char*)op->packet, op->bytes);
214    if (!header)
215    {
216       fprintf (stderr, "Cannot read header\n");
217       return NULL;
218    }
219    if (header->mode >= SPEEX_NB_MODES)
220    {
221       fprintf (stderr, "Mode number %d does not (any longer) exist in this version\n", 
222                header->mode);
223       return NULL;
224    }
225       
226    modeID = header->mode;
227    if (forceMode!=-1)
228       modeID = forceMode;
229    mode = speex_mode_list[modeID];
230    
231    if (mode->bitstream_version < header->mode_bitstream_version)
232    {
233       fprintf (stderr, "The file was encoded with a newer version of Speex. You need to upgrade in order to play it.\n");
234       return NULL;
235    }
236    if (mode->bitstream_version > header->mode_bitstream_version) 
237    {
238       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");
239       return NULL;
240    }
241    
242    st = speex_decoder_init(mode);
243    speex_decoder_ctl(st, SPEEX_SET_ENH, &enh_enabled);
244    speex_decoder_ctl(st, SPEEX_GET_FRAME_SIZE, frame_size);
245    
246    /* FIXME: need to adjust in case the forceMode option is set */
247    *rate = header->rate;
248    if (header->mode==1 && forceMode==0)
249       *rate/=2;
250    if (header->mode==0 && forceMode==1)
251       *rate*=2;
252    *nframes = header->frames_per_packet;
253    
254    fprintf (stderr, "Decoding %d Hz audio using %s mode", 
255             *rate, mode->modeName);
256
257    if (header->vbr)
258       fprintf (stderr, " (VBR)\n");
259    else
260       fprintf(stderr, "\n");
261    /*fprintf (stderr, "Decoding %d Hz audio at %d bps using %s mode\n", 
262     *rate, mode->bitrate, mode->modeName);*/
263
264    free(header);
265    return st;
266 }
267
268 int main(int argc, char **argv)
269 {
270    int c;
271    int option_index = 0;
272    char *inFile, *outFile;
273    FILE *fin, *fout=NULL;
274    short out[MAX_FRAME_SIZE];
275    float output[MAX_FRAME_SIZE];
276    int frame_size=0;
277    void *st=NULL;
278    SpeexBits bits;
279    int packet_count=0;
280    int stream_init = 0;
281    struct option long_options[] =
282    {
283       {"help", no_argument, NULL, 0},
284       {"version", no_argument, NULL, 0},
285       {"enh", no_argument, NULL, 0},
286       {"no-enh", no_argument, NULL, 0},
287       {"pf", no_argument, NULL, 0},
288       {"no-pf", no_argument, NULL, 0},
289       {"force-nb", no_argument, NULL, 0},
290       {"force-wb", no_argument, NULL, 0},
291       {"packet-loss", required_argument, NULL, 0},
292       {0, 0, 0, 0}
293    };
294    ogg_sync_state oy;
295    ogg_page       og;
296    ogg_packet     op;
297    ogg_stream_state os;
298    int enh_enabled;
299    int nframes=2;
300    int print_bitrate=0;
301    int close_in=0;
302    int eos=0;
303    int forceMode=-1;
304    int audio_size=0;
305    float loss_percent=-1;
306
307    enh_enabled = 0;
308
309    /*Process options*/
310    while(1)
311    {
312       c = getopt_long (argc, argv, "hvV",
313                        long_options, &option_index);
314       if (c==-1)
315          break;
316       
317       switch(c)
318       {
319       case 0:
320          if (strcmp(long_options[option_index].name,"help")==0)
321          {
322             usage();
323             exit(0);
324          } else if (strcmp(long_options[option_index].name,"version")==0)
325          {
326             version();
327             exit(0);
328          } else if (strcmp(long_options[option_index].name,"enh")==0)
329          {
330             enh_enabled=1;
331          } else if (strcmp(long_options[option_index].name,"no-enh")==0)
332          {
333             enh_enabled=0;
334          } else if (strcmp(long_options[option_index].name,"pf")==0)
335          {
336             fprintf (stderr, "--pf is deprecated, use --enh instead\n");
337             enh_enabled=1;
338          } else if (strcmp(long_options[option_index].name,"no-pf")==0)
339          {
340             fprintf (stderr, "--no-pf is deprecated, use --no-enh instead\n");
341             enh_enabled=0;
342          } else if (strcmp(long_options[option_index].name,"force-nb")==0)
343          {
344             forceMode=0;
345          } else if (strcmp(long_options[option_index].name,"force-wb")==0)
346          {
347             forceMode=1;
348          } else if (strcmp(long_options[option_index].name,"packet-loss")==0)
349          {
350             loss_percent = atof(optarg);
351          }
352          break;
353       case 'h':
354          usage();
355          exit(0);
356          break;
357       case 'v':
358          version();
359          exit(0);
360          break;
361       case 'V':
362          print_bitrate=1;
363          break;
364       case '?':
365          usage();
366          exit(1);
367          break;
368       }
369    }
370    if (argc-optind!=2 && argc-optind!=1)
371    {
372       usage();
373       exit(1);
374    }
375    inFile=argv[optind];
376
377    if (argc-optind==2)
378       outFile=argv[optind+1];
379    else
380       outFile = "";
381    /*Open input file*/
382    if (strcmp(inFile, "-")==0)
383    {
384 #if defined WIN32 || defined _WIN32
385       _setmode(_fileno(stdout), _O_BINARY);
386 #endif
387       fin=stdin;
388    }
389    else 
390    {
391 #if defined WIN32 || defined _WIN32
392       fin = fopen(inFile, "rb");
393 #else
394       fin = fopen(inFile, "r");
395 #endif
396       if (!fin)
397       {
398          perror(inFile);
399          exit(1);
400       }
401       close_in=1;
402    }
403
404
405    /*Init Ogg data struct*/
406    ogg_sync_init(&oy);
407    
408    speex_bits_init(&bits);
409    /*Main decoding loop*/
410    while (1)
411    {
412       char *data;
413       int i, j, nb_read;
414       /*Get the ogg buffer for writing*/
415       data = ogg_sync_buffer(&oy, 200);
416       /*Read bitstream from input file*/
417       nb_read = fread(data, sizeof(char), 200, fin);      
418       ogg_sync_wrote(&oy, nb_read);
419
420       /*Loop for all complete pages we got (most likely only one)*/
421       while (ogg_sync_pageout(&oy, &og)==1)
422       {
423          if (stream_init == 0) {
424             ogg_stream_init(&os, ogg_page_serialno(&og));
425             stream_init = 1;
426          }
427          /*Add page to the bitstream*/
428          ogg_stream_pagein(&os, &og);
429          /*Extract all available packets*/
430          while (!eos && ogg_stream_packetout(&os, &op)==1)
431          {
432             /*If first packet, process as Speex header*/
433             if (packet_count==0)
434             {
435                int rate;
436                st = process_header(&op, enh_enabled, &frame_size, &rate, &nframes, forceMode);
437                if (!nframes)
438                   nframes=1;
439                if (!st)
440                   exit(1);
441                fout = out_file_open(outFile, rate);
442
443             } else if (packet_count==1){
444                print_comments(op.packet, op.bytes);
445                /*
446                fprintf (stderr, "File comments: ");
447                fwrite(op.packet, 1, op.bytes, stderr);
448                fprintf (stderr, "\n");
449                */
450             } else {
451                
452                int lost=0;
453                if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent)
454                   lost=1;
455
456                /*End of stream condition*/
457                if (op.e_o_s)
458                   eos=1;
459
460                /*Copy Ogg packet to Speex bitstream*/
461                speex_bits_read_from(&bits, (char*)op.packet, op.bytes);
462                for (j=0;j<nframes;j++)
463                {
464                   /*Decode frame*/
465                   if (!lost)
466                      speex_decode(st, &bits, output);
467                   else
468                      speex_decode(st, NULL, output);
469
470                   if (print_bitrate) {
471                      int tmp;
472                      char ch=13;
473                      speex_decoder_ctl(st, SPEEX_GET_BITRATE, &tmp);
474                      fputc (ch, stderr);
475                      fprintf (stderr, "Bitrate is use: %d bps     ", tmp);
476                   }
477                   /*PCM saturation (just in case)*/
478                   for (i=0;i<frame_size;i++)
479                   {
480                      if (output[i]>32000.0)
481                         output[i]=32000.0;
482                      else if (output[i]<-32000.0)
483                         output[i]=-32000.0;
484                   }
485                   /*Convert to short and save to output file*/
486                   for (i=0;i<frame_size;i++)
487                      out[i]=(short)le_short(output[i]);
488 #if defined WIN32 || defined _WIN32
489                   if (strlen(outFile)==0)
490                       WIN_Play_Samples (out, sizeof(short) * frame_size);
491                   else
492 #endif
493                   fwrite(out, sizeof(short), frame_size, fout);
494                   audio_size+=sizeof(short)*frame_size;
495                }
496             }
497             packet_count++;
498          }
499       }
500       if (feof(fin))
501          break;
502
503    }
504
505    if (strcmp(outFile+strlen(outFile)-4,".wav")==0 || strcmp(inFile+strlen(inFile)-4,".WAV")==0)
506    {
507       if (fseek(fout,4,SEEK_SET)==0)
508       {
509          int tmp;
510          tmp = le_int(audio_size+36);
511          fwrite(&tmp,4,1,fout);
512          if (fseek(fout,32,SEEK_CUR)==0)
513          {
514             tmp = le_int(audio_size);
515             fwrite(&tmp,4,1,fout);
516          } else
517          {
518             fprintf (stderr, "First seek worked, second didn't\n");
519          }
520       } else {
521          fprintf (stderr, "Cannot seek on wave file, size will be incorrect\n");
522       }
523    }
524
525    if (st)
526       speex_decoder_destroy(st);
527    speex_bits_destroy(&bits);
528    ogg_sync_clear(&oy);
529    ogg_stream_clear(&os);
530
531    if (close_in)
532       fclose(fin);
533    fclose(fout);
534    return 1;
535 }