Fixed a couple pitch corrector bugs. Many remain, however.

This commit is contained in:
yaw-man 2022-09-16 11:41:33 -04:00
parent ef0c96f72a
commit bfe17282d1
2 changed files with 56 additions and 26 deletions

View File

@ -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)
}; };

View File

@ -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 &parameter) 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,17 +44,28 @@ 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 taux;
double period;
if (dpitch)
{
period = resamplers[chn].detectPeriodNear(dpitch);
double correctPeriod = scale.getNearestPeriod(period); double correctPeriod = scale.getNearestPeriod(period);
double taux = period / correctPeriod; taux = period / correctPeriod;
}
else
{
taux = 1.0;
period = 1.0;
}
resamplers[chn].resample(outputs[chn], frames, taux, period); resamplers[chn].resample(outputs[chn], frames, taux, period);
} }
} }
@ -66,7 +78,7 @@ private:
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();
} }