While reviewing a patch to port Tremor r17541, noticed that the
[platform/upstream/libvorbis.git] / lib / codebook.c
index d55baa2..d71525b 100644 (file)
 /********************************************************************
  *                                                                  *
- * THIS FILE IS PART OF THE Ogg Vorbis SOFTWARE CODEC SOURCE CODE.  *
- * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
- * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE.    *
- * PLEASE READ THESE TERMS DISTRIBUTING.                            *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
  *                                                                  *
- * THE OggSQUISH SOURCE CODE IS (C) COPYRIGHT 1994-2000             *
- * by Monty <monty@xiph.org> and The XIPHOPHORUS Company            *
- * http://www.xiph.org/                                             *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009             *
+ * by the Xiph.Org Foundation http://www.xiph.org/                  *
  *                                                                  *
  ********************************************************************
 
  function: basic codebook pack/unpack/code/decode operations
- last mod: $Id: codebook.c,v 1.7 2000/02/06 13:40:39 xiphmont Exp $
+ last mod: $Id$
 
  ********************************************************************/
 
 #include <stdlib.h>
+#include <string.h>
 #include <math.h>
+#include <ogg/ogg.h>
 #include "vorbis/codec.h"
-#include "vorbis/codebook.h"
-#include "bitwise.h"
-#include "bookinternal.h"
-
-/**** pack/unpack helpers ******************************************/
-static int ilog(unsigned int v){
-  int ret=0;
-  while(v){
-    ret++;
-    v>>=1;
-  }
-  return(ret);
-}
-
-/* code that packs the 24 bit float can be found in vq/bookutil.c */
-
-static double _float24_unpack(long val){
-  double mant=val&0x3ffff;
-  double sign=val&0x800000;
-  double exp =(val&0x7c0000)>>18;
-  if(sign)mant= -mant;
-  return(ldexp(mant,exp-17-VQ_FEXP_BIAS));
-}
-
-/* given a list of word lengths, generate a list of codewords.  Works
-   for length ordered or unordered, always assigns the lowest valued
-   codewords first */
-long *_make_words(long *l,long n){
-  long i,j;
-  long marker[33];
-  long *r=malloc(n*sizeof(long));
-  memset(marker,0,sizeof(marker));
-
-  for(i=0;i<n;i++){
-    long length=l[i];
-    long entry=marker[l[i]];
-
-    /* when we claim a node for an entry, we also claim the nodes
-       below it (pruning off the imagined tree that may have dangled
-       from it) as well as blocking the use of any nodes directly
-       above for leaves */
-
-    /* update ourself */
-    if(length<32 && (entry>>length)){
-      /* error condition; the lengths must specify an overpopulated tree */
-      free(r);
-      return(NULL);
-    }
-    r[i]=entry;
-    
-    /* Look to see if the next shorter marker points to the node
-       above. if so, update it and repeat.  */
-    {
-      for(j=length;j>0;j--){
-
-       if(marker[j]&1){
-         /* have to jump branches */
-         if(j==1)
-           marker[1]++;
-         else
-           marker[j]=marker[j-1]<<1;
-         break; /* invariant says next upper marker would already
-                   have been moved if it was on the same path */
-       }
-       marker[j]++;
-      }
-    }
-
-    /* prune the tree; the implicit invariant says all the longer
-       markers were dangling from our just-taken node.  Dangle them
-       from our *new* node. */
-    for(j=length+1;j<33;j++)
-      marker[j]=marker[j-1]<<1;
-  }
-
-  /* bitreverse the words because our bitwise packer/unpacker is LSb
-     endian */
-  for(i=0;i<n;i++){
-    long temp=0;
-    for(j=0;j<l[i];j++){
-      temp<<=1;
-      temp|=(r[i]>>j)&1;
-    }
-    r[i]=temp;
-  }
-
-  return(r);
-}
-
-/* build the decode helper tree from the codewords */
-decode_aux *_make_decode_tree(codebook *c){
-  const static_codebook *s=c->c;
-  long top=0,i,j;
-  decode_aux *t=malloc(sizeof(decode_aux));
-  long *ptr0=t->ptr0=calloc(c->entries*2,sizeof(long));
-  long *ptr1=t->ptr1=calloc(c->entries*2,sizeof(long));
-  long *codelist=_make_words(s->lengthlist,s->entries);
-
-  if(codelist==NULL)return(NULL);
-  t->aux=c->entries*2;
-
-  for(i=0;i<c->entries;i++){
-    long ptr=0;
-    for(j=0;j<s->lengthlist[i]-1;j++){
-      int bit=(codelist[i]>>j)&1;
-      if(!bit){
-       if(!ptr0[ptr])
-         ptr0[ptr]= ++top;
-       ptr=ptr0[ptr];
-      }else{
-       if(!ptr1[ptr])
-         ptr1[ptr]= ++top;
-       ptr=ptr1[ptr];
-      }
-    }
-    if(!((codelist[i]>>j)&1))
-      ptr0[ptr]=-i;
-    else
-      ptr1[ptr]=-i;
-  }
-  free(codelist);
-  return(t);
-}
-
-/* unpack the quantized list of values for encode/decode ***********/
-static double *_book_unquantize(const static_codebook *b){
-  long j,k;
-  double mindel=_float24_unpack(b->q_min);
-  double delta=_float24_unpack(b->q_delta);
-  double *r=malloc(sizeof(double)*b->entries*b->dim);
-
-  for(j=0;j<b->entries;j++){
-    double last=0.;
-    for(k=0;k<b->dim;k++){
-      double val=b->quantlist[j*b->dim+k]*delta+last+mindel;
-      r[j*b->dim+k]=val;
-      if(b->q_sequencep)last=val;
-    }
-  }
-  return(r);
-}
-
-void vorbis_staticbook_clear(static_codebook *b){
-  if(b->quantlist)free(b->quantlist);
-  if(b->lengthlist)free(b->lengthlist);
-  if(b->encode_tree){
-    free(b->encode_tree->ptr0);
-    free(b->encode_tree->ptr1);
-    free(b->encode_tree->p);
-    free(b->encode_tree->q);
-    memset(b->encode_tree,0,sizeof(encode_aux));
-    free(b->encode_tree);
-  }
-  memset(b,0,sizeof(static_codebook));
-}
-
-void vorbis_book_clear(codebook *b){
-  /* static book is not cleared; we're likely called on the lookup and
-     the static codebook belongs to the info struct */
-  if(b->decode_tree){
-    free(b->decode_tree->ptr0);
-    free(b->decode_tree->ptr1);
-    memset(b->decode_tree,0,sizeof(decode_aux));
-    free(b->decode_tree);
-  }
-  if(b->valuelist)free(b->valuelist);
-  if(b->codelist)free(b->codelist);
-  memset(b,0,sizeof(codebook));
-}
-
-int vorbis_book_init_encode(codebook *c,const static_codebook *s){
-  memset(c,0,sizeof(codebook));
-  c->c=s;
-  c->entries=s->entries;
-  c->dim=s->dim;
-  c->codelist=_make_words(s->lengthlist,s->entries);
-  c->valuelist=_book_unquantize(s);
-  return(0);
-}
-
-int vorbis_book_init_decode(codebook *c,const static_codebook *s){
-  memset(c,0,sizeof(codebook));
-  c->c=s;
-  c->entries=s->entries;
-  c->dim=s->dim;
-  c->valuelist=_book_unquantize(s);
-  c->decode_tree=_make_decode_tree(c);
-  if(c->decode_tree==NULL)goto err_out;
-  return(0);
- err_out:
-  vorbis_book_clear(c);
-  return(-1);
-}
+#include "codebook.h"
+#include "scales.h"
+#include "misc.h"
+#include "os.h"
 
 /* packs the given codebook into the bitstream **************************/
 
