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
********************************************************************/
#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);
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 *));
{
}
/* 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++)
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++)
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 */
+
+
+
+
+
+
+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 */
+
+}
+
+
+
+
* *
********************************************************************
- 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
********************************************************************/
} 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;
} 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 */
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
/* 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