Checkpoint ongoing work in the analysis interface
authorMonty <xiphmont@xiph.org>
Tue, 27 Jul 1999 08:44:54 +0000 (08:44 +0000)
committerMonty <xiphmont@xiph.org>
Tue, 27 Jul 1999 08:44:54 +0000 (08:44 +0000)
Monty 19990727

svn path=/trunk/vorbis/; revision=13

lib/Makefile.in
lib/analysis.c
lib/codec.h

index cb21314..3dfc755 100644 (file)
@@ -1,6 +1,6 @@
 # vorbis makefile configured for use with gcc on any platform
 
-# $Id: Makefile.in,v 1.1 1999/07/13 07:48:14 mwhitson Exp $
+# $Id: Makefile.in,v 1.2 1999/07/27 08:44:52 xiphmont Exp $
 
 ###############################################################################
 #                                                                             #
@@ -42,8 +42,10 @@ profile:
 selftest:
        $(MAKE) clean
        $(CC) $(DEBUG) $(LDFLAGS) -D_V_SELFTEST framing.c -o test_framing 
+       $(CC) $(DEBUG) $(LDFLAGS) -D_V_SELFTEST analysis.c -o test_blocking
        @echo
        @./test_framing
+       @./test_blocking
 
 target:        $(TARGETFILES)
 
index dc31653..f8903b7 100644 (file)
  function: PCM data vector blocking, windowing and dis/reassembly
  author: Monty <xiphmont@mit.edu>
  modifications by: Monty
- last modification date: Jun 26 1999
+ last modification date: Jul 27 1999
 
- Handle windowing, overlap-add, etc of the original (and synthesized)
- PCM vectors.  This is made more amusing by Vorbis' current two allowed
block sizes (512 and 2048 elements/channel).
+ Handle windowing, overlap-add, etc of the PCM vectors.  This is made
+ more amusing by Vorbis' current two allowed block sizes (512 and 2048
+ elements/channel).
  
  Vorbis manipulates the dynamic range of the incoming PCM data
  envelope to minimise time-domain energy leakage from percussive and
@@ -27,6 +27,8 @@
  ********************************************************************/
 
 #include <stdlib.h>
+#include <string.h>
+#include "codec.h"
 
 /* pcm accumulator and multipliers 
    examples (not exhaustive):
                                  mult[n]
 */
 
-typedef struct vorbis_state{
-  int samples_per_envelope_step;
-  int block_size[2];
-  double *window[2][2][2]; /* windowsize, leadin, leadout */
-
-  double **pcm;
-  int      pcm_storage;
-  int      pcm_channels;
-  int      pcm_current;
-
-  double **deltas;
-  int    **multipliers;
-  int      envelope_storage;
-  int      envelope_channels;
-  int      envelope_current;
-
-  int  initflag;
-
-  long lW;
-  long W;
-  long Sl;
-  long Sr;
-
-  long beginW;
-  long endW;
-  long beginSl;
-  long endSl;
-  long beginSr;
-  long endSr;
-
-  long frame;
-  long samples;
-
-} vorbis_state;
-
 /* arbitrary settings and spec-mandated numbers get filled in here */
