X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=lib%2Fenvelope.c;h=22d39aa6e0bb57572bffeebf606beeb0a6d79dab;hb=6819d9f05fc989d2b32c455f879d3243544ebc1a;hp=fa4da0d062e50ea5c88c071c24c291af5e3ecba4;hpb=08d23109f900f2301e75520ff17a43b16fdd076d;p=platform%2Fupstream%2Flibvorbis.git diff --git a/lib/envelope.c b/lib/envelope.c index fa4da0d..22d39aa 100644 --- a/lib/envelope.c +++ b/lib/envelope.c @@ -1,20 +1,16 @@ /******************************************************************** * * * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * - * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY * - * THE GNU LESSER/LIBRARY PUBLIC LICENSE, WHICH IS INCLUDED WITH * - * THIS SOURCE. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * 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 OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2000 * - * by Monty and the XIPHOPHORUS Company * - * http://www.xiph.org/ * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation https://xiph.org/ * * * ******************************************************************** - function: PCM data envelope analysis and manipulation - last mod: $Id: envelope.c,v 1.27 2000/12/13 07:37:20 msmith Exp $ - - Preecho calculation. + function: PCM data envelope analysis ********************************************************************/ @@ -29,196 +25,350 @@ #include "os.h" #include "scales.h" #include "envelope.h" +#include "mdct.h" #include "misc.h" -/* We use a Chebyshev bandbass for the preecho trigger bandpass; it's - close enough for sample rates 32000-48000 Hz (corner frequencies at - 6k/14k assuming sample rate of 44.1kHz) */ - -/* Digital filter designed by mkfilter/mkshape/gencode A.J. Fisher - Command line: /www/usr/fisher/helpers/mkfilter -Ch \ - -6.0000000000e+00 -Bp -o 5 -a 1.3605442177e-01 3.1746031746e-01 -l */ - -#if 0 -static int cheb_bandpass_stages=10; -static float cheb_bandpass_gain=5.589612458e+01; -static float cheb_bandpass_B[]={-1.,0.,5.,0.,-10.,0.,10.,0.,-5.,0.,1}; -static float cheb_bandpass_A[]={ - -0.1917409386, - 0.0078657069, - -0.7126903444, - 0.0266343467, - -1.4047174730, - 0.0466964232, - -1.9032773429, - 0.0451493360, - -1.4471447397, - 0.0303413711}; -#endif - -static int cheb_highpass_stages=10; -static float cheb_highpass_gain= 5.291963434e+01; -/* z^-stage, z^-stage+1... */ -static float cheb_highpass_B[]={1,-10,45,-120,210,-252,210,-120,45,-10,1}; -static float cheb_highpass_A[]={ - -0.1247628029, - 0.1334086523, - -0.3997715614, - 0.3213011089, - -1.1131924119, - 1.7692446626, - -3.6241199038, - 4.1950871291, - -4.2771757867, - 2.3920318913}; - void _ve_envelope_init(envelope_lookup *e,vorbis_info *vi){ codec_setup_info *ci=vi->codec_setup; + vorbis_info_psy_global *gi=&ci->psy_g_param; int ch=vi->channels; - int window=ci->envelopesa; - int i; - e->winlength=window; - e->minenergy=fromdB(ci->preecho_minenergy); - e->iir=_ogg_calloc(ch,sizeof(IIR_state)); - e->filtered=_ogg_calloc(ch,sizeof(float *)); + int i,j; + int n=e->winlength=128; + e->searchstep=64; /* not random */ + + e->minenergy=gi->preecho_minenergy; e->ch=ch; e->storage=128; - for(i=0;iiir+i,cheb_highpass_stages,cheb_highpass_gain, - cheb_highpass_A,cheb_highpass_B); - e->filtered[i]=_ogg_calloc(e->storage,sizeof(float)); + e->cursor=ci->blocksizes[1]/2; + e->mdct_win=_ogg_calloc(n,sizeof(*e->mdct_win)); + mdct_init(&e->mdct,n); + + for(i=0;imdct_win[i]=sin(i/(n-1.)*M_PI); + e->mdct_win[i]*=e->mdct_win[i]; + } + + /* magic follows */ + e->band[0].begin=2; e->band[0].end=4; + e->band[1].begin=4; e->band[1].end=5; + e->band[2].begin=6; e->band[2].end=6; + e->band[3].begin=9; e->band[3].end=8; + e->band[4].begin=13; e->band[4].end=8; + e->band[5].begin=17; e->band[5].end=8; + e->band[6].begin=22; e->band[6].end=8; + + for(j=0;jband[j].end; + e->band[j].window=_ogg_malloc(n*sizeof(*e->band[0].window)); + for(i=0;iband[j].window[i]=sin((i+.5)/n*M_PI); + e->band[j].total+=e->band[j].window[i]; + } + e->band[j].total=1./e->band[j].total; } - drft_init(&e->drft,window); - e->window=_ogg_malloc(e->winlength*sizeof(float)); - /* We just use a straight sin(x) window for this */ - for(i=0;iwinlength;i++) - e->window[i]=sin((i+.5)/e->winlength*M_PI); + e->filter=_ogg_calloc(VE_BANDS*ch,sizeof(*e->filter)); + e->mark=_ogg_calloc(e->storage,sizeof(*e->mark)); + } void _ve_envelope_clear(envelope_lookup *e){ int i; - for(i=0;ich;i++){ - IIR_clear((e->iir+i)); - _ogg_free(e->filtered[i]); - } - drft_clear(&e->drft); - _ogg_free(e->window); - _ogg_free(e->filtered); - _ogg_free(e->iir); - memset(e,0,sizeof(envelope_lookup)); + mdct_clear(&e->mdct); + for(i=0;iband[i].window); + _ogg_free(e->mdct_win); + _ogg_free(e->filter); + _ogg_free(e->mark); + memset(e,0,sizeof(*e)); } -static float _ve_deltai(envelope_lookup *ve,IIR_state *iir, - float *pre,float *post){ - long n2=ve->winlength*2; +/* fairly straight threshhold-by-band based until we find something + that works better and isn't patented. */ + +static int _ve_amp(envelope_lookup *ve, + vorbis_info_psy_global *gi, + float *data, + envelope_band *bands, + envelope_filter_state *filters){ long n=ve->winlength; + int ret=0; + long i,j; + float decay; - float *workA=alloca(sizeof(float)*n2),A=0.; - float *workB=alloca(sizeof(float)*n2),B=0.; - long i; + /* we want to have a 'minimum bar' for energy, else we're just + basing blocks on quantization noise that outweighs the signal + itself (for low power signals) */ - /*_analysis_output("A",granulepos,pre,n,0,0); - _analysis_output("B",granulepos,post,n,0,0);*/ + float minV=ve->minenergy; + float *vec=alloca(n*sizeof(*vec)); - for(i=0;iwindow[i]; - workB[i]=post[i]*ve->window[i]; - } + /* stretch is used to gradually lengthen the number of windows + considered prevoius-to-potential-trigger */ + int stretch=max(VE_MINSTRETCH,ve->stretch/2); + float penalty=gi->stretch_penalty-(ve->stretch/2-VE_MINSTRETCH); + if(penalty<0.f)penalty=0.f; + if(penalty>gi->stretch_penalty)penalty=gi->stretch_penalty; - /*_analysis_output("Awin",granulepos,workA,n,0,0); - _analysis_output("Bwin",granulepos,workB,n,0,0);*/ + /*_analysis_output_always("lpcm",seq2,data,n,0,0, + totalshift+pos*ve->searchstep);*/ - drft_forward(&ve->drft,workA); - drft_forward(&ve->drft,workB); + /* window and transform */ + for(i=0;imdct_win[i]; + mdct_forward(&ve->mdct,vec,vec); - /* we want to have a 'minimum bar' for energy, else we're just - basing blocks on quantization noise that outweighs the signal - itself (for low power signals) */ + /*_analysis_output_always("mdct",seq2,vec,n/2,0,1,0); */ + + /* near-DC spreading function; this has nothing to do with + psychoacoustics, just sidelobe leakage and window size */ { - float min=ve->minenergy; - for(i=0;inearptr; + + /* the accumulation is regularly refreshed from scratch to avoid + floating point creep */ + if(ptr==0){ + decay=filters->nearDC_acc=filters->nearDC_partialacc+temp; + filters->nearDC_partialacc=temp; + }else{ + decay=filters->nearDC_acc+=temp; + filters->nearDC_partialacc+=temp; } - } + filters->nearDC_acc-=filters->nearDC[ptr]; + filters->nearDC[ptr]=temp; - /*_analysis_output("Afft",granulepos,workA,n,0,0); - _analysis_output("Bfft",granulepos,workB,n,0,0);*/ + decay*=(1./(VE_NEARDC+1)); + filters->nearptr++; + if(filters->nearptr>=VE_NEARDC)filters->nearptr=0; + decay=todB(&decay)*.5-15.f; + } - for(i=0;i>1]=val; + decay-=8.; } - A=todB(A); - B=todB(B); + /*_analysis_output_always("spread",seq2++,vec,n/4,0,0,0);*/ + + /* perform preecho/postecho triggering by band */ + for(j=0;j=VE_AMP)filters[j].ampptr=0; + } + + /* look at min/max, decide trigger */ + if(valmax>gi->preecho_thresh[j]+penalty){ + ret|=1; + ret|=4; + } + if(valminpostecho_thresh[j]-penalty)ret|=2; + } - return(B-A); + return(ret); } -long _ve_envelope_search(vorbis_dsp_state *v,long searchpoint){ +#if 0 +static int seq=0; +static ogg_int64_t totalshift=-1024; +#endif + +long _ve_envelope_search(vorbis_dsp_state *v){ vorbis_info *vi=v->vi; codec_setup_info *ci=vi->codec_setup; - envelope_lookup *ve=((backend_lookup_state *)(v->backend_state))->ve; + vorbis_info_psy_global *gi=&ci->psy_g_param; + envelope_lookup *ve=((private_state *)(v->backend_state))->ve; long i,j; - + + int first=ve->current/ve->searchstep; + int last=v->pcm_current/ve->searchstep-VE_WIN; + if(first<0)first=0; + /* make sure we have enough storage to match the PCM */ - if(v->pcm_storage>ve->storage){ - ve->storage=v->pcm_storage; - for(i=0;ich;i++) - ve->filtered[i]=_ogg_realloc(ve->filtered[i],ve->storage*sizeof(float)); + if(last+VE_WIN+VE_POST>ve->storage){ + ve->storage=last+VE_WIN+VE_POST; /* be sure */ + ve->mark=_ogg_realloc(ve->mark,ve->storage*sizeof(*ve->mark)); } - /* catch up the highpass to match the pcm */ - for(i=0;ich;i++){ - float *filtered=ve->filtered[i]; - float *pcm=v->pcm[i]; - IIR_state *iir=ve->iir+i; - int flag=1; - - for(j=ve->current;jpcm_current;j++){ - filtered[j]=IIR_filter(iir,pcm[j]); - if(pcm[j])flag=0; + for(j=first;jstretch++; + if(ve->stretch>VE_MAXSTRETCH*2) + ve->stretch=VE_MAXSTRETCH*2; + + for(i=0;ich;i++){ + float *pcm=v->pcm[i]+ve->searchstep*(j); + ret|=_ve_amp(ve,gi,pcm,ve->band,ve->filter+i*VE_BANDS); + } + + ve->mark[j+VE_POST]=0; + if(ret&1){ + ve->mark[j]=1; + ve->mark[j+1]=1; + } + + if(ret&2){ + ve->mark[j]=1; + if(j>0)ve->mark[j-1]=1; } - if(flag && ve->current+64pcm_current)IIR_reset(iir); + + if(ret&4)ve->stretch=-1; } - ve->current=v->pcm_current; + ve->current=last*ve->searchstep; + + { + long centerW=v->centerW; + long testW= + centerW+ + ci->blocksizes[v->W]/4+ + ci->blocksizes[1]/2+ + ci->blocksizes[0]/4; - /* Now search through our cached highpass data for breaking points */ - /* starting point */ - if(v->W) - j=v->centerW+ci->blocksizes[1]/4-ci->blocksizes[0]/4; - else - j=v->centerW; + j=ve->cursor; - while(j+ve->winlength<=v->pcm_current){ - for(i=0;ich;i++){ - float *filtered=ve->filtered[i]+j; - IIR_state *iir=ve->iir+i; - float m=_ve_deltai(ve,iir,filtered-ve->winlength,filtered); - - if(m>ci->preecho_thresh){ - /*granulepos++;*/ - return(0); + while(jcurrent-(ve->searchstep)){/* account for postecho + working back one window */ + if(j>=testW)return(1); + + ve->cursor=j; + + if(ve->mark[j/ve->searchstep]){ + if(j>centerW){ + +#if 0 + if(j>ve->curmark){ + float *marker=alloca(v->pcm_current*sizeof(*marker)); + int l,m; + memset(marker,0,sizeof(*marker)*v->pcm_current); + fprintf(stderr,"mark! seq=%d, cursor:%fs time:%fs\n", + seq, + (totalshift+ve->cursor)/44100., + (totalshift+j)/44100.); + _analysis_output_always("pcmL",seq,v->pcm[0],v->pcm_current,0,0,totalshift); + _analysis_output_always("pcmR",seq,v->pcm[1],v->pcm_current,0,0,totalshift); + + _analysis_output_always("markL",seq,v->pcm[0],j,0,0,totalshift); + _analysis_output_always("markR",seq,v->pcm[1],j,0,0,totalshift); + + for(m=0;msearchstep]=ve->filter[m].markers[l]*.1; + _analysis_output_always(buf,seq,marker,v->pcm_current,0,0,totalshift); + } + + for(m=0;msearchstep]=ve->filter[m+VE_BANDS].markers[l]*.1; + _analysis_output_always(buf,seq,marker,v->pcm_current,0,0,totalshift); + } + + for(l=0;lsearchstep]=ve->mark[l]*.4; + _analysis_output_always("mark",seq,marker,v->pcm_current,0,0,totalshift); + + + seq++; + + } +#endif + + ve->curmark=j; + if(j>=testW)return(1); + return(0); + } } - /*granulepos++;*/ + j+=ve->searchstep; } - - j+=ci->blocksizes[0]/2; - if(j>=searchpoint)return(1); } - + return(-1); } -void _ve_envelope_shift(envelope_lookup *e,long shift){ - int i; - for(i=0;ich;i++) - memmove(e->filtered[i],e->filtered[i]+shift,(e->current-shift)* - sizeof(float)); - e->current-=shift; +int _ve_envelope_mark(vorbis_dsp_state *v){ + envelope_lookup *ve=((private_state *)(v->backend_state))->ve; + vorbis_info *vi=v->vi; + codec_setup_info *ci=vi->codec_setup; + long centerW=v->centerW; + long beginW=centerW-ci->blocksizes[v->W]/4; + long endW=centerW+ci->blocksizes[v->W]/4; + if(v->W){ + beginW-=ci->blocksizes[v->lW]/4; + endW+=ci->blocksizes[v->nW]/4; + }else{ + beginW-=ci->blocksizes[0]/4; + endW+=ci->blocksizes[0]/4; + } + + if(ve->curmark>=beginW && ve->curmarksearchstep; + long last=endW/ve->searchstep; + long i; + for(i=first;imark[i])return(1); + } + return(0); } +void _ve_envelope_shift(envelope_lookup *e,long shift){ + int smallsize=e->current/e->searchstep+VE_POST; /* adjust for placing marks + ahead of ve->current */ + int smallshift=shift/e->searchstep; + memmove(e->mark,e->mark+smallshift,(smallsize-smallshift)*sizeof(*e->mark)); + +#if 0 + for(i=0;ich;i++) + memmove(e->filter[i].markers, + e->filter[i].markers+smallshift, + (1024-smallshift)*sizeof(*(*e->filter).markers)); + totalshift+=shift; +#endif + + e->current-=shift; + if(e->curmark>=0) + e->curmark-=shift; + e->cursor-=shift; +}