Files
FrameLib/FrameLib_Objects/Vector/FrameLib_Peaks.cpp
T
2019-09-16 21:56:00 +02:00

165 lines
5.0 KiB
C++

#include "FrameLib_Peaks.h"
FrameLib_Peaks::FrameLib_Peaks(FrameLib_Context context, FrameLib_Parameters::Serial *serialisedParameters, FrameLib_Proxy *proxy) : FrameLib_Processor(context, proxy, nullptr, 1, 3)
{
mParameters.set(serialisedParameters);
}
// Info
std::string FrameLib_Peaks::objectInfo(bool verbose)
{
return formatInfo("Finds peaks in an input frame (spectrum): "
"Peaks are output in terms of interpolated sample position, interpolated amplitude and peak index. "
"The first output is the same size as the input, other outputs are as long as the number of detected peaks.",
"Finds peaks in an input frame (spectrum).", verbose);
}
std::string FrameLib_Peaks::inputInfo(unsigned long idx, bool verbose)
{
return "Input Frame / Spectrum";
}
std::string FrameLib_Peaks::outputInfo(unsigned long idx, bool verbose)
{
if (idx == 0) return formatInfo("Peak Index - for each input sample / bin the output lists the peak it belongs to", "Peak Index", verbose);
else if (idx == 1) return formatInfo("Peak Position - an interpolated position in samples / bins for each peak", "Peak Position", verbose);
else return formatInfo("Peak Amplitude - an interpolated amplitude for each peak", "Peak Amplitude", verbose);
}
// Helpers
double FrameLib_Peaks::logValue(double val)
{
val = log(val);
return val < -500.0 ? -500.0 : val;
}
void FrameLib_Peaks::refinePeak(double& pos, double& amp, double posUncorrected, double vm1, double v0, double vp1)
{
// FIX - neg values won't work with this interpolation - problem??
// Take log values (avoiding values that are too low)
vm1 = logValue(std::max(vm1, 0.0));
v0 = logValue(std::max(v0, 0.0));
vp1 = logValue(std::max(vp1, 0.0));
// Parabolic interpolation
double divisor = vm1 + vp1 - (2.0 * v0);
double correction = divisor ? (0.5 * (vm1 - vp1)) / divisor : 0.0;
// N.B - Leave amplitude in a log format
pos = posUncorrected + correction;
amp = exp(v0 - (0.25 * (vm1 - vp1) * correction));
}
// Process
void FrameLib_Peaks::process()
{
// Get Input
unsigned long sizeIn, sizeOut1, sizeOut2, sizeOut3;
unsigned long nPeaks = 0;
const double *input = getInput(0, &sizeIn);
if (!sizeIn)
return;
// Calculate number of peaks (for now ignore peaks in the top 2 positions)
if (sizeIn > 2 && (input[0] > input[1]) && (input[0] > input[2]))
nPeaks++;
else
{
if (sizeIn > 3 && (input[1] > input[2]) && (input[1] > input[3]) && (input[1] > input[0]))
nPeaks++;
}
for (unsigned long i = 2; i < (std::max(sizeIn, 2UL) - 2); i++)
if ((input[i] > input[i - 2]) && (input[i] > input[i - 1]) && (input[i] > input[i + 1]) && (input[i] > input[i + 2]))
nPeaks++;
// Allocate outputs
requestOutputSize(0, sizeIn);
requestOutputSize(1, nPeaks);
requestOutputSize(2, nPeaks);
allocateOutputs();
double *output1 = getOutput(0, &sizeOut1);
double *output2 = getOutput(1, &sizeOut2);
double *output3 = getOutput(2, &sizeOut3);
// Find and Refine Peaks (for now ignore peaks in the top 2 positions)
nPeaks = 0;
if (sizeOut1 && sizeOut2 && sizeOut3)
{
if (sizeIn > 2 && (input[0] > input[1]) && (input[0] > input[2]))
{
refinePeak(output2[0], output3[0], 0, input[1], input[0], input[1]);
nPeaks++;
}
else
{
if (sizeIn > 3 && (input[1] > input[2]) && (input[1] > input[3]) && (input[1] > input[0]))
{
refinePeak(output2[0], output3[0], 1, input[0], input[1], input[2]);
nPeaks++;
}
}
for (unsigned long i = 2; i < (std::max(sizeIn, 2UL) - 2); i++)
{
if ((input[i] > input[i - 2]) && (input[i] > input[i - 1]) && (input[i] > input[i + 1]) && (input[i] > input[i + 2]))
{
refinePeak(output2[nPeaks], output3[nPeaks], i, input[i - 1], input[i], input[i + 1]);
nPeaks++;
}
}
}
// Set indices
unsigned long binsFilled = 0;
unsigned long peak = 0;
unsigned long minPoint = 0;
if (nPeaks)
{
for (; peak < (nPeaks - 1); peak++)
{
unsigned long beg = truncToUInt(output2[peak]);
unsigned long end = roundToUInt(output2[peak + 1]);
double minValue = input[beg];
minPoint = beg;
for (unsigned long i = beg; i < end; i++)
{
if (input[i] < minValue)
{
minValue = input[i];
minPoint = i;
}
}
for (; binsFilled < minPoint; binsFilled++)
output1[binsFilled] = peak;
}
}
// Fill to the end
for (; binsFilled < sizeOut1; binsFilled++)
output1[binsFilled] = peak;
}