decoder excitation now in 16-bit precision (was 32), which saves quite a bit
[speexdsp.git] / src / speexdec.c
index 3d4e575..2a3876a 100644 (file)
-/* Copyright (C) 2002 Jean-Marc Valin 
+/* Copyright (C) 2002-2006 Jean-Marc Valin 
    File: speexdec.c
 
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
    
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
    
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   
+   - Neither the name of the Xiph.org Foundation nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+   
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
 #include <stdio.h>
+#if !defined WIN32 && !defined _WIN32
 #include <unistd.h>
 #include <getopt.h>
+#endif
+#ifndef HAVE_GETOPT_LONG
+#include "getopt_win.h"
+#endif
 #include <stdlib.h>
 #include <string.h>
 
-#include "speex.h"
-#include "ogg/ogg.h"
+#include <speex/speex.h>
+#include <ogg/ogg.h>
+
+#if defined WIN32 || defined _WIN32
+#include <windows.h>
+#include "getopt_win.h"
+#include "wave_out.h"
+/* We need the following two to set stdout to binary */
+#include <io.h>
+#include <fcntl.h>
+#endif
+#include <math.h>
+
+#ifdef __MINGW32__
+#include "wave_out.c"
+#endif
+
+#ifdef HAVE_SYS_SOUNDCARD_H
+#include <sys/soundcard.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#elif defined HAVE_SYS_AUDIOIO_H
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+#ifndef AUDIO_ENCODING_SLINEAR
+#define AUDIO_ENCODING_SLINEAR AUDIO_ENCODING_LINEAR /* Solaris */
+#endif
+
+#endif
+
+#include <string.h>
+#include "wav_io.h"
+#include <speex/speex_header.h>
+#include <speex/speex_stereo.h>
+#include <speex/speex_callbacks.h>
+#include "wav_io.h"
 
 #define MAX_FRAME_SIZE 2000
