vrshoot
diff libs/vorbis/envelope.c @ 0:b2f14e535253
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 01 Feb 2014 19:58:19 +0200 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libs/vorbis/envelope.c Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,375 @@ 1.4 +/******************************************************************** 1.5 + * * 1.6 + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * 1.7 + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * 1.8 + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * 1.9 + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * 1.10 + * * 1.11 + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * 1.12 + * by the Xiph.Org Foundation http://www.xiph.org/ * 1.13 + * * 1.14 + ******************************************************************** 1.15 + 1.16 + function: PCM data envelope analysis 1.17 + last mod: $Id: envelope.c 16227 2009-07-08 06:58:46Z xiphmont $ 1.18 + 1.19 + ********************************************************************/ 1.20 + 1.21 +#include <stdlib.h> 1.22 +#include <string.h> 1.23 +#include <stdio.h> 1.24 +#include <math.h> 1.25 +#include <ogg/ogg.h> 1.26 +#include "vorbis/codec.h" 1.27 +#include "codec_internal.h" 1.28 + 1.29 +#include "os.h" 1.30 +#include "scales.h" 1.31 +#include "envelope.h" 1.32 +#include "mdct.h" 1.33 +#include "misc.h" 1.34 + 1.35 +void _ve_envelope_init(envelope_lookup *e,vorbis_info *vi){ 1.36 + codec_setup_info *ci=vi->codec_setup; 1.37 + vorbis_info_psy_global *gi=&ci->psy_g_param; 1.38 + int ch=vi->channels; 1.39 + int i,j; 1.40 + int n=e->winlength=128; 1.41 + e->searchstep=64; /* not random */ 1.42 + 1.43 + e->minenergy=gi->preecho_minenergy; 1.44 + e->ch=ch; 1.45 + e->storage=128; 1.46 + e->cursor=ci->blocksizes[1]/2; 1.47 + e->mdct_win=_ogg_calloc(n,sizeof(*e->mdct_win)); 1.48 + mdct_init(&e->mdct,n); 1.49 + 1.50 + for(i=0;i<n;i++){ 1.51 + e->mdct_win[i]=sin(i/(n-1.)*M_PI); 1.52 + e->mdct_win[i]*=e->mdct_win[i]; 1.53 + } 1.54 + 1.55 + /* magic follows */ 1.56 + e->band[0].begin=2; e->band[0].end=4; 1.57 + e->band[1].begin=4; e->band[1].end=5; 1.58 + e->band[2].begin=6; e->band[2].end=6; 1.59 + e->band[3].begin=9; e->band[3].end=8; 1.60 + e->band[4].begin=13; e->band[4].end=8; 1.61 + e->band[5].begin=17; e->band[5].end=8; 1.62 + e->band[6].begin=22; e->band[6].end=8; 1.63 + 1.64 + for(j=0;j<VE_BANDS;j++){ 1.65 + n=e->band[j].end; 1.66 + e->band[j].window=_ogg_malloc(n*sizeof(*e->band[0].window)); 1.67 + for(i=0;i<n;i++){ 1.68 + e->band[j].window[i]=sin((i+.5)/n*M_PI); 1.69 + e->band[j].total+=e->band[j].window[i]; 1.70 + } 1.71 + e->band[j].total=1./e->band[j].total; 1.72 + } 1.73 + 1.74 + e->filter=_ogg_calloc(VE_BANDS*ch,sizeof(*e->filter)); 1.75 + e->mark=_ogg_calloc(e->storage,sizeof(*e->mark)); 1.76 + 1.77 +} 1.78 + 1.79 +void _ve_envelope_clear(envelope_lookup *e){ 1.80 + int i; 1.81 + mdct_clear(&e->mdct); 1.82 + for(i=0;i<VE_BANDS;i++) 1.83 + _ogg_free(e->band[i].window); 1.84 + _ogg_free(e->mdct_win); 1.85 + _ogg_free(e->filter); 1.86 + _ogg_free(e->mark); 1.87 + memset(e,0,sizeof(*e)); 1.88 +} 1.89 + 1.90 +/* fairly straight threshhold-by-band based until we find something 1.91 + that works better and isn't patented. */ 1.92 + 1.93 +static int _ve_amp(envelope_lookup *ve, 1.94 + vorbis_info_psy_global *gi, 1.95 + float *data, 1.96 + envelope_band *bands, 1.97 + envelope_filter_state *filters){ 1.98 + long n=ve->winlength; 1.99 + int ret=0; 1.100 + long i,j; 1.101 + float decay; 1.102 + 1.103 + /* we want to have a 'minimum bar' for energy, else we're just 1.104 + basing blocks on quantization noise that outweighs the signal 1.105 + itself (for low power signals) */ 1.106 + 1.107 + float minV=ve->minenergy; 1.108 + float *vec=alloca(n*sizeof(*vec)); 1.109 + 1.110 + /* stretch is used to gradually lengthen the number of windows 1.111 + considered prevoius-to-potential-trigger */ 1.112 + int stretch=max(VE_MINSTRETCH,ve->stretch/2); 1.113 + float penalty=gi->stretch_penalty-(ve->stretch/2-VE_MINSTRETCH); 1.114 + if(penalty<0.f)penalty=0.f; 1.115 + if(penalty>gi->stretch_penalty)penalty=gi->stretch_penalty; 1.116 + 1.117 + /*_analysis_output_always("lpcm",seq2,data,n,0,0, 1.118 + totalshift+pos*ve->searchstep);*/ 1.119 + 1.120 + /* window and transform */ 1.121 + for(i=0;i<n;i++) 1.122 + vec[i]=data[i]*ve->mdct_win[i]; 1.123 + mdct_forward(&ve->mdct,vec,vec); 1.124 + 1.125 + /*_analysis_output_always("mdct",seq2,vec,n/2,0,1,0); */ 1.126 + 1.127 + /* near-DC spreading function; this has nothing to do with 1.128 + psychoacoustics, just sidelobe leakage and window size */ 1.129 + { 1.130 + float temp=vec[0]*vec[0]+.7*vec[1]*vec[1]+.2*vec[2]*vec[2]; 1.131 + int ptr=filters->nearptr; 1.132 + 1.133 + /* the accumulation is regularly refreshed from scratch to avoid 1.134 + floating point creep */ 1.135 + if(ptr==0){ 1.136 + decay=filters->nearDC_acc=filters->nearDC_partialacc+temp; 1.137 + filters->nearDC_partialacc=temp; 1.138 + }else{ 1.139 + decay=filters->nearDC_acc+=temp; 1.140 + filters->nearDC_partialacc+=temp; 1.141 + } 1.142 + filters->nearDC_acc-=filters->nearDC[ptr]; 1.143 + filters->nearDC[ptr]=temp; 1.144 + 1.145 + decay*=(1./(VE_NEARDC+1)); 1.146 + filters->nearptr++; 1.147 + if(filters->nearptr>=VE_NEARDC)filters->nearptr=0; 1.148 + decay=todB(&decay)*.5-15.f; 1.149 + } 1.150 + 1.151 + /* perform spreading and limiting, also smooth the spectrum. yes, 1.152 + the MDCT results in all real coefficients, but it still *behaves* 1.153 + like real/imaginary pairs */ 1.154 + for(i=0;i<n/2;i+=2){ 1.155 + float val=vec[i]*vec[i]+vec[i+1]*vec[i+1]; 1.156 + val=todB(&val)*.5f; 1.157 + if(val<decay)val=decay; 1.158 + if(val<minV)val=minV; 1.159 + vec[i>>1]=val; 1.160 + decay-=8.; 1.161 + } 1.162 + 1.163 + /*_analysis_output_always("spread",seq2++,vec,n/4,0,0,0);*/ 1.164 + 1.165 + /* perform preecho/postecho triggering by band */ 1.166 + for(j=0;j<VE_BANDS;j++){ 1.167 + float acc=0.; 1.168 + float valmax,valmin; 1.169 + 1.170 + /* accumulate amplitude */ 1.171 + for(i=0;i<bands[j].end;i++) 1.172 + acc+=vec[i+bands[j].begin]*bands[j].window[i]; 1.173 + 1.174 + acc*=bands[j].total; 1.175 + 1.176 + /* convert amplitude to delta */ 1.177 + { 1.178 + int p,this=filters[j].ampptr; 1.179 + float postmax,postmin,premax=-99999.f,premin=99999.f; 1.180 + 1.181 + p=this; 1.182 + p--; 1.183 + if(p<0)p+=VE_AMP; 1.184 + postmax=max(acc,filters[j].ampbuf[p]); 1.185 + postmin=min(acc,filters[j].ampbuf[p]); 1.186 + 1.187 + for(i=0;i<stretch;i++){ 1.188 + p--; 1.189 + if(p<0)p+=VE_AMP; 1.190 + premax=max(premax,filters[j].ampbuf[p]); 1.191 + premin=min(premin,filters[j].ampbuf[p]); 1.192 + } 1.193 + 1.194 + valmin=postmin-premin; 1.195 + valmax=postmax-premax; 1.196 + 1.197 + /*filters[j].markers[pos]=valmax;*/ 1.198 + filters[j].ampbuf[this]=acc; 1.199 + filters[j].ampptr++; 1.200 + if(filters[j].ampptr>=VE_AMP)filters[j].ampptr=0; 1.201 + } 1.202 + 1.203 + /* look at min/max, decide trigger */ 1.204 + if(valmax>gi->preecho_thresh[j]+penalty){ 1.205 + ret|=1; 1.206 + ret|=4; 1.207 + } 1.208 + if(valmin<gi->postecho_thresh[j]-penalty)ret|=2; 1.209 + } 1.210 + 1.211 + return(ret); 1.212 +} 1.213 + 1.214 +#if 0 1.215 +static int seq=0; 1.216 +static ogg_int64_t totalshift=-1024; 1.217 +#endif 1.218 + 1.219 +long _ve_envelope_search(vorbis_dsp_state *v){ 1.220 + vorbis_info *vi=v->vi; 1.221 + codec_setup_info *ci=vi->codec_setup; 1.222 + vorbis_info_psy_global *gi=&ci->psy_g_param; 1.223 + envelope_lookup *ve=((private_state *)(v->backend_state))->ve; 1.224 + long i,j; 1.225 + 1.226 + int first=ve->current/ve->searchstep; 1.227 + int last=v->pcm_current/ve->searchstep-VE_WIN; 1.228 + if(first<0)first=0; 1.229 + 1.230 + /* make sure we have enough storage to match the PCM */ 1.231 + if(last+VE_WIN+VE_POST>ve->storage){ 1.232 + ve->storage=last+VE_WIN+VE_POST; /* be sure */ 1.233 + ve->mark=_ogg_realloc(ve->mark,ve->storage*sizeof(*ve->mark)); 1.234 + } 1.235 + 1.236 + for(j=first;j<last;j++){ 1.237 + int ret=0; 1.238 + 1.239 + ve->stretch++; 1.240 + if(ve->stretch>VE_MAXSTRETCH*2) 1.241 + ve->stretch=VE_MAXSTRETCH*2; 1.242 + 1.243 + for(i=0;i<ve->ch;i++){ 1.244 + float *pcm=v->pcm[i]+ve->searchstep*(j); 1.245 + ret|=_ve_amp(ve,gi,pcm,ve->band,ve->filter+i*VE_BANDS); 1.246 + } 1.247 + 1.248 + ve->mark[j+VE_POST]=0; 1.249 + if(ret&1){ 1.250 + ve->mark[j]=1; 1.251 + ve->mark[j+1]=1; 1.252 + } 1.253 + 1.254 + if(ret&2){ 1.255 + ve->mark[j]=1; 1.256 + if(j>0)ve->mark[j-1]=1; 1.257 + } 1.258 + 1.259 + if(ret&4)ve->stretch=-1; 1.260 + } 1.261 + 1.262 + ve->current=last*ve->searchstep; 1.263 + 1.264 + { 1.265 + long centerW=v->centerW; 1.266 + long testW= 1.267 + centerW+ 1.268 + ci->blocksizes[v->W]/4+ 1.269 + ci->blocksizes[1]/2+ 1.270 + ci->blocksizes[0]/4; 1.271 + 1.272 + j=ve->cursor; 1.273 + 1.274 + while(j<ve->current-(ve->searchstep)){/* account for postecho 1.275 + working back one window */ 1.276 + if(j>=testW)return(1); 1.277 + 1.278 + ve->cursor=j; 1.279 + 1.280 + if(ve->mark[j/ve->searchstep]){ 1.281 + if(j>centerW){ 1.282 + 1.283 +#if 0 1.284 + if(j>ve->curmark){ 1.285 + float *marker=alloca(v->pcm_current*sizeof(*marker)); 1.286 + int l,m; 1.287 + memset(marker,0,sizeof(*marker)*v->pcm_current); 1.288 + fprintf(stderr,"mark! seq=%d, cursor:%fs time:%fs\n", 1.289 + seq, 1.290 + (totalshift+ve->cursor)/44100., 1.291 + (totalshift+j)/44100.); 1.292 + _analysis_output_always("pcmL",seq,v->pcm[0],v->pcm_current,0,0,totalshift); 1.293 + _analysis_output_always("pcmR",seq,v->pcm[1],v->pcm_current,0,0,totalshift); 1.294 + 1.295 + _analysis_output_always("markL",seq,v->pcm[0],j,0,0,totalshift); 1.296 + _analysis_output_always("markR",seq,v->pcm[1],j,0,0,totalshift); 1.297 + 1.298 + for(m=0;m<VE_BANDS;m++){ 1.299 + char buf[80]; 1.300 + sprintf(buf,"delL%d",m); 1.301 + for(l=0;l<last;l++)marker[l*ve->searchstep]=ve->filter[m].markers[l]*.1; 1.302 + _analysis_output_always(buf,seq,marker,v->pcm_current,0,0,totalshift); 1.303 + } 1.304 + 1.305 + for(m=0;m<VE_BANDS;m++){ 1.306 + char buf[80]; 1.307 + sprintf(buf,"delR%d",m); 1.308 + for(l=0;l<last;l++)marker[l*ve->searchstep]=ve->filter[m+VE_BANDS].markers[l]*.1; 1.309 + _analysis_output_always(buf,seq,marker,v->pcm_current,0,0,totalshift); 1.310 + } 1.311 + 1.312 + for(l=0;l<last;l++)marker[l*ve->searchstep]=ve->mark[l]*.4; 1.313 + _analysis_output_always("mark",seq,marker,v->pcm_current,0,0,totalshift); 1.314 + 1.315 + 1.316 + seq++; 1.317 + 1.318 + } 1.319 +#endif 1.320 + 1.321 + ve->curmark=j; 1.322 + if(j>=testW)return(1); 1.323 + return(0); 1.324 + } 1.325 + } 1.326 + j+=ve->searchstep; 1.327 + } 1.328 + } 1.329 + 1.330 + return(-1); 1.331 +} 1.332 + 1.333 +int _ve_envelope_mark(vorbis_dsp_state *v){ 1.334 + envelope_lookup *ve=((private_state *)(v->backend_state))->ve; 1.335 + vorbis_info *vi=v->vi; 1.336 + codec_setup_info *ci=vi->codec_setup; 1.337 + long centerW=v->centerW; 1.338 + long beginW=centerW-ci->blocksizes[v->W]/4; 1.339 + long endW=centerW+ci->blocksizes[v->W]/4; 1.340 + if(v->W){ 1.341 + beginW-=ci->blocksizes[v->lW]/4; 1.342 + endW+=ci->blocksizes[v->nW]/4; 1.343 + }else{ 1.344 + beginW-=ci->blocksizes[0]/4; 1.345 + endW+=ci->blocksizes[0]/4; 1.346 + } 1.347 + 1.348 + if(ve->curmark>=beginW && ve->curmark<endW)return(1); 1.349 + { 1.350 + long first=beginW/ve->searchstep; 1.351 + long last=endW/ve->searchstep; 1.352 + long i; 1.353 + for(i=first;i<last;i++) 1.354 + if(ve->mark[i])return(1); 1.355 + } 1.356 + return(0); 1.357 +} 1.358 + 1.359 +void _ve_envelope_shift(envelope_lookup *e,long shift){ 1.360 + int smallsize=e->current/e->searchstep+VE_POST; /* adjust for placing marks 1.361 + ahead of ve->current */ 1.362 + int smallshift=shift/e->searchstep; 1.363 + 1.364 + memmove(e->mark,e->mark+smallshift,(smallsize-smallshift)*sizeof(*e->mark)); 1.365 + 1.366 +#if 0 1.367 + for(i=0;i<VE_BANDS*e->ch;i++) 1.368 + memmove(e->filter[i].markers, 1.369 + e->filter[i].markers+smallshift, 1.370 + (1024-smallshift)*sizeof(*(*e->filter).markers)); 1.371 + totalshift+=shift; 1.372 +#endif 1.373 + 1.374 + e->current-=shift; 1.375 + if(e->curmark>=0) 1.376 + e->curmark-=shift; 1.377 + e->cursor-=shift; 1.378 +}