From 09298000fd1cc7798a861b6735500f86c35cda2e Mon Sep 17 00:00:00 2001 From: Monty Date: Tue, 27 Jul 1999 08:44:54 +0000 Subject: [PATCH] Checkpoint ongoing work in the analysis interface Monty 19990727 svn path=/trunk/vorbis/; revision=13 --- lib/Makefile.in | 4 +- lib/analysis.c | 383 ++++++++++++++++++++++++++++++++++++-------------------- lib/codec.h | 128 +++++++++++-------- 3 files changed, 321 insertions(+), 194 deletions(-) diff --git a/lib/Makefile.in b/lib/Makefile.in index cb21314..3dfc755 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -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) diff --git a/lib/analysis.c b/lib/analysis.c index dc31653..f8903b7 100644 --- a/lib/analysis.c +++ b/lib/analysis.c @@ -14,11 +14,11 @@ function: PCM data vector blocking, windowing and dis/reassembly author: Monty 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 +#include +#include "codec.h" /* pcm accumulator and multipliers examples (not exhaustive): @@ -70,49 +72,14 @@ 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;ichannels;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;ipcm_channels;i++) if(v->pcm[i])free(v->pcm[i]); free(v->pcm); + free(v->pcmret); } if(v->deltas){ for(i=0;ienvelope_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;ipcm_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;ipcm_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;ipcm_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;ipcm_channels;i++) + v->pcmret[i]=v->pcm[i]+v->pcm_current; + + return(v->pcmret); +} - if(v->pcm_current-v->endSlblocksize[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;ipcm_channels;i++) + vb->pcm[i]=malloc(vb->pcm_storage*sizeof(double)); + + vb->mult=malloc(vb->mult_channels*sizeof(int *)); + for(i=0;imult_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;ipcm_channels;i++) + free(vb->pcm[i]); + free(vb->pcm); + } + if(vb->mult){ + for(i=0;imult_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(;ienvelope_storage;i++)if(v->multipliers[i])break; + + if(ienvelope_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_currentlW=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;ipcm_channels;i++) + memcpy(vb->pcm[i],v->pcm[i]+beginW,v->block_size[v->W]*sizeof(double)); + for(i=0;ienvelope_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;ipcm_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;ienvelope_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 */ + +} + + + + diff --git a/lib/codec.h b/lib/codec.h index 4e02fa0..f7a5c31 100644 --- a/lib/codec.h +++ b/lib/codec.h @@ -11,10 +11,10 @@ * * ******************************************************************** - function: PCM data vector blocking, windowing and dis/reassembly + function: codec headers author: Monty 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 -- 2.7.4