misc/libtremor/tremor/vorbisfile.c
changeset 7849 a12155461b34
parent 7697 767d3c4153a1
child 7859 519d5bc91dd3
--- a/misc/libtremor/tremor/vorbisfile.c	Sun Oct 28 03:48:37 2012 +0100
+++ b/misc/libtremor/tremor/vorbisfile.c	Sun Oct 28 04:00:07 2012 +0100
@@ -12,7 +12,7 @@
  ********************************************************************
 
  function: stdio-based convenience library for opening/seeking/decoding
- last mod: $Id: vorbisfile.c,v 1.6.2.4 2003/04/29 04:03:27 xiphmont Exp $
+ last mod: $Id: vorbisfile.c,v 1.6 2003/03/30 23:40:56 xiphmont Exp $
 
  ********************************************************************/
 
@@ -22,19 +22,11 @@
 #include <string.h>
 #include <math.h>
 
-#include "codec_internal.h"
+#include "ivorbiscodec.h"
 #include "ivorbisfile.h"
 
-#include "os.h"
 #include "misc.h"
 
-#define  NOTOPEN   0
-#define  PARTOPEN  1
-#define  OPENED    2
-#define  STREAMSET 3 /* serialno and link set, but not to current link */
-#define  LINKSET   4 /* serialno and link set to current link */
-#define  INITSET   5
-
 /* A 'chained bitstream' is a Vorbis bitstream that contains more than
    one logical bitstream arranged end to end (the only form of Ogg
    multiplexing allowed in a Vorbis bitstream; grouping [parallel
@@ -72,10 +64,10 @@
     unsigned char *buffer=ogg_sync_bufferin(vf->oy,CHUNKSIZE);
     long bytes=(vf->callbacks.read_func)(buffer,1,CHUNKSIZE,vf->datasource);
     if(bytes>0)ogg_sync_wrote(vf->oy,bytes);
-    if(bytes==0 && errno)return -1;
-    return bytes;
+    if(bytes==0 && errno)return(-1);
+    return(bytes);
   }else
-    return 0;
+    return(0);
 }
 
 /* save a tiny smidge of verbosity to make the code more readable */
