initial
This commit is contained in:
271
Source/Helmholtz.cpp
Normal file
271
Source/Helmholtz.cpp
Normal file
@ -0,0 +1,271 @@
|
||||
/*
|
||||
Class Helmholtz implements a period-length detector using Philip McLeod's
|
||||
Specially Normalized AutoCorrelation function (SNAC).
|
||||
|
||||
Licensed under three-clause BSD license.
|
||||
|
||||
Katja Vetter, Feb 2012.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "Helmholtz.h"
|
||||
|
||||
|
||||
Helmholtz::Helmholtz(int framearg, int overlaparg, t_float biasarg)
|
||||
{
|
||||
|
||||
setframesize(framearg);
|
||||
setoverlap(overlaparg);
|
||||
if (biasarg)setbias(biasarg);
|
||||
else biasfactor = DEFBIAS;
|
||||
|
||||
|
||||
timeindex = 0;
|
||||
periodindex = 0;
|
||||
periodlength = 0.;
|
||||
fidelity = 0.;
|
||||
minrms = DEFMINRMS;
|
||||
}
|
||||
|
||||
|
||||
Helmholtz::~Helmholtz()
|
||||
{
|
||||
}
|
||||
|
||||
/*********************************************************************************/
|
||||
/******************************** public *****************************************/
|
||||
/*********************************************************************************/
|
||||
|
||||
|
||||
void Helmholtz::iosamples(const t_float* in, t_float* out, int size)
|
||||
{
|
||||
int mask = framesize - 1;
|
||||
int outindex = 0;
|
||||
|
||||
// call analysis function when it is time
|
||||
if (!(timeindex & (framesize / overlap - 1))) analyzeframe();
|
||||
|
||||
while (size--)
|
||||
{
|
||||
inputbuf[timeindex++] = *in++;
|
||||
//out[outindex++] = processbuf[timeindex++];
|
||||
timeindex &= mask;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Helmholtz::setframesize(int frame)
|
||||
{
|
||||
if (!((frame == 128) || (frame == 256) || (frame == 512) || (frame == 1024) || (frame == 2048)))
|
||||
frame = DEFFRAMESIZE;
|
||||
framesize = frame;
|
||||
|
||||
|
||||
timeindex = 0;
|
||||
}
|
||||
|
||||
|
||||
void Helmholtz::setoverlap(int lap)
|
||||
{
|
||||
if (!((lap == 1) || (lap == 2) || (lap == 4) || (lap == 8)))
|
||||
lap = DEFOVERLAP;
|
||||
overlap = lap;
|
||||
}
|
||||
|
||||
|
||||
void Helmholtz::setbias(t_float bias)
|
||||
{
|
||||
if (bias > 1.) bias = 1.;
|
||||
if (bias < 0.) bias = 0.;
|
||||
biasfactor = bias;
|
||||
}
|
||||
|
||||
|
||||
void Helmholtz::setminRMS(t_float rms)
|
||||
{
|
||||
if (rms > 1.) rms = 1.;
|
||||
if (rms < 0.) rms = 0.;
|
||||
minrms = rms;
|
||||
}
|
||||
|
||||
|
||||
t_float Helmholtz::getperiod() const
|
||||
{
|
||||
return(periodlength);
|
||||
}
|
||||
|
||||
|
||||
t_float Helmholtz::getfidelity() const
|
||||
{
|
||||
return(fidelity);
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************/
|
||||
/***************************** private procedures ********************************/
|
||||
/*********************************************************************************/
|
||||
|
||||
|
||||
// main analysis function
|
||||
void Helmholtz::analyzeframe()
|
||||
{
|
||||
int n, tindex = timeindex;
|
||||
int mask = framesize - 1;
|
||||
int peak;
|
||||
t_float norm = 1. / sqrt(t_float(framesize * 2));
|
||||
|
||||
// copy input to processing buffer
|
||||
for (n = 0; n < framesize; n++) processbuf[n] = inputbuf[tindex++ & mask] * norm;
|
||||
|
||||
// copy for normalization function
|
||||
for (n = 0; n < framesize; n++) inputbuf2[n] = inputbuf[tindex++ & mask];
|
||||
|
||||
// zeropadding
|
||||
for (n = framesize; n < (framesize << 1); n++) processbuf[n] = 0.;
|
||||
|
||||
// call analysis procedures
|
||||
autocorrelation();
|
||||
normalize();
|
||||
pickpeak();
|
||||
periodandfidelity();
|
||||
}
|
||||
|
||||
|
||||
void Helmholtz::autocorrelation()
|
||||
{
|
||||
int n;
|
||||
int fftsize = framesize * 2;
|
||||
|
||||
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.;
|
||||
|
||||
}
|
||||
|
||||
REALIFFT(fftsize, processbuf);
|
||||
}
|
||||
|
||||
|
||||
void Helmholtz::normalize()
|
||||
{
|
||||
int n, mask = framesize - 1;
|
||||
int seek = framesize * SEEK;
|
||||
t_float signal1, signal2;
|
||||
|
||||
// minimum RMS implemented as minimum autocorrelation at index 0
|
||||
// effectively this means possible white noise addition
|
||||
t_float rms = minrms / sqrt(1. / (t_float)framesize);
|
||||
t_float minrzero = rms * rms;
|
||||
t_float rzero = processbuf[0];
|
||||
if (rzero < minrzero) rzero = minrzero;
|
||||
double normintegral = rzero * 2.;
|
||||
|
||||
// normalize biased autocorrelation function
|
||||
processbuf[0] = 1.;
|
||||
for (n = 1; n < seek; n++)
|
||||
{
|
||||
signal1 = inputbuf2[n - 1];
|
||||
signal2 = inputbuf2[framesize - n];
|
||||
normintegral -= (double)(signal1 * signal1 + signal2 * signal2);
|
||||
processbuf[n] /= (t_float)normintegral * 0.5;
|
||||
}
|
||||
|
||||
// flush instable function tail
|
||||
for (n = seek; n < framesize; n++) processbuf[n] = 0.;
|
||||
}
|
||||
|
||||
|
||||
// select the peak which most probably represents period length
|
||||
void Helmholtz::pickpeak()
|
||||
{
|
||||
int n, peakindex = 0;
|
||||
int seek = framesize * SEEK;
|
||||
t_float maxvalue = 0.;
|
||||
t_float previous[2];
|
||||
t_float bias = biasfactor / (t_float)framesize; // user-controlled bias
|
||||
t_float realpeak;
|
||||
|
||||
// skip main lobe
|
||||
for (n = 1; n < seek; n++)
|
||||
{
|
||||
if (processbuf[n] < 0.) break;
|
||||
}
|
||||
|
||||
// find interpolated / biased maximum in specially normalized autocorrelation 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
|
||||
{
|
||||
realpeak = interpolate3max(processbuf, n);
|
||||
|
||||
if ((realpeak * (1. - n * bias)) > maxvalue)
|
||||
{
|
||||
maxvalue = realpeak;
|
||||
peakindex = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
periodindex = peakindex;
|
||||
}
|
||||
|
||||
|
||||
void Helmholtz::periodandfidelity()
|
||||
{
|
||||
if (periodindex)
|
||||
{
|
||||
periodlength = periodindex + interpolate3phase(processbuf, periodindex);
|
||||
fidelity = interpolate3max(processbuf, periodindex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************/
|
||||
/***************************** private functions *********************************/
|
||||
/*********************************************************************************/
|
||||
|
||||
|
||||
inline t_float Helmholtz::interpolate3max(t_float* buf, int peakindex)
|
||||
{
|
||||
t_float realpeak;
|
||||
|
||||
t_float a = buf[peakindex - 1];
|
||||
t_float b = buf[peakindex];
|
||||
t_float c = buf[peakindex + 1];
|
||||
|
||||
realpeak = b + 0.5 * (0.5 * ((c - a) * (c - a)))
|
||||
/ (2 * b - a - c);
|
||||
|
||||
return(realpeak);
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline t_float Helmholtz::interpolate3phase(t_float* buf, int peakindex)
|
||||
{
|
||||
t_float fraction;
|
||||
|
||||
t_float a = buf[peakindex - 1];
|
||||
t_float b = buf[peakindex];
|
||||
t_float c = buf[peakindex + 1];
|
||||
|
||||
fraction = (0.5 * (c - a)) / (2. * b - a - c);
|
||||
|
||||
return(fraction);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user