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