@@ -111,7 +103,7 @@
   while(1){
     long more;
 
-    if(boundary>0 && vf->offset>=boundary)return OV_FALSE;
+    if(boundary>0 && vf->offset>=boundary)return(OV_FALSE);
     more=ogg_sync_pageseek(vf->oy,og);
     
     if(more<0){
@@ -120,18 +112,18 @@
     }else{
       if(more==0){
 	/* send more paramedics */
-	if(!boundary)return OV_FALSE;
+	if(!boundary)return(OV_FALSE);
 	{
 	  long ret=_get_data(vf);
-	  if(ret==0)return OV_EOF;
-	  if(ret<0)return OV_EREAD;
+	  if(ret==0)return(OV_EOF);
+	  if(ret<0)return(OV_EREAD);
 	}
       }else{
 	/* got a page.  Return the offset at the page beginning,
            advance the internal offset past the page end */
 	ogg_int64_t ret=vf->offset;
 	vf->offset+=more;
-	return ret;
+	return(ret);
 	
       }
     }
@@ -157,7 +149,7 @@
     _seek_helper(vf,begin);
     while(vf->offset<end){
       ret=_get_next_page(vf,og,end-vf->offset);
-      if(ret==OV_EREAD)return OV_EREAD;
+      if(ret==OV_EREAD)return(OV_EREAD);
       if(ret<0){
 	break;
       }else{
@@ -171,9 +163,9 @@
   ret=_get_next_page(vf,og,CHUNKSIZE);
   if(ret<0)
     /* this shouldn't be possible */
-    return OV_EFAULT;
+    return(OV_EFAULT);
 
-  return offset;
+  return(offset);
 }
 
 /* finds each bitstream link one at a time using a bisection search
@@ -204,7 +196,7 @@
     
     _seek_helper(vf,bisect);
     ret=_get_next_page(vf,&og,-1);
-    if(ret==OV_EREAD)return OV_EREAD;
+    if(ret==OV_EREAD)return(OV_EREAD);
     if(ret<0 || ogg_page_serialno(&og)!=currentno){
       endsearched=bisect;
       if(ret>=0)next=ret;
@@ -216,7 +208,7 @@
 
   _seek_helper(vf,next);
   ret=_get_next_page(vf,&og,-1);
-  if(ret==OV_EREAD)return OV_EREAD;
+  if(ret==OV_EREAD)return(OV_EREAD);
   
   if(searched>=end || ret<0){
     ogg_page_release(&og);
@@ -228,33 +220,17 @@
     ret=_bisect_forward_serialno(vf,next,vf->offset,
 				 end,ogg_page_serialno(&og),m+1);
     ogg_page_release(&og);
-    if(ret==OV_EREAD)return OV_EREAD;
+    if(ret==OV_EREAD)return(OV_EREAD);
   }
   
   vf->offsets[m]=begin;
   vf->serialnos[m]=currentno;
-  return 0;
-}
-
-static int _decode_clear(OggVorbis_File *vf){
-  if(vf->ready_state==INITSET){
-    vorbis_dsp_destroy(vf->vd);
-    vf->vd=0;
-    vf->ready_state=STREAMSET;
-  }
-  
-  if(vf->ready_state>=STREAMSET){
-    vorbis_info_clear(&vf->vi);
-    vorbis_comment_clear(&vf->vc);
-    vf->ready_state=OPENED;
-  }
-  return 0;
+  return(0);
 }
 
 /* uses the local ogg_stream storage in vf; this is important for
    non-streaming input sources */
 /* consumes the page that's passed in (if any) */
-/* state is LINKSET upon successful return */
 
 static int _fetch_headers(OggVorbis_File *vf,
 			  vorbis_info *vi,
@@ -265,17 +241,16 @@
   ogg_packet op={0,0,0,0,0,0};
   int i,ret;
   
-  if(vf->ready_state>OPENED)_decode_clear(vf);
-
   if(!og_ptr){
     ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE);
-    if(llret==OV_EREAD)return OV_EREAD;
+    if(llret==OV_EREAD)return(OV_EREAD);
     if(llret<0)return OV_ENOTVORBIS;
     og_ptr=&og;
   }
 
   ogg_stream_reset_serialno(vf->os,ogg_page_serialno(og_ptr));
   if(serialno)*serialno=vf->os->serialno;
+  vf->ready_state=STREAMSET;
   
   /* extract the initial header from the first page and verify that the
      Ogg bitstream is in fact Vorbis data */
@@ -293,7 +268,7 @@
 	ret=OV_EBADHEADER;
 	goto bail_header;
       }
-      if((ret=vorbis_dsp_headerin(vi,vc,&op))){
+      if((ret=vorbis_synthesis_headerin(vi,vc,&op))){
 	goto bail_header;
       }
       i++;
@@ -307,7 +282,6 @@
 
   ogg_packet_release(&op);
   ogg_page_release(&og);
-  vf->ready_state=LINKSET;
   return 0; 
 
  bail_header:
@@ -320,45 +294,22 @@
   return ret;
 }
 
-/* we no longer preload all vorbis_info (and the associated
-   codec_setup) structs.  Call this to seek and fetch the info from
-   the bitstream, if needed */
-static int _set_link_number(OggVorbis_File *vf,int link){
-  if(link != vf->current_link) _decode_clear(vf);
-  if(vf->ready_state<STREAMSET){
-    _seek_helper(vf,vf->offsets[link]);
-    ogg_stream_reset_serialno(vf->os,vf->serialnos[link]);
-    vf->current_serialno=vf->serialnos[link];
-    vf->current_link=link;
-    return _fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,NULL);
-  }
-  return 0;
-}
-
-static int _set_link_number_preserve_pos(OggVorbis_File *vf,int link){
-  ogg_int64_t pos=vf->offset;
-  int ret=_set_link_number(vf,link);
-  if(ret)return ret;
-  _seek_helper(vf,pos);
-  if(pos<vf->offsets[link] || pos>=vf->offsets[link+1])
-    vf->ready_state=STREAMSET;
-  return 0;
-}
-
-/* last step of the OggVorbis_File initialization; get all the offset
-   positions.  Only called by the seekable initialization (local
-   stream storage is hacked slightly; pay attention to how that's
-   done) */
+/* last step of the OggVorbis_File initialization; get all the
+   vorbis_info structs and PCM positions.  Only called by the seekable
+   initialization (local stream storage is hacked slightly; pay
+   attention to how that's done) */
 
 /* this is void and does not propogate errors up because we want to be
    able to open and use damaged bitstreams as well as we can.  Just
    watch out for missing information for links in the OggVorbis_File
    struct */
-static void _prefetch_all_offsets(OggVorbis_File *vf, ogg_int64_t dataoffset){
+static void _prefetch_all_headers(OggVorbis_File *vf, ogg_int64_t dataoffset){
   ogg_page og={0,0,0,0};
   int i;
   ogg_int64_t ret;
   
+  vf->vi=_ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi));
+  vf->vc=_ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc));
   vf->dataoffsets=_ogg_malloc(vf->links*sizeof(*vf->dataoffsets));
   vf->pcmlengths=_ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths));
   
@@ -373,7 +324,7 @@
       /* seek to the location of the initial header */
 
       _seek_helper(vf,vf->offsets[i]);
