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