ref: fe27383f5704ddc2d8ed4fad975fe937bca129c1
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" #include <intrin.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, LEAF* const leaf) { tEnvelopeFollower_initToPool(ef, attackThreshold, decayCoeff, &leaf->mempool); } void tEnvelopeFollower_initToPool (tEnvelopeFollower* const ef, float attackThreshold, float decayCoeff, tMempool* const mp) { _tMempool* m = *mp; _tEnvelopeFollower* e = *ef = (_tEnvelopeFollower*) mpool_alloc(sizeof(_tEnvelopeFollower), m); e->mempool = m; e->y = 0.0f; e->a_thresh = attackThreshold; e->d_coeff = decayCoeff; } void tEnvelopeFollower_free (tEnvelopeFollower* const ef) { _tEnvelopeFollower* e = *ef; mpool_free((char*)e, e->mempool); } float tEnvelopeFollower_tick(tEnvelopeFollower* const ef, float x) { _tEnvelopeFollower* e = *ef; if (x < 0.0f ) x = -x; /* Absolute value. */ if (isnan(x)) return 0.0f; 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 #ifdef NO_DENORMAL_CHECK #else if( e->y < VSF) e->y = 0.0f; #endif return e->y; } void tEnvelopeFollower_setDecayCoefficient(tEnvelopeFollower* const ef, float decayCoeff) { _tEnvelopeFollower* e = *ef; e->d_coeff = decayCoeff; } void tEnvelopeFollower_setAttackThreshold(tEnvelopeFollower* const ef, float attackThresh) { _tEnvelopeFollower* e = *ef; e->a_thresh = attackThresh; } // zero crossing detector void tZeroCrossingCounter_init (tZeroCrossingCounter* const zc, int maxWindowSize, LEAF* const leaf) { tZeroCrossingCounter_initToPool (zc, maxWindowSize, &leaf->mempool); } void tZeroCrossingCounter_initToPool (tZeroCrossingCounter* const zc, int maxWindowSize, tMempool* const mp) { _tMempool* m = *mp; _tZeroCrossingCounter* z = *zc = (_tZeroCrossingCounter*) mpool_alloc(sizeof(_tZeroCrossingCounter), m); z->mempool = m; z->count = 0; z->maxWindowSize = maxWindowSize; z->currentWindowSize = maxWindowSize; z->invCurrentWindowSize = 1.0f / (float)maxWindowSize; z->position = 0; z->prevPosition = maxWindowSize; z->inBuffer = (float*) mpool_calloc(sizeof(float) * maxWindowSize, m); z->countBuffer = (uint16_t*) mpool_calloc(sizeof(uint16_t) * maxWindowSize, m); } void tZeroCrossingCounter_free (tZeroCrossingCounter* const zc) { _tZeroCrossingCounter* z = *zc; mpool_free((char*)z->inBuffer, z->mempool); mpool_free((char*)z->countBuffer, z->mempool); mpool_free((char*)z, z->mempool); } //returns proportion of zero crossings within window size (0.0 would be none in window, 1.0 would be all zero crossings) float tZeroCrossingCounter_tick (tZeroCrossingCounter* const zc, float input) { _tZeroCrossingCounter* z = *zc; z->inBuffer[z->position] = input; int futurePosition = ((z->position + 1) % z->currentWindowSize); float output = 0.0f; //add new value to count if ((z->inBuffer[z->position] * z->inBuffer[z->prevPosition]) < 0.0f) { //zero crossing happened, add it to the count array z->countBuffer[z->position] = 1; z->count++; } else { z->countBuffer[z->position] = 0; } //remove oldest value from count if (z->countBuffer[futurePosition] > 0) { z->count--; if (z->count < 0) { z->count = 0; } } z->prevPosition = z->position; z->position = futurePosition; output = z->count * z->invCurrentWindowSize; return output; } void tZeroCrossingCounter_setWindowSize (tZeroCrossingCounter* const zc, float windowSize) { _tZeroCrossingCounter* z = *zc; if (windowSize <= z->maxWindowSize) { z->currentWindowSize = windowSize; } else { z->currentWindowSize = z->maxWindowSize; } z->invCurrentWindowSize = 1.0f / z->currentWindowSize; } //=========================================================================== /* Power Follower */ //=========================================================================== void tPowerFollower_init(tPowerFollower* const pf, float factor, LEAF* const leaf) { tPowerFollower_initToPool(pf, factor, &leaf->mempool); } void tPowerFollower_initToPool (tPowerFollower* const pf, float factor, tMempool* const mp) { _tMempool* m = *mp; _tPowerFollower* p = *pf = (_tPowerFollower*) mpool_alloc(sizeof(_tPowerFollower), m); p->mempool = m; p->curr=0.0f; p->factor=factor; p->oneminusfactor=1.0f-factor; } void tPowerFollower_free (tPowerFollower* const pf) { _tPowerFollower* p = *pf; mpool_free((char*)p, p->mempool); } void 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; } 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_getPower(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, LEAF* const leaf) { tEnvPD_initToPool(xpd, ws, hs, bs, &leaf->mempool); } void tEnvPD_initToPool (tEnvPD* const xpd, int ws, int hs, int bs, tMempool* const mp) { _tMempool* m = *mp; _tEnvPD* x = *xpd = (_tEnvPD*) mpool_calloc(sizeof(_tEnvPD), m); x->mempool = m; 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; mpool_free((char*)x, x->mempool); } 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, int atk, int rel, LEAF* const leaf) { tAttackDetection_initToPool(ad, blocksize, atk, rel, &leaf->mempool); } void tAttackDetection_initToPool (tAttackDetection* const ad, int blocksize, int atk, int rel, tMempool* const mp) { _tMempool* m = *mp; _tAttackDetection* a = *ad = (_tAttackDetection*) mpool_alloc(sizeof(_tAttackDetection), m); a->mempool = m; atkdtk_init(ad, blocksize, atk, rel); } void tAttackDetection_free (tAttackDetection* const ad) { _tAttackDetection* a = *ad; mpool_free((char*)a, a->mempool); } /*******Public Functions***********/ void tAttackDetection_setBlocksize(tAttackDetection* const ad, int size) { _tAttackDetection* a = *ad; a->blocksize = size; } void tAttackDetection_setSamplerate(tAttackDetection* const ad, int inRate) { _tAttackDetection* a = *ad; a->samplerate = inRate; //Reset atk and rel to recalculate coeff tAttackDetection_setAttack(ad, a->atk); tAttackDetection_setRelease(ad, a->rel); } void tAttackDetection_setThreshold(tAttackDetection* const ad, float thres) { _tAttackDetection* a = *ad; a->threshold = thres; } void tAttackDetection_setAttack(tAttackDetection* const ad, int inAtk) { _tAttackDetection* a = *ad; a->atk = inAtk; a->atk_coeff = powf(0.01f, 1.0f/(a->atk * a->samplerate * 0.001f)); } void tAttackDetection_setRelease(tAttackDetection* const ad, int inRel) { _tAttackDetection* a = *ad; a->rel = inRel; a->rel_coeff = powf(0.01f, 1.0f/(a->rel * a->samplerate * 0.001f)); } 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; LEAF* leaf = a->mempool->leaf; a->env = 0; a->blocksize = blocksize; a->threshold = DEFTHRESHOLD; a->samplerate = leaf->sampleRate; a->prevAmp = 0; a->env = 0; tAttackDetection_setAttack(ad, atk); tAttackDetection_setRelease(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; } } //=========================================================================== // 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, LEAF* const leaf) { tSNAC_initToPool(snac, overlaparg, &leaf->mempool); } void tSNAC_initToPool (tSNAC* const snac, int overlaparg, tMempool* const mp) { _tMempool* m = *mp; _tSNAC* s = *snac = (_tSNAC*) mpool_alloc(sizeof(_tSNAC), m); s->mempool = m; 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*) mpool_calloc(sizeof(float) * SNAC_FRAME_SIZE, m); s->processbuf = (float*) mpool_calloc(sizeof(float) * (SNAC_FRAME_SIZE * 2), m); s->spectrumbuf = (float*) mpool_calloc(sizeof(float) * (SNAC_FRAME_SIZE / 2), m); s->biasbuf = (float*) mpool_calloc(sizeof(float) * SNAC_FRAME_SIZE, m); snac_biasbuf(snac); tSNAC_setOverlap(snac, overlaparg); } void tSNAC_free (tSNAC* const snac) { _tSNAC* s = *snac; mpool_free((char*)s->inputbuf, s->mempool); mpool_free((char*)s->processbuf, s->mempool); mpool_free((char*)s->spectrumbuf, s->mempool); mpool_free((char*)s->biasbuf, s->mempool); mpool_free((char*)s, s->mempool); } /******************************************************************************/ /************************** public access functions****************************/ /******************************************************************************/ //void tSNAC_ioSamples(tSNAC* const snac, float *in, float *out, int size) void tSNAC_ioSamples(tSNAC* const snac, float *in, 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++; 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.f / sqrtf((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.f; } // 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 / sqrtf(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]; //could this be switched to float resolution without issue? -JS 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.0f) 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.0f) / periodlength; startbin = (int)(peaklocation * 0.8f + 0.5f); if(startbin < 1) startbin = 1; stopbin = (int)(peaklocation * 1.25f + 0.5f); 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] = sqrtf(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 / logf((float)(maxperiod - 4)); float *biasbuf = s->biasbuf; for(n=0; n<5; n++) // periods < 5 samples can't be tracked { biasbuf[n] = 0.0f; } for(n=5; n<maxperiod; n++) { biasbuf[n] = 1.0f - (float)logf(n - 4.f) * bias; } } //=========================================================================== // PERIODDETECTION //=========================================================================== void tPeriodDetection_init (tPeriodDetection* const pd, float* in, int bufSize, int frameSize, LEAF* const leaf) { tPeriodDetection_initToPool(pd, in, bufSize, frameSize, &leaf->mempool); } void tPeriodDetection_initToPool (tPeriodDetection* const pd, float* in, int bufSize, int frameSize, tMempool* const mp) { _tMempool* m = *mp; _tPeriodDetection* p = *pd = (_tPeriodDetection*) mpool_calloc(sizeof(_tPeriodDetection), m); p->mempool = m; LEAF* leaf = p->mempool->leaf; p->inBuffer = in; 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_initToPool(&p->env, p->windowSize, p->hopSize, p->frameSize, mp); tSNAC_initToPool(&p->snac, DEFOVERLAP, mp); p->history = 0.0f; p->alpha = 1.0f; p->tolerance = 1.0f; p->timeConstant = DEFTIMECONSTANT; p->radius = expf(-1000.0f * p->hopSize * leaf->invSampleRate / p->timeConstant); p->fidelityThreshold = 0.95f; } void tPeriodDetection_free (tPeriodDetection* const pd) { _tPeriodDetection* p = *pd; tEnvPD_free(&p->env); tSNAC_free(&p->snac); mpool_free((char*)p, p->mempool); } float tPeriodDetection_tick (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->frameSize); // Fidelity threshold recommended by Katja Vetters is 0.95 for most instruments/voices http://www.katjaas.nl/helmholtz/helmholtz.html 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; } return p->period; } float tPeriodDetection_getPeriod(tPeriodDetection* pd) { _tPeriodDetection* p = *pd; return p->period; } float tPeriodDetection_getFidelity(tPeriodDetection* pd) { _tPeriodDetection* p = *pd; return tSNAC_getFidelity(&p->snac); } 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; } void tPeriodDetection_setFidelityThreshold(tPeriodDetection* pd, float threshold) { _tPeriodDetection* p = *pd; p->fidelityThreshold = threshold; } void tPeriodDetection_setAlpha (tPeriodDetection* pd, float alpha) { _tPeriodDetection* p = *pd; p->alpha = LEAF_clip(0.0f, alpha, 1.0f); } void tPeriodDetection_setTolerance (tPeriodDetection* pd, float tolerance) { _tPeriodDetection* p = *pd; if (tolerance < 0.0f) p->tolerance = 0.0f; else p->tolerance = tolerance; } void tZeroCrossingInfo_init (tZeroCrossingInfo* const zc, LEAF* const leaf) { tZeroCrossingInfo_initToPool(zc, &leaf->mempool); } void tZeroCrossingInfo_initToPool (tZeroCrossingInfo* const zc, tMempool* const mp) { _tMempool* m = *mp; _tZeroCrossingInfo* z = *zc = (_tZeroCrossingInfo*) mpool_alloc(sizeof(_tZeroCrossingInfo), m); z->mempool = m; z->_leading_edge = INT_MIN; z->_trailing_edge = INT_MIN; z->_width = 0.0f; } void tZeroCrossingInfo_free (tZeroCrossingInfo* const zc) { _tZeroCrossingInfo* z = *zc; mpool_free((char*)z, z->mempool); } void tZeroCrossingInfo_updatePeak(tZeroCrossingInfo* const zc, float s, int pos) { _tZeroCrossingInfo* z = *zc; z->_peak = fmaxf(s, z->_peak); if ((z->_width == 0.0f) && (s < (z->_peak * 0.3f))) z->_width = pos - z->_leading_edge; } int tZeroCrossingInfo_period(tZeroCrossingInfo* const zc, tZeroCrossingInfo* const next) { _tZeroCrossingInfo* z = *zc; _tZeroCrossingInfo* n = *next; return n->_leading_edge - z->_leading_edge; } float tZeroCrossingInfo_fractionalPeriod(tZeroCrossingInfo* const zc, tZeroCrossingInfo* const next) { _tZeroCrossingInfo* z = *zc; _tZeroCrossingInfo* n = *next; // Get the start edge float prev1 = z->_before_crossing; float curr1 = z->_after_crossing; float dy1 = curr1 - prev1; float dx1 = -prev1 / dy1; // Get the next edge float prev2 = n->_before_crossing; float curr2 = n->_after_crossing; float dy2 = curr2 - prev2; float dx2 = -prev2 / dy2; // Calculate the fractional period float result = n->_leading_edge - z->_leading_edge; return result + (dx2 - dx1); } int tZeroCrossingInfo_getWidth(tZeroCrossingInfo* const zc) { _tZeroCrossingInfo* z = *zc; return z->_width; } static inline void update_state(tZeroCrossingCollector* const zc, float s); static inline void shift(tZeroCrossingCollector* const zc, int n); static inline void reset(tZeroCrossingCollector* const zc); void tZeroCrossingCollector_init (tZeroCrossingCollector* const zc, int windowSize, float hysteresis, LEAF* const leaf) { tZeroCrossingCollector_initToPool(zc, windowSize, hysteresis, &leaf->mempool); } void tZeroCrossingCollector_initToPool (tZeroCrossingCollector* const zc, int windowSize, float hysteresis, tMempool* const mp) { _tMempool* m = *mp; _tZeroCrossingCollector* z = *zc = (_tZeroCrossingCollector*) mpool_alloc(sizeof(_tZeroCrossingCollector), m); z->mempool = m; z->_hysteresis = -dbtoa(hysteresis); int bits = CHAR_BIT * sizeof(unsigned int); z->_window_size = fmax(2, (windowSize + bits - 1) / bits) * bits; int size = z->_window_size / 2; // Ensure size is a power of 2 z->_size = pow(2.0, ceil(log2((double)size))); z->_mask = z->_size - 1; z->_info = (tZeroCrossingInfo*) mpool_calloc(sizeof(tZeroCrossingInfo) * z->_size, m); for (unsigned i = 0; i < z->_size; i++) tZeroCrossingInfo_initToPool(&z->_info[i], mp); z->_pos = 0; z->_prev = 0.0f; z->_state = 0; z->_num_edges = 0; z->_frame = 0; z->_ready = 0; z->_peak_update = 0.0f; z->_peak = 0.0f; } void tZeroCrossingCollector_free (tZeroCrossingCollector* const zc) { _tZeroCrossingCollector* z = *zc; for (unsigned i = 0; i < z->_size; i++) tZeroCrossingInfo_free(&z->_info[i]); mpool_free((char*)z->_info, z->mempool); mpool_free((char*)z, z->mempool); } int tZeroCrossingCollector_tick(tZeroCrossingCollector* const zc, float s) { _tZeroCrossingCollector* z = *zc; // Offset s by half of hysteresis, so that zero cross detection is // centered on the actual zero. s += z->_hysteresis * 0.5f; if (z->_num_edges >= (int)z->_size) reset(zc); if ((z->_frame == z->_window_size/2) && (z->_num_edges == 0)) reset(zc); update_state(zc, s); if ((++z->_frame >= z->_window_size) && !z->_state) { // Remove half the size from _frame, so we can continue seamlessly z->_frame -= z->_window_size / 2; // We need at least two rising edges. if (z->_num_edges > 1) z->_ready = 1; else reset(zc); } return z->_state; } int tZeroCrossingCollector_getState(tZeroCrossingCollector* const zc) { _tZeroCrossingCollector* z = *zc; return z->_state; } tZeroCrossingInfo const tZeroCrossingCollector_getCrossing(tZeroCrossingCollector* const zc, int index) { _tZeroCrossingCollector* z = *zc; int i = (z->_num_edges - 1) - index; return z->_info[(z->_pos + i) & z->_mask]; } int tZeroCrossingCollector_getNumEdges(tZeroCrossingCollector* const zc) { _tZeroCrossingCollector* z = *zc; return z->_num_edges; } int tZeroCrossingCollector_getCapacity(tZeroCrossingCollector* const zc) { _tZeroCrossingCollector* z = *zc; return (int)z->_size; } int tZeroCrossingCollector_getFrame(tZeroCrossingCollector* const zc) { _tZeroCrossingCollector* z = *zc; return z->_frame; } int tZeroCrossingCollector_getWindowSize(tZeroCrossingCollector* const zc) { _tZeroCrossingCollector* z = *zc; return z->_window_size; } int tZeroCrossingCollector_isReady(tZeroCrossingCollector* const zc) { _tZeroCrossingCollector* z = *zc; return z->_ready; } float tZeroCrossingCollector_getPeak(tZeroCrossingCollector* const zc) { _tZeroCrossingCollector* z = *zc; return fmaxf(z->_peak, z->_peak_update); } int tZeroCrossingCollector_isReset(tZeroCrossingCollector* const zc) { _tZeroCrossingCollector* z = *zc; return z->_frame == 0; } void tZeroCrossingCollector_setHysteresis(tZeroCrossingCollector* const zc, float hysteresis) { _tZeroCrossingCollector* z = *zc; z->_hysteresis = -dbtoa(hysteresis); } static inline void update_state(tZeroCrossingCollector* const zc, float s) { _tZeroCrossingCollector* z = *zc; if (z->_ready) { shift(zc, z->_window_size / 2); z->_ready = 0; z->_peak = z->_peak_update; z->_peak_update = 0.0f; } if (z->_num_edges >= (int)z->_size) reset(zc); if (s > 0.0f) { if (!z->_state) { --z->_pos; z->_pos &= z->_mask; tZeroCrossingInfo crossing = z->_info[z->_pos]; crossing->_before_crossing = z->_prev; crossing->_after_crossing = s; crossing->_peak = s; crossing->_leading_edge = (int) z->_frame; crossing->_trailing_edge = INT_MIN; crossing->_width = 0.0f; ++z->_num_edges; z->_state = 1; } else { tZeroCrossingInfo_updatePeak(&z->_info[z->_pos], s, z->_frame); } if (s > z->_peak_update) { z->_peak_update = s; } } else if (z->_state && (s < z->_hysteresis)) { z->_state = 0; z->_info[z->_pos]->_trailing_edge = z->_frame; if (z->_peak == 0.0f) z->_peak = z->_peak_update; } if (z->_frame > z->_window_size * 2) reset(zc); z->_prev = s; } static inline void shift(tZeroCrossingCollector* const zc, int n) { _tZeroCrossingCollector* z = *zc; tZeroCrossingInfo crossing = z->_info[z->_pos]; crossing->_leading_edge -= n; if (!z->_state) crossing->_trailing_edge -= n; int i = 1; for (; i != z->_num_edges; ++i) { int idx = (z->_pos + i) & z->_mask; z->_info[idx]->_leading_edge -= n; int edge = (z->_info[idx]->_trailing_edge -= n); if (edge < 0.0f) break; } z->_num_edges = i; } static inline void reset(tZeroCrossingCollector* const zc) { _tZeroCrossingCollector* z = *zc; z->_num_edges = 0; z->_state = 0; z->_frame = 0; } void tBitset_init (tBitset* const bitset, int numBits, LEAF* const leaf) { tBitset_initToPool(bitset, numBits, &leaf->mempool); } void tBitset_initToPool (tBitset* const bitset, int numBits, tMempool* const mempool) { _tMempool* m = *mempool; _tBitset* b = *bitset = (_tBitset*) mpool_alloc(sizeof(_tBitset), m); b->mempool = m; // Size of the array value in bits b->_value_size = (CHAR_BIT * sizeof(unsigned int)); // Size of the array needed to store numBits bits b->_size = (numBits + b->_value_size - 1) / b->_value_size; // Siz of the array in bits b->_bit_size = b->_size * b->_value_size; b->_bits = (unsigned int*) mpool_calloc(sizeof(unsigned int) * b->_size, m); } void tBitset_free (tBitset* const bitset) { _tBitset* b = *bitset; mpool_free((char*) b->_bits, b->mempool); mpool_free((char*) b, b->mempool); } int tBitset_get (tBitset* const bitset, int index) { _tBitset* b = *bitset; // Check we don't get past the storage if (index > b->_bit_size) return -1; unsigned int mask = 1 << (index % b->_value_size); return (b->_bits[index / b->_value_size] & mask) != 0; } unsigned int* tBitset_getData (tBitset* const bitset) { _tBitset* b = *bitset; return b->_bits; } void tBitset_set (tBitset* const bitset, int index, unsigned int val) { _tBitset* b = *bitset; if (index > b->_bit_size) return; unsigned int mask = 1 << (index % b->_value_size); int i = index / b->_value_size; b->_bits[i] ^= (-val ^ b->_bits[i]) & mask; } void tBitset_setMultiple (tBitset* const bitset, int index, int n, unsigned int val) { _tBitset* b = *bitset; // Check that the index (i) does not get past size if (index > b->_bit_size) return; // Check that the n does not get past the size if ((index+n) > b->_bit_size) n = b->_bit_size - index; // index is the bit index, i is the integer index int i = index / b->_value_size; // Do the first partial int int mod = index & (b->_value_size - 1); if (mod) { // mask off the high n bits we want to set mod = b->_value_size - mod; // Calculate the mask unsigned int mask = ~(UINT_MAX >> mod); // Adjust the mask if we're not going to reach the end of this int if (n < mod) mask &= (UINT_MAX >> (mod - n)); if (val) b->_bits[i] |= mask; else b->_bits[i] &= ~mask; // Fast exit if we're done here! if (n < mod) return; n -= mod; ++i; } // Write full ints while we can - effectively doing value_size bits at a time if (n >= b->_value_size) { // Store a local value to work with unsigned int val_ = val ? UINT_MAX : 0; do { b->_bits[i++] = val_; n -= b->_value_size; } while (n >= b->_value_size); } // Now do the final partial int, if necessary if (n) { mod = n & (b->_value_size - 1); // Calculate the mask unsigned int mask = (1 << mod) - 1; if (val) b->_bits[i] |= mask; else b->_bits[i] &= ~mask; } } int tBitset_getSize (tBitset* const bitset) { _tBitset* b = *bitset; return b->_bit_size; } void tBitset_clear (tBitset* const bitset) { _tBitset* b = *bitset; for (int i = 0; i < b->_size; ++i) { b->_bits[i] = 0; } } void tBACF_init (tBACF* const bacf, tBitset* const bitset, LEAF* const leaf) { tBACF_initToPool(bacf, bitset, &leaf->mempool); } void tBACF_initToPool (tBACF* const bacf, tBitset* const bitset, tMempool* const mempool) { _tMempool* m = *mempool; _tBACF* b = *bacf = (_tBACF*) mpool_alloc(sizeof(_tBACF), m); b->mempool = m; b->_bitset = *bitset; b->_mid_array = ((b->_bitset->_bit_size / b->_bitset->_value_size) / 2) - 1; } void tBACF_free (tBACF* const bacf) { _tBACF* b = *bacf; mpool_free((char*) b, b->mempool); } int tBACF_getCorrelation (tBACF* const bacf, int pos) { _tBACF* b = *bacf; int value_size = b->_bitset->_value_size; const int index = pos / value_size; const int shift = pos % value_size; const unsigned int* p1 = b->_bitset->_bits; const unsigned int* p2 = b->_bitset->_bits + index; int count = 0; if (shift == 0) { for (unsigned i = 0; i != b->_mid_array; ++i) { // built in compiler popcount functions should be faster but we want this to be portable // could try to add some define that call the correct function depending on compiler // or let the user pointer popcount() to whatever they want // something to look into... #ifdef __GNUC__ count += __builtin_popcount(*p1++ ^ *p2++); #elif _MSC_VER count += __popcnt(*p1++ ^ *p2++); #else count += popcount(*p1++ ^ *p2++); #endif } } else { const int shift2 = value_size - shift; for (unsigned i = 0; i != b->_mid_array; ++i) { unsigned int v = *p2++ >> shift; v |= *p2 << shift2; #ifdef __GNUC__ count += __builtin_popcount(*p1++ ^ v++); #elif _MSC_VER count += __popcnt(*p1++ ^ v++); #else count += popcount(*p1++ ^ v++); #endif } } return count; } void tBACF_set (tBACF* const bacf, tBitset* const bitset) { _tBACF* b = *bacf; b->_bitset = *bitset; b->_mid_array = ((b->_bitset->_bit_size / b->_bitset->_value_size) / 2) - 1; } static inline void set_bitstream(tPeriodDetector* const detector); static inline void autocorrelate(tPeriodDetector* const detector); static inline void sub_collector_init(_sub_collector* collector, tZeroCrossingCollector* const crossings, float pdt, int range); static inline float sub_collector_period_of(_sub_collector* collector, _auto_correlation_info info); static inline void sub_collector_save(_sub_collector* collector, _auto_correlation_info info); static inline int sub_collector_try_sub_harmonic(_sub_collector* collector, int harmonic, _auto_correlation_info info, float incoming_period); static inline int sub_collector_process_harmonics(_sub_collector* collector, _auto_correlation_info info); static inline void sub_collector_process(_sub_collector* collector, _auto_correlation_info info); static inline void sub_collector_get(_sub_collector* collector, _auto_correlation_info info, _period_info* result); void tPeriodDetector_init (tPeriodDetector* const detector, float lowestFreq, float highestFreq, float hysteresis, LEAF* const leaf) { tPeriodDetector_initToPool(detector, lowestFreq, highestFreq, hysteresis, &leaf->mempool); } void tPeriodDetector_initToPool (tPeriodDetector* const detector, float lowestFreq, float highestFreq, float hysteresis, tMempool* const mempool) { _tMempool* m = *mempool; _tPeriodDetector* p = *detector = (_tPeriodDetector*) mpool_alloc(sizeof(_tPeriodDetector), m); p->mempool = m; LEAF* leaf = p->mempool->leaf; tZeroCrossingCollector_initToPool(&p->_zc, (1.0f / lowestFreq) * leaf->sampleRate * 2.0f, hysteresis, mempool); p->_min_period = (1.0f / highestFreq) * leaf->sampleRate; p->_range = highestFreq / lowestFreq; int windowSize = tZeroCrossingCollector_getWindowSize(&p->_zc); tBitset_initToPool(&p->_bits, windowSize, mempool); p->_weight = 2.0f / windowSize; p->_mid_point = windowSize / 2; p->_periodicity_diff_threshold = p->_mid_point * PERIODICITY_DIFF_FACTOR; p->_predicted_period = -1.0f; p->_edge_mark = 0; p->_predict_edge = 0; p->_num_pulses = 0; p->_half_empty = 0; tBACF_initToPool(&p->_bacf, &p->_bits, mempool); } void tPeriodDetector_free (tPeriodDetector* const detector) { _tPeriodDetector* p = *detector; tZeroCrossingCollector_free(&p->_zc); tBitset_free(&p->_bits); tBACF_free(&p->_bacf); mpool_free((char*) p, p->mempool); } int tPeriodDetector_tick (tPeriodDetector* const detector, float s) { _tPeriodDetector* p = *detector; // Zero crossing int prev = tZeroCrossingCollector_getState(&p->_zc); int zc = tZeroCrossingCollector_tick(&p->_zc, s); if (!zc && prev != zc) { ++p->_edge_mark; p->_predicted_period = -1.0f; } if (tZeroCrossingCollector_isReset(&p->_zc)) { p->_fundamental.period = -1.0f; p->_fundamental.periodicity = 0.0f; } if (tZeroCrossingCollector_isReady(&p->_zc)) { set_bitstream(detector); autocorrelate(detector); return 1; } return 0; } float tPeriodDetector_getPeriod (tPeriodDetector* const detector) { _tPeriodDetector* p = *detector; return p->_fundamental.period; } float tPeriodDetector_getPeriodicity (tPeriodDetector* const detector) { _tPeriodDetector* p = *detector; return p->_fundamental.periodicity; } float tPeriodDetector_harmonic (tPeriodDetector* const detector, int harmonicIndex) { _tPeriodDetector* p = *detector; if (harmonicIndex > 0) { if (harmonicIndex == 1) return p->_fundamental.periodicity; float target_period = p->_fundamental.period / (float) harmonicIndex; if (target_period >= p->_min_period && target_period < p->_mid_point) { int count = tBACF_getCorrelation(&p->_bacf, roundf(target_period)); float periodicity = 1.0f - (count * p->_weight); return periodicity; } } return 0.0f; } float tPeriodDetector_predictPeriod (tPeriodDetector* const detector) { _tPeriodDetector* p = *detector; if (p->_predicted_period == -1.0f && p->_edge_mark != p->_predict_edge) { p->_predict_edge = p->_edge_mark; int n = tZeroCrossingCollector_getNumEdges(&p->_zc); if (n > 1) { float threshold = tZeroCrossingCollector_getPeak(&p->_zc) * PULSE_THRESHOLD; for (int i = n - 1; i > 0; --i) { tZeroCrossingInfo edge2 = tZeroCrossingCollector_getCrossing(&p->_zc, i); if (edge2->_peak >= threshold) { for (int j = i-1; j >= 0; --j) { tZeroCrossingInfo edge1 = tZeroCrossingCollector_getCrossing(&p->_zc, j); if (edge1->_peak >= threshold) { float period = tZeroCrossingInfo_fractionalPeriod(&edge1, &edge2); if (period > p->_min_period) return (p->_predicted_period = period); } } return p->_predicted_period = -1.0f; } } } } return p->_predicted_period; } int tPeriodDetector_isReady (tPeriodDetector* const detector) { _tPeriodDetector* p = *detector; return tZeroCrossingCollector_isReady(&p->_zc); } int tPeriodDetector_isReset (tPeriodDetector* const detector) { _tPeriodDetector* p = *detector; return tZeroCrossingCollector_isReset(&p->_zc); } void tPeriodDetector_setHysteresis (tPeriodDetector* const detector, float hysteresis) { _tPeriodDetector* p = *detector; return tZeroCrossingCollector_setHysteresis(&p->_zc, hysteresis); } static inline void set_bitstream(tPeriodDetector* const detector) { _tPeriodDetector* p = *detector; float threshold = tZeroCrossingCollector_getPeak(&p->_zc) * PULSE_THRESHOLD; unsigned int leading_edge = tZeroCrossingCollector_getWindowSize(&p->_zc); unsigned int trailing_edge = 0; p->_num_pulses = 0; tBitset_clear(&p->_bits); for (int i = 0; i != tZeroCrossingCollector_getNumEdges(&p->_zc); ++i) { tZeroCrossingInfo info = tZeroCrossingCollector_getCrossing(&p->_zc, i); if (info->_peak >= threshold) { ++p->_num_pulses; if (info->_leading_edge < leading_edge) leading_edge = info->_leading_edge; if (info->_trailing_edge > trailing_edge) trailing_edge = info->_trailing_edge; int pos = fmax(info->_leading_edge, 0); int n = info->_trailing_edge - pos; tBitset_setMultiple(&p->_bits, pos, n, 1); } } p->_half_empty = (leading_edge > p->_mid_point) || (trailing_edge < p->_mid_point); } static inline void autocorrelate(tPeriodDetector* const detector) { _tPeriodDetector* p = *detector; float threshold = tZeroCrossingCollector_getPeak(&p->_zc) * PULSE_THRESHOLD; _sub_collector collect; sub_collector_init(&collect, &p->_zc, p->_periodicity_diff_threshold, p->_range); if (p->_half_empty || p->_num_pulses < 2) { p->_fundamental.periodicity = -1.0f; return; } else { int shouldBreak = 0; int n = tZeroCrossingCollector_getNumEdges(&p->_zc); for (int i = 0; i != n - 1; ++i) { tZeroCrossingInfo curr = tZeroCrossingCollector_getCrossing(&p->_zc, i); if (curr->_peak >= threshold) { for (int j = i + 1; j != n; ++j) { tZeroCrossingInfo next = tZeroCrossingCollector_getCrossing(&p->_zc, j); if (next->_peak >= threshold) { int period = tZeroCrossingInfo_period(&curr, &next); if (period > p->_mid_point) break; if (period >= p->_min_period) { int count = tBACF_getCorrelation(&p->_bacf, period); int mid = p->_bacf->_mid_array * CHAR_BIT * sizeof(unsigned int); int start = period; if ((collect._fundamental._period == -1.0f) && count == 0) { if (tBACF_getCorrelation(&p->_bacf, period / 2.0f) == 0) count = -1; } else if (period < 32) // Search minimum if the resolution is low { // Search upwards for the minimum autocorrelation count for (int d = start + 1; d < mid; ++d) { int c = tBACF_getCorrelation(&p->_bacf, d); if (c > count) break; count = c; period = d; } // Search downwards for the minimum autocorrelation count for (int d = start - 1; d > p->_min_period; --d) { int c = tBACF_getCorrelation(&p->_bacf, d); if (c > count) break; count = c; period = d; } } if (count == -1) { shouldBreak = 1; break; // Return early if we have false correlation } float periodicity = 1.0f - (count * p->_weight); _auto_correlation_info info = { i, j, (int) period, periodicity }; sub_collector_process(&collect, info); if (count == 0) { shouldBreak = 1; break; // Return early if we have perfect correlation } } } } } if (shouldBreak > 0) break; } } // Get the final resuts sub_collector_get(&collect, collect._fundamental, &p->_fundamental); } static inline void sub_collector_init(_sub_collector* collector, tZeroCrossingCollector* const crossings, float pdt, int range) { collector->_zc = *crossings; collector->_harmonic_threshold = HARMONIC_PERIODICITY_FACTOR * 2.0f / (float)collector->_zc->_window_size; collector->_periodicity_diff_threshold = pdt; collector->_range = range; collector->_fundamental._i1 = -1; collector->_fundamental._i2 = -1; collector->_fundamental._period = -1; collector->_fundamental._periodicity = 0.0f; collector->_fundamental._harmonic = 0; } static inline float sub_collector_period_of(_sub_collector* collector, _auto_correlation_info info) { tZeroCrossingInfo first = tZeroCrossingCollector_getCrossing(&collector->_zc, info._i1); tZeroCrossingInfo next = tZeroCrossingCollector_getCrossing(&collector->_zc, info._i2); return tZeroCrossingInfo_fractionalPeriod(&first, &next); } static inline void sub_collector_save(_sub_collector* collector, _auto_correlation_info info) { collector->_fundamental = info; collector->_fundamental._harmonic = 1; collector->_first_period = sub_collector_period_of(collector, collector->_fundamental); } static inline int sub_collector_try_sub_harmonic(_sub_collector* collector, int harmonic, _auto_correlation_info info, float incoming_period) { if (fabsf(incoming_period - collector->_first_period) < collector->_periodicity_diff_threshold) { // If incoming is a different harmonic and has better // periodicity ... if (info._periodicity > collector->_fundamental._periodicity && harmonic != collector->_fundamental._harmonic) { float periodicity_diff = fabsf(info._periodicity - collector->_fundamental._periodicity); // If incoming periodicity is within the harmonic // periodicity threshold, then replace _fundamental with // incoming. Take note of the harmonic for later. if (periodicity_diff <= collector->_harmonic_threshold) { collector->_fundamental._i1 = info._i1; collector->_fundamental._i2 = info._i2; collector->_fundamental._periodicity = info._periodicity; collector->_fundamental._harmonic = harmonic; } // If not, then we save incoming (replacing the current // _fundamental). else { sub_collector_save(collector, info); } } return 1; } return 0; } static inline int sub_collector_process_harmonics(_sub_collector* collector, _auto_correlation_info info) { if (info._period < collector->_first_period) return 0; float incoming_period = sub_collector_period_of(collector, info); int multiple = fmaxf(1.0f, roundf( incoming_period / collector->_first_period)); return sub_collector_try_sub_harmonic(collector, fmin(collector->_range, multiple), info, incoming_period/multiple); } static inline void sub_collector_process(_sub_collector* collector, _auto_correlation_info info) { if (collector->_fundamental._period == -1.0f) sub_collector_save(collector, info); else if (sub_collector_process_harmonics(collector, info)) return; else if (info._periodicity > collector->_fundamental._periodicity) sub_collector_save(collector, info); } static inline void sub_collector_get(_sub_collector* collector, _auto_correlation_info info, _period_info* result) { if (info._period != -1.0f) { result->period = sub_collector_period_of(collector, info) / info._harmonic; result->periodicity = info._periodicity; } else { result->period = -1.0f; result->period = 0.0f; } } static inline float calculate_frequency(tPitchDetector* const detector); static inline void bias(tPitchDetector* const detector, _pitch_info incoming); void tPitchDetector_init (tPitchDetector* const detector, float lowestFreq, float highestFreq, LEAF* const leaf) { tPitchDetector_initToPool(detector, lowestFreq, highestFreq, &leaf->mempool); } void tPitchDetector_initToPool (tPitchDetector* const detector, float lowestFreq, float highestFreq, tMempool* const mempool) { _tMempool* m = *mempool; _tPitchDetector* p = *detector = (_tPitchDetector*) mpool_alloc(sizeof(_tPitchDetector), m); p->mempool = m; tPeriodDetector_initToPool(&p->_pd, lowestFreq, highestFreq, -120.0f, mempool); p->_current.frequency = 0.0f; p->_current.periodicity = 0.0f; p->_frames_after_shift = 0; } void tPitchDetector_free (tPitchDetector* const detector) { _tPitchDetector* p = *detector; tPeriodDetector_free(&p->_pd); mpool_free((char*) p, p->mempool); } int tPitchDetector_tick (tPitchDetector* const detector, float s) { _tPitchDetector* p = *detector; tPeriodDetector_tick(&p->_pd, s); if (tPeriodDetector_isReset(&p->_pd)) { p->_current.frequency = 0.0f; p->_current.periodicity = 0.0f; } int ready = tPeriodDetector_isReady(&p->_pd); if (ready) { float periodicity = p->_pd->_fundamental.periodicity; if (periodicity == -1.0f) { p->_current.frequency = 0.0f; p->_current.periodicity = 0.0f; return 0; } if (p->_current.frequency == 0.0f) { if (periodicity >= ONSET_PERIODICITY) { float f = calculate_frequency(detector); if (f > 0.0f) { p->_current.frequency = f; p->_current.periodicity = periodicity; p->_frames_after_shift = 0; } } } else { if (periodicity < MIN_PERIODICITY) p->_frames_after_shift = 0; float f = calculate_frequency(detector); if (f > 0.0f) { _pitch_info info = { f, periodicity }; bias(detector, info); } } } return ready; } float tPitchDetector_getFrequency (tPitchDetector* const detector) { _tPitchDetector* p = *detector; return p->_current.frequency; } float tPitchDetector_getPeriodicity (tPitchDetector* const detector) { _tPitchDetector* p = *detector; return p->_current.periodicity; } float tPitchDetector_harmonic (tPitchDetector* const detector, int harmonicIndex) { _tPitchDetector* p = *detector; return tPeriodDetector_harmonic(&p->_pd, harmonicIndex); } float tPitchDetector_predictFrequency (tPitchDetector* const detector) { _tPitchDetector* p = *detector; LEAF* leaf = p->mempool->leaf; float period = tPeriodDetector_predictPeriod(&p->_pd); if (period > 0.0f) return leaf->sampleRate / period; return 0.0f; } int tPitchDetector_indeterminate (tPitchDetector* const detector) { _tPitchDetector* p = *detector; return p->_current.frequency == 0.0f; } void tPitchDetector_setHysteresis (tPitchDetector* const detector, float hysteresis) { _tPitchDetector* p = *detector; tPeriodDetector_setHysteresis(&p->_pd, hysteresis); } static inline float calculate_frequency(tPitchDetector* const detector) { _tPitchDetector* p = *detector; LEAF* leaf = p->mempool->leaf; float period = p->_pd->_fundamental.period; if (period > 0.0f) return leaf->sampleRate / period; return 0.0f; } static inline void bias(tPitchDetector* const detector, _pitch_info incoming) { _tPitchDetector* p = *detector; ++p->_frames_after_shift; int shifted = 0; _pitch_info result; //============================================================================= //_pitch_info result = bias(current, incoming, shift); { float error = p->_current.frequency * 0.015625; // approx 1/4 semitone float diff = fabsf(p->_current.frequency - incoming.frequency); int done = 0; // Try fundamental if (diff < error) { result = incoming; done = 1; } // Try harmonics and sub-harmonics else if (p->_frames_after_shift > 1) { if (p->_current.frequency > incoming.frequency) { int multiple = roundf(p->_current.frequency / incoming.frequency); if (multiple > 1) { float f = incoming.frequency * multiple; if (fabsf(p->_current.frequency - f) < error) { result.frequency = f; result.periodicity = incoming.periodicity; done = 1; } } } else { int multiple = roundf(incoming.frequency / p->_current.frequency); if (multiple > 1) { float f = incoming.frequency / multiple; if (fabsf(p->_current.frequency - f) < error) { result.frequency = f; result.periodicity = incoming.periodicity; done = 1; } } } } // Don't do anything if the latest autocorrelation is not periodic // enough. Note that we only do this check on frequency shifts (i.e. at // this point, we are looking at a potential frequency shift, after // passing through the code above, checking for fundamental and // harmonic matches). if (!done) { if (p->_pd->_fundamental.periodicity > MIN_PERIODICITY) { // Now we have a frequency shift shifted = 1; result = incoming; } else result = p->_current; } } //============================================================================= // Don't do anything if incoming is not periodic enough // Note that we only do this check on frequency shifts if (shifted) { float periodicity = p->_pd->_fundamental.periodicity; if (periodicity >= ONSET_PERIODICITY) { p->_frames_after_shift = 0; p->_current = result; } else if (periodicity < MIN_PERIODICITY) { p->_current.frequency = 0.0f; p->_current.periodicity = 0.0f; } } else { p->_current = result; } } static inline void compute_predicted_frequency(tDualPitchDetector* const detector); void tDualPitchDetector_init (tDualPitchDetector* const detector, float lowestFreq, float highestFreq, float* inBuffer, int bufSize, LEAF* const leaf) { tDualPitchDetector_initToPool(detector, lowestFreq, highestFreq, inBuffer, bufSize, &leaf->mempool); } void tDualPitchDetector_initToPool (tDualPitchDetector* const detector, float lowestFreq, float highestFreq, float* inBuffer, int bufSize, tMempool* const mempool) { _tMempool* m = *mempool; _tDualPitchDetector* p = *detector = (_tDualPitchDetector*) mpool_alloc(sizeof(_tDualPitchDetector), m); p->mempool = m; tPeriodDetection_initToPool(&p->_pd1, inBuffer, bufSize, bufSize / 2, mempool); tPitchDetector_initToPool(&p->_pd2, lowestFreq, highestFreq, mempool); p->_current.frequency = 0.0f; p->_current.periodicity = 0.0f; p->_mean = lowestFreq + ((highestFreq - lowestFreq) / 2.0f); p->_predicted_frequency = 0.0f; p->_first = 1; p->thresh = 0.98f; p->lowest = lowestFreq; p->highest = highestFreq; } void tDualPitchDetector_free (tDualPitchDetector* const detector) { _tDualPitchDetector* p = *detector; tPeriodDetection_free(&p->_pd1); tPitchDetector_free(&p->_pd2); mpool_free((char*) p, p->mempool); } int tDualPitchDetector_tick (tDualPitchDetector* const detector, float sample) { _tDualPitchDetector* p = *detector; LEAF* leaf = p->mempool->leaf; tPeriodDetection_tick(&p->_pd1, sample); int ready = tPitchDetector_tick(&p->_pd2, sample); if (ready) { int pd2_indeterminate = tPitchDetector_indeterminate(&p->_pd2); int disagreement = 0; float period = tPeriodDetection_getPeriod(&p->_pd1); if (!pd2_indeterminate && period != 0.0f) { _pitch_info _i1; _i1.frequency = leaf->sampleRate / tPeriodDetection_getPeriod(&p->_pd1); _i1.periodicity = tPeriodDetection_getFidelity(&p->_pd1); _pitch_info _i2 = p->_pd2->_current; float pd1_diff = fabsf(_i1.frequency - p->_mean); float pd2_diff = fabsf(_i2.frequency - p->_mean); _pitch_info i; disagreement = fabsf(_i1.frequency - _i2.frequency) > (p->_mean * 0.03125f); // If they agree, we'll use bacf if (!disagreement) i = _i2; // A disagreement implies a change // Start with smaller changes else if (pd2_diff < p->_mean * 0.03125f) i = _i2; else if (pd1_diff < p->_mean * 0.03125f) i = _i1; // Now filter out lower fidelity stuff else if (_i1.periodicity < p->thresh) return ready; // Changing up (bacf tends to lead changes) else if ((_i1.frequency > p->_mean && _i2.frequency > p->_mean) && (_i1.frequency < _i2.frequency) && (_i2.periodicity > p->thresh)) { if (roundf(_i2.frequency / _i1.frequency) > 1) i = _i1; else i = _i2; } // Changing down else if ((_i1.frequency < p->_mean && _i2.frequency < p->_mean) && (_i1.frequency > _i2.frequency) && (_i2.periodicity > p->thresh)) { if (roundf(_i1.frequency / _i2.frequency) > 1) i = _i1; else i = _i2; } // A bit of handling for stuff out of bacf range, won't be as solid but better than nothing else if (_i1.frequency > p->highest) { if (roundf(_i1.frequency / _i2.frequency) > 1) i = _i2; else i = _i1; } else if (_i1.frequency < p->lowest) { if (roundf(_i2.frequency / _i1.frequency) > 1) i = _i2; else i = _i1; } // Don't change if we met non of these, probably a bad read else return ready; if (p->_first) { p->_current = i; p->_mean = p->_current.frequency; p->_first = 0; p->_predicted_frequency = 0.0f; } else { p->_current = i; p->_mean = (0.2222222 * p->_current.frequency) + (0.7777778 * p->_mean); p->_predicted_frequency = 0.0f; } return ready; } } return ready; } float tDualPitchDetector_getFrequency (tDualPitchDetector* const detector) { _tDualPitchDetector* p = *detector; return p->_current.frequency; } float tDualPitchDetector_getPeriodicity (tDualPitchDetector* const detector) { _tDualPitchDetector* p = *detector; return p->_current.periodicity; } float tDualPitchDetector_predictFrequency (tDualPitchDetector* const detector) { _tDualPitchDetector* p = *detector; if (p->_predicted_frequency == 0.0f) compute_predicted_frequency(detector); return p->_predicted_frequency; } void tDualPitchDetector_setHysteresis (tDualPitchDetector* const detector, float hysteresis) { _tDualPitchDetector* p = *detector; tPitchDetector_setHysteresis(&p->_pd2, hysteresis); } void tDualPitchDetector_setPeriodicityThreshold (tDualPitchDetector* const detector, float thresh) { _tDualPitchDetector* p = *detector; p->thresh = thresh; } static inline void compute_predicted_frequency(tDualPitchDetector* const detector) { _tDualPitchDetector* p = *detector; float f1 = 1.0f / tPeriodDetection_getPeriod(&p->_pd1); float f2 = tPitchDetector_predictFrequency(&p->_pd2); if (f2 > 0.0f) { float error = f1 * 0.1f; if (fabsf(f1 - f2) < error) { p->_predicted_frequency = f1; return; } } p->_predicted_frequency = 0.0f; }