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