-#define MAX_FRAME_BYTES 1000
+
+#define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
+                           ((buf[base+2]<<16)&0xff0000)| \
+                           ((buf[base+1]<<8)&0xff00)| \
+                           (buf[base]&0xff))
+
+static void print_comments(char *comments, int length)
+{
+   char *c=comments;
+   int len, i, nb_fields;
+   char *end;
+   
+   if (length<8)
+   {
+      fprintf (stderr, "Invalid/corrupted comments\n");
+      return;
+   }
+   end = c+length;
+   len=readint(c, 0);
+   c+=4;
+   if (c+len>end)
+   {
+      fprintf (stderr, "Invalid/corrupted comments\n");
+      return;
+   }
+   fwrite(c, 1, len, stderr);
+   c+=len;
+   fprintf (stderr, "\n");
+   if (c+4>end)
+   {
+      fprintf (stderr, "Invalid/corrupted comments\n");
+      return;
+   }
+   nb_fields=readint(c, 0);
+   c+=4;
+   for (i=0;i<nb_fields;i++)
+   {
+      if (c+4>end)
+      {
+         fprintf (stderr, "Invalid/corrupted comments\n");
+         return;
+      }
+      len=readint(c, 0);
+      c+=4;
+      if (c+len>end)
+      {
+         fprintf (stderr, "Invalid/corrupted comments\n");
+         return;
+      }
+      fwrite(c, 1, len, stderr);
+      c+=len;
+      fprintf (stderr, "\n");
+   }
+}
+
+FILE *out_file_open(char *outFile, int rate, int *channels)
+{
+   FILE *fout=NULL;
+   /*Open output file*/
+   if (strlen(outFile)==0)
+   {
+#if defined HAVE_SYS_SOUNDCARD_H
+      int audio_fd, format, stereo;
+      audio_fd=open("/dev/dsp", O_WRONLY);
+      if (audio_fd<0)
+      {
+         perror("Cannot open /dev/dsp");
+         exit(1);         
+      }
+
+      format=AFMT_S16_NE;
+      if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format)==-1)
+      {
+         perror("SNDCTL_DSP_SETFMT");
+         close(audio_fd);
+         exit(1);
+      }
+
+      stereo=0;
+      if (*channels==2)
+         stereo=1;
+      if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo)==-1)
+      {
+         perror("SNDCTL_DSP_STEREO");
+         close(audio_fd);
+         exit(1);
+      }
+      if (stereo!=0)
+      {
+         if (*channels==1)
+            fprintf (stderr, "Cannot set mono mode, will decode in stereo\n");
+         *channels=2;
+      }
+
+      if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate)==-1)
+      {
+         perror("SNDCTL_DSP_SPEED");
+         close(audio_fd);
+         exit(1);
+      }
+      fout = fdopen(audio_fd, "w");
+#elif defined HAVE_SYS_AUDIOIO_H
+      audio_info_t info;
+      int audio_fd;
+      
+      audio_fd = open("/dev/audio", O_WRONLY);
+      if (audio_fd<0)
+      {
+         perror("Cannot open /dev/audio");
+         exit(1);
+      }
+
+      AUDIO_INITINFO(&info);
+#ifdef AUMODE_PLAY    /* NetBSD/OpenBSD */
+      info.mode = AUMODE_PLAY;
+#endif
+      info.play.encoding = AUDIO_ENCODING_SLINEAR;
+      info.play.precision = 16;
+      info.play.sample_rate = rate;
+      info.play.channels = *channels;
+      
+      if (ioctl(audio_fd, AUDIO_SETINFO, &info) < 0)
+      {
+         perror ("AUDIO_SETINFO");
+         exit(1);
+      }
+      fout = fdopen(audio_fd, "w");
+#elif defined WIN32 || defined _WIN32
+      {
+         unsigned int speex_channels = *channels;
+         if (Set_WIN_Params (INVALID_FILEDESC, rate, SAMPLE_SIZE, speex_channels))
+         {
+            fprintf (stderr, "Can't access %s\n", "WAVE OUT");
+            exit(1);
+         }
+      }
+#else
+      fprintf (stderr, "No soundcard support\n");
+      exit(1);
+#endif
+   } else {
+      if (strcmp(outFile,"-")==0)
+      {
+#if defined WIN32 || defined _WIN32
+         _setmode(_fileno(stdout), _O_BINARY);
+#endif
+         fout=stdout;
+      }
+      else 
+      {
+         fout = fopen(outFile, "wb");
+         if (!fout)
+         {
+            perror(outFile);
+            exit(1);
+         }
+         if (strcmp(outFile+strlen(outFile)-4,".wav")==0 || strcmp(outFile+strlen(outFile)-4,".WAV")==0)
+            write_wav_header(fout, rate, *channels, 0, 0);
+      }
+   }
+   return fout;
+}
 
 void usage()
 {
-   fprintf (stderr, "speexenc [options] <input file> <output file>\n");
-   fprintf (stderr, "options:\n");
-   fprintf (stderr, "\t--help       -h    This help\n"); 
-   fprintf (stderr, "\t--version    -v    Version information\n"); 
+   printf ("Usage: speexdec [options] input_file.spx [output_file]\n");
+   printf ("\n");
+   printf ("Decodes a Speex file and produce a WAV file or raw file\n");
+   printf ("\n");
+   printf ("input_file can be:\n");
+   printf ("  filename.spx         regular Speex file\n");
+   printf ("  -                    stdin\n");
+   printf ("\n");  
+   printf ("output_file can be:\n");
+   printf ("  filename.wav         Wav file\n");
+   printf ("  filename.*           Raw PCM file (any extension other that .wav)\n");
+   printf ("  -                    stdout\n");
+   printf ("  (nothing)            Will be played to soundcard\n");
+   printf ("\n");  
+   printf ("Options:\n");
+   printf (" --enh                 Enable perceptual enhancement (default)\n");
+   printf (" --no-enh              Disable perceptual enhancement\n");
+   printf (" --force-nb            Force decoding in narrowband\n");
+   printf (" --force-wb            Force decoding in wideband\n");
+   printf (" --force-uwb           Force decoding in ultra-wideband\n");
+   printf (" --mono                Force decoding in mono\n");
+   printf (" --stereo              Force decoding in stereo\n");
+   printf (" --rate n              Force decoding at sampling rate n Hz\n");
+   printf (" --packet-loss n       Simulate n %% random packet loss\n");
+   printf (" -V                    Verbose mode (show bit-rate)\n"); 
+   printf (" -h, --help            This help\n");
+   printf (" -v, --version         Version information\n");
+   printf (" --pf                  Deprecated, use --enh instead\n");
+   printf (" --no-pf               Deprecated, use --no-enh instead\n");
+   printf ("\n");
+   printf ("More information is available from the Speex site: http://www.speex.org\n");
+   printf ("\n");
+   printf ("Please report bugs to the mailing list `speex-dev@xiph.org'.\n");
 }
 
 void version()
 {
-   fprintf (stderr, "Speex encoder version " VERSION "\n");
+   printf ("speexdec (Speex decoder) version " SPEEX_VERSION " (compiled " __DATE__ ")\n");
+   printf ("Copyright (C) 2002-2006 Jean-Marc Valin\n");
+}
+
+void version_short()
+{
+   printf ("speexdec version " SPEEX_VERSION "\n");
+   printf ("Copyright (C) 2002-2006 Jean-Marc Valin\n");
+}
+
+static void *process_header(ogg_packet *op, int enh_enabled, int *frame_size, int *rate, int *nframes, int forceMode, int *channels, SpeexStereoState *stereo, int *extra_headers, int quiet)
+{
+   void *st;
+   const SpeexMode *mode;
+   SpeexHeader *header;
+   int modeID;
+   SpeexCallback callback;
+      
+   header = speex_packet_to_header((char*)op->packet, op->bytes);
+   if (!header)
+   {
+      fprintf (stderr, "Cannot read header\n");
+      return NULL;
+   }
+   if (header->mode >= SPEEX_NB_MODES || header->mode<0)
+   {
+      fprintf (stderr, "Mode number %d does not (yet/any longer) exist in this version\n", 
+               header->mode);
+      return NULL;
+   }
+      
+   modeID = header->mode;
+   if (forceMode!=-1)
+      modeID = forceMode;
+
+   mode = speex_lib_get_mode (modeID);
+   
+   if (header->speex_version_id > 1)
+   {
+      fprintf (stderr, "This file was encoded with Speex bit-stream version %d, which I don't know how to decode\n", header->speex_version_id);
+      return NULL;
+   }
+
+   if (mode->bitstream_version < header->mode_bitstream_version)
+   {
+      fprintf (stderr, "The file was encoded with a newer version of Speex. You need to upgrade in order to play it.\n");
+      return NULL;
+   }
+   if (mode->bitstream_version > header->mode_bitstream_version) 
+   {
+      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");
+      return NULL;
+   }
+   
+   st = speex_decoder_init(mode);
+   if (!st)
+   {
+      fprintf (stderr, "Decoder initialization failed.\n");
+      return NULL;
+   }
+   speex_decoder_ctl(st, SPEEX_SET_ENH, &enh_enabled);
+   speex_decoder_ctl(st, SPEEX_GET_FRAME_SIZE, frame_size);
+
+   if (!(*channels==1))
+   {
+      callback.callback_id = SPEEX_INBAND_STEREO;
+      callback.func = speex_std_stereo_request_handler;
+      callback.data = stereo;
+      speex_decoder_ctl(st, SPEEX_SET_HANDLER, &callback);
+   }
+   if (!*rate)
+      *rate = header->rate;
+   /* Adjust rate if --force-* options are used */
+   if (forceMode!=-1)
+   {
+      if (header->mode < forceMode)
+         *rate <<= (forceMode - header->mode);
+      if (header->mode > forceMode)
+         *rate >>= (header->mode - forceMode);
+   }
+
+   speex_decoder_ctl(st, SPEEX_SET_SAMPLING_RATE, rate);
+
+   *nframes = header->frames_per_packet;
+
+   if (*channels==-1)
+      *channels = header->nb_channels;
+   
+   if (!quiet)
+   {
+      fprintf (stderr, "Decoding %d Hz audio using %s mode", 
+               *rate, mode->modeName);
+
+      if (*channels==1)
+         fprintf (stderr, " (mono");
+      else
+         fprintf (stderr, " (stereo");
+      
+      if (header->vbr)
+         fprintf (stderr, ", VBR)\n");
+      else
+         fprintf(stderr, ")\n");
+      /*fprintf (stderr, "Decoding %d Hz audio at %d bps using %s mode\n", 
+       *rate, mode->bitrate, mode->modeName);*/
+   }
+
+   *extra_headers = header->extra_headers;
+
+   free(header);
+   return st;
 }
 
 int main(int argc, char **argv)
