Fixed a couple pitch corrector bugs. Many remain, however.
This commit is contained in:
parent
ef0c96f72a
commit
bfe17282d1
|
@ -1,6 +1,13 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
# define DISTRHO_DECLARE_NON_COPYABLE(ClassName) \
|
||||||
|
private: \
|
||||||
|
ClassName(ClassName&) = delete; \
|
||||||
|
ClassName(const ClassName&) = delete; \
|
||||||
|
ClassName& operator=(ClassName&) = delete; \
|
||||||
|
ClassName& operator=(const ClassName&) = delete;
|
||||||
|
|
||||||
static constexpr double BIG_DOUBLE = 10000.0;
|
static constexpr double BIG_DOUBLE = 10000.0;
|
||||||
|
|
||||||
class Scale
|
class Scale
|
||||||
|
@ -75,6 +82,8 @@ class Resampler
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Ring buffer.
|
// Ring buffer.
|
||||||
//TODO: refactor into its own class and look up operator[] semantics.
|
//TODO: refactor into its own class and look up operator[] semantics.
|
||||||
std::array<T, resamplerBufferSize> array = {};
|
std::array<T, resamplerBufferSize> array = {};
|
||||||
|
@ -103,6 +112,8 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
Resampler(){};
|
||||||
|
|
||||||
// Detect accurate period from scratch near a target period.
|
// Detect accurate period from scratch near a target period.
|
||||||
inline double detectPeriodNear(const uint nearPeriod)
|
inline double detectPeriodNear(const uint nearPeriod)
|
||||||
{
|
{
|
||||||
|
@ -136,23 +147,24 @@ 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)
|
||||||
{
|
{
|
||||||
|
|
||||||
for (; frames; --frames)
|
for (uint32_t i = 0; i < frames; ++i)
|
||||||
{
|
{
|
||||||
// TODO: add interpolation filter.
|
// TODO: add interpolation filter.
|
||||||
*output = array[static_cast<uint>(readIdx)];
|
*output = array[static_cast<uint>(readIdx)];
|
||||||
++output;
|
++output;
|
||||||
|
|
||||||
if ((readIdx < writeIdx) &&
|
if ((readIdx + rate > writeIdx))
|
||||||
(readIdx > writeIdx - rate))
|
|
||||||
readIdx -= period;
|
readIdx -= period;
|
||||||
readIdx += rate;
|
readIdx += rate;
|
||||||
|
|
||||||
if (readIdx > resamplerBufferSize)
|
if (readIdx >= resamplerBufferSize)
|
||||||
readIdx -= resamplerBufferSize;
|
readIdx -= resamplerBufferSize;
|
||||||
if (readIdx < 0)
|
if (readIdx < 0)
|
||||||
readIdx = 0;
|
readIdx = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DISTRHO_DECLARE_NON_COPYABLE(Resampler)
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -169,6 +181,9 @@ class Detector
|
||||||
static constexpr uint maxPeriod = dBufferSize / 2;
|
static constexpr uint maxPeriod = dBufferSize / 2;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
Detector(){};
|
||||||
|
|
||||||
// Read inputFrames from input buffer, downsample by factor and write to our ring buffer
|
// Read inputFrames from input buffer, downsample by factor and write to our ring buffer
|
||||||
// TODO: implement downsampling filter. This will probably have way too much antialiasing!
|
// TODO: implement downsampling filter. This will probably have way too much antialiasing!
|
||||||
inline void downsample(const T* const input, const uint32_t inputFrames)
|
inline void downsample(const T* const input, const uint32_t inputFrames)
|
||||||
|
@ -183,6 +198,7 @@ public:
|
||||||
// Incrementally detect all possible pitches. Return coarse pitch match.
|
// Incrementally detect all possible pitches. Return coarse pitch match.
|
||||||
inline uint detectPitch(uint32_t frames)
|
inline uint detectPitch(uint32_t frames)
|
||||||
{
|
{
|
||||||
|
frames /= dFactor;
|
||||||
uint idx = writeIdx - frames;
|
uint idx = writeIdx - frames;
|
||||||
while (frames)
|
while (frames)
|
||||||
{
|
{
|
||||||
|
@ -213,4 +229,6 @@ public:
|
||||||
}
|
}
|
||||||
return per * dFactor;
|
return per * dFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DISTRHO_DECLARE_NON_COPYABLE(Detector)
|
||||||
};
|
};
|
|
@ -7,21 +7,22 @@ class PitchCorrector : public Plugin
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PitchCorrector()
|
PitchCorrector()
|
||||||
: Plugin(0, 0, 0)
|
: Plugin(0, 0, 0),
|
||||||
|
resamplers{},
|
||||||
|
detectors{}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const char* getLabel() const override { return "yaw-totune"; }
|
const char *getLabel() const override { return "yaw-totune"; }
|
||||||
const char* getDescription() const override { return "Pitch corrector"; }
|
const char *getDescription() const override { return "Pitch corrector"; }
|
||||||
const char* getMaker() const override { return "yaw-audio"; }
|
const char *getMaker() const override { return "yaw-audio"; }
|
||||||
const char* getHomePage() const override { return "https://yaw.man/plugins/yaw-totune"; }
|
const char *getHomePage() const override { return "https://yaw.man/plugins/yaw-totune"; }
|
||||||
const char* getLicense() const override { return "Fuck you pay me"; }
|
const char *getLicense() const override { return "Fuck you pay me"; }
|
||||||
uint32_t getVersion() const override { return d_version(1, 0, 0); }
|
uint32_t getVersion() const override { return d_version(1, 0, 0); }
|
||||||
int64_t getUniqueId() const override { return d_cconst('y', 't', 't', 'n'); }
|
int64_t getUniqueId() const override { return d_cconst('y', 't', 't', 'n'); }
|
||||||
|
|
||||||
|
void initParameter(uint32_t index, Parameter ¶meter) override
|
||||||
void initParameter(uint32_t index, Parameter& parameter) override
|
|
||||||
{
|
{
|
||||||
parameter.hints = kParameterIsAutomable;
|
parameter.hints = kParameterIsAutomable;
|
||||||
parameter.ranges.def = 0.0f;
|
parameter.ranges.def = 0.0f;
|
||||||
|
@ -43,30 +44,41 @@ protected:
|
||||||
|
|
||||||
void setParameterValue(uint32_t idx, float val) override
|
void setParameterValue(uint32_t idx, float val) override
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void run(const float** inputs, float** outputs, uint32_t frames) override
|
void run(const float **inputs, float **outputs, uint32_t frames) override
|
||||||
{
|
{
|
||||||
for(int chn = 0; chn < 2; ++chn){
|
for (int chn = 0; chn < 2; ++chn)
|
||||||
resamplers[chn].write(inputs[chn], frames);
|
{
|
||||||
|
resamplers[chn].write(inputs[chn], frames);
|
||||||
detectors[chn].downsample(inputs[chn], frames);
|
detectors[chn].downsample(inputs[chn], frames);
|
||||||
double period = resamplers[chn].detectPeriodNear(detectors[chn].detectPitch(frames));
|
uint dpitch = detectors[chn].detectPitch(frames);
|
||||||
double correctPeriod = scale.getNearestPeriod(period);
|
double taux;
|
||||||
double taux = period / correctPeriod;
|
double period;
|
||||||
resamplers[chn].resample(outputs[chn], frames, taux, period);
|
if (dpitch)
|
||||||
}
|
{
|
||||||
|
period = resamplers[chn].detectPeriodNear(dpitch);
|
||||||
|
double correctPeriod = scale.getNearestPeriod(period);
|
||||||
|
taux = period / correctPeriod;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
taux = 1.0;
|
||||||
|
period = 1.0;
|
||||||
|
}
|
||||||
|
resamplers[chn].resample(outputs[chn], frames, taux, period);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
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;
|
||||||
Scale scale{440.0};
|
Scale scale{440.0};
|
||||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PitchCorrector)
|
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PitchCorrector)
|
||||||
};
|
};
|
||||||
|
|
||||||
Plugin* createPlugin()
|
Plugin *createPlugin()
|
||||||
{
|
{
|
||||||
return new PitchCorrector();
|
return new PitchCorrector();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue