Renamed to win32 compatibility getopt.h to getopt_win.h to avoid conflicts
[speexdsp.git] / src / speexdec.c
1 /* Copyright (C) 2002 Jean-Marc Valin 
2    File: speexdec.c
3
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8    
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13    
14    You should have received a copy of the GNU Lesser General Public
15    License along with this library; if not, write to the Free Software
16    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18
19 #include <stdio.h>
20 #if !defined WIN32 && !defined _WIN32
21 #include <unistd.h>
22 #include <getopt.h>
23 #endif
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "speex.h"
28 #include "ogg/ogg.h"
29
30 #if defined WIN32 || defined _WIN32
31 #include <windows.h>
32 #include "getopt_win.h"
33 #include "wave_out.h"
34 /* We need the following two to set stdout to binary */
35 #include <io.h>
36 #include <fcntl.h>
37 #endif
38
39 #ifdef HAVE_SYS_SOUNDCARD_H
40 #include <sys/soundcard.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <fcntl.h>
44 #include <sys/ioctl.h>
45 #endif
46
47 #include <string.h>
48 #include "wav_io.h"
49 #include "speex_header.h"
50 #include "misc.h"
51
52 #define MAX_FRAME_SIZE 2000
53
54 FILE *out_file_open(char *outFile, int rate)
55 {
56    FILE *fout;
57    /*Open output file*/
58    if (strlen(outFile)==0)
59    {
60 #if defined HAVE_SYS_SOUNDCARD_H
61       int audio_fd, format, stereo;
62       audio_fd=open("/dev/dsp", O_WRONLY);
63       
64       format=AFMT_S16_LE;
65       if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format)==-1)
66       {
67          perror("SNDCTL_DSP_SETFMT");
68          close(audio_fd);
69          exit(1);
70       }
71       
72       stereo=0;
73       if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo)==-1)
74       {
75          perror("SNDCTL_DSP_STEREO");
76          close(audio_fd);
77          exit(1);
78       }
79       if (stereo!=0)
80       {
81          fprintf (stderr, "Cannot set mono mode\n");
82          exit(1);
83       }
84
85       if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate)==-1)
86       {
87          perror("SNDCTL_DSP_SPEED");
88          close(audio_fd);
89          exit(1);
90       }
91       fout = fdopen(audio_fd, "w");
92 #elif defined WIN32 || defined _WIN32
93       if (Set_WIN_Params (INVALID_FILEDESC, rate, SAMPLE_SIZE, 1))
94       {
95          fprintf (stderr, "Can't access %s\n", "WAVE OUT");
96          exit(1);
97       }
98 #else
99       fprintf (stderr, "No soundcard support\n");
100       exit(1);
101 #endif
102    } else {
103       if (strcmp(outFile,"-")==0)
104       {
105 #if defined WIN32 || defined _WIN32
106          _setmode(_fileno(stdout), _O_BINARY);
107 #endif
108          fout=stdout;
109       }
110       else 
111       {
112 #if defined WIN32 || defined _WIN32
113          fout = fopen(outFile, "wb");
114 #else
115          fout = fopen(outFile, "w");
116 #endif
117          if (!fout)
118          {
119             perror(outFile);
120             exit(1);
121          }
122          if (strcmp(outFile+strlen(outFile)-4,".wav")==0)
123             write_wav_header(fout, rate, 1, 0, 0);
124       }
125    }
126    return fout;
127 }
128
129 void usage()
130 {
131    fprintf (stderr, "Speex decoder version " VERSION " (compiled " __DATE__ ")\n");
132    fprintf (stderr, "\n");
133    fprintf (stderr, "usage: speexdec [options] input_file.spx\n");
134    fprintf (stderr, "       speexdec [options] input_file.spx output_file.wav\n");
135    fprintf (stderr, "\n");
136    fprintf (stderr, "input_file can be:\n");
137    fprintf (stderr, "  filename.spx          regular Speex file\n");
138    fprintf (stderr, "  -                     stdin\n");
139    fprintf (stderr, "\n");  
140    fprintf (stderr, "output_file can be:\n");
141    fprintf (stderr, "  filename.wav          wav file\n");
142    fprintf (stderr, "  filename.*            raw PCM file (any extension other that .wav)\n");
143    fprintf (stderr, "  -                     stdout\n");
144    fprintf (stderr, "  (nothing)             will be played to soundcard\n");
145    fprintf (stderr, "\n");  
146    fprintf (stderr, "options:\n");
147    fprintf (stderr, "  --enh                 Enable perceptual enhancement\n");
148    fprintf (stderr, "  --no-enh              Disable perceptual enhancement (default FOR NOW)\n");
149    fprintf (stderr, "  --force-nb            Force decoding in narrowband, even for wideband\n");
150    fprintf (stderr, "  --force-wb            Force decoding in wideband, even for narrowband\n");
151    fprintf (stderr, "  -V                    Verbose mode (show bit-rate)\n"); 
152    fprintf (stderr, "  --help       -h       This help\n");
153    fprintf (stderr, "  --version    -v       Version information\n");
154    fprintf (stderr, "  --pf                  Deprecated, use --pf instead\n");
155    fprintf (stderr, "  --no-pf               Deprecated, use --no-pf instead\n");
156 }
157
158 void version()
159 {
160    fprintf (stderr, "Speex decoder version " VERSION " (compiled " __DATE__ ")\n");
161 }
162
163 static void *process_header(ogg_packet *op, int enh_enabled, int *frame_size, int *rate, int *nframes, int forceMode)
164 {
165    void *st;
166    SpeexMode *mode;
167    SpeexHeader *header;
168    int modeID;
169    
170    header = speex_packet_to_header((char*)op->packet, op->bytes);
171    if (!header)
172    {
173       fprintf (stderr, "Cannot read header\n");
174       return NULL;
175    }
176    if (header->mode >= SPEEX_NB_MODES)
177    {
178       fprintf (stderr, "Mode number %d does not (any longer) exist in this version\n", 
179                header->mode);
180       return NULL;
181    }
182       
183    modeID = header->mode;
184    if (forceMode!=-1)
185       modeID = forceMode;
186    mode = speex_mode_list[modeID];
187    
188    if (mode->bitstream_version < header->mode_bitstream_version)
189    {
190       fprintf (stderr, "The file was encoded with a newer version of Speex. You need to upgrade in order to play it.\n");
191       return NULL;
192    }
193    if (mode->bitstream_version > header->mode_bitstream_version) 
194    {
195       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");
196       return NULL;
197    }
198    
199    st = speex_decoder_init(mode);
200    speex_decoder_ctl(st, SPEEX_SET_ENH, &enh_enabled);
201    speex_decoder_ctl(st, SPEEX_GET_FRAME_SIZE, frame_size);
202    
203    /* FIXME: need to adjust in case the forceMode option is set */
204    *rate = header->rate;
205    if (header->mode==1 && forceMode==0)
206       *rate/=2;
207    if (header->mode==0 && forceMode==1)
208       *rate*=2;
209    *nframes = header->frames_per_packet;
210    
211    fprintf (stderr, "Decoding %d Hz audio using %s mode", 
212             *rate, mode->modeName);
213
214    if (header->vbr)
215       fprintf (stderr, " (VBR)\n");
216    else
217       fprintf(stderr, "\n");
218    /*fprintf (stderr, "Decoding %d Hz audio at %d bps using %s mode\n", 
219     *rate, mode->bitrate, mode->modeName);*/
220
221    free(header);
222    return st;
223 }
224
225 int main(int argc, char **argv)
226 {
227    int c;
228    int option_index = 0;
229    char *inFile, *outFile;
230    FILE *fin, *fout=NULL;
231    short out[MAX_FRAME_SIZE];
232    float output[MAX_FRAME_SIZE];
233    int frame_size=0;
234    void *st=NULL;
235    SpeexBits bits;
236    int packet_count=0;
237    int stream_init = 0;
238    struct option long_options[] =
239    {
240       {"help", no_argument, NULL, 0},
241       {"version", no_argument, NULL, 0},
242       {"enh", no_argument, NULL, 0},
243       {"no-enh", no_argument, NULL, 0},
244       {"pf", no_argument, NULL, 0},
245       {"no-pf", no_argument, NULL, 0},
246       {"force-nb", no_argument, NULL, 0},
247       {"force-wb", no_argument, NULL, 0},
248       {0, 0, 0, 0}
249    };
250    ogg_sync_state oy;
251    ogg_page       og;
252    ogg_packet     op;
253    ogg_stream_state os;
254    int enh_enabled;
255    int nframes=2;
256    int print_bitrate=0;
257    int close_in=0;
258    int eos=0;
259    int forceMode=-1;
260
261    enh_enabled = 0;
262
263    /*Process options*/
264    while(1)
265    {
266       c = getopt_long (argc, argv, "hvV",
267                        long_options, &option_index);
268       if (c==-1)
269          break;
270       
271       switch(c)
272       {
273       case 0:
274          if (strcmp(long_options[option_index].name,"help")==0)
275          {
276             usage();
277             exit(0);
278          } else if (strcmp(long_options[option_index].name,"version")==0)
279          {
280             version();
281             exit(0);
282          } else if (strcmp(long_options[option_index].name,"enh")==0)
283          {
284             enh_enabled=1;
285          } else if (strcmp(long_options[option_index].name,"no-enh")==0)
286          {
287             enh_enabled=0;
288          } else if (strcmp(long_options[option_index].name,"pf")==0)
289          {
290             fprintf (stderr, "--pf is deprecated, use --enh instead\n");
291             enh_enabled=1;
292          } else if (strcmp(long_options[option_index].name,"no-pf")==0)
293          {
294             fprintf (stderr, "--no-pf is deprecated, use --no-enh instead\n");
295             enh_enabled=0;
296          } else if (strcmp(long_options[option_index].name,"force-nb")==0)
297          {
298             forceMode=0;
299          } else if (strcmp(long_options[option_index].name,"force-wb")==0)
300          {
301             forceMode=1;
302          }
303          break;
304       case 'h':
305          usage();
306          exit(0);
307          break;
308       case 'v':
309          version();
310          exit(0);
311          break;
312       case 'V':
313          print_bitrate=1;
314          break;
315       case '?':
316          usage();
317          exit(1);
318          break;
319       }
320    }
321    if (argc-optind!=2 && argc-optind!=1)
322    {
323       usage();
324       exit(1);
325    }
326    inFile=argv[optind];
327
328    if (argc-optind==2)
329       outFile=argv[optind+1];
330    else
331       outFile = "";
332    /*Open input file*/
333    if (strcmp(inFile, "-")==0)
334    {
335 #if defined WIN32 || defined _WIN32
336       _setmode(_fileno(stdout), _O_BINARY);
337 #endif
338       fin=stdin;
339    }
340    else 
341    {
342 #if defined WIN32 || defined _WIN32
343       fin = fopen(inFile, "rb");
344 #else
345       fin = fopen(inFile, "r");
346 #endif
347       if (!fin)
348       {
349          perror(inFile);
350          exit(1);
351       }
352       close_in=1;
353    }
354
355
356    /*Init Ogg data struct*/
357    ogg_sync_init(&oy);
358    
359    speex_bits_init(&bits);
360    /*Main decoding loop*/
361    while (1)
362    {
363       char *data;
364       int i, j, nb_read;
365       /*Get the ogg buffer for writing*/
366       data = ogg_sync_buffer(&oy, 200);
367       /*Read bitstream from input file*/
368       nb_read = fread(data, sizeof(char), 200, fin);      
369       ogg_sync_wrote(&oy, nb_read);
370
371       /*Loop for all complete pages we got (most likely only one)*/
372       while (ogg_sync_pageout(&oy, &og)==1)
373       {
374          if (stream_init == 0) {
375             ogg_stream_init(&os, ogg_page_serialno(&og));
376             stream_init = 1;
377          }
378          /*Add page to the bitstream*/
379          ogg_stream_pagein(&os, &og);
380          /*Extract all available packets*/
381          while (!eos && ogg_stream_packetout(&os, &op)==1)
382          {
383             /*If first packet, process as Speex header*/
384             if (packet_count==0)
385             {
386                int rate;
387                st = process_header(&op, enh_enabled, &frame_size, &rate, &nframes, forceMode);
388                if (!nframes)
389                   nframes=1;
390                if (!st)
391                   exit(1);
392                fout = out_file_open(outFile, rate);
393
394             } else if (packet_count==1){
395                fprintf (stderr, "File comments: ");
396                fwrite(op.packet, 1, op.bytes, stderr);
397                fprintf (stderr, "\n");
398             } else {
399
400                /*End of stream condition*/
401                if (op.e_o_s)
402                   eos=1;
403
404                /*Copy Ogg packet to Speex bitstream*/
405                speex_bits_read_from(&bits, (char*)op.packet, op.bytes);
406                for (j=0;j<nframes;j++)
407                {
408                   /*Decode frame*/
409                   speex_decode(st, &bits, output);
410                
411                   if (print_bitrate) {
412                      int tmp;
413                      char ch=13;
414                      speex_decoder_ctl(st, SPEEX_GET_BITRATE, &tmp);
415                      fputc (ch, stderr);
416                      fprintf (stderr, "Bitrate is use: %d bps     ", tmp);
417                   }
418                   /*PCM saturation (just in case)*/
419                   for (i=0;i<frame_size;i++)
420                   {
421                      if (output[i]>32000)
422                         output[i]=32000;
423                      else if (output[i]<-32000)
424                         output[i]=-32000;
425                   }
426                   /*Convert to short and save to output file*/
427                   for (i=0;i<frame_size;i++)
428                      out[i]=(short)le_short(output[i]);
429 #if defined WIN32 || defined _WIN32
430                   if (strlen(outFile)==0)
431                       WIN_Play_Samples (out, sizeof(short) * frame_size);
432                   else
433 #endif
434                   fwrite(out, sizeof(short), frame_size, fout);
435                }
436             }
437             packet_count++;
438          }
439       }
440       if (feof(fin))
441          break;
442
443    }
444
445    if (st)
446       speex_decoder_destroy(st);
447    speex_bits_destroy(&bits);
448    ogg_sync_clear(&oy);
449    ogg_stream_clear(&os);
450
451    if (close_in)
452       fclose(fin);
453    fclose(fout);
454    return 1;
455 }