@@ -46,28 +406,61 @@ int main(int argc, char **argv)
    int c;
    int option_index = 0;
    char *inFile, *outFile;
-   FILE *fin, *fout;
+   FILE *fin, *fout=NULL;
    short out[MAX_FRAME_SIZE];
-   float output[MAX_FRAME_SIZE];
-   int frame_size;
-   SpeexMode *mode=NULL;
-   void *st;
-   FrameBits bits;
-   int first = 1;
+   short output[MAX_FRAME_SIZE];
+   int frame_size=0;
+   void *st=NULL;
+   SpeexBits bits;
+   int packet_count=0;
+   int stream_init = 0;
+   int quiet = 0;
+   ogg_int64_t page_granule=0, last_granule=0;
+   int skip_samples=0, page_nb_packets;
    struct option long_options[] =
    {
       {"help", no_argument, NULL, 0},
+      {"quiet", no_argument, NULL, 0},
       {"version", no_argument, NULL, 0},
+      {"version-short", no_argument, NULL, 0},
+      {"enh", no_argument, NULL, 0},
+      {"no-enh", no_argument, NULL, 0},
+      {"pf", no_argument, NULL, 0},
+      {"no-pf", no_argument, NULL, 0},
+      {"force-nb", no_argument, NULL, 0},
+      {"force-wb", no_argument, NULL, 0},
+      {"force-uwb", no_argument, NULL, 0},
+      {"rate", required_argument, NULL, 0},
+      {"mono", no_argument, NULL, 0},
+      {"stereo", no_argument, NULL, 0},
+      {"packet-loss", required_argument, NULL, 0},
       {0, 0, 0, 0}
    };
    ogg_sync_state oy;
    ogg_page       og;
    ogg_packet     op;
    ogg_stream_state os;
