From ea0d1fec9da42fa847a1c4775e978e1375c1c779 Mon Sep 17 00:00:00 2001 From: Monty Date: Sat, 7 Aug 1999 00:09:17 +0000 Subject: [PATCH] Envelope infrastructure now in place. Basic preecho running (but woefully naieve) Monty 19990806 svn path=/trunk/vorbis/; revision=20 --- lib/Makefile.in | 11 +- lib/block.c | 117 +++++++------------ lib/codec.h | 16 ++- lib/envelope.c | 346 ++++++++++++++++++++++++++++++++------------------------ lib/envelope.h | 6 +- 5 files changed, 261 insertions(+), 235 deletions(-) diff --git a/lib/Makefile.in b/lib/Makefile.in index 9b5246c..70ae7fd 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.5 1999/08/04 09:18:28 xiphmont Exp $ +# $Id: Makefile.in,v 1.6 1999/08/07 00:09:13 xiphmont Exp $ ############################################################################### # # @@ -27,7 +27,7 @@ AR=@AR@ RANLIB=@RANLIB@ LIBS=@LIBS@ -lm -OFILES = framing.o +OFILES = framing.o mdct.o smallft.o block.o envelope.o window.o TARGETFILES = libvorbis.a all: @@ -39,14 +39,19 @@ debug: profile: $(MAKE) target CFLAGS="$(PROFILE)" -selftest: +selftest: $(MAKE) clean $(CC) $(DEBUG) $(LDFLAGS) -D_V_SELFTEST framing.c -o test_framing $(CC) $(DEBUG) $(LDFLAGS) -D_V_SELFTEST bitwise.c\ -o test_bitwise -lm + $(CC) $(DEBUG) -c envelope.c mdct.c window.c + $(CC) $(DEBUG) $(LDFLAGS) -D_V_SELFTEST envelope.o mdct.o window.c\ + block.c -o test_blocking -lm + @echo @./test_framing @./test_bitwise + @./test_blocking target: $(TARGETFILES) diff --git a/lib/block.c b/lib/block.c index dde6938..8ae8861 100644 --- a/lib/block.c +++ b/lib/block.c @@ -14,11 +14,10 @@ function: PCM data vector blocking, windowing and dis/reassembly author: Monty modifications by: Monty - last modification date: Jul 29 1999 + last modification date: Aug 05 1999 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). + more amusing by Vorbis' current two allowed block sizes. Vorbis manipulates the dynamic range of the incoming PCM data envelope to minimise time-domain energy leakage from percussive and @@ -73,6 +72,10 @@ static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi){ memset(v,0,sizeof(vorbis_dsp_state)); + + memcpy(&v->vi,vi,sizeof(vorbis_info)); + _ve_envelope_init(&v->ve,vi->envelopesa); + v->samples_per_envelope_step=vi->envelopesa; v->block_size[0]=vi->smallblock; v->block_size[1]=vi->largeblock; @@ -112,11 +115,11 @@ static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi){ if(vi->envelopech){ v->envelope_storage=v->pcm_storage/v->samples_per_envelope_step; v->envelope_channels=vi->envelopech; - v->multipliers=calloc(v->envelope_channels,sizeof(int *)); + v->multipliers=calloc(v->envelope_channels,sizeof(double *)); { int i; for(i=0;ienvelope_channels;i++){ - v->multipliers[i]=calloc(v->envelope_storage,sizeof(int)); + v->multipliers[i]=calloc(v->envelope_storage,sizeof(double)); } } } @@ -140,6 +143,11 @@ int vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi){ vi->largeblock=2048; vi->envelopesa=64; vi->envelopech=vi->channels; + vi->preecho_thresh=10.; + vi->preecho_thresh=4.; + vi->envelopemap=calloc(2,sizeof(int)); + vi->envelopemap[0]=0; + vi->envelopemap[1]=1; _vds_shared_init(v,vi); @@ -229,9 +237,9 @@ int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb){ for(i=0;ipcm_channels;i++) vb->pcm[i]=malloc(vb->pcm_storage*sizeof(double)); - vb->mult=malloc(vb->mult_channels*sizeof(int *)); + vb->mult=malloc(vb->mult_channels*sizeof(double *)); for(i=0;imult_channels;i++) - vb->mult[i]=malloc(vb->mult_storage*sizeof(int)); + vb->mult[i]=malloc(vb->mult_storage*sizeof(double)); return(0); } @@ -265,6 +273,8 @@ int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ data, fill them up in before proceeding. */ if(v->pcm_current/v->samples_per_envelope_step>v->envelope_current){ + /* This generates the multipliers, but does not sparsify the vector. + That's done by block before coding */ _ve_envelope_multipliers(v); } @@ -286,7 +296,8 @@ int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ for(;ienvelope_current;i++){ for(j=0;jenvelope_channels;j++) - if(v->multipliers[j][i])break; + if(v->multipliers[j][i-1]*v->vi.preecho_thresh< + v->multipliers[j][i])break; if(jenvelope_channels)break; } @@ -349,7 +360,7 @@ int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ 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)); + v->samples_per_envelope_step*sizeof(double)); vb->frameno=v->frame; @@ -370,13 +381,17 @@ int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ int movementW=centerNext-new_centerNext; int movementM=movementW/v->samples_per_envelope_step; + /* the multipliers and pcm stay synced up because the blocksizes + must be multiples of samples_per_envelope_step (minimum + multiple is 2) */ + for(i=0;ipcm_channels;i++) memmove(v->pcm[i],v->pcm[i]+movementW, (v->pcm_current-movementW)*sizeof(double)); for(i=0;ienvelope_channels;i++){ memmove(v->multipliers[i],v->multipliers[i]+movementM, - (v->envelope_current-movementM)*sizeof(int)); + (v->envelope_current-movementM)*sizeof(double)); } v->pcm_current-=movementW; @@ -398,15 +413,20 @@ int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ } int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){ + int temp=vi->envelopech; + vi->envelopech=0; /* we don't need multiplier buffering in syn */ _vds_shared_init(v,vi); + vi->envelopech=temp; + /* Adjust centerW to allow an easier mechanism for determining output */ v->pcm_returned=v->centerW; v->centerW-= v->block_size[v->W]/4+v->block_size[v->lW]/4; return(0); } -/* Unike in analysis, the window is only partially applied, and - envelope *not* applied. */ +/* Unike in analysis, the window is only partially applied. Envelope + is previously applied (the whole envelope, if any, is shipped in + each block) */ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ @@ -433,15 +453,13 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ } { - int envperlong=v->block_size[1]/v->samples_per_envelope_step; - int envperW=v->block_size[v->W]/v->samples_per_envelope_step; int sizeW=v->block_size[vb->W]; int centerW=v->centerW+v->block_size[vb->lW]/4+sizeW/4; int beginW=centerW-sizeW/2; int endW=beginW+sizeW; int beginSl; int endSl; - int i,j,k; + int i,j; double *window; /* Do we have enough PCM storage for the block? */ @@ -454,45 +472,6 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ v->pcm[i]=realloc(v->pcm[i],v->pcm_storage*sizeof(double)); } - /* multiplier storage works differently in decode than it does in - encode; we only need to buffer the last 'half largeblock' and - we don't need to keep it aligned with the PCM. */ - - /* fill in the first half of the block's multipliers */ - i=v->envelope_current-1; - j=envperW/2-1; - for(;j>=0;j--) - for(k=0;kenvelope_channels;k++) - vb->mult[k][j]=v->multipliers[k][i]; - - /* shift out unneeded buffered multipliers */ - { - int needed=(envperlong-envperW)/2; - if(needed){ - /* We need to keep some of the buffered ones */ - for(k=0;kenvelope_channels;k++) - memmove(v->multipliers[k], - v->multipliers[k]+v->envelope_current-needed, - needed*sizeof(int)); - } - v->envelope_current=needed; - } - - /* add block's second half to the multiplier buffer */ - /* init makes certain we have enough storage; we only buffer a - half longblock */ - - for(i=envperW/2;ienvelope_current; - for(k=0;kenvelope_channels;k++) - v->multipliers[k][j++]=vb->mult[k][i]; - } - v->envelope_current+=envperW/2; - - /* manufacture/apply multiplier vector */ - - _ve_envelope_apply(vb); - /* Overlap/add */ switch(vb->W){ case 0: @@ -549,26 +528,6 @@ int vorbis_synthesis_read(vorbis_dsp_state *v,int bytes){ #include #include -void _ve_envelope_multipliers(vorbis_dsp_state *v){ - /* set 'random' deltas... */ - int new_current=v->pcm_current/v->samples_per_envelope_step; - int i,j; - - for(i=v->envelope_current;isamples_per_envelope_step;j++) - if(v->pcm[0][j+i*v->samples_per_envelope_step]>5)flag=1; - - for(j=0;jenvelope_channels;j++) - v->multipliers[j][i]=flag; - } - v->envelope_current=i; -} - -void _ve_envelope_apply(vorbis_block *vb){ - -} - /* basic test of PCM blocking: construct a PCM vector and block it using preset sizing in our fake @@ -594,7 +553,6 @@ int main(){ vi.rate=44100; vi.version=0; vi.mode=0; - vi.user_comments=temp; vi.vendor="Xiphophorus"; @@ -631,6 +589,10 @@ int main(){ double *window=encode.window[vb.W][vb.lW][vb.nW]; + /* apply envelope */ + _ve_envelope_sparsify(&vb); + _ve_envelope_apply(&vb,0); + for(i=0;i modifications by: Monty - last modification date: Jun 17 1999 + last modification date: Aug 05 1999 Vorbis manipulates the dynamic range of the incoming PCM data envelope to minimise time-domain energy leakage from percussive and @@ -23,183 +23,235 @@ ********************************************************************/ #include +#include #include #include -static typedef struct { - int divisor; - double *window; -} envelope_lookup; +#include "codec.h" +#include "envelope.h" -static double oPI = 3.14159265358979323846; - -envelope_lookup *init_envelope(int length,int divleng){ - envelope_lookup *ret=malloc(sizeof(envelope_lookup)); +void _ve_envelope_init(envelope_lookup *e,int samples_per){ int i; - ret->length=divleng; - ret->window=malloc(divleng*sizeof(double)*2); + e->winlen=samples_per*2; + e->window=malloc(e->winlen*sizeof(double)); /* We just use a straight sin^2(x) window for this */ - for(i=0;iwindow[i]=temp*temp; + for(i=0;iwinlen;i++){ + double temp=sin((i+.5)/e->winlen*M_PI); + e->window[i]=temp*temp; } } -/* right now, we do things simple and dirty. Should this prove - inadequate, then we'll think of something different. The details - of the encoding format do not depend on the exact behavior, only - the format of the bits that come out. - - Using residual from an LPC whitening filter to judge envelope - energy would probably yield cleaner results, but that's slow. - Let's see if simple delta analysis gives us acceptible results. */ +/* initial and final blocks are special cases. Eg: + ______ + `--_ + |_______|_`-.___|_______|_______| -int analyze_envelope0(double *vector, envelope_lookup *init, int n, - double *deltas){ + ___ + _--' `--_ + |___.-'_|_______|_`-.___|_______| - int divisor=init->length; - int divs=n/divisor-1; - double *win=init->window; - int i,j,count=0; + ___ + _--' `--_ + |_______|___.-'_|_______|_`-.___| - double max,spanlo,spanhi; - - /* initial and final blocks are special cases. Eg: - ______________ - \ - |_______|______\|_______|_______| - - ___________ - / \ - |_______|/______|______\|_______| - - _____________ - / - |_______|_______|/______|_______| + _____ + _--' + |_______|_______|____.-'|_______| - as we go block by block, we watch the collective metrics span. If we - span the threshhold (assuming the threshhold is active), we use an - abbreviated vector */ + as we go block by block, we watch the collective metrics span. If we + span the threshhold (assuming the threshhold is active), we use an + abbreviated vector */ + +static void _ve_envelope_generate(double *mult,double *env,double *look, + int n,int step){ + int i,j,p; + double m; + n*=step; + + /* first multiplier special case */ + m=ldexp(2,mult[0]-1); + for(i=0;ispanhi)spanhi=max; - if(threshhold>1 && spanlo*threshholdn0-divisor/2)break; + p++; } +} - /* last frame */ - if(!abbrevflag){ - max=0; - for(i=1;isamples_per_envelope_step; + static int frame=0; + + /* we need a 1-1/4 envelope window overlap begin and 1/4 end */ + int dtotal=(v->pcm_current-step/2)/v->samples_per_envelope_step; + int dcurr=v->envelope_current; + double *window=v->ve.window; + int winlen=v->ve.winlen; + int pch,ech; + vorbis_info *vi=&v->vi; + + if(dtotal>dcurr){ + for(ech=0;echenvelopech;ech++){ + double *mult=v->multipliers[ech]+dcurr; + memset(mult,0,sizeof(double)*(dtotal-dcurr)); + + for(pch=0;pchchannels;pch++){ + + /* does this channel contribute to the envelope analysis */ + if(vi->envelopemap[pch]==ech){ + + /* we need a 1/4 envelope window overlap front and back */ + double *pcm=v->pcm[pch]+dcurr*step-step/2; + _ve_deltas(mult,pcm,dtotal-dcurr,window,winlen); + + } + } } - deltas[count++]=max; - if(maxspanhi)spanhi=max; - if(threshhold>1 && spanlo*threshholdenvelope_current=dtotal; + frame++; } +} - if(abbrevflag)return(n0); - return(n); -} - -/* also decide if we're going with a full sized or abbreviated - vector. Some encoding tactics might want to use envelope massaging - fully and discard abbreviated vectors entriely. We make that - decision here */ - -int analyze_envelope1(envelope_lookup *init,int n, - double triggerthresh,double spanthresh, - double *deltas){ - - /* Look at the delta values; decide if we need to do any envelope - manipulation at all on this vector; if so, choose the - multipliers and placeholders. - - '0' is a placeholder. Other values specify a - multiplier/divisor. Multipliers are used by the decoder, divisors - in the encoder. The mapped m/d value for each segment is - 2^(n-1). Placeholders (zeros) take on the value of the last - non-zero multiplier/divisor. When the placeholder is not - preceeded by a non-placeholder value in the current vector, it - assumes the value of the *next* non-zero value. In this way, the - vector manipulation is local to the current vector and does not - rely on preceeding vectors. - - */ - - /* scan forward with sliding windows; we start manipulating envelopes - when the collective deltas span over a threshhold. If in fact we - begin manipulating, we can manage on a finer scale than the - original threshhold. first look for the larger threshhold and if - we span it, manipulate the vector to hold within the smaller span - threshhold. */ - - /* scan for the trigger */ - - int divisor=init->length; - int divs=n/divisor-1; - int i,triggerflag=0; - double spanlo,spanhi; - - spanlo=spanhi=deltas[0]; - - for(i=1;ispanhi)spanhi=max; - if(spanlo*triggerthreshvd->vi.envelopech;ch++){ + int flag=0; + double *mult=vb->mult[ch]; + int n=vb->multend; + double first=mult[0]; + double last=first; + double clamp; + int i; + + /* are we going to multiply anything? */ + + for(i=1;i=last*vb->vd->vi.preecho_thresh){ + flag=1; + break; + } + if(i=last*vb->vd->vi.preecho_thresh){ + flag=1; + break; + } + last=mult[i]; } + + if(flag){ + /* we need to adjust, so we might as well go nuts */ + + int begin=i; + clamp=last?last:1; + + for(i=0;iclamp*vb->vd->vi.preecho_thresh){ + last=mult[i]/vb->vd->vi.preecho_clamp; + + mult[i]=floor(log(mult[i]/clamp/vb->vd->vi.preecho_clamp)/log(2))-1; + if(mult[i]>15)mult[i]=15; + }else{ + mult[i]=0; + } + } + }else + memset(mult,0,sizeof(double)*n); } +} - if(triggerflag){ - /* choose divisors/multipliers to fit the vector into the - specified span. In the decoder, these values are *multipliers*, so */ - - - - - - - - - +void _ve_envelope_apply(vorbis_block *vb,int multp){ + vorbis_info *vi=&vb->vd->vi; + double env[vb->multend*vi->envelopesa]; + envelope_lookup *look=&vb->vd->ve; + int i,j,k; + + for(i=0;ienvelopech;i++){ + double *mult=vb->mult[i]; + double last=1.; + + /* fill in the multiplier placeholders */ + + for(j=0;jmultend;j++){ + if(mult[j]){ + last=mult[j]; + }else + mult[j]=last; + } + /* compute the envelope curve */ + _ve_envelope_generate(mult,env,look->window,vb->multend,vi->envelopesa); + /* apply the envelope curve */ + for(j=0;jchannels;j++){ + /* check to see if the generated envelope applies to this channel */ + if(vi->envelopemap[j]==i){ + + if(multp) + for(k=0;kmultend*vi->envelopesa;k++) + vb->pcm[j][k]*=env[k]; + else + for(k=0;kmultend*vi->envelopesa;k++) + vb->pcm[j][k]/=env[k]; + } + } } - return(triggerflag); } diff --git a/lib/envelope.h b/lib/envelope.h index 0b10b5e..8f25966 100644 --- a/lib/envelope.h +++ b/lib/envelope.h @@ -14,7 +14,7 @@ function: PCM data envelope analysis and manipulation author: Monty modifications by: Monty - last modification date: Jul 27 1999 + last modification date: Aug 06 1999 ********************************************************************/ @@ -22,7 +22,9 @@ #define _V_ENVELOPE_ extern void _ve_envelope_multipliers(vorbis_dsp_state *v); -extern void _ve_envelope_apply(vorbis_block *vb); +extern void _ve_envelope_apply(vorbis_block *vb,int multp); +extern void _ve_envelope_sparsify(vorbis_block *vb); +extern void _ve_envelope_init(envelope_lookup *e,int samples_per); #endif -- 2.7.4