Fixed resampling bugs, pitch detection still pilked
This commit is contained in:
parent
645ce2e092
commit
f4eb2bdfaf
|
@ -3,5 +3,7 @@ dpf_add_plugin(yaw-totune
|
||||||
FILES_DSP
|
FILES_DSP
|
||||||
dsp.cpp)
|
dsp.cpp)
|
||||||
|
|
||||||
|
|
||||||
target_include_directories(yaw-totune PUBLIC
|
target_include_directories(yaw-totune PUBLIC
|
||||||
".")
|
"."
|
||||||
|
"../../lib")
|
|
@ -8,53 +8,6 @@ private: \
|
||||||
ClassName& operator=(ClassName&) = delete; \
|
ClassName& operator=(ClassName&) = delete; \
|
||||||
ClassName& operator=(const ClassName&) = delete;
|
ClassName& operator=(const ClassName&) = delete;
|
||||||
|
|
||||||
static constexpr double BIG_DOUBLE = 10000.0;
|
|
||||||
|
|
||||||
class Scale
|
|
||||||
{
|
|
||||||
double sampleRate = 48000.0;
|
|
||||||
|
|
||||||
// freqs in hz, periods in samples
|
|
||||||
std::vector<double> frequencies;
|
|
||||||
std::vector<double> periods;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void newSampleRate(double rate)
|
|
||||||
{
|
|
||||||
double ratio = rate / sampleRate;
|
|
||||||
sampleRate = rate;
|
|
||||||
for (double ¬e : periods)
|
|
||||||
note *= ratio;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Default ctor: 12TET @ 48kHz
|
|
||||||
Scale(double hz = 440.0)
|
|
||||||
{
|
|
||||||
hz /= 32.0;
|
|
||||||
|
|
||||||
for (int i = 0; i < 12 * 8; ++i)
|
|
||||||
{
|
|
||||||
frequencies.push_back(hz);
|
|
||||||
periods.push_back(sampleRate / hz);
|
|
||||||
hz *= exp2(1.0 / 12.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double getNearestPeriod(double period)
|
|
||||||
{
|
|
||||||
for (auto note : periods)
|
|
||||||
{
|
|
||||||
if (period > note)
|
|
||||||
return note;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This should NOT happen.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: parse scala files. new ctor that parses arbitrary scales
|
|
||||||
};
|
|
||||||
|
|
||||||
// x that minimizes the quadratic function determined by the three points
|
// x that minimizes the quadratic function determined by the three points
|
||||||
static double secondOrderMinimum(float x0, float y0, float x1, float y1, float x2, float y2)
|
static double secondOrderMinimum(float x0, float y0, float x1, float y1, float x2, float y2)
|
||||||
{
|
{
|
||||||
|
@ -77,6 +30,10 @@ static constexpr uint dFactor = 1 << dLogFactor;
|
||||||
static constexpr size_t dBufferSize = 1 << (pSize - dLogFactor);
|
static constexpr size_t dBufferSize = 1 << (pSize - dLogFactor);
|
||||||
static constexpr size_t dBufferMask = dBufferSize - 1;
|
static constexpr size_t dBufferMask = dBufferSize - 1;
|
||||||
|
|
||||||
|
static constexpr double BIG_DOUBLE = 10000.0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class Resampler
|
class Resampler
|
||||||
{
|
{
|
||||||
|
@ -93,7 +50,6 @@ private:
|
||||||
// Pointer for pitch detection.
|
// Pointer for pitch detection.
|
||||||
uint writeIdx = 0;
|
uint writeIdx = 0;
|
||||||
|
|
||||||
|
|
||||||
// Calculate autocorrelation from scratch for given period
|
// Calculate autocorrelation from scratch for given period
|
||||||
inline double ac(const uint per)
|
inline double ac(const uint per)
|
||||||
{
|
{
|
||||||
|
@ -119,7 +75,7 @@ public:
|
||||||
{
|
{
|
||||||
double minAC = BIG_DOUBLE;
|
double minAC = BIG_DOUBLE;
|
||||||
double period = nearPeriod;
|
double period = nearPeriod;
|
||||||
for (uint per = nearPeriod - dFactor; per < nearPeriod + dFactor; ++per)
|
for (uint per = nearPeriod - dFactor / 2; per < nearPeriod + dFactor / 2; ++per)
|
||||||
{
|
{
|
||||||
double curAC = ac(per);
|
double curAC = ac(per);
|
||||||
if (curAC < minAC)
|
if (curAC < minAC)
|
||||||
|
@ -147,20 +103,33 @@ public:
|
||||||
inline void resample(T *output, uint32_t frames, const double rate, const double period)
|
inline void resample(T *output, uint32_t frames, const double rate, const double period)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
//Bounds of read index.
|
||||||
|
double end = writeIdx;
|
||||||
|
while ( readIdx > end ) { end += resamplerBufferSize; };
|
||||||
|
double start = end - period;
|
||||||
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < frames; ++i)
|
for (uint32_t i = 0; i < frames; ++i)
|
||||||
{
|
{
|
||||||
// TODO: add interpolation filter.
|
// TODO: add interpolation filter. Linear for now.
|
||||||
*output = array[static_cast<uint>(readIdx)];
|
uint idx = static_cast<uint>(readIdx);
|
||||||
++output;
|
float frac = readIdx - idx;
|
||||||
|
*output = (1.f - frac) * array[(idx - 1) & resamplerMask] + frac * array[idx & resamplerMask];
|
||||||
if ((readIdx + rate > writeIdx))
|
|
||||||
readIdx -= period;
|
|
||||||
readIdx += rate;
|
readIdx += rate;
|
||||||
|
|
||||||
if (readIdx >= resamplerBufferSize)
|
while ( readIdx < start )
|
||||||
readIdx -= resamplerBufferSize;
|
{
|
||||||
if (readIdx < 0)
|
readIdx += period;
|
||||||
readIdx = 0;
|
}
|
||||||
|
|
||||||
|
while ( readIdx > end )
|
||||||
|
{
|
||||||
|
readIdx -= period;
|
||||||
|
}
|
||||||
|
|
||||||
|
++output;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +160,8 @@ public:
|
||||||
for (uint32_t i = 0; i < inputFrames;
|
for (uint32_t i = 0; i < inputFrames;
|
||||||
i += dFactor, writeIdx = (writeIdx + 1) & dBufferMask)
|
i += dFactor, writeIdx = (writeIdx + 1) & dBufferMask)
|
||||||
{
|
{
|
||||||
array[writeIdx] = input[i];
|
uint32_t j = (i > 3) ? i - 4 : 0;
|
||||||
|
array[writeIdx] = 0.5f * (input[i] + input[j]); //Lousy lowpass filter just for now.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,17 +170,18 @@ public:
|
||||||
{
|
{
|
||||||
frames /= dFactor;
|
frames /= dFactor;
|
||||||
uint idx = writeIdx - frames;
|
uint idx = writeIdx - frames;
|
||||||
|
bool a = false;
|
||||||
while (frames)
|
while (frames)
|
||||||
{
|
{
|
||||||
|
for (uint per = 1; per < squares.size(); ++per)
|
||||||
// TODO: start at 1 rather than 0
|
|
||||||
for (int per = 0; per < squares.size(); ++per)
|
|
||||||
{
|
{
|
||||||
T x = array[idx & dBufferMask];
|
T x = array[idx & dBufferMask];
|
||||||
T y = array[(idx - per) & dBufferMask];
|
T y = array[(idx - per) & dBufferMask];
|
||||||
T z = array[(idx - 2 * per) & dBufferMask];
|
T z = array[(idx - 2 * per) & dBufferMask];
|
||||||
squares[per] += x * x - z * z;
|
squares[per] += x * x - z * z;
|
||||||
crosses[per] += x * y - y * z;
|
crosses[per] += x * y - y * z;
|
||||||
|
if(squares[per] - 2.0 * crosses[per] < -0.1)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
--frames;
|
--frames;
|
||||||
++idx;
|
++idx;
|
||||||
|
@ -218,7 +189,7 @@ public:
|
||||||
|
|
||||||
double least = BIG_DOUBLE;
|
double least = BIG_DOUBLE;
|
||||||
uint per = 0;
|
uint per = 0;
|
||||||
for (uint i = 0; i < squares.size(); ++i)
|
for (uint i = 1; i < squares.size(); ++i)
|
||||||
{
|
{
|
||||||
double uac = squares[i] - 2 * crosses[i];
|
double uac = squares[i] - 2 * crosses[i];
|
||||||
if (uac < least)
|
if (uac < least)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "DistrhoPlugin.hpp"
|
#include "DistrhoPlugin.hpp"
|
||||||
#include "Resampler.hpp"
|
#include "Resampler.hpp"
|
||||||
|
#include "scale/scale.h"
|
||||||
|
|
||||||
START_NAMESPACE_DISTRHO
|
START_NAMESPACE_DISTRHO
|
||||||
|
|
||||||
|
@ -7,7 +8,7 @@ class PitchCorrector : public Plugin
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PitchCorrector()
|
PitchCorrector()
|
||||||
: Plugin(0, 0, 0),
|
: Plugin(1, 0, 0),
|
||||||
resamplers{},
|
resamplers{},
|
||||||
detectors{}
|
detectors{}
|
||||||
{
|
{
|
||||||
|
@ -27,7 +28,7 @@ protected:
|
||||||
parameter.hints = kParameterIsAutomable;
|
parameter.hints = kParameterIsAutomable;
|
||||||
parameter.ranges.def = 0.0f;
|
parameter.ranges.def = 0.0f;
|
||||||
parameter.ranges.min = 0.0f;
|
parameter.ranges.min = 0.0f;
|
||||||
parameter.ranges.max = 1.0f;
|
parameter.ranges.max = 1000.0f;
|
||||||
parameter.name = "param";
|
parameter.name = "param";
|
||||||
parameter.symbol = "param";
|
parameter.symbol = "param";
|
||||||
}
|
}
|
||||||
|
@ -39,7 +40,7 @@ protected:
|
||||||
|
|
||||||
float getParameterValue(uint32_t index) const override
|
float getParameterValue(uint32_t index) const override
|
||||||
{
|
{
|
||||||
return 0;
|
return curPitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setParameterValue(uint32_t idx, float val) override
|
void setParameterValue(uint32_t idx, float val) override
|
||||||
|
@ -53,24 +54,22 @@ protected:
|
||||||
resamplers[chn].write(inputs[chn], frames);
|
resamplers[chn].write(inputs[chn], frames);
|
||||||
detectors[chn].downsample(inputs[chn], frames);
|
detectors[chn].downsample(inputs[chn], frames);
|
||||||
uint dpitch = detectors[chn].detectPitch(frames);
|
uint dpitch = detectors[chn].detectPitch(frames);
|
||||||
double taux;
|
curPitch = dpitch;
|
||||||
double period;
|
double taux = 1.0;
|
||||||
|
double period = 1.0;
|
||||||
if (dpitch)
|
if (dpitch)
|
||||||
{
|
{
|
||||||
period = resamplers[chn].detectPeriodNear(dpitch);
|
period = resamplers[chn].detectPeriodNear(dpitch);
|
||||||
double correctPeriod = scale.getNearestPeriod(period);
|
double correctPeriod = scale.getNearestPeriod(period);
|
||||||
taux = period / correctPeriod;
|
if( correctPeriod > 1.0 ) taux = period / correctPeriod;
|
||||||
}
|
}
|
||||||
else
|
//resamplers[chn].resample(outputs[chn], frames, taux, period);
|
||||||
{
|
resamplers[chn].resample(outputs[chn], frames, 1.3, period);
|
||||||
taux = 1.0;
|
|
||||||
period = 1.0;
|
|
||||||
}
|
|
||||||
resamplers[chn].resample(outputs[chn], frames, taux, period);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
uint curPitch = 0;
|
||||||
double hzNyq;
|
double hzNyq;
|
||||||
std::array<Resampler<float>, 2> resamplers;
|
std::array<Resampler<float>, 2> resamplers;
|
||||||
std::array<Detector<float>, 2> detectors;
|
std::array<Detector<float>, 2> detectors;
|
||||||
|
|
Loading…
Reference in New Issue