@@ -220,377 +32,448 @@ int vorbis_staticbook_pack(const static_codebook *c,oggpack_buffer *opb){
   int ordered=0;
 
   /* first the basic parameters */
-  _oggpack_write(opb,0x564342,24);
-  _oggpack_write(opb,c->dim,16);
-  _oggpack_write(opb,c->entries,24);
+  oggpack_write(opb,0x564342,24);
+  oggpack_write(opb,c->dim,16);
+  oggpack_write(opb,c->entries,24);
 
   /* pack the codewords.  There are two packings; length ordered and
      length random.  Decide between the two now. */
-  
+
   for(i=1;i<c->entries;i++)
-    if(c->lengthlist[i]<c->lengthlist[i-1])break;
+    if(c->lengthlist[i-1]==0 || c->lengthlist[i]<c->lengthlist[i-1])break;
   if(i==c->entries)ordered=1;
-  
+
   if(ordered){
     /* length ordered.  We only need to say how many codewords of
        each length.  The actual codewords are generated
        deterministically */
 
     long count=0;
-    _oggpack_write(opb,1,1);  /* ordered */
-    _oggpack_write(opb,c->lengthlist[0]-1,5); /* 1 to 32 */
+    oggpack_write(opb,1,1);  /* ordered */
+    oggpack_write(opb,c->lengthlist[0]-1,5); /* 1 to 32 */
 
     for(i=1;i<c->entries;i++){
       long this=c->lengthlist[i];
       long last=c->lengthlist[i-1];
       if(this>last){
-       for(j=last;j<this;j++){
-         _oggpack_write(opb,i-count,ilog(c->entries-count));
-         count=i;
-       }
+        for(j=last;j<this;j++){
+          oggpack_write(opb,i-count,_ilog(c->entries-count));
+          count=i;
+        }
       }
     }
-    _oggpack_write(opb,i-count,ilog(c->entries-count));
+    oggpack_write(opb,i-count,_ilog(c->entries-count));
 
   }else{
     /* length random.  Again, we don't code the codeword itself, just
        the length.  This time, though, we have to encode each length */
-    _oggpack_write(opb,0,1);   /* unordered */
+    oggpack_write(opb,0,1);   /* unordered */
+
+    /* algortihmic mapping has use for 'unused entries', which we tag
+       here.  The algorithmic mapping happens as usual, but the unused
+       entry has no codeword. */
     for(i=0;i<c->entries;i++)
-      _oggpack_write(opb,c->lengthlist[i]-1,5);
+      if(c->lengthlist[i]==0)break;
+
+    if(i==c->entries){
+      oggpack_write(opb,0,1); /* no unused entries */
+      for(i=0;i<c->entries;i++)
+        oggpack_write(opb,c->lengthlist[i]-1,5);
+    }else{
+      oggpack_write(opb,1,1); /* we have unused entries; thus we tag */
+      for(i=0;i<c->entries;i++){
+        if(c->lengthlist[i]==0){
+          oggpack_write(opb,0,1);
+        }else{
+          oggpack_write(opb,1,1);
+          oggpack_write(opb,c->lengthlist[i]-1,5);
+        }
+      }
+    }
   }
 
   /* is the entry number the desired return value, or do we have a
-     mapping? */
-  if(c->quantlist){
-    /* we have a mapping.  bundle it out. */
-    _oggpack_write(opb,1,1);
+     mapping? If we have a mapping, what type? */
+  oggpack_write(opb,c->maptype,4);
+  switch(c->maptype){
+  case 0:
+    /* no mapping */
+    break;
+  case 1:case 2:
+    /* implicitly populated value mapping */
+    /* explicitly populated value mapping */
+
+    if(!c->quantlist){
+      /* no quantlist?  error */
+      return(-1);
+    }
 
     /* values that define the dequantization */
-    _oggpack_write(opb,c->q_min,24);
-    _oggpack_write(opb,c->q_delta,24);
-    _oggpack_write(opb,c->q_quant-1,4);
-    _oggpack_write(opb,c->q_sequencep,1);
+    oggpack_write(opb,c->q_min,32);
+    oggpack_write(opb,c->q_delta,32);
+    oggpack_write(opb,c->q_quant-1,4);
+    oggpack_write(opb,c->q_sequencep,1);
 
-    /* quantized values */
-    for(i=0;i<c->entries*c->dim;i++)
-      _oggpack_write(opb,c->quantlist[i],c->q_quant);
+    {
+      int quantvals;
+      switch(c->maptype){
+      case 1:
+        /* a single column of (c->entries/c->dim) quantized values for
+           building a full value list algorithmically (square lattice) */
+        quantvals=_book_maptype1_quantvals(c);
+        break;
+      case 2:
+        /* every value (c->entries*c->dim total) specified explicitly */
+        quantvals=c->entries*c->dim;
+        break;
+      default: /* NOT_REACHABLE */
+        quantvals=-1;
+      }
 
-  }else{
-    /* no mapping. */
-    _oggpack_write(opb,0,1);
+      /* quantized values */
+      for(i=0;i<quantvals;i++)
+        oggpack_write(opb,labs(c->quantlist[i]),c->q_quant);
+
+    }
+    break;
+  default:
+    /* error case; we don't have any other map types now */
+    return(-1);
   }
-  
+
   return(0);
 }
 
 /* unpacks a codebook from the packet buffer into the codebook struct,
    readies the codebook auxiliary structures for decode *************/
-int vorbis_staticbook_unpack(oggpack_buffer *opb,static_codebook *s){
+static_codebook *vorbis_staticbook_unpack(oggpack_buffer *opb){
   long i,j;
-  memset(s,0,sizeof(static_codebook));
+  static_codebook *s=_ogg_calloc(1,sizeof(*s));
+  s->allocedp=1;
 
   /* make sure alignment is correct */
-  if(_oggpack_read(opb,24)!=0x564342)goto _eofout;
+  if(oggpack_read(opb,24)!=0x564342)goto _eofout;
 
   /* first the basic parameters */
-  s->dim=_oggpack_read(opb,16);
-  s->entries=_oggpack_read(opb,24);
+  s->dim=oggpack_read(opb,16);
+  s->entries=oggpack_read(opb,24);
   if(s->entries==-1)goto _eofout;
 
+  if(_ilog(s->dim)+_ilog(s->entries)>24)goto _eofout;
+
   /* codeword ordering.... length ordered or unordered? */
-  switch(_oggpack_read(opb,1)){
-  case 0:
+  switch((int)oggpack_read(opb,1)){
+  case 0:{
+    long unused;
+    /* allocated but unused entries? */
+    unused=oggpack_read(opb,1);
+    if((s->entries*(unused?1:5)+7)>>3>opb->storage-oggpack_bytes(opb))
+      goto _eofout;
     /* unordered */
-    s->lengthlist=malloc(sizeof(long)*s->entries);
-    for(i=0;i<s->entries;i++){
-      long num=_oggpack_read(opb,5);
-      if(num==-1)goto _eofout;
-      s->lengthlist[i]=num+1;
+    s->lengthlist=_ogg_malloc(sizeof(*s->lengthlist)*s->entries);
+
+    /* allocated but unused entries? */
+    if(unused){
+      /* yes, unused entries */
+
+      for(i=0;i<s->entries;i++){
+        if(oggpack_read(opb,1)){
+          long num=oggpack_read(opb,5);
+          if(num==-1)goto _eofout;
+          s->lengthlist[i]=num+1;
+        }else
+          s->lengthlist[i]=0;
+      }
+    }else{
+      /* all entries used; no tagging */
+      for(i=0;i<s->entries;i++){
+        long num=oggpack_read(opb,5);
+        if(num==-1)goto _eofout;
+        s->lengthlist[i]=num+1;
+      }
     }
-    
+
     break;
+  }
   case 1:
     /* ordered */
     {
-      long length=_oggpack_read(opb,5)+1;
-      s->lengthlist=malloc(sizeof(long)*s->entries);
-      
+      long length=oggpack_read(opb,5)+1;
+      if(length==0)goto _eofout;
+      s->lengthlist=_ogg_malloc(sizeof(*s->lengthlist)*s->entries);
+
       for(i=0;i<s->entries;){
-       long num=_oggpack_read(opb,ilog(s->entries-i));
-       if(num==-1)goto _eofout;
-       for(j=0;j<num;j++,i++)
-         s->lengthlist[i]=length;
-       length++;
+        long num=oggpack_read(opb,_ilog(s->entries-i));
+        if(num==-1)goto _eofout;
+        if(length>32 || num>s->entries-i ||
+           (num>0 && (num-1)>>(length-1)>1)){
+          goto _errout;
+        }
+        if(length>32)goto _errout;
+        for(j=0;j<num;j++,i++)
+          s->lengthlist[i]=length;
+        length++;
       }
     }
     break;
   default:
     /* EOF */
-    return(-1);
+    goto _eofout;
   }
-  
+
   /* Do we have a mapping to unpack? */
-  if(_oggpack_read(opb,1)){
+  switch((s->maptype=oggpack_read(opb,4))){
+  case 0:
+    /* no mapping */
+    break;
+  case 1: case 2:
+    /* implicitly populated value mapping */
+    /* explicitly populated value mapping */
 
-    /* values that define the dequantization */
-    s->q_min=_oggpack_read(opb,24);
-    s->q_delta=_oggpack_read(opb,24);
-    s->q_quant=_oggpack_read(opb,4)+1;
-    s->q_sequencep=_oggpack_read(opb,1);
-
-    /* quantized values */
-    s->quantlist=malloc(sizeof(double)*s->entries*s->dim);
-    for(i=0;i<s->entries*s->dim;i++)
-      s->quantlist[i]=_oggpack_read(opb,s->q_quant);
-    if(s->quantlist[i-1]==-1)goto _eofout;
+    s->q_min=oggpack_read(opb,32);
+    s->q_delta=oggpack_read(opb,32);
+    s->q_quant=oggpack_read(opb,4)+1;
+    s->q_sequencep=oggpack_read(opb,1);
+    if(s->q_sequencep==-1)goto _eofout;
+
+    {
+      int quantvals=0;
+      switch(s->maptype){
+      case 1:
+        quantvals=(s->dim==0?0:_book_maptype1_quantvals(s));
+        break;
+      case 2:
+        quantvals=s->entries*s->dim;
+        break;
+      }
+
+      /* quantized values */
+      if((quantvals*s->q_quant+7>>3)>opb->storage-oggpack_bytes(opb))
+        goto _eofout;
+      s->quantlist=_ogg_malloc(sizeof(*s->quantlist)*quantvals);
+      for(i=0;i<quantvals;i++)
+        s->quantlist[i]=oggpack_read(opb,s->q_quant);
+
+      if(quantvals&&s->quantlist[quantvals-1]==-1)goto _eofout;
+    }
+    break;
+  default:
+    goto _errout;
   }
 
   /* all set */
-  return(0);
+  return(s);
 
  _errout:
  _eofout:
-  vorbis_staticbook_clear(s);
-  return(-1); 
+  vorbis_staticbook_destroy(s);
+  return(NULL);
 }
 
-/* returns the number of bits ***************************************/
+/* returns the number of bits ************************************************/
 int vorbis_book_encode(codebook *book, int a, oggpack_buffer *b){
-  _oggpack_write(b,book->codelist[a],book->c->lengthlist[a]);
+  if(a<0 || a>=book->c->entries)return(0);
+  oggpack_write(b,book->codelist[a],book->c->lengthlist[a]);
   return(book->c->lengthlist[a]);
 }
 
-/* returns the number of bits and *modifies a* to the residual error *****/
-int vorbis_book_encodev(codebook *book, double *a, oggpack_buffer *b){
-  encode_aux *t=book->c->encode_tree;
-  int dim=book->dim;
-  int ptr=0,k;
-
-  while(1){
-    double c=0.;
-    double *p=book->valuelist+t->p[ptr];
-    double *q=book->valuelist+t->q[ptr];
-    
-    for(k=0;k<dim;k++)
-      c+=(p[k]-q[k])*(a[k]-(p[k]+q[k])*.5);
-
-    if(c>0.) /* in A */
-      ptr= -t->ptr0[ptr];
-    else     /* in B */
-      ptr= -t->ptr1[ptr];
-    if(ptr<=0)break;
+/* the 'eliminate the decode tree' optimization actually requires the
+   codewords to be MSb first, not LSb.  This is an annoying inelegancy
+   (and one of the first places where carefully thought out design
+   turned out to be wrong; Vorbis II and future Ogg codecs should go
+   to an MSb bitpacker), but not actually the huge hit it appears to
+   be.  The first-stage decode table catches most words so that
+   bitreverse is not in the main execution path. */
+
+static ogg_uint32_t bitreverse(ogg_uint32_t x){
+  x=    ((x>>16)&0x0000ffff) | ((x<<16)&0xffff0000);
+  x=    ((x>> 8)&0x00ff00ff) | ((x<< 8)&0xff00ff00);
+  x=    ((x>> 4)&0x0f0f0f0f) | ((x<< 4)&0xf0f0f0f0);
+  x=    ((x>> 2)&0x33333333) | ((x<< 2)&0xcccccccc);
+  return((x>> 1)&0x55555555) | ((x<< 1)&0xaaaaaaaa);
+}
+
+STIN long decode_packed_entry_number(codebook *book, oggpack_buffer *b){
+  int  read=book->dec_maxlength;
+  long lo,hi;
+  long lok = oggpack_look(b,book->dec_firsttablen);
+
+  if (lok >= 0) {
+    long entry = book->dec_firsttable[lok];
+    if(entry&0x80000000UL){
+      lo=(entry>>15)&0x7fff;
+      hi=book->used_entries-(entry&0x7fff);
+    }else{
+      oggpack_adv(b, book->dec_codelengths[entry-1]);
+      return(entry-1);
+    }
+  }else{
+    lo=0;
+    hi=book->used_entries;
+  }
+
+  lok = oggpack_look(b, read);
+
+  while(lok<0 && read>1)
+    lok = oggpack_look(b, --read);
+  if(lok<0)return -1;
+
+  /* bisect search for the codeword in the ordered list */
+  {
+    ogg_uint32_t testword=bitreverse((ogg_uint32_t)lok);
+
+    while(hi-lo>1){
+      long p=(hi-lo)>>1;
+      long test=book->codelist[lo+p]>testword;
+      lo+=p&(test-1);
+      hi-=p&(-test);
+      }
+
+    if(book->dec_codelengths[lo]<=read){
+      oggpack_adv(b, book->dec_codelengths[lo]);
+      return(lo);
+    }
   }
-  for(k=0;k<dim;k++)a[k]-=(book->valuelist-ptr*dim)[k];
-  return(vorbis_book_encode(book,-ptr,b));
+
+  oggpack_adv(b, read);
+
+  return(-1);
 }
 
-/* returns the entry number or -1 on eof ****************************/
+/* Decode side is specced and easier, because we don't need to find
+   matches using different criteria; we simply read and map.  There are
+   two things we need to do 'depending':
+
+   We may need to support interleave.  We don't really, but it's
+   convenient to do it here rather than rebuild the vector later.
+
+   Cascades may be additive or multiplicitive; this is not inherent in
+   the codebook, but set in the code using the codebook.  Like
+   interleaving, it's easiest to do it here.
+   addmul==0 -> declarative (set the value)
+   addmul==1 -> additive
+   addmul==2 -> multiplicitive */
+
+/* returns the [original, not compacted] entry number or -1 on eof *********/
 long vorbis_book_decode(codebook *book, oggpack_buffer *b){
-  long ptr=0;
-  decode_aux *t=book->decode_tree;
-  do{
-    switch(_oggpack_read1(b)){
-    case 0:
-      ptr=t->ptr0[ptr];
-      break;
-    case 1:
-      ptr=t->ptr1[ptr];
-      break;
-    case -1:
-      return(-1);
+  if(book->used_entries>0){
+    long packed_entry=decode_packed_entry_number(book,b);
+    if(packed_entry>=0)
+      return(book->dec_index[packed_entry]);
+  }
+
+  /* if there's no dec_index, the codebook unpacking isn't collapsed */
+  return(-1);
+}
+
+/* returns 0 on OK or -1 on eof *************************************/
+long vorbis_book_decodevs_add(codebook *book,float *a,oggpack_buffer *b,int n){
+  if(book->used_entries>0){
+    int step=n/book->dim;
+    long *entry = alloca(sizeof(*entry)*step);
+    float **t = alloca(sizeof(*t)*step);
+    int i,j,o;
+
+    for (i = 0; i < step; i++) {
+      entry[i]=decode_packed_entry_number(book,b);
+      if(entry[i]==-1)return(-1);
+      t[i] = book->valuelist+entry[i]*book->dim;
     }
-  }while(ptr>0);
-  return(-ptr);
+    for(i=0,o=0;i<book->dim;i++,o+=step)
+      for (j=0;j<step;j++)
+        a[o+j]+=t[j][i];
+  }
+  return(0);
 }
 
-/* returns the entry number or -1 on eof ****************************/
-long vorbis_book_decodev(codebook *book, double *a, oggpack_buffer *b){
-  long entry=vorbis_book_decode(book,b);
-  int i;
-  if(entry==-1)return(-1);
-  for(i=0;i<book->dim;i++)a[i]+=(book->valuelist+entry*book->dim)[i];
-  return(entry);
+long vorbis_book_decodev_add(codebook *book,float *a,oggpack_buffer *b,int n){
+  if(book->used_entries>0){
+    int i,j,entry;
+    float *t;
+
+    if(book->dim>8){
+      for(i=0;i<n;){
+        entry = decode_packed_entry_number(book,b);
+        if(entry==-1)return(-1);
+        t     = book->valuelist+entry*book->dim;
+        for (j=0;j<book->dim;)
+          a[i++]+=t[j++];
+      }
+    }else{
+      for(i=0;i<n;){
+        entry = decode_packed_entry_number(book,b);
+        if(entry==-1)return(-1);
+        t     = book->valuelist+entry*book->dim;
+        j=0;
+        switch((int)book->dim){
+        case 8:
+          a[i++]+=t[j++];
+        case 7:
+          a[i++]+=t[j++];
+        case 6:
+          a[i++]+=t[j++];
+        case 5:
+          a[i++]+=t[j++];
+        case 4:
+          a[i++]+=t[j++];
+        case 3:
+          a[i++]+=t[j++];
+        case 2:
+          a[i++]+=t[j++];
+        case 1:
+          a[i++]+=t[j++];
+        case 0:
+          break;
+        }
+      }
+    }
+  }
+  return(0);
 }
 
-#ifdef _V_SELFTEST
-
-/* Simple enough; pack a few candidate codebooks, unpack them.  Code a
-   number of vectors through (keeping track of the quantized values),
-   and decode using the unpacked book.  quantized version of in should
-   exactly equal out */
-
-#include <stdio.h>
-#include "vorbis/book/lsp20_0.vqh"
-#include "vorbis/book/lsp32_0.vqh"
-#define TESTSIZE 40
-#define TESTDIM 4
-
-double test1[40]={
-  0.105939,
-  0.215373,
-  0.429117,
-  0.587974,
-
-  0.181173,
-  0.296583,
-  0.515707,
-  0.715261,
-
-  0.162327,
-  0.263834,
-  0.342876,
-  0.406025,
-
-  0.103571,
-  0.223561,
-  0.368513,
-  0.540313,
-
-  0.136672,
-  0.395882,
-  0.587183,
-  0.652476,
-
-  0.114338,
-  0.417300,
-  0.525486,
-  0.698679,
-
-  0.147492,
-  0.324481,
-  0.643089,
-  0.757582,
-
-  0.139556,
-  0.215795,
-  0.324559,
-  0.399387,
-
-  0.120236,
-  0.267420,
-  0.446940,
-  0.608760,
-
-  0.115587,
-  0.287234,
-  0.571081,
-  0.708603,
-};
-
-double test2[40]={
-  0.088654,
-  0.165742,
-  0.279013,
-  0.395894,
-
-  0.110812,
-  0.218422,
-  0.283423,
-  0.371719,
-
-  0.136985,
-  0.186066,
-  0.309814,
-  0.381521,
-
-  0.123925,
-  0.211707,
-  0.314771,
-  0.433026,
-
-  0.088619,
-  0.192276,
-  0.277568,
-  0.343509,
-
-  0.068400,
-  0.132901,
-  0.223999,
-  0.302538,
-
-  0.202159,
-  0.306131,
-  0.360362,
-  0.416066,
-
-  0.072591,
-  0.178019,
-  0.304315,
-  0.376516,
-
-  0.094336,
-  0.188401,
-  0.325119,
-  0.390264,
-
-  0.091636,
-  0.223099,
-  0.282899,
-  0.375124,
-};
-
-static_codebook *testlist[]={&_vq_book_lsp20_0,&_vq_book_lsp32_0,NULL};
-double   *testvec[]={test1,test2};
-
-int main(){
-  oggpack_buffer write;
-  oggpack_buffer read;
-  long ptr=0,i;
-  _oggpack_writeinit(&write);
-  
-  fprintf(stderr,"Testing codebook abstraction...:\n");
-
-  while(testlist[ptr]){
-    codebook c;
-    static_codebook s;
-    double *qv=alloca(sizeof(double)*TESTSIZE);
-    double *iv=alloca(sizeof(double)*TESTSIZE);
-    memcpy(qv,testvec[ptr],sizeof(double)*TESTSIZE);
-    memset(iv,0,sizeof(double)*TESTSIZE);
-
-    fprintf(stderr,"\tpacking/coding %ld... ",ptr);
-
-    /* pack the codebook, write the testvector */
-    _oggpack_reset(&write);
-    vorbis_book_init_encode(&c,testlist[ptr]); /* get it into memory
-                                                  we can write */
-    vorbis_staticbook_pack(testlist[ptr],&write);
-    fprintf(stderr,"Codebook size %ld bytes... ",_oggpack_bytes(&write));
-    for(i=0;i<TESTSIZE;i+=TESTDIM)
-      vorbis_book_encodev(&c,qv+i,&write);
-    vorbis_book_clear(&c);
-
-    fprintf(stderr,"OK.\n");
-    fprintf(stderr,"\tunpacking/decoding %ld... ",ptr);
-
-    /* transfer the write data to a read buffer and unpack/read */
-    _oggpack_readinit(&read,_oggpack_buffer(&write),_oggpack_bytes(&write));
-    if(vorbis_staticbook_unpack(&read,&s)){
-      fprintf(stderr,"Error unpacking codebook.\n");
-      exit(1);
+long vorbis_book_decodev_set(codebook *book,float *a,oggpack_buffer *b,int n){
+  if(book->used_entries>0){
+    int i,j,entry;
+    float *t;
+
+    for(i=0;i<n;){
+      entry = decode_packed_entry_number(book,b);
+      if(entry==-1)return(-1);
+      t     = book->valuelist+entry*book->dim;
+      for (j=0;j<book->dim;)
+        a[i++]=t[j++];
     }
-    if(vorbis_book_init_decode(&c,&s)){
-      fprintf(stderr,"Error initializing codebook.\n");
-      exit(1);
+  }else{
+    int i,j;
+
+    for(i=0;i<n;){
+      for (j=0;j<book->dim;)
+        a[i++]=0.f;
     }
+  }
+  return(0);
+}
 
-    for(i=0;i<TESTSIZE;i+=TESTDIM)
-      if(vorbis_book_decodev(&c,iv+i,&read)==-1){
-       fprintf(stderr,"Error reading codebook test data (EOP).\n");
-       exit(1);
-      }
-    for(i=0;i<TESTSIZE;i++)
-      if(fabs(testvec[ptr][i]-qv[i]-iv[i])>.000001){
-       fprintf(stderr,"input (%g) != output (%g) at position (%ld)\n",
-               iv[i],testvec[ptr][i]-qv[i],i);
-       exit(1);
+long vorbis_book_decodevv_add(codebook *book,float **a,long offset,int ch,
+                              oggpack_buffer *b,int n){
+
+  long i,j,entry;
+  int chptr=0;
+  if(book->used_entries>0){
+    for(i=offset/ch;i<(offset+n)/ch;){
+      entry = decode_packed_entry_number(book,b);
+      if(entry==-1)return(-1);
+      {
+        const float *t = book->valuelist+entry*book->dim;
+        for (j=0;j<book->dim;j++){
+          a[chptr++][i]+=t[j];
+          if(chptr==ch){
+            chptr=0;
+            i++;
+          }
+        }
       }
-         
-    fprintf(stderr,"OK\n");
-    ptr++;
+    }
   }
-  exit(0);
+  return(0);
 }
-
-#endif