+   int enh_enabled;
+   int nframes=2;
+   int print_bitrate=0;
+   int close_in=0;
+   int eos=0;
+   int forceMode=-1;
+   int audio_size=0;
+   float loss_percent=-1;
+   SpeexStereoState stereo = SPEEX_STEREO_STATE_INIT;
+   int channels=-1;
+   int rate=0;
+   int extra_headers;
+   int wav_format=0;
+   int lookahead;
+
+   enh_enabled = 1;
 
+   /*Process options*/
    while(1)
    {
-      c = getopt_long (argc, argv, "hv",
+      c = getopt_long (argc, argv, "hvV",
                        long_options, &option_index);
       if (c==-1)
          break;
@@ -79,111 +472,259 @@ int main(int argc, char **argv)
          {
             usage();
             exit(0);
+         } else if (strcmp(long_options[option_index].name,"quiet")==0)
+         {
+            quiet = 1;
          } else if (strcmp(long_options[option_index].name,"version")==0)
          {
             version();
             exit(0);
+         } else if (strcmp(long_options[option_index].name,"version-short")==0)
+         {
+            version_short();
+            exit(0);
+         } else if (strcmp(long_options[option_index].name,"enh")==0)
+         {
+            enh_enabled=1;
+         } else if (strcmp(long_options[option_index].name,"no-enh")==0)
+         {
+            enh_enabled=0;
+         } else if (strcmp(long_options[option_index].name,"pf")==0)
+         {
+            fprintf (stderr, "--pf is deprecated, use --enh instead\n");
+            enh_enabled=1;
+         } else if (strcmp(long_options[option_index].name,"no-pf")==0)
+         {
+            fprintf (stderr, "--no-pf is deprecated, use --no-enh instead\n");
+            enh_enabled=0;
+         } else if (strcmp(long_options[option_index].name,"force-nb")==0)
+         {
+            forceMode=0;
+         } else if (strcmp(long_options[option_index].name,"force-wb")==0)
+         {
+            forceMode=1;
+         } else if (strcmp(long_options[option_index].name,"force-uwb")==0)
+         {
+            forceMode=2;
+         } else if (strcmp(long_options[option_index].name,"mono")==0)
+         {
+            channels=1;
+         } else if (strcmp(long_options[option_index].name,"stereo")==0)
+         {
+            channels=2;
+         } else if (strcmp(long_options[option_index].name,"rate")==0)
+         {
+            rate=atoi (optarg);
+         } else if (strcmp(long_options[option_index].name,"packet-loss")==0)
+         {
+            loss_percent = atof(optarg);
          }
          break;
       case 'h':
          usage();
+         exit(0);
          break;
       case 'v':
          version();
          exit(0);
          break;
+      case 'V':
+         print_bitrate=1;
+         break;
       case '?':
          usage();
          exit(1);
          break;
       }
    }
