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