ref: 730c142fc22aad6d78c2f299ba3d64d85b7d90c6
dir: /LEAF/Src/leaf-analysis.c/
/*============================================================================== leaf-analysis.c Created: 30 Nov 2018 11:56:49am Author: airship ==============================================================================*/ #if _WIN32 || _WIN64 #include "..\Inc\leaf-analysis.h" #include "..\Externals\d_fft_mayer.h" #else #include "../Inc/leaf-analysis.h" #include "../Externals/d_fft_mayer.h" #endif //=========================================================================== /* Envelope Follower */ //=========================================================================== void tEnvelopeFollower_init(tEnvelopeFollower* const ef, float attackThreshold, float decayCoeff) { _tEnvelopeFollower* e = *ef = (_tEnvelopeFollower*) leaf_alloc(sizeof(_tEnvelopeFollower)); e->y = 0.0f; e->a_thresh = attackThreshold; e->d_coeff = decayCoeff; } void tEnvelopeFollower_free(tEnvelopeFollower* const ef) { _tEnvelopeFollower* e = *ef; leaf_free(e); } float tEnvelopeFollower_tick(tEnvelopeFollower* const ef, float x) { _tEnvelopeFollower* e = *ef; if (x < 0.0f ) x = -x; /* Absolute value. */ if ((x >= e->y) && (x > e->a_thresh)) e->y = x; /* If we hit a peak, ride the peak to the top. */ else e->y = e->y * e->d_coeff; /* Else, exponential decay of output. */ //ef->y = envelope_pow[(uint16_t)(ef->y * (float)UINT16_MAX)] * ef->d_coeff; //not quite the right behavior - too much loss of precision? //ef->y = powf(ef->y, 1.000009f) * ef->d_coeff; // too expensive if( e->y < VSF) e->y = 0.0f; return e->y; } int tEnvelopeFollower_decayCoeff(tEnvelopeFollower* const ef, float decayCoeff) { _tEnvelopeFollower* e = *ef; return e->d_coeff = decayCoeff; } int tEnvelopeFollower_attackThresh(tEnvelopeFollower* const ef, float attackThresh) { _tEnvelopeFollower* e = *ef; return e->a_thresh = attackThresh; } //=========================================================================== /* Power Follower */ //=========================================================================== void tPowerFollower_init(tPowerFollower* const pf, float factor) { _tPowerFollower* p = *pf = (_tPowerFollower*) leaf_alloc(sizeof(_tPowerFollower)); p->curr=0.0f; p->factor=factor; p->oneminusfactor=1.0f-factor; } void tPowerFollower_free(tPowerFollower* const pf) { _tPowerFollower* p = *pf; leaf_free(p); } int tPowerFollower_setFactor(tPowerFollower* const pf, float factor) { _tPowerFollower* p = *pf; if (factor<0) factor=0; if (factor>1) factor=1; p->factor=factor; p->oneminusfactor=1.0f-factor; return 0; } float tPowerFollower_tick(tPowerFollower* const pf, float input) { _tPowerFollower* p = *pf; p->curr = p->factor*input*input+p->oneminusfactor*p->curr; return p->curr; } float tPowerFollower_sample(tPowerFollower* const pf) { _tPowerFollower* p = *pf; return p->curr; } //=========================================================================== /* ---------------- env~ - simple envelope follower. ----------------- */ //=========================================================================== void tEnvPD_init(tEnvPD* const xpd, int ws, int hs, int bs) { _tEnvPD* x = *xpd = (_tEnvPD*) leaf_alloc(sizeof(_tEnvPD)); int period = hs, npoints = ws; int i; if (npoints < 1) npoints = 1024; if (period < 1) period = npoints/2; if (period < npoints / MAXOVERLAP + 1) period = npoints / MAXOVERLAP + 1; x->x_npoints = npoints; x->x_phase = 0; x->x_period = period; x->windowSize = npoints; x->hopSize = period; x->blockSize = bs; for (i = 0; i < MAXOVERLAP; i++) x->x_sumbuf[i] = 0; for (i = 0; i < npoints; i++) x->buf[i] = (1.0f - cosf((2 * PI * i) / npoints))/npoints; for (; i < npoints+INITVSTAKEN; i++) x->buf[i] = 0; x->x_f = 0; x->x_allocforvs = INITVSTAKEN; // ~ ~ ~ dsp ~ ~ ~ if (x->x_period % x->blockSize) { x->x_realperiod = x->x_period + x->blockSize - (x->x_period % x->blockSize); } else { x->x_realperiod = x->x_period; } // ~ ~ ~ ~ ~ ~ ~ ~ } void tEnvPD_free (tEnvPD* const xpd) { _tEnvPD* x = *xpd; leaf_free(x); } float tEnvPD_tick (tEnvPD* const xpd) { _tEnvPD* x = *xpd; return powtodb(x->x_result); } void tEnvPD_processBlock(tEnvPD* const xpd, float* in) { _tEnvPD* x = *xpd; int n = x->blockSize; int count; t_sample *sump; in += n; for (count = x->x_phase, sump = x->x_sumbuf; count < x->x_npoints; count += x->x_realperiod, sump++) { t_sample *hp = x->buf + count; t_sample *fp = in; t_sample sum = *sump; int i; for (i = 0; i < n; i++) { fp--; sum += *hp++ * (*fp * *fp); } *sump = sum; } sump[0] = 0; x->x_phase -= n; if (x->x_phase < 0) { x->x_result = x->x_sumbuf[0]; for (count = x->x_realperiod, sump = x->x_sumbuf; count < x->x_npoints; count += x->x_realperiod, sump++) sump[0] = sump[1]; sump[0] = 0; x->x_phase = x->x_realperiod - n; } } //=========================================================================== // ATTACKDETECTION //=========================================================================== /********Private function prototypes**********/ static void atkdtk_init(tAttackDetection* const a, int blocksize, int atk, int rel); static void atkdtk_envelope(tAttackDetection* const a, float *in); /********Constructor/Destructor***************/ void tAttackDetection_init(tAttackDetection* const ad, int blocksize) { *ad = (_tAttackDetection*) leaf_alloc(sizeof(_tAttackDetection)); atkdtk_init(ad, blocksize, DEFATTACK, DEFRELEASE); } void tAttackDetection_init_expanded(tAttackDetection* const ad, int blocksize, int atk, int rel) { *ad = (_tAttackDetection*) leaf_alloc(sizeof(_tAttackDetection)); atkdtk_init(ad, blocksize, atk, rel); } void tAttackDetection_free(tAttackDetection* const ad) { _tAttackDetection* a = *ad; leaf_free(a); } /*******Public Functions***********/ void tAttackDetection_setBlocksize(tAttackDetection* const ad, int size) { _tAttackDetection* a = *ad; if(!((size==64)|(size==128)|(size==256)|(size==512)|(size==1024)|(size==2048))) size = DEFBLOCKSIZE; a->blocksize = size; return; } void tAttackDetection_setSamplerate(tAttackDetection* const ad, int inRate) { _tAttackDetection* a = *ad; a->samplerate = inRate; //Reset atk and rel to recalculate coeff tAttackDetection_setAtk(ad, a->atk); tAttackDetection_setRel(ad, a->rel); } void tAttackDetection_setThreshold(tAttackDetection* const ad, float thres) { _tAttackDetection* a = *ad; a->threshold = thres; } void tAttackDetection_setAtk(tAttackDetection* const ad, int inAtk) { _tAttackDetection* a = *ad; a->atk = inAtk; a->atk_coeff = pow(0.01, 1.0/(a->atk * a->samplerate * 0.001)); } void tAttackDetection_setRel(tAttackDetection* const ad, int inRel) { _tAttackDetection* a = *ad; a->rel = inRel; a->rel_coeff = pow(0.01, 1.0/(a->rel * a->samplerate * 0.001)); } int tAttackDetection_detect(tAttackDetection* const ad, float *in) { _tAttackDetection* a = *ad; int result; atkdtk_envelope(ad, in); if(a->env >= a->prevAmp*2) //2 times greater = 6dB increase result = 1; else result = 0; a->prevAmp = a->env; return result; } /*******Private Functions**********/ static void atkdtk_init(tAttackDetection* const ad, int blocksize, int atk, int rel) { _tAttackDetection* a = *ad; a->env = 0; a->blocksize = blocksize; a->threshold = DEFTHRESHOLD; a->samplerate = leaf.sampleRate; a->prevAmp = 0; a->env = 0; tAttackDetection_setAtk(ad, atk); tAttackDetection_setRel(ad, rel); } static void atkdtk_envelope(tAttackDetection* const ad, float *in) { _tAttackDetection* a = *ad; int i = 0; float tmp; for(i = 0; i < a->blocksize; ++i){ tmp = fastabsf(in[i]); if(tmp > a->env) a->env = a->atk_coeff * (a->env - tmp) + tmp; else a->env = a->rel_coeff * (a->env - tmp) + tmp; } } //=========================================================================== // PERIODDETECTION //=========================================================================== void tPeriodDetection_init (tPeriodDetection* const pd, float* in, float* out, int bufSize, int frameSize) { _tPeriodDetection* p = *pd = (_tPeriodDetection*) leaf_alloc(sizeof(_tPeriodDetection)); p->inBuffer = in; p->outBuffer = out; p->bufSize = bufSize; p->frameSize = frameSize; p->framesPerBuffer = p->bufSize / p->frameSize; p->curBlock = 1; p->lastBlock = 0; p->index = 0; p->hopSize = DEFHOPSIZE; p->windowSize = DEFWINDOWSIZE; p->fba = FBA; tEnvPD_init(&p->env, p->windowSize, p->hopSize, p->frameSize); tSNAC_init(&p->snac, DEFOVERLAP); p->timeConstant = DEFTIMECONSTANT; p->radius = expf(-1000.0f * p->hopSize * leaf.invSampleRate / p->timeConstant); } void tPeriodDetection_free (tPeriodDetection* const pd) { _tPeriodDetection* p = *pd; tEnvPD_free(&p->env); tSNAC_free(&p->snac); leaf_free(p); } float tPeriodDetection_findPeriod (tPeriodDetection* pd, float sample) { _tPeriodDetection* p = *pd; int i, iLast; i = (p->curBlock*p->frameSize); iLast = (p->lastBlock*p->frameSize)+p->index; p->i = i; p->iLast = iLast; p->inBuffer[i+p->index] = sample; p->index++; p->indexstore = p->index; if (p->index >= p->frameSize) { p->index = 0; tEnvPD_processBlock(&p->env, &(p->inBuffer[i])); tSNAC_ioSamples(&p->snac, &(p->inBuffer[i]), &(p->outBuffer[i]), p->frameSize); p->period = tSNAC_getPeriod(&p->snac); p->curBlock++; if (p->curBlock >= p->framesPerBuffer) p->curBlock = 0; p->lastBlock++; if (p->lastBlock >= p->framesPerBuffer) p->lastBlock = 0; } // changed from period to p->period return p->period; } void tPeriodDetection_setHopSize(tPeriodDetection* pd, int hs) { _tPeriodDetection* p = *pd; p->hopSize = hs; } void tPeriodDetection_setWindowSize(tPeriodDetection* pd, int ws) { _tPeriodDetection* p = *pd; p->windowSize = ws; } //=========================================================================== // SNAC //=========================================================================== /******************************************************************************/ /***************************** private procedures *****************************/ /******************************************************************************/ #define REALFFT mayer_realfft #define REALIFFT mayer_realifft static void snac_analyzeframe(tSNAC* const s); static void snac_autocorrelation(tSNAC* const s); static void snac_normalize(tSNAC* const s); static void snac_pickpeak(tSNAC* const s); static void snac_periodandfidelity(tSNAC* const s); static void snac_biasbuf(tSNAC* const s); static float snac_spectralpeak(tSNAC* const s, float periodlength); /******************************************************************************/ /******************************** constructor, destructor *********************/ /******************************************************************************/ void tSNAC_init(tSNAC* const snac, int overlaparg) { _tSNAC* s = *snac = (_tSNAC*) leaf_alloc(sizeof(_tSNAC)); s->biasfactor = DEFBIAS; s->timeindex = 0; s->periodindex = 0; s->periodlength = 0.; s->fidelity = 0.; s->minrms = DEFMINRMS; s->framesize = SNAC_FRAME_SIZE; s->inputbuf = (float*) leaf_alloc(sizeof(float) * SNAC_FRAME_SIZE); s->processbuf = (float*) leaf_alloc(sizeof(float) * (SNAC_FRAME_SIZE * 2)); s->spectrumbuf = (float*) leaf_alloc(sizeof(float) * (SNAC_FRAME_SIZE / 2)); s->biasbuf = (float*) leaf_alloc(sizeof(float) * SNAC_FRAME_SIZE); snac_biasbuf(snac); tSNAC_setOverlap(snac, overlaparg); } void tSNAC_free(tSNAC* const snac) { _tSNAC* s = *snac; leaf_free(s->inputbuf); leaf_free(s->processbuf); leaf_free(s->spectrumbuf); leaf_free(s->biasbuf); leaf_free(s); } /******************************************************************************/ /************************** public access functions****************************/ /******************************************************************************/ void tSNAC_ioSamples(tSNAC* const snac, float *in, float *out, int size) { _tSNAC* s = *snac; int timeindex = s->timeindex; int mask = s->framesize - 1; int outindex = 0; float *inputbuf = s->inputbuf; float *processbuf = s->processbuf; // call analysis function when it is time if(!(timeindex & (s->framesize / s->overlap - 1))) snac_analyzeframe(snac); while(size--) { inputbuf[timeindex] = *in++; out[outindex++] = processbuf[timeindex++]; timeindex &= mask; } s->timeindex = timeindex; } void tSNAC_setOverlap(tSNAC* const snac, int lap) { _tSNAC* s = *snac; if(!((lap==1)|(lap==2)|(lap==4)|(lap==8))) lap = DEFOVERLAP; s->overlap = lap; } void tSNAC_setBias(tSNAC* const snac, float bias) { _tSNAC* s = *snac; if(bias > 1.) bias = 1.; if(bias < 0.) bias = 0.; s->biasfactor = bias; snac_biasbuf(snac); return; } void tSNAC_setMinRMS(tSNAC* const snac, float rms) { _tSNAC* s = *snac; if(rms > 1.) rms = 1.; if(rms < 0.) rms = 0.; s->minrms = rms; return; } float tSNAC_getPeriod(tSNAC* const snac) { _tSNAC* s = *snac; return(s->periodlength); } float tSNAC_getFidelity(tSNAC* const snac) { _tSNAC* s = *snac; return(s->fidelity); } /******************************************************************************/ /***************************** private procedures *****************************/ /******************************************************************************/ // main analysis function static void snac_analyzeframe(tSNAC* const snac) { _tSNAC* s = *snac; int n, tindex = s->timeindex; int framesize = s->framesize; int mask = framesize - 1; float norm = 1. / sqrt((float)(framesize * 2)); float *inputbuf = s->inputbuf; float *processbuf = s->processbuf; // copy input to processing buffers for(n=0; n<framesize; n++) { processbuf[n] = inputbuf[tindex] * norm; tindex++; tindex &= mask; } // zeropadding for(n=framesize; n<(framesize<<1); n++) processbuf[n] = 0.; // call analysis procedures snac_autocorrelation(snac); snac_normalize(snac); snac_pickpeak(snac); snac_periodandfidelity(snac); } static void snac_autocorrelation(tSNAC* const snac) { _tSNAC* s = *snac; int n, m; int framesize = s->framesize; int fftsize = framesize * 2; float *processbuf = s->processbuf; float *spectrumbuf = s->spectrumbuf; REALFFT(fftsize, processbuf); // compute power spectrum processbuf[0] *= processbuf[0]; // DC processbuf[framesize] *= processbuf[framesize]; // Nyquist for(n=1; n<framesize; n++) { processbuf[n] = processbuf[n] * processbuf[n] + processbuf[fftsize-n] * processbuf[fftsize-n]; // imag coefficients appear reversed processbuf[fftsize-n] = 0.; } // store power spectrum up to SR/4 for possible later use for(m=0; m<(framesize>>1); m++) { spectrumbuf[m] = processbuf[m]; } // transform power spectrum to autocorrelation function REALIFFT(fftsize, processbuf); return; } static void snac_normalize(tSNAC* const snac) { _tSNAC* s = *snac; int framesize = s->framesize; int framesizeplustimeindex = s->framesize + s->timeindex; int timeindexminusone = s->timeindex - 1; int n, m; int mask = framesize - 1; int seek = framesize * SEEK; float *inputbuf = s->inputbuf; float *processbuf= s->processbuf; float signal1, signal2; // minimum RMS implemented as minimum autocorrelation at index 0 // functionally equivalent to white noise floor float rms = s->minrms / sqrt(1.0f / (float)framesize); float minrzero = rms * rms; float rzero = processbuf[0]; if(rzero < minrzero) rzero = minrzero; double normintegral = (double)rzero * 2.; // normalize biased autocorrelation function // inputbuf is circular buffer: timeindex may be non-zero when overlap > 1 processbuf[0] = 1; for(n=1, m=s->timeindex+1; n<seek; n++, m++) { signal1 = inputbuf[(n + timeindexminusone)&mask]; signal2 = inputbuf[(framesizeplustimeindex - n)&mask]; normintegral -= (double)(signal1 * signal1 + signal2 * signal2); processbuf[n] /= (float)normintegral * 0.5f; } // flush instable function tail for(n = seek; n<framesize; n++) processbuf[n] = 0.; return; } static void snac_periodandfidelity(tSNAC* const snac) { _tSNAC* s = *snac; float periodlength; if(s->periodindex) { periodlength = (float)s->periodindex + interpolate3phase(s->processbuf, s->periodindex); if(periodlength < 8) periodlength = snac_spectralpeak(snac, periodlength); s->periodlength = periodlength; s->fidelity = interpolate3max(s->processbuf, s->periodindex); } return; } // select the peak which most probably represents period length static void snac_pickpeak(tSNAC* const snac) { _tSNAC* s = *snac; int n, peakindex=0; int seek = s->framesize * SEEK; float *processbuf= s->processbuf; float maxvalue = 0.; float biasedpeak; float *biasbuf = s->biasbuf; // skip main lobe for(n=1; n<seek; n++) { if(processbuf[n] < 0.) break; } // find interpolated / biased maximum in SNAC function // interpolation finds the 'real maximum' // biasing favours the first candidate for(; n<seek-1; n++) { if(processbuf[n] >= processbuf[n-1]) { if(processbuf[n] > processbuf[n+1]) // we have a local peak { biasedpeak = interpolate3max(processbuf, n) * biasbuf[n]; if(biasedpeak > maxvalue) { maxvalue = biasedpeak; peakindex = n; } } } } s->periodindex = peakindex; return; } // verify period length via frequency domain (up till SR/4) // frequency domain is more precise than lag domain for period lengths < 8 // argument 'periodlength' is initial estimation from autocorrelation static float snac_spectralpeak(tSNAC* const snac, float periodlength) { _tSNAC* s = *snac; if(periodlength < 4.) return periodlength; float max = 0.; int n, startbin, stopbin, peakbin = 0; int spectrumsize = s->framesize>>1; float *spectrumbuf = s->spectrumbuf; float peaklocation = (float)(s->framesize * 2.) / periodlength; startbin = (int)(peaklocation * 0.8 + 0.5); if(startbin < 1) startbin = 1; stopbin = (int)(peaklocation * 1.25 + 0.5); if(stopbin >= spectrumsize - 1) stopbin = spectrumsize - 1; for(n=startbin; n<stopbin; n++) { if(spectrumbuf[n] >= spectrumbuf[n-1]) { if(spectrumbuf[n] > spectrumbuf[n+1]) { if(spectrumbuf[n] > max) { max = spectrumbuf[n]; peakbin = n; } } } } // calculate amplitudes in peak region for(n=(peakbin-1); n<(peakbin+2); n++) { spectrumbuf[n] = sqrt(spectrumbuf[n]); } peaklocation = (float)peakbin + interpolate3phase(spectrumbuf, peakbin); periodlength = (float)(s->framesize * 2.0f) / peaklocation; return periodlength; } // modified logarithmic bias function static void snac_biasbuf(tSNAC* const snac) { _tSNAC* s = *snac; int n; int maxperiod = (int)(s->framesize * (float)SEEK); float bias = s->biasfactor / log((float)(maxperiod - 4)); float *biasbuf = s->biasbuf; for(n=0; n<5; n++) // periods < 5 samples can't be tracked { biasbuf[n] = 0.; } for(n=5; n<maxperiod; n++) { biasbuf[n] = 1.0f - (float)log(n - 4) * bias; } }