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