-   if (argc-optind!=2)
+   if (argc-optind!=2 && argc-optind!=1)
    {
       usage();
       exit(1);
    }
    inFile=argv[optind];
-   outFile=argv[optind+1];
 
+   if (argc-optind==2)
+      outFile=argv[optind+1];
+   else
+      outFile = "";
+   wav_format = strlen(outFile)>=4 && (
+                                       strcmp(outFile+strlen(outFile)-4,".wav")==0
+                                       || strcmp(outFile+strlen(outFile)-4,".WAV")==0);
+   /*Open input file*/
    if (strcmp(inFile, "-")==0)
+   {
+#if defined WIN32 || defined _WIN32
+      _setmode(_fileno(stdin), _O_BINARY);
+#endif
       fin=stdin;
+   }
    else 
    {
-      fin = fopen(inFile, "r");
+      fin = fopen(inFile, "rb");
       if (!fin)
       {
          perror(inFile);
          exit(1);
       }
-   }
-   if (strcmp(outFile,"-")==0)
-      fout=stdout;
-   else 
-   {
-      fout = fopen(outFile, "w");
-      if (!fout)
-      {
-         perror(outFile);
-         exit(1);
-      }
+      close_in=1;
    }
 
+
+   /*Init Ogg data struct*/
    ogg_sync_init(&oy);
-   ogg_stream_init(&os, 0);
    
-   /* FIXME: Read header here...*/
-   /*st = decoder_init(mode);*/
-
-   /*frame_size=mode->frameSize;*/
-
    speex_bits_init(&bits);
