| 
							- /*
 -  * DISTRHO Plugin Framework (DPF)
 -  * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
 -  * Copyright (C) 2020 Takamitsu Endo
 -  *
 -  * Permission to use, copy, modify, and/or distribute this software for any purpose with
 -  * or without fee is hereby granted, provided that the above copyright notice and this
 -  * permission notice appear in all copies.
 -  *
 -  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
 -  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
 -  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 -  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 -  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 -  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 -  */
 - 
 - #include "DistrhoPlugin.hpp"
 - 
 - START_NAMESPACE_DISTRHO
 - 
 - // -----------------------------------------------------------------------------------------------------------
 - 
 - /**
 -   1-pole lowpass filter to smooth out parameters and envelopes.
 -   This filter is guaranteed not to overshoot.
 -  */
 - class Smoother {
 -     float kp;
 - 
 - public:
 -     float value;
 - 
 -     Smoother()
 -         : kp(0.0f),
 -           value(0.0f) {}
 - 
 -     /**
 -       Set kp from cutoff frequency in Hz.
 -       For derivation, see the answer of Matt L. on the url below. Equation 3 is used.
 - 
 -       Computation is done on double for accuracy. When using float, kp will be inaccurate
 -       if the cutoffHz is below around 3.0 to 4.0 Hz.
 - 
 -       Reference:
 -       - Single-pole IIR low-pass filter - which is the correct formula for the decay coefficient?
 -         https://dsp.stackexchange.com/questions/54086/single-pole-iir-low-pass-filter-which-is-the-correct-formula-for-the-decay-coe
 -      */
 -     void setCutoff(const float sampleRate, const float cutoffHz)
 -     {
 -         double omega_c = 2.0 * M_PI * cutoffHz / sampleRate;
 -         double y = 1.0 - std::cos(omega_c);
 -         kp = float(-y + std::sqrt((y + 2.0) * y));
 -     }
 - 
 -     inline float process(const float input)
 -     {
 -         return value += kp * (input - value);
 -     }
 - };
 - 
 - // -----------------------------------------------------------------------------------------------------------
 - 
 - /**
 -   Plugin that demonstrates tempo sync in DPF.
 -   The tempo sync implementation is on the first if branch in run() method.
 -  */
 - class ExamplePluginMetronome : public Plugin
 - {
 - public:
 -     ExamplePluginMetronome()
 -         : Plugin(4, 0, 0), // 4 parameters, 0 programs, 0 states
 -           sampleRate(getSampleRate()),
 -           counter(0),
 -           phase(0.0f),
 -           decay(0.0f),
 -           gain(0.5f),
 -           semitone(72),
 -           cent(0),
 -           decayTime(0.2f)
 -     {
 -         sampleRateChanged(sampleRate);
 -     }
 - 
 - protected:
 -    /* --------------------------------------------------------------------------------------------------------
 -     * Information */
 - 
 -    /**
 -       Get the plugin label.
 -       A plugin label follows the same rules as Parameter::symbol, with the exception that it can start with numbers.
 -     */
 -     const char* getLabel() const override
 -     {
 -         return "Metronome";
 -     }
 - 
 -    /**
 -       Get an extensive comment/description about the plugin.
 -     */
 -     const char* getDescription() const override
 -     {
 -         return "Simple metronome plugin which outputs impulse at the start of every beat.";
 -     }
 - 
 -    /**
 -       Get the plugin author/maker.
 -     */
 -     const char* getMaker() const override
 -     {
 -         return "DISTRHO";
 -     }
 - 
 -    /**
 -       Get the plugin homepage.
 -     */
 -     const char* getHomePage() const override
 -     {
 -         return "https://github.com/DISTRHO/DPF";
 -     }
 - 
 -    /**
 -       Get the plugin license name (a single line of text).
 -       For commercial plugins this should return some short copyright information.
 -     */
 -     const char* getLicense() const override
 -     {
 -         return "ISC";
 -     }
 - 
 -    /**
 -       Get the plugin version, in hexadecimal.
 -     */
 -     uint32_t getVersion() const override
 -     {
 -         return d_version(1, 0, 0);
 -     }
 - 
 -    /**
 -       Get the plugin unique Id.
 -       This value is used by LADSPA, DSSI and VST plugin formats.
 -     */
 -     int64_t getUniqueId() const override
 -     {
 -         return d_cconst('d', 'M', 'e', 't');
 -     }
 - 
 -    /* --------------------------------------------------------------------------------------------------------
 -     * Init */
 - 
 -    /**
 -       Initialize the parameter @a index.
 -       This function will be called once, shortly after the plugin is created.
 -     */
 -     void initParameter(uint32_t index, Parameter& parameter) override
 -     {
 -         parameter.hints = kParameterIsAutomable;
 - 
 -         switch (index)
 -         {
 -         case 0:
 -             parameter.name = "Gain";
 -             parameter.hints |= kParameterIsLogarithmic;
 -             parameter.ranges.min = 0.001f;
 -             parameter.ranges.max = 1.0f;
 -             parameter.ranges.def = 0.5f;
 -             break;
 -         case 1:
 -             parameter.name = "DecayTime";
 -             parameter.hints |= kParameterIsLogarithmic;
 -             parameter.ranges.min = 0.001f;
 -             parameter.ranges.max = 1.0f;
 -             parameter.ranges.def = 0.2f;
 -             break;
 -         case 2:
 -             parameter.name = "Semitone";
 -             parameter.hints |= kParameterIsInteger;
 -             parameter.ranges.min = 0;
 -             parameter.ranges.max = 127;
 -             parameter.ranges.def = 72;
 -             break;
 -         case 3:
 -             parameter.name = "Cent";
 -             parameter.hints |= kParameterIsInteger;
 -             parameter.ranges.min = -100;
 -             parameter.ranges.max = 100;
 -             parameter.ranges.def = 0;
 -             break;
 -         }
 - 
 -         parameter.symbol = parameter.name;
 -     }
 - 
 -    /* --------------------------------------------------------------------------------------------------------
 -     * Internal data */
 - 
 -    /**
 -       Get the current value of a parameter.
 -     */
 -     float getParameterValue(uint32_t index) const override
 -     {
 -         switch (index)
 -         {
 -         case 0:
 -             return gain;
 -         case 1:
 -             return decayTime;
 -         case 2:
 -             return semitone;
 -         case 3:
 -             return cent;
 -         }
 - 
 -         return 0.0f;
 -     }
 - 
 -    /**
 -       Change a parameter value.
 -     */
 -     void setParameterValue(uint32_t index, float value) override
 -     {
 -         switch (index)
 -         {
 -         case 0:
 -             gain = value;
 -             break;
 -         case 1:
 -             decayTime = value;
 -             break;
 -         case 2:
 -             semitone = value;
 -             break;
 -         case 3:
 -             cent = value;
 -             break;
 -         }
 -     }
 - 
 -    /* --------------------------------------------------------------------------------------------------------
 -     * Process */
 - 
 -    /**
 -       Activate this plugin.
 -       We use this to reset our filter states.
 -     */
 -    void activate() override
 -    {
 -         deltaPhaseSmoother.value = 0.0f;
 -         envelopeSmoother.value = 0.0f;
 -         gainSmoother.value = gain;
 -    }
 - 
 -    /**
 -       Run/process function for plugins without MIDI input.
 -       `inputs` is commented out because this plugin has no inputs.
 -     */
 -     void run(const float** /* inputs */, float** outputs, uint32_t frames) override
 -     {
 -         const TimePosition& timePos(getTimePosition());
 -         float* const output = outputs[0];
 - 
 -         if (timePos.playing && timePos.bbt.valid)
 -         {
 -             // Better to use double when manipulating time.
 -             double secondsPerBeat = 60.0 / timePos.bbt.beatsPerMinute;
 -             double framesPerBeat  = sampleRate * secondsPerBeat;
 -             double beatFraction   = timePos.bbt.tick / timePos.bbt.ticksPerBeat;
 - 
 -             // If beatFraction is zero, next beat is exactly at the start of currenct cycle.
 -             // Otherwise, reset counter to the frames to the next beat.
 -             counter = d_isZero(beatFraction)
 -                     ? 0
 -                     : static_cast<uint32_t>(framesPerBeat * (1.0 - beatFraction));
 - 
 -             // Compute deltaPhase in normalized frequency.
 -             // semitone is midi note number, which is A4 (440Hz at standard tuning) at 69.
 -             // Frequency goes up to 1 octave higher at the start of bar.
 -             float frequency = 440.0f * std::pow(2.0f, (100.0f * (semitone - 69.0f) + cent) / 1200.0f);
 -             float deltaPhase = frequency / sampleRate;
 -             float octave = timePos.bbt.beat == 1 ? 2.0f : 1.0f;
 - 
 -             // Envelope reaches 1e-5 at decayTime after triggering.
 -             decay = std::pow(1e-5, 1.0 / (decayTime * sampleRate));
 - 
 -             // Reset phase and frequency at the start of transpose.
 -             if (!wasPlaying)
 -             {
 -                 phase = 0.0f;
 - 
 -                 deltaPhaseSmoother.value = deltaPhase;
 -                 envelopeSmoother.value = 0.0f;
 -                 gainSmoother.value = 0.0f;
 -             }
 - 
 -             for (uint32_t i = 0; i < frames; ++i)
 -             {
 -                 if (counter <= 0)
 -                 {
 -                     envelope = 1.0f;
 -                     counter = static_cast<uint32_t>(framesPerBeat + 0.5);
 -                     octave = (!wasPlaying || timePos.bbt.beat == static_cast<int32_t>(timePos.bbt.beatsPerBar)) ? 2.0f
 -                                                                                                                 : 1.0f;
 -                 }
 -                 --counter;
 - 
 -                 envelope *= decay;
 - 
 -                 phase += octave * deltaPhaseSmoother.process(deltaPhase);
 -                 phase -= std::floor(phase);
 - 
 -                 output[i] = gainSmoother.process(gain)
 -                           * envelopeSmoother.process(envelope)
 -                           * std::sin(float(2.0 * M_PI) * phase);
 -             }
 -         }
 -         else
 -         {
 -             // Stop metronome if not playing or timePos.bbt is invalid.
 -             std::memset(output, 0, sizeof(float)*frames);
 -         }
 - 
 -         wasPlaying = timePos.playing;
 -     }
 - 
 -    /* --------------------------------------------------------------------------------------------------------
 -     * Callbacks (optional) */
 - 
 -    /**
 -       Optional callback to inform the plugin about a sample rate change.
 -       This function will only be called when the plugin is deactivated.
 -     */
 -     void sampleRateChanged(double newSampleRate) override
 -     {
 -         sampleRate = newSampleRate;
 - 
 -         // Cutoff value was tuned manually.
 -         deltaPhaseSmoother.setCutoff(sampleRate, 100.0f);
 -         gainSmoother.setCutoff(sampleRate, 500.0f);
 -         envelopeSmoother.setCutoff(sampleRate, 250.0f);
 -     }
 - 
 -     // -------------------------------------------------------------------------------------------------------
 - 
 - private:
 -     float sampleRate;
 -     uint32_t counter; // Stores number of frames to the next beat.
 -     bool wasPlaying;  // Used to reset phase and frequency at the start of transpose.
 -     float phase;      // Sine wave phase. Normalized in [0, 1).
 -     float envelope;   // Current value of gain envelope.
 -     float decay;      // Coefficient to decay envelope in a frame.
 - 
 -     Smoother deltaPhaseSmoother;
 -     Smoother envelopeSmoother;
 -     Smoother gainSmoother;
 - 
 -     // Parameters.
 -     float gain;
 -     float semitone;
 -     float cent;
 -     float decayTime;
 - 
 -    /**
 -       Set our plugin class as non-copyable and add a leak detector just in case.
 -     */
 -     DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ExamplePluginMetronome)
 - };
 - 
 - /* ------------------------------------------------------------------------------------------------------------
 -  * Plugin entry point, called by DPF to create a new plugin instance. */
 - 
 - Plugin* createPlugin()
 - {
 -     return new ExamplePluginMetronome();
 - }
 - 
 - // -----------------------------------------------------------------------------------------------------------
 - 
 - END_NAMESPACE_DISTRHO
 
 
  |