From bfe17282d13eb77920850f60ecea9da81d5b4724 Mon Sep 17 00:00:00 2001 From: yaw-man Date: Fri, 16 Sep 2022 11:41:33 -0400 Subject: [PATCH] Fixed a couple pitch corrector bugs. Many remain, however. --- src/yaw-totune/Resampler.hpp | 26 ++++++++++++++--- src/yaw-totune/dsp.cpp | 56 ++++++++++++++++++++++-------------- 2 files changed, 56 insertions(+), 26 deletions(-) diff --git a/src/yaw-totune/Resampler.hpp b/src/yaw-totune/Resampler.hpp index 768c3ec..f76be76 100644 --- a/src/yaw-totune/Resampler.hpp +++ b/src/yaw-totune/Resampler.hpp @@ -1,6 +1,13 @@ #include #include +# 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; class Scale @@ -75,6 +82,8 @@ class Resampler { private: + + // Ring buffer. //TODO: refactor into its own class and look up operator[] semantics. std::array array = {}; @@ -103,6 +112,8 @@ private: public: + Resampler(){}; + // Detect accurate period from scratch near a target period. 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) { - for (; frames; --frames) + for (uint32_t i = 0; i < frames; ++i) { // TODO: add interpolation filter. *output = array[static_cast(readIdx)]; ++output; - if ((readIdx < writeIdx) && - (readIdx > writeIdx - rate)) + if ((readIdx + rate > writeIdx)) readIdx -= period; readIdx += rate; - if (readIdx > resamplerBufferSize) + if (readIdx >= resamplerBufferSize) readIdx -= resamplerBufferSize; if (readIdx < 0) readIdx = 0; }; } + + DISTRHO_DECLARE_NON_COPYABLE(Resampler) }; template @@ -169,6 +181,9 @@ class Detector static constexpr uint maxPeriod = dBufferSize / 2; public: + + Detector(){}; + // 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! 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. inline uint detectPitch(uint32_t frames) { + frames /= dFactor; uint idx = writeIdx - frames; while (frames) { @@ -213,4 +229,6 @@ public: } return per * dFactor; } + + DISTRHO_DECLARE_NON_COPYABLE(Detector) }; \ No newline at end of file diff --git a/src/yaw-totune/dsp.cpp b/src/yaw-totune/dsp.cpp index c2aa317..3c57733 100644 --- a/src/yaw-totune/dsp.cpp +++ b/src/yaw-totune/dsp.cpp @@ -7,21 +7,22 @@ class PitchCorrector : public Plugin { public: PitchCorrector() - : Plugin(0, 0, 0) + : Plugin(0, 0, 0), + resamplers{}, + detectors{} { } protected: - const char* getLabel() const override { return "yaw-totune"; } - const char* getDescription() const override { return "Pitch corrector"; } - const char* getMaker() const override { return "yaw-audio"; } - const char* getHomePage() const override { return "https://yaw.man/plugins/yaw-totune"; } - const char* getLicense() const override { return "Fuck you pay me"; } - uint32_t getVersion() const override { return d_version(1, 0, 0); } - int64_t getUniqueId() const override { return d_cconst('y', 't', 't', 'n'); } + const char *getLabel() const override { return "yaw-totune"; } + const char *getDescription() const override { return "Pitch corrector"; } + const char *getMaker() const override { return "yaw-audio"; } + const char *getHomePage() const override { return "https://yaw.man/plugins/yaw-totune"; } + const char *getLicense() const override { return "Fuck you pay me"; } + uint32_t getVersion() const override { return d_version(1, 0, 0); } + 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 ¶meter) override { parameter.hints = kParameterIsAutomable; parameter.ranges.def = 0.0f; @@ -43,30 +44,41 @@ protected: 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){ - resamplers[chn].write(inputs[chn], frames); + for (int chn = 0; chn < 2; ++chn) + { + resamplers[chn].write(inputs[chn], frames); detectors[chn].downsample(inputs[chn], frames); - double period = resamplers[chn].detectPeriodNear(detectors[chn].detectPitch(frames)); - double correctPeriod = scale.getNearestPeriod(period); - double taux = period / correctPeriod; - resamplers[chn].resample(outputs[chn], frames, taux, period); - } + uint dpitch = detectors[chn].detectPitch(frames); + double taux; + double 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: double hzNyq; - std::array, 2> resamplers; + std::array, 2> resamplers; std::array, 2> detectors; - Scale scale{440.0}; + Scale scale{440.0}; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PitchCorrector) }; -Plugin* createPlugin() +Plugin *createPlugin() { return new PitchCorrector(); }