-   while (1/*||feof(fin)*/)
+   /*Main decoding loop*/
+   while (1)
    {
       char *data;
-      int i, nb_read;
+      int i, j, nb_read;
+      /*Get the ogg buffer for writing*/
       data = ogg_sync_buffer(&oy, 200);
+      /*Read bitstream from input file*/
       nb_read = fread(data, sizeof(char), 200, fin);      
       ogg_sync_wrote(&oy, nb_read);
+
+      /*Loop for all complete pages we got (most likely only one)*/
       while (ogg_sync_pageout(&oy, &og)==1)
       {
+         int packet_no;
+         if (stream_init == 0) {
+            ogg_stream_init(&os, ogg_page_serialno(&og));
+            stream_init = 1;
+         }
+         /*Add page to the bitstream*/
          ogg_stream_pagein(&os, &og);
-         while (ogg_stream_packetout(&os, &op)==1)
+         page_granule = ogg_page_granulepos(&og);
+         page_nb_packets = ogg_page_packets(&og);
+         if (page_granule>0 && frame_size)
+         {
+            /* FIXME: shift the granule values if --force-* is specified */
+            skip_samples = page_nb_packets*frame_size*nframes - (page_granule-last_granule);
+            if (ogg_page_eos(&og))
+               skip_samples = -skip_samples;
+            /*else if (!ogg_page_bos(&og))
+               skip_samples = 0;*/
+         } else
+         {
+            skip_samples = 0;
+         }
+         /*printf ("page granulepos: %d %d %d\n", skip_samples, page_nb_packets, (int)page_granule);*/
+         last_granule = page_granule;
+         /*Extract all available packets*/
+         packet_no=0;
+         while (!eos && ogg_stream_packetout(&os, &op)==1)
          {
-            if (first)
+            /*If first packet, process as Speex header*/
+            if (packet_count==0)
             {
-               if (strncmp((char *)op.packet, "speex wideband**", 12)==0)
-               {
-                  fprintf (stderr, "Wideband!!\n");
-                  mode = &speex_wb_mode;
-               } else if (strncmp((char *)op.packet, "speex narrowband", 12)==0)
-               {
-                  fprintf (stderr, "Narrowband!!\n");
-                  mode = &speex_nb_mode;
-               } else {
-                  fprintf (stderr, "This Ogg file is not a Speex bitstream\n");
+               st = process_header(&op, enh_enabled, &frame_size, &rate, &nframes, forceMode, &channels, &stereo, &extra_headers, quiet);
+               speex_decoder_ctl(st, SPEEX_GET_LOOKAHEAD, &lookahead);
+               if (!nframes)
+                  nframes=1;
+               if (!st)
                   exit(1);
-               }
-               st = decoder_init(mode);
-               frame_size=mode->frameSize;
-               first=0;
+               fout = out_file_open(outFile, rate, &channels);
+
+            } else if (packet_count==1)
+            {
+               if (!quiet)
+                  print_comments((char*)op.packet, op.bytes);
+            } else if (packet_count<=1+extra_headers)
+            {
+               /* Ignore extra headers */
             } else {
-               if (strncmp((char *)op.packet, "END OF STREAM", 13)==0)
-                  break;
-               speex_bits_init_from(&bits, (char*)op.packet, op.bytes);
-               decode(st, &bits, output);
-               
-               for (i=0;i<frame_size;i++)
+               int lost=0;
+               packet_no++;
+               if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent)
+                  lost=1;
+
+               /*End of stream condition*/
+               if (op.e_o_s)
+                  eos=1;
+
+               /*Copy Ogg packet to Speex bitstream*/
+               speex_bits_read_from(&bits, (char*)op.packet, op.bytes);
+               for (j=0;j!=nframes;j++)
                {
-                  if (output[i]>32000)
-                     output[i]=32000;
-                  else if (output[i]<-32000)
-                     output[i]=-32000;
+                  int ret;
+                  /*Decode frame*/
+                  if (!lost)
+                     ret = speex_decode_int(st, &bits, output);
+                  else
+                     ret = speex_decode_int(st, NULL, output);
+
+                  /*for (i=0;i<frame_size*channels;i++)
+                    printf ("%d\n", (int)output[i]);*/
+
+                  if (ret==-1)
+                     break;
+                  if (ret==-2)
+                  {
+                     fprintf (stderr, "Decoding error: corrupted stream?\n");
+                     break;
+                  }
+                  if (speex_bits_remaining(&bits)<0)
+                  {
+                     fprintf (stderr, "Decoding overflow: corrupted stream?\n");
+                     break;
+                  }
+                  if (channels==2)
+                     speex_decode_stereo_int(output, frame_size, &stereo);
+
+                  if (print_bitrate) {
+                     int tmp;
+                     char ch=13;
+                     speex_decoder_ctl(st, SPEEX_GET_BITRATE, &tmp);
+                     fputc (ch, stderr);
+                     fprintf (stderr, "Bitrate is use: %d bps     ", tmp);
+                  }
+                  /*Convert to short and save to output file*/
+                 if (strlen(outFile)!=0)
+                  {
+                     for (i=0;i<frame_size*channels;i++)
+                        out[i]=le_short(output[i]);
+                 } else {
+                     for (i=0;i<frame_size*channels;i++)
+                        out[i]=output[i];
+                 }
+                  {
+                     int frame_offset = 0;
+                     int new_frame_size = frame_size;
+                     /*printf ("packet %d %d\n", packet_no, skip_samples);*/
+                     /*fprintf (stderr, "packet %d %d %d\n", packet_no, skip_samples, lookahead);*/
+                     if (packet_no == 1 && j==0 && skip_samples > 0)
+                     {
+                        /*printf ("chopping first packet\n");*/
+                        new_frame_size -= skip_samples+lookahead;
+                        frame_offset = skip_samples+lookahead;
+                     }
+                     if (packet_no == page_nb_packets && skip_samples < 0)
+                     {
+                        int packet_length = nframes*frame_size+skip_samples+lookahead;
+                        new_frame_size = packet_length - j*frame_size;
+                        if (new_frame_size<0)
+                           new_frame_size = 0;
+                        if (new_frame_size>frame_size)
+                           new_frame_size = frame_size;
+                        /*printf ("chopping end: %d %d %d\n", new_frame_size, packet_length, packet_no);*/
+                     }
+                     if (new_frame_size>0)
+                     {  
+#if defined WIN32 || defined _WIN32
+                        if (strlen(outFile)==0)
+                           WIN_Play_Samples (out+frame_offset*channels, sizeof(short) * new_frame_size*channels);
+                        else
+#endif
+                           fwrite(out+frame_offset*channels, sizeof(short), new_frame_size*channels, fout);
+                  
+                        audio_size+=sizeof(short)*new_frame_size*channels;
+                     }
+                  }
                }
-               for (i=0;i<frame_size;i++)
-                  out[i]=output[i];
-               fwrite(out, sizeof(short), frame_size, fout);
             }
+            packet_count++;
          }
       }
       if (feof(fin))