-      if(_fetch_headers(vf,&vf->vi,&vf->vc,NULL,NULL)<0){
+      if(_fetch_headers(vf,vf->vi+i,vf->vc+i,NULL,NULL)<0){
     	vf->dataoffsets[i]=-1;
       }else{
 	vf->dataoffsets[i]=vf->offset;
@@ -407,7 +358,7 @@
 	ogg_stream_pagein(vf->os,&og);
 	while((result=ogg_stream_packetout(vf->os,&op))){
 	  if(result>0){ /* ignore holes */
-	    long thisblock=vorbis_packet_blocksize(&vf->vi,&op);
+	    long thisblock=vorbis_packet_blocksize(vf->vi+i,&op);
 	    if(lastblock!=-1)
 	      accumulated+=(lastblock+thisblock)>>2;
 	    lastblock=thisblock;
@@ -439,8 +390,8 @@
 	ret=_get_prev_page(vf,&og);
 	if(ret<0){
 	  /* this should not be possible */
-	  vorbis_info_clear(&vf->vi);
-	  vorbis_comment_clear(&vf->vc);
+	  vorbis_info_clear(vf->vi+i);
+	  vorbis_comment_clear(vf->vc+i);
 	  break;
 	}
 	if(ogg_page_granulepos(&og)!=-1){
@@ -454,28 +405,18 @@
   ogg_page_release(&og);
 }
 
-static int _make_decode_ready(OggVorbis_File *vf){
-  int i;
-  switch(vf->ready_state){
-  case OPENED:
-  case STREAMSET:
-    for(i=0;i<vf->links;i++)
-      if(vf->offsets[i+1]>=vf->offset)break;
-    if(i==vf->links)return -1;
-    i=_set_link_number_preserve_pos(vf,i);
-    if(i)return i;
-    /* fall through */
-  case LINKSET:
-    vf->vd=vorbis_dsp_create(&vf->vi);
-    vf->ready_state=INITSET;
-    vf->bittrack=0;
-    vf->samptrack=0;
-  case INITSET:
-    return 0;
-  default:
-    return -1;
-  }
-  
+static void _make_decode_ready(OggVorbis_File *vf){
+  if(vf->ready_state!=STREAMSET)return;
+  if(vf->seekable){
+    vorbis_synthesis_init(&vf->vd,vf->vi+vf->current_link);
+  }else{
+    vorbis_synthesis_init(&vf->vd,vf->vi);
+  }    
+  vorbis_block_init(&vf->vd,&vf->vb);
+  vf->ready_state=INITSET;
+  vf->bittrack=0;
+  vf->samptrack=0;
+  return;
 }
 
 static int _open_seekable2(OggVorbis_File *vf){
@@ -493,7 +434,7 @@
   /* We get the offset for the last page of the physical bitstream.
      Most OggVorbis files will contain a single logical bitstream */
   end=_get_prev_page(vf,&og);
-  if(end<0)return end;
+  if(end<0)return(end);
 
   /* more than one logical bitstream? */
   tempserialno=ogg_page_serialno(&og);
@@ -503,18 +444,25 @@
 
     /* Chained bitstream. Bisect-search each logical bitstream
        section.  Do so based on serial number only */
-    if(_bisect_forward_serialno(vf,0,0,end+1,serialno,0)<0)return OV_EREAD;
+    if(_bisect_forward_serialno(vf,0,0,end+1,serialno,0)<0)return(OV_EREAD);
 
   }else{
 
     /* Only one logical bitstream */
-    if(_bisect_forward_serialno(vf,0,end,end+1,serialno,0))return OV_EREAD;
+    if(_bisect_forward_serialno(vf,0,end,end+1,serialno,0))return(OV_EREAD);
 
   }
 
   /* the initial header memory is referenced by vf after; don't free it */
-  _prefetch_all_offsets(vf,dataoffset);
-  return ov_raw_seek(vf,0);
+  _prefetch_all_headers(vf,dataoffset);
+  return(ov_raw_seek(vf,0));
+}
+
+/* clear out the current logical bitstream decoder */ 
+static void _decode_clear(OggVorbis_File *vf){
+  vorbis_dsp_clear(&vf->vd);
+  vorbis_block_clear(&vf->vb);
+  vf->ready_state=OPENED;
 }
 
 /* fetch and process a packet.  Handles the case where we're at a
@@ -553,16 +501,28 @@
 	if(result>0){
 	  /* got a packet.  process it */
 	  granulepos=op.granulepos;
-	  if(!vorbis_dsp_synthesis(vf->vd,&op,1)){ /* lazy check for lazy
+	  if(!vorbis_synthesis(&vf->vb,&op,1)){ /* lazy check for lazy
 						      header handling.  The
 						      header packets aren't
 						      audio, so if/when we
 						      submit them,
 						      vorbis_synthesis will
 						      reject them */
-	    
-	    vf->samptrack+=vorbis_dsp_pcmout(vf->vd,NULL,0);
-	    vf->bittrack+=op.bytes*8;
+
+	    /* suck in the synthesis data and track bitrate */
+	    {
+	      int oldsamples=vorbis_synthesis_pcmout(&vf->vd,NULL);
+	      /* for proper use of libvorbis within libvorbisfile,
+                 oldsamples will always be zero. */
+	      if(oldsamples){
+		ret=OV_EFAULT;
+		goto cleanup;
+	      }
+
+	      vorbis_synthesis_blockin(&vf->vd,&vf->vb);
+	      vf->samptrack+=vorbis_synthesis_pcmout(&vf->vd,NULL)-oldsamples;
+	      vf->bittrack+=op.bytes*8;
+	    }
 	  
 	    /* update the pcm offset. */
 	    if(granulepos!=-1 && !op.e_o_s){
@@ -589,7 +549,7 @@
 					       here unless the stream
 					       is very broken */
 
-	      samples=vorbis_dsp_pcmout(vf->vd,NULL,0);
+	      samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
 	    
 	      granulepos-=samples;
 	      for(i=0;i<link;i++)
@@ -629,6 +589,11 @@
 	  }
 
 	  _decode_clear(vf);
+	  
+	  if(!vf->seekable){
+	    vorbis_info_clear(vf->vi);
+	    vorbis_comment_clear(vf->vc);
+	  }
 	}
       }
     }
@@ -646,7 +611,7 @@
     */
 
     if(vf->ready_state!=INITSET){ 
-      int link,ret;
+      int link;
 
       if(vf->ready_state<STREAMSET){
 	if(vf->seekable){
@@ -662,22 +627,24 @@
 				leave machine uninitialized */
 	    goto cleanup;
 	  }
-
+	  
 	  vf->current_link=link;
-	  ret=_fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,&og);
-	  if(ret) goto cleanup;
+	  
+	  ogg_stream_reset_serialno(vf->os,vf->current_serialno);
+	  vf->ready_state=STREAMSET;
 	  
 	}else{
 	  /* we're streaming */
 	  /* fetch the three header packets, build the info struct */
 	  
-	  int ret=_fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,&og);
+	  int ret=_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,&og);
 	  if(ret) goto cleanup;
 	  vf->current_link++;
+	  link=0;
 	}
       }
       
