annotate src/libvorbis-1.3.3/lib/envelope.c @ 83:ae30d91d2ffe

Replace these with versions built using an older toolset (so as to avoid ABI compatibilities when linking on Ubuntu 14.04 for packaging purposes)
author Chris Cannam
date Fri, 07 Feb 2020 11:51:13 +0000
parents 05aa0afa9217
children
rev   line source
Chris@1 1 /********************************************************************
Chris@1 2 * *
Chris@1 3 * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
Chris@1 4 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
Chris@1 5 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
Chris@1 6 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
Chris@1 7 * *
Chris@1 8 * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 *
Chris@1 9 * by the Xiph.Org Foundation http://www.xiph.org/ *
Chris@1 10 * *
Chris@1 11 ********************************************************************
Chris@1 12
Chris@1 13 function: PCM data envelope analysis
Chris@1 14 last mod: $Id: envelope.c 16227 2009-07-08 06:58:46Z xiphmont $
Chris@1 15
Chris@1 16 ********************************************************************/
Chris@1 17
Chris@1 18 #include <stdlib.h>
Chris@1 19 #include <string.h>
Chris@1 20 #include <stdio.h>
Chris@1 21 #include <math.h>
Chris@1 22 #include <ogg/ogg.h>
Chris@1 23 #include "vorbis/codec.h"
Chris@1 24 #include "codec_internal.h"
Chris@1 25
Chris@1 26 #include "os.h"
Chris@1 27 #include "scales.h"
Chris@1 28 #include "envelope.h"
Chris@1 29 #include "mdct.h"
Chris@1 30 #include "misc.h"
Chris@1 31
Chris@1 32 void _ve_envelope_init(envelope_lookup *e,vorbis_info *vi){
Chris@1 33 codec_setup_info *ci=vi->codec_setup;
Chris@1 34 vorbis_info_psy_global *gi=&ci->psy_g_param;
Chris@1 35 int ch=vi->channels;
Chris@1 36 int i,j;
Chris@1 37 int n=e->winlength=128;
Chris@1 38 e->searchstep=64; /* not random */
Chris@1 39
Chris@1 40 e->minenergy=gi->preecho_minenergy;
Chris@1 41 e->ch=ch;
Chris@1 42 e->storage=128;
Chris@1 43 e->cursor=ci->blocksizes[1]/2;
Chris@1 44 e->mdct_win=_ogg_calloc(n,sizeof(*e->mdct_win));
Chris@1 45 mdct_init(&e->mdct,n);
Chris@1 46
Chris@1 47 for(i=0;i<n;i++){
Chris@1 48 e->mdct_win[i]=sin(i/(n-1.)*M_PI);
Chris@1 49 e->mdct_win[i]*=e->mdct_win[i];
Chris@1 50 }
Chris@1 51
Chris@1 52 /* magic follows */
Chris@1 53 e->band[0].begin=2; e->band[0].end=4;
Chris@1 54 e->band[1].begin=4; e->band[1].end=5;
Chris@1 55 e->band[2].begin=6; e->band[2].end=6;
Chris@1 56 e->band[3].begin=9; e->band[3].end=8;
Chris@1 57 e->band[4].begin=13; e->band[4].end=8;
Chris@1 58 e->band[5].begin=17; e->band[5].end=8;
Chris@1 59 e->band[6].begin=22; e->band[6].end=8;
Chris@1 60
Chris@1 61 for(j=0;j<VE_BANDS;j++){
Chris@1 62 n=e->band[j].end;
Chris@1 63 e->band[j].window=_ogg_malloc(n*sizeof(*e->band[0].window));
Chris@1 64 for(i=0;i<n;i++){
Chris@1 65 e->band[j].window[i]=sin((i+.5)/n*M_PI);
Chris@1 66 e->band[j].total+=e->band[j].window[i];
Chris@1 67 }
Chris@1 68 e->band[j].total=1./e->band[j].total;
Chris@1 69 }
Chris@1 70
Chris@1 71 e->filter=_ogg_calloc(VE_BANDS*ch,sizeof(*e->filter));
Chris@1 72 e->mark=_ogg_calloc(e->storage,sizeof(*e->mark));
Chris@1 73
Chris@1 74 }
Chris@1 75
Chris@1 76 void _ve_envelope_clear(envelope_lookup *e){
Chris@1 77 int i;
Chris@1 78 mdct_clear(&e->mdct);
Chris@1 79 for(i=0;i<VE_BANDS;i++)
Chris@1 80 _ogg_free(e->band[i].window);
Chris@1 81 _ogg_free(e->mdct_win);
Chris@1 82 _ogg_free(e->filter);
Chris@1 83 _ogg_free(e->mark);
Chris@1 84 memset(e,0,sizeof(*e));
Chris@1 85 }
Chris@1 86
Chris@1 87 /* fairly straight threshhold-by-band based until we find something
Chris@1 88 that works better and isn't patented. */
Chris@1 89
Chris@1 90 static int _ve_amp(envelope_lookup *ve,
Chris@1 91 vorbis_info_psy_global *gi,
Chris@1 92 float *data,
Chris@1 93 envelope_band *bands,
Chris@1 94 envelope_filter_state *filters){
Chris@1 95 long n=ve->winlength;
Chris@1 96 int ret=0;
Chris@1 97 long i,j;
Chris@1 98 float decay;
Chris@1 99
Chris@1 100 /* we want to have a 'minimum bar' for energy, else we're just
Chris@1 101 basing blocks on quantization noise that outweighs the signal
Chris@1 102 itself (for low power signals) */
Chris@1 103
Chris@1 104 float minV=ve->minenergy;
Chris@1 105 float *vec=alloca(n*sizeof(*vec));
Chris@1 106
Chris@1 107 /* stretch is used to gradually lengthen the number of windows
Chris@1 108 considered prevoius-to-potential-trigger */
Chris@1 109 int stretch=max(VE_MINSTRETCH,ve->stretch/2);
Chris@1 110 float penalty=gi->stretch_penalty-(ve->stretch/2-VE_MINSTRETCH);
Chris@1 111 if(penalty<0.f)penalty=0.f;
Chris@1 112 if(penalty>gi->stretch_penalty)penalty=gi->stretch_penalty;
Chris@1 113
Chris@1 114 /*_analysis_output_always("lpcm",seq2,data,n,0,0,
Chris@1 115 totalshift+pos*ve->searchstep);*/
Chris@1 116
Chris@1 117 /* window and transform */
Chris@1 118 for(i=0;i<n;i++)
Chris@1 119 vec[i]=data[i]*ve->mdct_win[i];
Chris@1 120 mdct_forward(&ve->mdct,vec,vec);
Chris@1 121
Chris@1 122 /*_analysis_output_always("mdct",seq2,vec,n/2,0,1,0); */
Chris@1 123
Chris@1 124 /* near-DC spreading function; this has nothing to do with
Chris@1 125 psychoacoustics, just sidelobe leakage and window size */
Chris@1 126 {
Chris@1 127 float temp=vec[0]*vec[0]+.7*vec[1]*vec[1]+.2*vec[2]*vec[2];
Chris@1 128 int ptr=filters->nearptr;
Chris@1 129
Chris@1 130 /* the accumulation is regularly refreshed from scratch to avoid
Chris@1 131 floating point creep */
Chris@1 132 if(ptr==0){
Chris@1 133 decay=filters->nearDC_acc=filters->nearDC_partialacc+temp;
Chris@1 134 filters->nearDC_partialacc=temp;
Chris@1 135 }else{
Chris@1 136 decay=filters->nearDC_acc+=temp;
Chris@1 137 filters->nearDC_partialacc+=temp;
Chris@1 138 }
Chris@1 139 filters->nearDC_acc-=filters->nearDC[ptr];
Chris@1 140 filters->nearDC[ptr]=temp;
Chris@1 141
Chris@1 142 decay*=(1./(VE_NEARDC+1));
Chris@1 143 filters->nearptr++;
Chris@1 144 if(filters->nearptr>=VE_NEARDC)filters->nearptr=0;
Chris@1 145 decay=todB(&decay)*.5-15.f;
Chris@1 146 }
Chris@1 147
Chris@1 148 /* perform spreading and limiting, also smooth the spectrum. yes,
Chris@1 149 the MDCT results in all real coefficients, but it still *behaves*
Chris@1 150 like real/imaginary pairs */
Chris@1 151 for(i=0;i<n/2;i+=2){
Chris@1 152 float val=vec[i]*vec[i]+vec[i+1]*vec[i+1];
Chris@1 153 val=todB(&val)*.5f;
Chris@1 154 if(val<decay)val=decay;
Chris@1 155 if(val<minV)val=minV;
Chris@1 156 vec[i>>1]=val;
Chris@1 157 decay-=8.;
Chris@1 158 }
Chris@1 159
Chris@1 160 /*_analysis_output_always("spread",seq2++,vec,n/4,0,0,0);*/
Chris@1 161
Chris@1 162 /* perform preecho/postecho triggering by band */
Chris@1 163 for(j=0;j<VE_BANDS;j++){
Chris@1 164 float acc=0.;
Chris@1 165 float valmax,valmin;
Chris@1 166
Chris@1 167 /* accumulate amplitude */
Chris@1 168 for(i=0;i<bands[j].end;i++)
Chris@1 169 acc+=vec[i+bands[j].begin]*bands[j].window[i];
Chris@1 170
Chris@1 171 acc*=bands[j].total;
Chris@1 172
Chris@1 173 /* convert amplitude to delta */
Chris@1 174 {
Chris@1 175 int p,this=filters[j].ampptr;
Chris@1 176 float postmax,postmin,premax=-99999.f,premin=99999.f;
Chris@1 177
Chris@1 178 p=this;
Chris@1 179 p--;
Chris@1 180 if(p<0)p+=VE_AMP;
Chris@1 181 postmax=max(acc,filters[j].ampbuf[p]);
Chris@1 182 postmin=min(acc,filters[j].ampbuf[p]);
Chris@1 183
Chris@1 184 for(i=0;i<stretch;i++){
Chris@1 185 p--;
Chris@1 186 if(p<0)p+=VE_AMP;
Chris@1 187 premax=max(premax,filters[j].ampbuf[p]);
Chris@1 188 premin=min(premin,filters[j].ampbuf[p]);
Chris@1 189 }
Chris@1 190
Chris@1 191 valmin=postmin-premin;
Chris@1 192 valmax=postmax-premax;
Chris@1 193
Chris@1 194 /*filters[j].markers[pos]=valmax;*/
Chris@1 195 filters[j].ampbuf[this]=acc;
Chris@1 196 filters[j].ampptr++;
Chris@1 197 if(filters[j].ampptr>=VE_AMP)filters[j].ampptr=0;
Chris@1 198 }
Chris@1 199
Chris@1 200 /* look at min/max, decide trigger */
Chris@1 201 if(valmax>gi->preecho_thresh[j]+penalty){
Chris@1 202 ret|=1;
Chris@1 203 ret|=4;
Chris@1 204 }
Chris@1 205 if(valmin<gi->postecho_thresh[j]-penalty)ret|=2;
Chris@1 206 }
Chris@1 207
Chris@1 208 return(ret);
Chris@1 209 }
Chris@1 210
Chris@1 211 #if 0
Chris@1 212 static int seq=0;
Chris@1 213 static ogg_int64_t totalshift=-1024;
Chris@1 214 #endif
Chris@1 215
Chris@1 216 long _ve_envelope_search(vorbis_dsp_state *v){
Chris@1 217 vorbis_info *vi=v->vi;
Chris@1 218 codec_setup_info *ci=vi->codec_setup;
Chris@1 219 vorbis_info_psy_global *gi=&ci->psy_g_param;
Chris@1 220 envelope_lookup *ve=((private_state *)(v->backend_state))->ve;
Chris@1 221 long i,j;
Chris@1 222
Chris@1 223 int first=ve->current/ve->searchstep;
Chris@1 224 int last=v->pcm_current/ve->searchstep-VE_WIN;
Chris@1 225 if(first<0)first=0;
Chris@1 226
Chris@1 227 /* make sure we have enough storage to match the PCM */
Chris@1 228 if(last+VE_WIN+VE_POST>ve->storage){
Chris@1 229 ve->storage=last+VE_WIN+VE_POST; /* be sure */
Chris@1 230 ve->mark=_ogg_realloc(ve->mark,ve->storage*sizeof(*ve->mark));
Chris@1 231 }
Chris@1 232
Chris@1 233 for(j=first;j<last;j++){
Chris@1 234 int ret=0;
Chris@1 235
Chris@1 236 ve->stretch++;
Chris@1 237 if(ve->stretch>VE_MAXSTRETCH*2)
Chris@1 238 ve->stretch=VE_MAXSTRETCH*2;
Chris@1 239
Chris@1 240 for(i=0;i<ve->ch;i++){
Chris@1 241 float *pcm=v->pcm[i]+ve->searchstep*(j);
Chris@1 242 ret|=_ve_amp(ve,gi,pcm,ve->band,ve->filter+i*VE_BANDS);
Chris@1 243 }
Chris@1 244
Chris@1 245 ve->mark[j+VE_POST]=0;
Chris@1 246 if(ret&1){
Chris@1 247 ve->mark[j]=1;
Chris@1 248 ve->mark[j+1]=1;
Chris@1 249 }
Chris@1 250
Chris@1 251 if(ret&2){
Chris@1 252 ve->mark[j]=1;
Chris@1 253 if(j>0)ve->mark[j-1]=1;
Chris@1 254 }
Chris@1 255
Chris@1 256 if(ret&4)ve->stretch=-1;
Chris@1 257 }
Chris@1 258
Chris@1 259 ve->current=last*ve->searchstep;
Chris@1 260
Chris@1 261 {
Chris@1 262 long centerW=v->centerW;
Chris@1 263 long testW=
Chris@1 264 centerW+
Chris@1 265 ci->blocksizes[v->W]/4+
Chris@1 266 ci->blocksizes[1]/2+
Chris@1 267 ci->blocksizes[0]/4;
Chris@1 268
Chris@1 269 j=ve->cursor;
Chris@1 270
Chris@1 271 while(j<ve->current-(ve->searchstep)){/* account for postecho
Chris@1 272 working back one window */
Chris@1 273 if(j>=testW)return(1);
Chris@1 274
Chris@1 275 ve->cursor=j;
Chris@1 276
Chris@1 277 if(ve->mark[j/ve->searchstep]){
Chris@1 278 if(j>centerW){
Chris@1 279
Chris@1 280 #if 0
Chris@1 281 if(j>ve->curmark){
Chris@1 282 float *marker=alloca(v->pcm_current*sizeof(*marker));
Chris@1 283 int l,m;
Chris@1 284 memset(marker,0,sizeof(*marker)*v->pcm_current);
Chris@1 285 fprintf(stderr,"mark! seq=%d, cursor:%fs time:%fs\n",
Chris@1 286 seq,
Chris@1 287 (totalshift+ve->cursor)/44100.,
Chris@1 288 (totalshift+j)/44100.);
Chris@1 289 _analysis_output_always("pcmL",seq,v->pcm[0],v->pcm_current,0,0,totalshift);
Chris@1 290 _analysis_output_always("pcmR",seq,v->pcm[1],v->pcm_current,0,0,totalshift);
Chris@1 291
Chris@1 292 _analysis_output_always("markL",seq,v->pcm[0],j,0,0,totalshift);
Chris@1 293 _analysis_output_always("markR",seq,v->pcm[1],j,0,0,totalshift);
Chris@1 294
Chris@1 295 for(m=0;m<VE_BANDS;m++){
Chris@1 296 char buf[80];
Chris@1 297 sprintf(buf,"delL%d",m);
Chris@1 298 for(l=0;l<last;l++)marker[l*ve->searchstep]=ve->filter[m].markers[l]*.1;
Chris@1 299 _analysis_output_always(buf,seq,marker,v->pcm_current,0,0,totalshift);
Chris@1 300 }
Chris@1 301
Chris@1 302 for(m=0;m<VE_BANDS;m++){
Chris@1 303 char buf[80];
Chris@1 304 sprintf(buf,"delR%d",m);
Chris@1 305 for(l=0;l<last;l++)marker[l*ve->searchstep]=ve->filter[m+VE_BANDS].markers[l]*.1;
Chris@1 306 _analysis_output_always(buf,seq,marker,v->pcm_current,0,0,totalshift);
Chris@1 307 }
Chris@1 308
Chris@1 309 for(l=0;l<last;l++)marker[l*ve->searchstep]=ve->mark[l]*.4;
Chris@1 310 _analysis_output_always("mark",seq,marker,v->pcm_current,0,0,totalshift);
Chris@1 311
Chris@1 312
Chris@1 313 seq++;
Chris@1 314
Chris@1 315 }
Chris@1 316 #endif
Chris@1 317
Chris@1 318 ve->curmark=j;
Chris@1 319 if(j>=testW)return(1);
Chris@1 320 return(0);
Chris@1 321 }
Chris@1 322 }
Chris@1 323 j+=ve->searchstep;
Chris@1 324 }
Chris@1 325 }
Chris@1 326
Chris@1 327 return(-1);
Chris@1 328 }
Chris@1 329
Chris@1 330 int _ve_envelope_mark(vorbis_dsp_state *v){
Chris@1 331 envelope_lookup *ve=((private_state *)(v->backend_state))->ve;
Chris@1 332 vorbis_info *vi=v->vi;
Chris@1 333 codec_setup_info *ci=vi->codec_setup;
Chris@1 334 long centerW=v->centerW;
Chris@1 335 long beginW=centerW-ci->blocksizes[v->W]/4;
Chris@1 336 long endW=centerW+ci->blocksizes[v->W]/4;
Chris@1 337 if(v->W){
Chris@1 338 beginW-=ci->blocksizes[v->lW]/4;
Chris@1 339 endW+=ci->blocksizes[v->nW]/4;
Chris@1 340 }else{
Chris@1 341 beginW-=ci->blocksizes[0]/4;
Chris@1 342 endW+=ci->blocksizes[0]/4;
Chris@1 343 }
Chris@1 344
Chris@1 345 if(ve->curmark>=beginW && ve->curmark<endW)return(1);
Chris@1 346 {
Chris@1 347 long first=beginW/ve->searchstep;
Chris@1 348 long last=endW/ve->searchstep;
Chris@1 349 long i;
Chris@1 350 for(i=first;i<last;i++)
Chris@1 351 if(ve->mark[i])return(1);
Chris@1 352 }
Chris@1 353 return(0);
Chris@1 354 }
Chris@1 355
Chris@1 356 void _ve_envelope_shift(envelope_lookup *e,long shift){
Chris@1 357 int smallsize=e->current/e->searchstep+VE_POST; /* adjust for placing marks
Chris@1 358 ahead of ve->current */
Chris@1 359 int smallshift=shift/e->searchstep;
Chris@1 360
Chris@1 361 memmove(e->mark,e->mark+smallshift,(smallsize-smallshift)*sizeof(*e->mark));
Chris@1 362
Chris@1 363 #if 0
Chris@1 364 for(i=0;i<VE_BANDS*e->ch;i++)
Chris@1 365 memmove(e->filter[i].markers,
Chris@1 366 e->filter[i].markers+smallshift,
Chris@1 367 (1024-smallshift)*sizeof(*(*e->filter).markers));
Chris@1 368 totalshift+=shift;
Chris@1 369 #endif
Chris@1 370
Chris@1 371 e->current-=shift;
Chris@1 372 if(e->curmark>=0)
Chris@1 373 e->curmark-=shift;
Chris@1 374 e->cursor-=shift;
Chris@1 375 }