@@ -191,8 +732,46 @@ int main(int argc, char **argv)
 
    }
 
-   decoder_destroy(st);
+   if (wav_format)
+   {
+      if (fseek(fout,4,SEEK_SET)==0)
+      {
+         int tmp;
+         tmp = le_int(audio_size+36);
+         fwrite(&tmp,4,1,fout);
+         if (fseek(fout,32,SEEK_CUR)==0)
+         {
+            tmp = le_int(audio_size);
+            fwrite(&tmp,4,1,fout);
+         } else
+         {
+            fprintf (stderr, "First seek worked, second didn't\n");
+         }
+      } else {
+         fprintf (stderr, "Cannot seek on wave file, size will be incorrect\n");
+      }
+   }
+
+   if (st)
+      speex_decoder_destroy(st);
+   else 
+   {
+      fprintf (stderr, "This doesn't look like a Speex file\n");
+   }
+   speex_bits_destroy(&bits);
+   if (stream_init)
+      ogg_stream_clear(&os);
+   ogg_sync_clear(&oy);
+
+#if defined WIN32 || defined _WIN32
+   if (strlen(outFile)==0)
+      WIN_Audio_close ();
+#endif
+
+   if (close_in)
+      fclose(fin);
+   if (fout != NULL)
+      fclose(fout);   
 
-   exit(0);
-   return 1;
+   return 0;
 }