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