-      if(_make_decode_ready(vf)) return OV_EBADLINK;
+      _make_decode_ready(vf);
     }
     ogg_stream_pagein(vf->os,&og);
   }
@@ -690,7 +657,7 @@
 /* if, eg, 64 bit stdio is configured by default, this will build with
    fseek64 */
 static int _fseek64_wrap(FILE *f,ogg_int64_t off,int whence){
-  if(f==NULL)return -1;
+  if(f==NULL)return(-1);
   return fseek(f,off,whence);
 }
 
@@ -700,11 +667,6 @@
   int ret;
 
   memset(vf,0,sizeof(*vf));
-
-  /* Tremor assumes in multiple places that right shift of a signed
-     integer is an arithmetic shift */
-  if( (-1>>1) != -1) return OV_EIMPL;
-
   vf->datasource=f;
   vf->callbacks = callbacks;
 
@@ -727,15 +689,17 @@
   /* No seeking yet; Set up a 'single' (current) logical bitstream
      entry for partial open */
   vf->links=1;
+  vf->vi=_ogg_calloc(vf->links,sizeof(*vf->vi));
+  vf->vc=_ogg_calloc(vf->links,sizeof(*vf->vc));
   vf->os=ogg_stream_create(-1); /* fill in the serialno later */
 
   /* Try to fetch the headers, maintaining all the storage */
-  if((ret=_fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,NULL))<0){
+  if((ret=_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,NULL))<0){
     vf->datasource=NULL;
     ov_clear(vf);
   }else if(vf->ready_state < PARTOPEN)
     vf->ready_state=PARTOPEN;
-  return ret;
+  return(ret);
 }
 
 static int _ov_open2(OggVorbis_File *vf){
@@ -747,7 +711,7 @@
       vf->datasource=NULL;
       ov_clear(vf);
     }
-    return ret;
+    return(ret);
   }
   return 0;
 }