-void vorbis_init_state(vorbis_state *v,int channels,int mode){
-  memset(v,0,sizeof(vorbis_state));
+int vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi){
+  memset(v,0,sizeof(vorbis_dsp_state));
   v->samples_per_envelope_step=64;
   v->block_size[0]=512; 
   v->block_size[1]=2048;
   
-  v->window[0][0][0]=vorbis_window(v->block_size[0],
+  /*  v->window[0][0][0]=vorbis_window(v->block_size[0],
                                   v->block_size[0]/2,v->block_size[0]/2);
   v->window[1][0][0]=vorbis_window(v->block_size[1],
                                   v->block_size[0]/2,v->block_size[0]/2);
@@ -121,26 +88,27 @@ void vorbis_init_state(vorbis_state *v,int channels,int mode){
   v->window[1][1][0]=vorbis_window(v->block_size[1],
                                   v->block_size[1]/2,v->block_size[0]/2);
   v->window[1][1][1]=vorbis_window(v->block_size[1],
-                                  v->block_size[1]/2,v->block_size[1]/2);
+  v->block_size[1]/2,v->block_size[1]/2);*/
 
   /* initialize the storage vectors to a decent size greater than the
      minimum */
   
-  v->pcm_storage=8192; /* 8k samples.  we'll assume later that we have
-                         a minimum of twice the blocksize (2k) of
+  v->pcm_storage=8192; /* we'll assume later that we have
+                         a minimum of twice the blocksize of
                          accumulated samples in analysis */
-  v->pcm_channels=channels;
-  v->pcm=malloc(channels*sizeof(double *));
+  v->pcm_channels=v->vi.channels=vi->channels;
+  v->pcm=malloc(vi->channels*sizeof(double *));
+  v->pcmret=malloc(vi->channels*sizeof(double *));
   {
     int i;
-    for(i=0;i<channels;i++)
+    for(i=0;i<vi->channels;i++)
       v->pcm[i]=calloc(v->pcm_storage,sizeof(double));
   }
 
   /* Initialize the envelope multiplier storage */
   
-  v->envelope_storage=v->pcmstorage/v->samples_per_envelope_step+1;
-  v->envelope_channels=channels;
+  v->envelope_storage=v->pcm_storage/v->samples_per_envelope_step;
+  v->envelope_channels=vi->channels;
   v->deltas=calloc(v->envelope_channels,sizeof(double *));
   v->multipliers=calloc(v->envelope_channels,sizeof(int *));
   {
@@ -152,29 +120,19 @@ void vorbis_init_state(vorbis_state *v,int channels,int mode){
   }
 
   /* all 1 (large block) or 0 (small block) */
-  /*v->lW=0; previous window size */
-  /*v->W=0;  determined during analysis */
-  /*v->Sl=0; previous Sr */
-  /*v->Sr=0; determined during analysis */
+  /* explicitly set for the sake of clarity */
+  v->lW=0; /* previous window size */
+  v->W=0;  /* current window size */
 
   /* all vector indexes; multiples of samples_per_envelope_step */
-  /*v->beginW=0;  determined during analysis */
-  /*v->endW=0;    determined during analysis */
-    v->beginSl=v->block_size[1]/4-v->block_size[0]/4;
-    v->endSl=v->beginSl+v->block_size[0]/2;
-  /*v->beginSr=0; determined during analysis */
-  /*v->endSr=0;   determined during analysis */
-
-  /*v->frame=0;*/
-  /*v->samples=0;*/
-
-  v->pcm_current=v->endSl;
-  v->last_multiplier=v->endSl/v->samples_per_envelope_step+1;
+  v->centerW=v->block_size[1]/2;
 
-  v->initflag=1;
+  v->pcm_current=v->centerW;
+  v->envelope_current=v->centerW/v->samples_per_envelope_step;
+  return(0);
 }
 
-void vorbis_free_state(vorbis_state *v){
+void vorbis_analysis_clear(vorbis_dsp_state *v){
   int i,j,k;
   if(v){
     for(i=0;i<2;i++)
@@ -185,6 +143,7 @@ void vorbis_free_state(vorbis_state *v){
       for(i=0;i<v->pcm_channels;i++)
        if(v->pcm[i])free(v->pcm[i]);
       free(v->pcm);
+      free(v->pcmret);
     }
     if(v->deltas){
       for(i=0;i<v->envelope_channels;i++)
@@ -196,110 +155,228 @@ void vorbis_free_state(vorbis_state *v){
        if(v->multipliers[i])free(v->multipliers[i]);
       free(v->multipliers);
     }
-    free(v);
+    memset(v,0,sizeof(vorbis_dsp_state));
   }
 }
 
-int vorbis_analysis(vorbis_state *v, double **pcm, int vals){
+/* call with val==0 to set eof */
+double **vorbis_analysis_buffer(vorbis_dsp_state *v, int vals){
   int i;
+  if(vals<=0){
+    /* We're encoding the end of the stream.  Just make sure we have
+       [at least] a full block of zeroes at the end. */
+    v->eofflag=v->pcm_current;
+    v->pcm_current+=v->block_size[1]*2;
+    vals=0;
+  }
 
-  /* vorbis encode state initialization */
-  if(!v->initflag)
-    vorbis_init_settings(v);
-
-  /* first we need to handle incoming data (if any) */
-  
-  if(vals>0){
-    /* Do we have enough storage space for the incoming data? If not,
-       expand the PCM storage */
+  /* Do we have enough storage space for the requested buffer? If not,
+     expand the PCM (and envelope) storage */
+    
+  if(v->pcm_current+vals>=v->pcm_storage){
+    v->pcm_storage=v->pcm_current+vals*2;
+    v->envelope_storage=v->pcm_storage/v->samples_per_envelope_step;
+   
+    for(i=0;i<v->pcm_channels;i++){
+      v->pcm[i]=realloc(v->pcm[i],v->pcm_storage*sizeof(double));
+      v->deltas[i]=realloc(v->deltas[i],v->envelope_storage*sizeof(double));
+      v->multipliers[i]=realloc(v->multipliers[i],
+                               v->envelope_storage*sizeof(double));
 
-    if(v->pcm_current+vals>=pcm_storage){
-      for(i=0;i<v->pcm_channels;i++)
-       v->pcm[i]=realloc(v->pcm[i],
-                         (v->pcm_current+vals*2)*sizeof(double));
-      v->pcm_storage=v->pcm_current+vals*2;
     }
+  }
 
-    /* If we're encoding the end of the stream and we're handing in
-       padding, vals will be set, but the passed in buffer will be
-       NULL; just add in zeroes */
-
+  if(vals<=0){
+    /* We're encoding the end of the stream.  Just make sure we have
+       [at least] a full block of zeroes at the end. */
+    
     for(i=0;i<v->pcm_channels;i++)
-      if(pcm==NULL)
-       memset(v->pcm[i]+v->pcm_current,0,vals*sizeof(double));
-      else
-       memcpy(v->pcm[i]+v->pcm_current,pcm[i],vals*sizeof(double));
-
-    v->pcm_current+=vals;
+      memset(v->pcm[i]+v->eofflag,0,
+            (v->pcm_current-v->eofflag)*sizeof(double));
   }
 
-  /* Do we definately have enough for a frame? We assume we have more
-     than actually necessary to encode the current block to make some
-     analysis easier. */
+  for(i=0;i<v->pcm_channels;i++)
+    v->pcmret[i]=v->pcm[i]+v->pcm_current;
+    
+  return(v->pcmret);
+}
 
-  if(v->pcm_current-v->endSl<v->blocksize[1]*2)
-    return(0);
+int vorbis_analysis_wrote(vorbis_dsp_state *v, int vals){
+  if(v->pcm_current+vals>v->pcm_storage)
+    return(-1);
 
-  /* we have enough. begin analysis */
-  /* complete the envelope analysis vectors */
+  v->pcm_current+=vals;
+  return(0);
+}
 
+int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb){
+  int i;
+  vb->pcm_storage=v->block_size[1];
+  vb->pcm_channels=v->pcm_channels;
+  vb->mult_storage=v->block_size[1]/v->samples_per_envelope_step;
+  vb->mult_channels=v->envelope_channels;
   
+  vb->pcm=malloc(vb->pcm_channels*sizeof(double *));
+  for(i=0;i<vb->pcm_channels;i++)
+    vb->pcm[i]=malloc(vb->pcm_storage*sizeof(double));
+  
+  vb->mult=malloc(vb->mult_channels*sizeof(int *));
+  for(i=0;i<vb->mult_channels;i++)
+    vb->mult[i]=malloc(vb->mult_storage*sizeof(int));
+  return(0);
+}
 
-  /* decide the blocksize of this frame */
+int vorbis_block_clear(vorbis_block *vb){
+  int i;
+  if(vb->pcm){
+    for(i=0;i<vb->pcm_channels;i++)
+      free(vb->pcm[i]);
+    free(vb->pcm);
+  }
+  if(vb->mult){
+    for(i=0;i<vb->mult_channels;i++)
+      free(vb->mult[i]);
+    free(vb->mult);
+  }
+  memset(vb,0,sizeof(vorbis_block));
+  return(0);
+}
 
+/* do the deltas, envelope shaping, pre-echo and determine the size of
+   the next block on which to continue analysis */
+int vorbis_analysis_block(vorbis_dsp_state *v,vorbis_block *vb){
+  int i;
+  long beginW=v->centerW-v->block_size[v->W]/2,centerNext;
+  long beginM=beginW/v->samples_per_envelope_step;
 
-  /* algebra to set the rest of the window alignment vectors; many are
-     just derived, but they make the process clearer for the time
-     being */
+  /* check to see if we're done... */
+  if(v->eofflag==-1)return(0);
 
+  /* if we have any unfilled envelope blocks for which we have PCM
+     data, fill them up in before proceeding. */
 
+  if(v->pcm_current/v->samples_per_envelope_step>v->envelope_current){
+    _va_envelope_deltas(v);
+    _va_envelope_multipliers(v);
+  }
 
-  /* the real analysis begins; forward MDCT with window */
+  /* By our invariant, we have lW, W and centerW set.  Search for
+     the next boundary so we can determine nW (the next window size)
+     which lets us compute the shape of the current block's window */
 
+  /* overconserve for now; any block with a non-placeholder multiplier
+     should be minimal size. We can be greedy and only look at nW size */
   
-  /* Noise floor, resolution floor */
+  if(v->W)
+    /* this is a long window; we start the search forward of centerW
+       because that's the fastest we could react anyway */
+    i=v->centerW+v->block_size[1]/4-v->block_size[0]/4;
+  else
+    /* short window.  Search from centerW */
+    i=v->centerW;
+  i/=v->samples_per_envelope_step;
+
+  for(;i<v->envelope_storage;i++)if(v->multipliers[i])break;
+  
+  if(i<v->envelope_storage){
+    /* Ooo, we hit a multiplier. Is it beyond the boundary to make the
+       upcoming block large ? */
+    long largebound;
+    if(v->W)
+      largebound=v->centerW+v->block_size[1];
+    else
+      largebound=v->centerW+v->block_size[0]/4+v->block_size[1]*3/4;
+    largebound/=v->samples_per_envelope_step;
+
+    if(i>=largebound)
+      v->nW=1;
+    else
+      v->nW=0;
+
+  }else{
+    /* Assume maximum; if the block is incomplete given current
+       buffered data, this will be detected below */
+    v->nW=1;
+  }
 
-  /* encode the floor into LSP; get the actual floor back for quant */
+  /* Do we actually have enough data *now* for the next block? The
+     reason to check is that if we had no multipliers, that could
+     simply been due to running out of data.  In that case, we don;t
+     know the size of the next block for sure and we need that now to
+     figure out the window shape of this block */
+  
+  {
+    long blockbound=centerNext+v->block_size[v->nW]/2;
+    if(v->pcm_current<blockbound)return(0); /* not enough data yet */    
+  }
 
-  /* use noise floor, res floor for culling, actual floor for quant */
+  /* fill in the block */
+  vb->lW=v->lW;
+  vb->W=v->W;
+  vb->nW=v->nW;
+  vb->vd=v;
 
-  /* encode residue */
+  vb->pcmend=v->block_size[v->W];
+  vb->multend=vb->pcmend / v->samples_per_envelope_step;
+
+  if(v->pcm_channels!=vb->pcm_channels ||
+     v->block_size[1]!=vb->pcm_storage ||
+     v->envelope_channels!=vb->mult_channels){
+
+    /* Storage not initialized or initilized for some other codec
+       instance with different settings */
+
+    vorbis_block_clear(vb);
+    vorbis_block_init(v,vb);
+  }
+
+  /* copy the vectors */
+  for(i=0;i<v->pcm_channels;i++)
+    memcpy(vb->pcm[i],v->pcm[i]+beginW,v->block_size[v->W]*sizeof(double));
+  for(i=0;i<v->envelope_channels;i++)
+    memcpy(vb->mult[i],v->multipliers[i]+beginM,v->block_size[v->W]/
+          v->samples_per_envelope_step*sizeof(int));
+
+  vb->frameno=v->frame;
+
+  /* handle eof detection: eof==0 means that we've not yet received EOF
+                           eof>0  marks the last 'real' sample in pcm[]
+                           eof<0  'no more to do'; doesn't get here */
+
+  if(v->eofflag){
+    long endW=beginW+v->block_size[v->W];
+    if(endW>=v->eofflag){
+      v->eofflag=-1;
+      vb->eofflag=1;
+    }
+  }
 
   /* advance storage vectors and clean up */
-  /* center the window leadout on blocksize[1]/4 */
   {
-    int new_beginSr,new_endSr,movement,emove;
-
-    /* first do the pcm storage */
-    if(v->Sr){
-      new_beginSl=0;
-      new_endSl=v->blocksize[1]/2;
-    }else{
-      new_beginSl=v->blocksize[1]/4-v->blocksize[0]/4;
-      new_endSl=new_beginSr+v->blocksize[0]/2;
-    }
-    movement=v->beginSr-new_beginSl;
+    int new_centerNext=v->block_size[1]/2;
+    int movementW=centerNext-new_centerNext;
+    int movementM=movementW/v->samples_per_envelope_step;
 
     for(i=0;i<v->pcm_channels;i++)
-      memmove(v->pcm[i],v->pcm[i]+movement,
-             (v->pcm_current-movement)*sizeof(double));
-    v->pcm_current-=movement;
-    v->lW=W;
-    v->Sl=v->Sr;
-    v->beginSl=new_beginSl;
-    v->endSl=new_endSl;
-    v->frame++;
-    v->samples+=movement;
-
-    /* now advance the multipliers */
-    emove=movement/samples_per_envelope_step; 
+      memmove(v->pcm[i],v->pcm[i]+movementW,
+             (v->pcm_current-movementW)*sizeof(double));
+    
     for(i=0;i<v->envelope_channels;i++){
-      memmove(v->deltas[i],v->deltas[i]+emove,
-             (v->envelope_current-emove)*sizeof(double));
-      memmove(v->multipliers[i],v->multipliers[i]+emove,
-             (v->envelope_current-emove)*sizeof(int));
+      memmove(v->deltas[i],v->deltas[i]+movementM,
+             (v->envelope_current-movementM)*sizeof(double));
+      memmove(v->multipliers[i],v->multipliers[i]+movementM,
+             (v->envelope_current-movementM)*sizeof(int));
     }
-    v->envelope_current-=emove;
+
+    v->pcm_current-=movementW;
+    v->envelope_current-=movementM;
+
+    v->lW=v->W;
+    v->W=v->nW;
+    v->centerW=new_centerNext;
+
+    v->frame++;
+    v->samples+=movementW;
   }
 
   /* done */
@@ -309,3 +386,31 @@ int vorbis_analysis(vorbis_state *v, double **pcm, int vals){
 
 
 
+
+
+
+
+
+
+int vorbis_analysis_packetout(vorbis_dsp_state *v, vorbis_block *vb,
+                             ogg_packet *op){
+
+  /* find block's envelope vector and apply it */
+
+
+  /* the real analysis begins; forward MDCT with window */
+
+  
+  /* Noise floor, resolution floor */
+
+  /* encode the floor into LSP; get the actual floor back for quant */
+
+  /* use noise floor, res floor for culling, actual floor for quant */
+
+  /* encode residue */
+
+}
+
+
+
+
index 4e02fa0..f7a5c31 100644 (file)
  *                                                                  *
  ********************************************************************
 
- function: PCM data vector blocking, windowing and dis/reassembly
+ function: codec headers
  author: Monty <xiphmont@mit.edu>
  modifications by: Monty
- last modification date: Jul 13 1999
+ last modification date: Jul 25 1999
 
  ********************************************************************/
 
@@ -31,41 +31,6 @@ typedef struct vorbis_info{
 
 } vorbis_info;
  
-typedef struct vorbis_dsp_state{
-  int samples_per_envelope_step;
-  int block_size[2];
-  double *window[2][2][2]; /* windowsize, leadin, leadout */
-
-  double **pcm;
-  int      pcm_storage;
-  int      pcm_channels;
-  int      pcm_current;
-
-  double **deltas;
-  int    **multipliers;
-  int      envelope_storage;
-  int      envelope_channels;
-  int      envelope_current;
-
-  int  initflag;
-
-  long lW;
-  long W;
-  long Sl;
-  long Sr;
-
-  long beginW;
-  long endW;
-  long beginSl;
-  long endSl;
-  long beginSr;
-  long endSr;
-
-  long frame;
-  long samples;
-
-} vorbis_dsp_state;
-
 typedef struct {
   unsigned char *header;
   long header_len;
@@ -74,17 +39,6 @@ typedef struct {
 } ogg_page;
 
 typedef struct {
-  /*             _________________________________________________
-     body_data: |_________________________________________________|
-     body_returned ----^       ^                          ^       ^
-     body_processed------------'                          |       |
-     body_fill     ---------------------------------------'       |     
-     body_storage  _______________________________________________' 
-
-     the header is labelled the same way.  Not all the pointers are 
-     used by both encode and decode */
-
   unsigned char   *body_data;    /* bytes from packet bodies */
   long    body_storage;          /* storage elements allocated */
   long    body_fill;             /* elements stored; fill mark */
@@ -134,6 +88,63 @@ typedef struct {
   int bodybytes;
 } ogg_sync_state;
 
+typedef struct {
+    int divisor;
+  double *window;
+} _ve_lookup;
+
+
+typedef struct vorbis_dsp_state{
+  int samples_per_envelope_step;
+  int block_size[2];
+  double *window[2][2][2]; /* windowsize, leadin, leadout */
+
+  _ve_lookup ve;
+  vorbis_info vi;
+
+  double **pcm;
+  double **pcmret;
+  int      pcm_storage;
+  int      pcm_channels;
+  int      pcm_current;
+
+  double **deltas;
+  int    **multipliers;
+  int      envelope_storage;
+  int      envelope_channels;
+  int      envelope_current;
+
+  int  eofflag;
+
+  long lW;
+  long W;
+  long nW;
+  long centerW;
+
+  long frame;
+  long samples;
+
+} vorbis_dsp_state;
+
+typedef struct vorbis_block{
+  double **pcm;
+  int    **mult;
+  int    pcm_channels; /* allocated, not used */
+  int    pcm_storage;  /* allocated, not used */
+  int    mult_channels; /* allocated, not used */
+  int    mult_storage;  /* allocated, not used */
+
+  long  lW;
+  long  W;
+  long  nW;
+  int   pcmend;
+  int   multend;
+
+  int eofflag;
+  int frameno;
+  vorbis_dsp_state *vd; /* For read-only access of configuration */
+} vorbis_block;
+
 /* libvorbis encodes in two abstraction layers; first we perform DSP
    and produce a packet (see docs/analysis.txt).  The packet is then
    coded into a framed OggSquish bitstream by the second layer (see
@@ -182,16 +193,25 @@ extern int    ogg_page_pageno(ogg_page *og);
 
 /* Vorbis PRIMITIVES: analysis/DSP layer ****************************/
 
-extern int  vorbis_analysis_init(vorbis_dsp_state *vd,vorbis_info *vi);
-extern void vorbis_dsp_state_free(vorbis_dsp_state *vd);
-extern int  vorbis_analysis_input(vorbis_dsp_state *vd,double **pcm,int vals);
-extern int  vorbis_analysis(vorbis_dsp_state *vd,ogg_packet *op);
+extern int      vorbis_analysis_init(vorbis_dsp_state *vd,vorbis_info *vi);
+extern int      vorbis_analysis_reset(vorbis_dsp_state *vd);
+extern void     vorbis_analysis_free(vorbis_dsp_state *vd);
+extern double **vorbis_analysis_buffer(vorbis_dsp_state *vd,int vals);
+extern int      vorbis_analysis_wrote(vorbis_dsp_state *vd,int vals);
+extern int      vorbis_analysis_block(vorbis_dsp_state *vd,vorbis_block *vb);
+extern int      vorbis_analysis_packetout(vorbis_dsp_state *vd,
+                                         vorbis_block *vb,
+                                         ogg_packet *op);
 
 /* Vorbis PRIMITIVES: synthesis layer *******************************/
 
+extern void vorbis_synthesis_free(vorbis_dsp_state *vd);
+extern int  vorbis_synthesis_init(vorbis_dsp_state *vd);
 extern int  vorbis_synthesis_info(vorbis_dsp_state *vd,vorbis_info *vi);
-extern int  vorbis_synthesis_packet(vorbis_dsp_state *vd,ogg_packet *op);
-extern int  vorbis_synthesis_output(vorbis_dsp_state *vd,double **pcm);
+extern int  vorbis_synthesis_packetin(vorbis_dsp_state *vd,vorbis_block *vb,
+                                     ogg_packet *op);
+extern int  vorbis_synthesis_pcmout(vorbis_dsp_state *vd,vorbis_block *vb,
+                                     double **pcm);
 
 #endif