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