@@ -756,24 +720,33 @@
 /* clear out the OggVorbis_File struct */
 int ov_clear(OggVorbis_File *vf){
   if(vf){
-    vorbis_dsp_destroy(vf->vd);
-    vf->vd=0;
+    vorbis_block_clear(&vf->vb);
+    vorbis_dsp_clear(&vf->vd);
     ogg_stream_destroy(vf->os);
-    vorbis_info_clear(&vf->vi);
-    vorbis_comment_clear(&vf->vc);
+    
+    if(vf->vi && vf->links){
+      int i;
+      for(i=0;i<vf->links;i++){
+	vorbis_info_clear(vf->vi+i);
+	vorbis_comment_clear(vf->vc+i);
+      }
+      _ogg_free(vf->vi);
+      _ogg_free(vf->vc);
+    }
     if(vf->dataoffsets)_ogg_free(vf->dataoffsets);
     if(vf->pcmlengths)_ogg_free(vf->pcmlengths);
     if(vf->serialnos)_ogg_free(vf->serialnos);
     if(vf->offsets)_ogg_free(vf->offsets);
     ogg_sync_destroy(vf->oy);
 
-    if(vf->datasource)(vf->callbacks.close_func)(vf->datasource);
+    if(vf->datasource && vf->callbacks.close_func)
+      (vf->callbacks.close_func)(vf->datasource);
     memset(vf,0,sizeof(*vf));
   }
 #ifdef DEBUG_LEAKS
   _VDBG_dump();
 #endif
-  return 0;
+  return(0);
 }
 
 /* inspects the OggVorbis file and finds/documents all the logical
@@ -825,7 +798,7 @@
 }
   
 int ov_test_open(OggVorbis_File *vf){
-  if(vf->ready_state!=PARTOPEN)return OV_EINVAL;
+  if(vf->ready_state!=PARTOPEN)return(OV_EINVAL);
   return _ov_open2(vf);
 }
 
@@ -849,9 +822,9 @@
    vorbis_info structs */
 
 long ov_bitrate(OggVorbis_File *vf,int i){
-  if(vf->ready_state<OPENED)return OV_EINVAL;
-  if(i>=vf->links)return OV_EINVAL;
-  if(!vf->seekable && i!=0)return ov_bitrate(vf,0);
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
+  if(i>=vf->links)return(OV_EINVAL);
+  if(!vf->seekable && i!=0)return(ov_bitrate(vf,0));
   if(i<0){
     ogg_int64_t bits=0;
     int i;
@@ -861,24 +834,24 @@
      * gcc 3.x on x86 miscompiled this at optimisation level 2 and above,
      * so this is slightly transformed to make it work.
      */
-    return bits*1000/ov_time_total(vf,-1);
+    return(bits*1000/ov_time_total(vf,-1));
   }else{
     if(vf->seekable){
       /* return the actual bitrate */
-      return (vf->offsets[i+1]-vf->dataoffsets[i])*8000/ov_time_total(vf,i);
+      return((vf->offsets[i+1]-vf->dataoffsets[i])*8000/ov_time_total(vf,i));
     }else{
       /* return nominal if set */
-      if(vf->vi.bitrate_nominal>0){
-	return vf->vi.bitrate_nominal;
+      if(vf->vi[i].bitrate_nominal>0){
+	return vf->vi[i].bitrate_nominal;
       }else{
-	if(vf->vi.bitrate_upper>0){
-	  if(vf->vi.bitrate_lower>0){
-	    return (vf->vi.bitrate_upper+vf->vi.bitrate_lower)/2;
+	if(vf->vi[i].bitrate_upper>0){
+	  if(vf->vi[i].bitrate_lower>0){
+	    return (vf->vi[i].bitrate_upper+vf->vi[i].bitrate_lower)/2;
 	  }else{
-	    return vf->vi.bitrate_upper;
+	    return vf->vi[i].bitrate_upper;
 	  }
 	}
-	return OV_FALSE;
+	return(OV_FALSE);
       }
     }
   }
@@ -889,23 +862,24 @@
    EINVAL if stream is only partially open 
 */
 long ov_bitrate_instant(OggVorbis_File *vf){
+  int link=(vf->seekable?vf->current_link:0);
   long ret;
-  if(vf->ready_state<OPENED)return OV_EINVAL;
-  if(vf->samptrack==0)return OV_FALSE;
-  ret=vf->bittrack/vf->samptrack*vf->vi.rate;
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
+  if(vf->samptrack==0)return(OV_FALSE);
+  ret=vf->bittrack/vf->samptrack*vf->vi[link].rate;
   vf->bittrack=0;
   vf->samptrack=0;
-  return ret;
+  return(ret);
 }
 
 /* Guess */
 long ov_serialnumber(OggVorbis_File *vf,int i){
-  if(i>=vf->links)return ov_serialnumber(vf,vf->links-1);
-  if(!vf->seekable && i>=0)return ov_serialnumber(vf,-1);
+  if(i>=vf->links)return(ov_serialnumber(vf,vf->links-1));
+  if(!vf->seekable && i>=0)return(ov_serialnumber(vf,-1));
   if(i<0){
-    return vf->current_serialno;
+    return(vf->current_serialno);
   }else{
-    return vf->serialnos[i];
+    return(vf->serialnos[i]);
   }
 }
 
@@ -915,16 +889,16 @@
 	    or if stream is only partially open
 */
 ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i){
-  if(vf->ready_state<OPENED)return OV_EINVAL;
-  if(!vf->seekable || i>=vf->links)return OV_EINVAL;
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
+  if(!vf->seekable || i>=vf->links)return(OV_EINVAL);
   if(i<0){
     ogg_int64_t acc=0;
     int i;
     for(i=0;i<vf->links;i++)
       acc+=ov_raw_total(vf,i);
-    return acc;
+    return(acc);
   }else{
-    return vf->offsets[i+1]-vf->offsets[i];
+    return(vf->offsets[i+1]-vf->offsets[i]);
   }
 }
 
@@ -934,16 +908,16 @@
 	    length) or only partially open 
 */
 ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){
-  if(vf->ready_state<OPENED)return OV_EINVAL;
-  if(!vf->seekable || i>=vf->links)return OV_EINVAL;
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
+  if(!vf->seekable || i>=vf->links)return(OV_EINVAL);
   if(i<0){
     ogg_int64_t acc=0;
     int i;
     for(i=0;i<vf->links;i++)
       acc+=ov_pcm_total(vf,i);
-    return acc;
+    return(acc);
   }else{
-    return vf->pcmlengths[i*2+1];
+    return(vf->pcmlengths[i*2+1]);
   }
 }
 
@@ -953,16 +927,16 @@
 	    length) or only partially open 
 */
 ogg_int64_t ov_time_total(OggVorbis_File *vf,int i){
-  if(vf->ready_state<OPENED)return OV_EINVAL;
-  if(!vf->seekable || i>=vf->links)return OV_EINVAL;
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
+  if(!vf->seekable || i>=vf->links)return(OV_EINVAL);
   if(i<0){
     ogg_int64_t acc=0;
     int i;
     for(i=0;i<vf->links;i++)
       acc+=ov_time_total(vf,i);
-    return acc;
+    return(acc);
   }else{
-    return ((ogg_int64_t)vf->pcmlengths[i*2+1])*1000/vf->vi.rate;
+    return(((ogg_int64_t)vf->pcmlengths[i*2+1])*1000/vf->vi[i].rate);
   }
 }
 
@@ -978,11 +952,11 @@
   ogg_page og={0,0,0,0};
   ogg_packet op={0,0,0,0,0,0};
   
-  if(vf->ready_state<OPENED)return OV_EINVAL;
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
   if(!vf->seekable)
-    return OV_ENOSEEK; /* don't dump machine if we can't seek */
+    return(OV_ENOSEEK); /* don't dump machine if we can't seek */
 
-  if(pos<0 || pos>vf->end)return OV_EINVAL;
+  if(pos<0 || pos>vf->end)return(OV_EINVAL);
 
   /* don't yet clear out decoding machine (if it's initialized), in
      the case we're in the same link.  Restart the decode lapping, and
@@ -991,7 +965,7 @@
   vf->pcm_offset=-1;
   ogg_stream_reset_serialno(vf->os,
 			    vf->current_serialno); /* must set serialno */
-  vorbis_dsp_restart(vf->vd);
+  vorbis_synthesis_restart(&vf->vd);
     
   _seek_helper(vf,pos);
 
@@ -1014,7 +988,7 @@
     int lastblock=0;
     int accblock=0;
     int thisblock;
-    int eosflag;
+    int eosflag=0;
 
     work_os=ogg_stream_create(vf->current_serialno); /* get the memory ready */
     while(1){
@@ -1024,8 +998,8 @@
       
 	if(result>0){
 
-	  if(vf->vi.codec_setup){
-	    thisblock=vorbis_packet_blocksize(&vf->vi,&op);
+	  if(vf->vi[vf->current_link].codec_setup){
+	    thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
 	    if(thisblock<0){
 	      ogg_stream_packetout(vf->os,NULL);
 	      thisblock=0;
@@ -1065,7 +1039,7 @@
 	break;
       }
       
-      /* did we just grab a page from other than current link? */
+      /* has our decoding just traversed a bitstream boundary? */
       if(vf->ready_state>=STREAMSET)
 	if(vf->current_serialno!=ogg_page_serialno(&og)){
 	  _decode_clear(vf); /* clear out stream state */
@@ -1082,14 +1056,11 @@
 	  goto seek_error; /* sign of a bogus stream.  error out,
 			      leave machine uninitialized */
  
-	/* need to initialize machine to this link */
-	{
-	  int ret=_set_link_number_preserve_pos(vf,link);
-	  if(ret) goto seek_error;
-	}
+	vf->current_link=link;
+	
 	ogg_stream_reset_serialno(vf->os,vf->current_serialno);
 	ogg_stream_reset_serialno(work_os,vf->current_serialno); 
-	
+	vf->ready_state=STREAMSET;
 	
       }
     
@@ -1108,7 +1079,7 @@
   ogg_stream_destroy(work_os);
   vf->bittrack=0;
   vf->samptrack=0;
-  return 0;
+  return(0);
 
  seek_error:
   ogg_packet_release(&op);
@@ -1134,9 +1105,9 @@
   ogg_page og={0,0,0,0};
   ogg_packet op={0,0,0,0,0,0};
 
-  if(vf->ready_state<OPENED)return OV_EINVAL;
-  if(!vf->seekable)return OV_ENOSEEK;
-  if(pos<0 || pos>total)return OV_EINVAL;
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
+  if(!vf->seekable)return(OV_ENOSEEK);
+  if(pos<0 || pos>total)return(OV_EINVAL);
  
   /* which bitstream section does this pcm offset occur in? */
   for(link=vf->links-1;link>=0;link--){
@@ -1144,16 +1115,6 @@
     if(pos>=total)break;
   }
 
-
-  if(link!=vf->current_link){
-    int ret=_set_link_number(vf,link);
-    if(ret) goto seek_error;
-  }else{
-    vorbis_dsp_restart(vf->vd);
-  }
-
-  ogg_stream_reset_serialno(vf->os,vf->serialnos[link]);
-
   /* search within the logical bitstream for the page with the highest
      pcm_pos preceeding (or equal to) pos.  There is a danger here;
      missing pages or incorrect frame number information in the
@@ -1236,9 +1197,22 @@
       
       if(_get_next_page(vf,&og,-1)<0){
 	ogg_page_release(&og);
-	return OV_EOF; /* shouldn't happen */
+	return(OV_EOF); /* shouldn't happen */
       }
 
+      if(link!=vf->current_link){
+	/* Different link; dump entire decode machine */
+	_decode_clear(vf);  
+	
+	vf->current_link=link;
+	vf->current_serialno=ogg_page_serialno(&og);
+	vf->ready_state=STREAMSET;
+	
+      }else{
+	vorbis_synthesis_restart(&vf->vd);
+      }
+
+      ogg_stream_reset_serialno(vf->os,vf->current_serialno);
       ogg_stream_pagein(vf->os,&og);
 
       /* pull out all but last packet; the one with granulepos */
@@ -1287,7 +1261,7 @@
 
   ogg_page_release(&og);
   ogg_packet_release(&op);
-  return 0;
+  return(0);
   
  seek_error:
 
@@ -1308,8 +1282,8 @@
   ogg_page og={0,0,0,0};
   int thisblock,lastblock=0;
   int ret=ov_pcm_seek_page(vf,pos);
-  if(ret<0)return ret;
-  if(_make_decode_ready(vf))return OV_EBADLINK;
+  if(ret<0)return(ret);
+  _make_decode_ready(vf);
 
   /* discard leading packets we don't need for the lapping of the
      position we want; don't decode them */
@@ -1318,7 +1292,7 @@
 
     int ret=ogg_stream_packetpeek(vf->os,&op);
     if(ret>0){
-      thisblock=vorbis_packet_blocksize(&vf->vi,&op);
+      thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
       if(thisblock<0){
 	ogg_stream_packetout(vf->os,NULL);
 	continue; /* non audio packet */
@@ -1326,13 +1300,14 @@
       if(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2;
       
       if(vf->pcm_offset+((thisblock+
-			  vorbis_info_blocksize(&vf->vi,1))>>2)>=pos)break;
+			  vorbis_info_blocksize(vf->vi,1))>>2)>=pos)break;
       
       /* remove the packet from packet queue and track its granulepos */
       ogg_stream_packetout(vf->os,NULL);
-      vorbis_dsp_synthesis(vf->vd,&op,0);  /* set up a vb with
-					      only tracking, no
-					      pcm_decode */
+      vorbis_synthesis(&vf->vb,&op,0);  /* set up a vb with
+					   only tracking, no
+					   pcm_decode */
+      vorbis_synthesis_blockin(&vf->vd,&vf->vb); 
       
       /* end of logical stream case is hard, especially with exact
 	 length positioning. */
@@ -1356,7 +1331,7 @@
       if(vf->current_serialno!=ogg_page_serialno(&og))_decode_clear(vf);
       
       if(vf->ready_state<STREAMSET){
-	int link,ret;
+	int link;
 	
 	vf->current_serialno=ogg_page_serialno(&og);
 	for(link=0;link<vf->links;link++)
@@ -1364,14 +1339,13 @@
 	if(link==vf->links){
 	  ogg_page_release(&og);
 	  ogg_packet_release(&op);
-	  return OV_EBADLINK;
+	  return(OV_EBADLINK);
 	}
-
-
 	vf->current_link=link;
-	ret=_fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,&og);
-	if(ret) return ret;
-	if(_make_decode_ready(vf))return OV_EBADLINK;
+	
+	ogg_stream_reset_serialno(vf->os,vf->current_serialno); 
+	vf->ready_state=STREAMSET;      
+	_make_decode_ready(vf);
 	lastblock=0;
       }
 
@@ -1385,10 +1359,10 @@
      logical bitstream boundary with abandon is OK. */
   while(vf->pcm_offset<pos){
     ogg_int64_t target=pos-vf->pcm_offset;
-    long samples=vorbis_dsp_pcmout(vf->vd,NULL,0);
+    long samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
 
     if(samples>target)samples=target;
-    vorbis_dsp_read(vf->vd,samples);
+    vorbis_synthesis_read(&vf->vd,samples);
     vf->pcm_offset+=samples;
     
     if(samples<target)
@@ -1410,9 +1384,9 @@
   ogg_int64_t pcm_total=ov_pcm_total(vf,-1);
   ogg_int64_t time_total=ov_time_total(vf,-1);
 
-  if(vf->ready_state<OPENED)return OV_EINVAL;
-  if(!vf->seekable)return OV_ENOSEEK;
-  if(milliseconds<0 || milliseconds>time_total)return OV_EINVAL;
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
+  if(!vf->seekable)return(OV_ENOSEEK);
+  if(milliseconds<0 || milliseconds>time_total)return(OV_EINVAL);
   
   /* which bitstream section does this time offset occur in? */
   for(link=vf->links-1;link>=0;link--){
@@ -1423,11 +1397,8 @@
 
   /* enough information to convert time offset to pcm offset */
   {
-    int ret=_set_link_number(vf,link);
-    if(ret)return ret;
-    return 
-      ov_pcm_seek(vf,pcm_total+(milliseconds-time_total)*
-		  vf->vi.rate/1000);
+    ogg_int64_t target=pcm_total+(milliseconds-time_total)*vf->vi[link].rate/1000;
+    return(ov_pcm_seek(vf,target));
   }
 }
 
@@ -1440,9 +1411,9 @@
   ogg_int64_t pcm_total=ov_pcm_total(vf,-1);
   ogg_int64_t time_total=ov_time_total(vf,-1);
 
-  if(vf->ready_state<OPENED)return OV_EINVAL;
-  if(!vf->seekable)return OV_ENOSEEK;
-  if(milliseconds<0 || milliseconds>time_total)return OV_EINVAL;
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
+  if(!vf->seekable)return(OV_ENOSEEK);
+  if(milliseconds<0 || milliseconds>time_total)return(OV_EINVAL);
   
   /* which bitstream section does this time offset occur in? */
   for(link=vf->links-1;link>=0;link--){
@@ -1453,25 +1424,22 @@
 
   /* enough information to convert time offset to pcm offset */
   {
-    int ret=_set_link_number(vf,link);
-    if(ret)return ret;
-    return 
-      ov_pcm_seek_page(vf,pcm_total+(milliseconds-time_total)*
-		       vf->vi.rate/1000);
+    ogg_int64_t target=pcm_total+(milliseconds-time_total)*vf->vi[link].rate/1000;
+    return(ov_pcm_seek_page(vf,target));
   }
 }
 
 /* tell the current stream offset cursor.  Note that seek followed by
    tell will likely not give the set offset due to caching */
 ogg_int64_t ov_raw_tell(OggVorbis_File *vf){
-  if(vf->ready_state<OPENED)return OV_EINVAL;
-  return vf->offset;
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
+  return(vf->offset);
 }
 
 /* return PCM offset (sample) of next PCM sample to be read */
 ogg_int64_t ov_pcm_tell(OggVorbis_File *vf){
-  if(vf->ready_state<OPENED)return OV_EINVAL;
-  return vf->pcm_offset;
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
+  return(vf->pcm_offset);
 }
 
 /* return time offset (milliseconds) of next PCM sample to be read */
@@ -1480,7 +1448,7 @@
   ogg_int64_t pcm_total=0;
   ogg_int64_t time_total=0;
   
-  if(vf->ready_state<OPENED)return OV_EINVAL;
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
   if(vf->seekable){
     pcm_total=ov_pcm_total(vf,-1);
     time_total=ov_time_total(vf,-1);
@@ -1493,7 +1461,7 @@
     }
   }
 
-  return time_total+(1000*vf->pcm_offset-pcm_total)/vf->vi.rate;
+  return(time_total+(1000*vf->pcm_offset-pcm_total)/vf->vi[link].rate);
 }
 
 /*  link:   -1) return the vorbis_info struct for the bitstream section
@@ -1506,25 +1474,37 @@
 
 vorbis_info *ov_info(OggVorbis_File *vf,int link){
   if(vf->seekable){
-    if(link>=vf->links)return NULL;
-    if(link>=0){
-      int ret=_set_link_number_preserve_pos(vf,link);
-      if(ret)return NULL;
-    }
+    if(link<0)
+      if(vf->ready_state>=STREAMSET)
+	return vf->vi+vf->current_link;
+      else
+      return vf->vi;
+    else
+      if(link>=vf->links)
+	return NULL;
+      else
+	return vf->vi+link;
+  }else{
+    return vf->vi;
   }
-  return &vf->vi;
 }
 
 /* grr, strong typing, grr, no templates/inheritence, grr */
 vorbis_comment *ov_comment(OggVorbis_File *vf,int link){
   if(vf->seekable){
-    if(link>=vf->links)return NULL;
-    if(link>=0){
-      int ret=_set_link_number_preserve_pos(vf,link);
-      if(ret)return NULL;
-    }
+    if(link<0)
+      if(vf->ready_state>=STREAMSET)
+	return vf->vc+vf->current_link;
+      else
+	return vf->vc;
+    else
+      if(link>=vf->links)
+	return NULL;
+      else
+	return vf->vc+link;
+  }else{
+    return vf->vc;
   }
-  return &vf->vc;
 }
 
 /* up to this point, everything could more or less hide the multiple
@@ -1554,36 +1534,54 @@
 
 	    *section) set to the logical bitstream number */
 
-long ov_read(OggVorbis_File *vf,void *buffer,int bytes_req,int *bitstream){
+long ov_read(OggVorbis_File *vf,char *buffer,int bytes_req,int *bitstream){
+  int i,j;
 
+  ogg_int32_t **pcm;
   long samples;
-  long channels;
 
-  if(vf->ready_state<OPENED)return OV_EINVAL;
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
 
   while(1){
     if(vf->ready_state==INITSET){
-      channels=vf->vi.channels;
-      samples=vorbis_dsp_pcmout(vf->vd,buffer,(bytes_req>>1)/channels);
-      if(samples){
-	if(samples>0){
-	  vorbis_dsp_read(vf->vd,samples);
-	  vf->pcm_offset+=samples;
-	  if(bitstream)*bitstream=vf->current_link;
-	  return samples*2*channels;
-	}
-	return samples;
-      }
+      samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
+      if(samples)break;
     }
 
     /* suck in another packet */
     {
       int ret=_fetch_and_process_packet(vf,1,1);
       if(ret==OV_EOF)
-	return 0;
+	return(0);
       if(ret<=0)
-	return ret;
+	return(ret);
     }
 
   }
+
+  if(samples>0){
+  
+    /* yay! proceed to pack data into the byte buffer */
+    
+    long channels=ov_info(vf,-1)->channels;
+
+    if(samples>(bytes_req/(2*channels)))
+      samples=bytes_req/(2*channels);      
+    
+    for(i=0;i<channels;i++) { /* It's faster in this order */
+      ogg_int32_t *src=pcm[i];
+      short *dest=((short *)buffer)+i;
+      for(j=0;j<samples;j++) {
+        *dest=CLIP_TO_15(src[j]>>9);
+        dest+=channels;
+      }
+    }
+    
+    vorbis_synthesis_read(&vf->vd,samples);
+    vf->pcm_offset+=samples;
+    if(bitstream)*bitstream=vf->current_link;
+    return(samples*2*channels);
+  }else{
+    return(samples);
+  }
 }