################################################################################
# schumann-pll - stereo model of the Schumann Electronics PLL analog harmonizer:
# a fuzz/harmony processor built around a CD4046 phase-locked loop that
# derives frequency-multiplied and frequency-divided square waves from the
# input's fundamental. All 15 documented controls across the pedal's 4 real
# sections (Input Processing, Input Tracking, Frequency Selection, Output
# Processing). Single LV2 bundle - presets ship inside the same
# schumann-pll.lv2 bundle as the plugin, referenced from the same manifest.ttl
# (MOD's buildroot generic-package only supports one bundle per package).
################################################################################

SCHUMANN_PLL_VERSION = 61d38eb638449647fb8395a35c5b8dab7e981ba7
SCHUMANN_PLL_SITE = https://github.com/DISTRHO/DPF.git
SCHUMANN_PLL_SITE_METHOD = git
SCHUMANN_PLL_BUNDLES = schumann-pll.lv2

define SCHUMANN_PLL_PLUGIN_INFO_H
#ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED
#define DISTRHO_PLUGIN_INFO_H_INCLUDED

#define DISTRHO_PLUGIN_BRAND   "AustinDSP"
#define DISTRHO_PLUGIN_NAME    "Schumann PLL Model"
#define DISTRHO_PLUGIN_URI     "https://github.com/austin-dsp/lv2-plugins/schumann-pll"
#define DISTRHO_PLUGIN_CLAP_ID "com.austindsp.schumannpll"

#define DISTRHO_PLUGIN_NUM_INPUTS   2
#define DISTRHO_PLUGIN_NUM_OUTPUTS  2
#define DISTRHO_PLUGIN_IS_RT_SAFE   1
#define DISTRHO_PLUGIN_IS_SYNTH     0
#define DISTRHO_PLUGIN_HAS_UI       0
#define DISTRHO_PLUGIN_WANT_STATE   0
#define DISTRHO_PLUGIN_WANT_LATENCY 0
#define DISTRHO_PLUGIN_WANT_TIMEPOS 0
#define DISTRHO_PLUGIN_WANT_MIDI_INPUT  0
#define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 0

enum Parameters {
    pEnabled = 0,
    pPreAmp,
    pTrigger,
    pLoopTrack,
    pResponse,
    pLoopSpeed,
    pLagTime,
    pMultiplierRatio,
    pDividerRatio,
    pMultiplierPhase,
    pDividerPhase,
    pSquareLevel,
    pMultiplierLevel,
    pDividerLevel,
    pWaveShape,
    pMaster,

    kParameterCount
};

#endif // DISTRHO_PLUGIN_INFO_H_INCLUDED

endef

define SCHUMANN_PLL_PLUGIN_CPP
#include "DistrhoPlugin.hpp"
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>

START_NAMESPACE_DISTRHO

static const float kTwoPi = 6.28318530718f;

static inline float clampf(float v, float lo, float hi) { return v<lo?lo:(v>hi?hi:v); }

// ---------------------------------------------------------------------------
// One-pole filter, reused for input band-limiting, loop-filter smoothing,
// the output DC blocker, and the Wave Shape output lowpass.
// ---------------------------------------------------------------------------
struct OnePole {
    float z;
    OnePole() : z(0.f) {}
    void init() { z=0.f; }
    float lowpass(float in, float coeff) { z = in + coeff*(z-in); return z; }
};

// ---------------------------------------------------------------------------
// Section 1 (INPUT PROCESSING): band-limit the input to preserve the
// fundamental (simplified from the paper's cascaded state-space filter
// stages into a highpass+lowpass pair), apply Pre Amp gain, then an op-amp
// comparator with a Trigger-controlled DC offset, followed by a Schmitt
// trigger inverter (hysteresis threshold detector) - this is what turns the
// continuous input into the one-bit square wave (SIG IN) the rest of the
// circuit's digital logic runs on. Algorithm follows the paper's Algorithm 1
// almost directly.
// ---------------------------------------------------------------------------
struct InputStage {
    OnePole hp, lp;
    float hpZ; // highpass needs the input too for a simple 1st-order HP
    int schmittState;
    float prevComparator;

    void init() {
        hp.init(); lp.init(); hpZ=0.f;
        schmittState = 1;
        prevComparator = 0.f;
    }

    // Returns the bipolar (+/-1) square wave "SIG IN" for the PLL/logic stages.
    float process(float in, float preAmp01, float triggerOffset, float sr) {
        // Simple bandpass: 1-pole HP ~80Hz cascaded with 1-pole LP ~3kHz
        float hpCoeff = std::exp(-kTwoPi*80.f/sr);
        float lpCoeff = std::exp(-kTwoPi*3000.f/sr);
        float lowpassed = hp.lowpass(in, hpCoeff); // reuse as a lowpass tracker of 'in'
        float highpassed = in - lowpassed;
        float filtered = lp.lowpass(highpassed, lpCoeff);

        float gain = 1.f + preAmp01*39.f; // 0-100% -> up to ~+32dB
        float comparatorIn = filtered*gain - triggerOffset;

        // Schmitt trigger inverter (Algorithm 1): hysteresis around +/-0.12
        const float vp = 0.12f, vn = -0.12f;
        if(comparatorIn >= vp && prevComparator < vp) schmittState = 0;
        else if(comparatorIn <= vn && prevComparator > vn) schmittState = 1;
        prevComparator = comparatorIn;

        return schmittState ? 1.f : -1.f;
    }
};

// ---------------------------------------------------------------------------
// Section 2 core (INPUT TRACKING) + PLL: Phase-Frequency Detector (Algorithm
// 2), loop filter (smoothing, controlled by Loop Track/Response/Loop
// Speed), and VCO (Lag Time controls center frequency and modulation
// depth). This is the heart of the "locks onto one frequency" behaviour.
// ---------------------------------------------------------------------------
struct PhaseFreqDetector {
    int qup, qdown;
    float prevSig, prevComp;
    float vcontrol;

    void init() { qup=0; qdown=0; prevSig=-1.f; prevComp=-1.f; vcontrol=0.5f; }

    float process(float sigIn, float compIn) {
        bool sigRise = (sigIn > 0.f && prevSig <= 0.f);
        bool compRise = (compIn > 0.f && prevComp <= 0.f);
        if(sigRise) qup = 1;
        if(compRise) qdown = 1;
        if(qup && qdown) { qup = 0; qdown = 0; }
        if(qup) vcontrol = 1.f;
        else if(qdown) vcontrol = 0.f;
        // else: held (HIZ) - vcontrol keeps its last driven value, and the
        // loop filter's own smoothing supplies the decay/hold dynamics.
        prevSig = sigIn; prevComp = compIn;
        return vcontrol;
    }
};

struct Vco {
    float phase;
    int prevHigh;
    void init() { phase=0.f; prevHigh=0; }

    // Returns bipolar square wave output; risingEdge flags a 0->1 transition
    // this sample, for the downstream decade counters to detect.
    float process(float controlVoltage01, float centerHz, float depthOctaves, float sr, bool& risingEdge) {
        float freq = centerHz * std::pow(2.f, (controlVoltage01-0.5f)*depthOctaves*2.f);
        freq = clampf(freq, 20.f, sr*0.45f);
        phase += freq/sr;
        if(phase >= 1.f) phase -= 1.f;
        int high = phase < 0.5f ? 1 : 0;
        risingEdge = (high==1 && prevHigh==0);
        prevHigh = high;
        return high ? 1.f : -1.f;
    }
};

// ---------------------------------------------------------------------------
// Section 3 (FREQUENCY SELECTION): the two CD4017 decade counters. The
// Multiplier counter sits in the PLL's own feedback path (Algorithm 3),
// routing a short reset pulse back to the phase detector's COMP IN so the
// VCO locks at SIG_IN's frequency times the Multiplier ratio. The Divider
// counter (Algorithm 4) further divides the multiplied VCO output down to a
// sub-harmonic.
// ---------------------------------------------------------------------------
struct MultiplierCounter {
    int inputPulseCount, outputState, outputTimer;
    float prevIn;
    void init() { inputPulseCount=0; outputState=0; outputTimer=0; prevIn=-1.f; }

    float process(bool vinRisingEdge, int rotarySetting) {
        if(vinRisingEdge) {
            inputPulseCount++;
            if(inputPulseCount >= rotarySetting) { outputState=1; inputPulseCount=0; outputTimer=0; }
        }
        float outVal = 0.f;
        if(outputState==1) {
            outVal = 1.f;
            outputTimer++;
            if(outputTimer >= 2) { outputTimer=0; outputState=0; } // ~2-sample reset pulse
        }
        return outVal;
    }
};

struct DividerCounter {
    int inputPulseCount, outputState;
    void init() { inputPulseCount=0; outputState=0; }

    // Algorithm 4, adapted to take an explicit rising-edge flag (already
    // computed from the multiplied VCO square wave) rather than re-deriving
    // it from raw sample comparison.
    float process(bool vinRisingEdge, int rotarySetting) {
        if(vinRisingEdge) {
            inputPulseCount++;
            if(outputState==1) outputState=0;
            if(inputPulseCount >= rotarySetting) { inputPulseCount=0; outputState=1; }
        }
        return outputState ? 1.f : -1.f;
    }
};

// ---------------------------------------------------------------------------
// One full Schumann PLL channel (the module is inherently monophonic-
// tracking; both stereo channels run an identical instance so the effect
// still works as a true stereo insert).
// ---------------------------------------------------------------------------
struct SchumannChannel {
    InputStage input;
    PhaseFreqDetector pfd;
    Vco vco;
    MultiplierCounter multCounter;
    DividerCounter divCounter;
    OnePole loopFilter;
    OnePole waveShapeFilter;
    OnePole dcBlockAvg;
    bool vcoPrevRisingForDivider;

    void init() {
        input.init(); pfd.init(); vco.init();
        multCounter.init(); divCounter.init();
        loopFilter.init(); waveShapeFilter.init(); dcBlockAvg.init();
        vcoPrevRisingForDivider = false;
    }

    float process(float in, float preAmp01, float triggerOffset,
                  float loopTrack01, float response01, float loopSpeedScale, float lagTime01,
                  int multiplierRatio, int dividerRatio, bool multiplierPhaseInvert, bool dividerPhaseInvert,
                  float squareLevel01, float multiplierLevel01, float dividerLevel01,
                  float waveShape01, float master01, float sr) {

        float sigIn = input.process(in, preAmp01, triggerOffset, sr);

        // Multiplier counter's reset pulse (fed back as COMP IN) closes the loop

        float vcontrol = pfd.process(sigIn, multCounter.process(vcoPrevRisingForDivider, multiplierRatio));

        float loopTimeConstant = (0.002f + loopTrack01*0.05f + response01*0.2f) * loopSpeedScale;
        float loopCoeff = std::exp(-1.f/(std::max(0.0002f,loopTimeConstant)*sr));
        float smoothedControl = loopFilter.lowpass(vcontrol, loopCoeff);

        float centerHz = 60.f + lagTime01*300.f;
        float depthOctaves = 2.f + lagTime01*4.f;
        bool vcoRisingEdge;
        float vcoOut = vco.process(smoothedControl, centerHz, depthOctaves, sr, vcoRisingEdge);
        vcoPrevRisingForDivider = vcoRisingEdge;

        float divOut = divCounter.process(vcoRisingEdge, dividerRatio);

        float mult = vcoOut * (multiplierPhaseInvert ? -1.f : 1.f) * multiplierLevel01;
        float div = divOut * (dividerPhaseInvert ? -1.f : 1.f) * dividerLevel01;
        float fund = sigIn * squareLevel01;

        float sum = fund + mult + div;
        // Single-supply op-amp: output can't go below 0V, so the summed
        // square waves get their negative excursions clipped flat - this
        // asymmetric clip is a big part of the pedal's harsh character.
        float clipped = std::max(sum, 0.f);

        // DC blocker (standard output coupling cap) - the clip above biases
        // the signal heavily positive, so this is needed before it's usable
        // as an audio signal at all.
        float dcEst = dcBlockAvg.lowpass(clipped, 0.9995f);
        float acCoupled = clipped - dcEst;

        float shapeCutoff = 250.f + waveShape01*waveShape01*10000.f;
        float shapeCoeff = std::exp(-kTwoPi*shapeCutoff/sr);
        float shaped = waveShapeFilter.lowpass(acCoupled, shapeCoeff);

        float out = shaped * (master01*1.3f);
        return clampf(out, -3.f, 3.f);
    }
};

// ---------------------------------------------------------------------------
// Main plugin
// ---------------------------------------------------------------------------
class SchumannPllPlugin : public Plugin {
public:
    SchumannPllPlugin() : Plugin(kParameterCount, 0, 0), fSr(48000.f) {
        for(int i=0;i<kParameterCount;++i) fParams[i]=0.f;

        // CHECKLIST: every buffer/state-owning struct instance below MUST get
        // .init() called here - a missing call has previously crashed the
        // Dwarf outright on the very first audio block.
        fLeft.init();
        fRight.init();

        setDefaultParams();
        sampleRateChanged(getSampleRate());
    }

protected:
    const char* getLabel() const override { return "SchumannPLL"; }
    const char* getDescription() const override {
        return "Stereo model of the Schumann Electronics PLL analog "
               "harmonizer: a fuzz/harmony processor built around a "
               "phase-locked loop that derives a frequency-multiplied and a "
               "frequency-divided square wave from the input's fundamental, "
               "then sums and clips them together. All 15 documented "
               "controls across the pedal's 4 real sections - Input "
               "Processing (Pre Amp, Trigger), Input Tracking (Loop Track, "
               "Response, Loop Speed, Lag Time), Frequency Selection "
               "(Multiplier, Divider, Multiplier/Divider Phase), and Output "
               "Processing (Square Wave, Multiplier, Divider levels, Wave "
               "Shape, Master). The digital-logic stages (phase detector, "
               "decade counters, VCO) follow the algorithms published in "
               "Farrell & Bilbao's DAFx24 paper on digitizing this pedal; "
               "the analog input filter/comparator stages are simplified "
               "equivalents rather than the paper's exact state-space "
               "matrices. The harsh, aliased one-bit square-wave character "
               "is intentional and authentic to the original circuit, not "
               "band-limited away.";
    }
    const char* getMaker() const override { return "AustinDSP"; }
    const char* getLicense() const override { return "MIT"; }
    uint32_t getVersion() const override { return d_version(1,0,0); }
    int64_t getUniqueId() const override { return d_cconst('S','C','H','M'); }

    void sampleRateChanged(double newSampleRate) override {
        fSr = (float)newSampleRate;
        fLeft.init();
        fRight.init();
    }

    void setDefaultParams() {
        static const float defaults[kParameterCount] = {
            /* enabled */ 1,
            /* pre_amp,trigger */ 50.f, 50.f,
            /* loop_track,response,loop_speed,lag_time */ 50.f, 50.f, 1, 50.f,
            /* multiplier_ratio,divider_ratio,mult_phase,div_phase */ 2, 2, 0, 0,
            /* square_level,mult_level,div_level,wave_shape,master */ 70.f, 50.f, 30.f, 60.f, 70.f
        };
        for(int i=0;i<kParameterCount;++i) fParams[i]=defaults[i];
    }

    static void setEnumHints(Parameter& parameter, const char* const* labels, int count) {
        parameter.enumValues.count = count;
        parameter.enumValues.restrictedMode = true;
        ParameterEnumerationValue* values = new ParameterEnumerationValue[count];
        for(int i=0;i<count;++i) { values[i].value=(float)i; values[i].label=labels[i]; }
        parameter.enumValues.values = values;
    }

    void initParameter(uint32_t index, Parameter& parameter) override {
        parameter.hints = kParameterIsAutomatable;
        switch(index) {
            case pEnabled: parameter.name="Enabled"; parameter.symbol="enabled";
                parameter.hints|=kParameterIsBoolean; parameter.ranges.def=1; parameter.ranges.min=0; parameter.ranges.max=1; break;
            case pPreAmp: parameter.name="Pre Amp"; parameter.symbol="pre_amp"; parameter.unit="%";
                parameter.ranges.def=50.f; parameter.ranges.min=0.f; parameter.ranges.max=100.f; break;
            case pTrigger: parameter.name="Trigger"; parameter.symbol="trigger"; parameter.unit="%";
                parameter.ranges.def=50.f; parameter.ranges.min=0.f; parameter.ranges.max=100.f; break;
            case pLoopTrack: parameter.name="Loop Track"; parameter.symbol="loop_track"; parameter.unit="%";
                parameter.ranges.def=50.f; parameter.ranges.min=0.f; parameter.ranges.max=100.f; break;
            case pResponse: parameter.name="Response"; parameter.symbol="response"; parameter.unit="%";
                parameter.ranges.def=50.f; parameter.ranges.min=0.f; parameter.ranges.max=100.f; break;
            case pLoopSpeed: parameter.name="Loop Speed"; parameter.symbol="loop_speed";
                parameter.hints|=kParameterIsInteger; parameter.ranges.def=1; parameter.ranges.min=0; parameter.ranges.max=2;
                { static const char* labels[]={"Slow","Medium","Fast"}; setEnumHints(parameter,labels,3); } break;
            case pLagTime: parameter.name="Lag Time"; parameter.symbol="lag_time"; parameter.unit="%";
                parameter.ranges.def=50.f; parameter.ranges.min=0.f; parameter.ranges.max=100.f; break;
            case pMultiplierRatio: parameter.name="Multiplier"; parameter.symbol="multiplier_ratio";
                parameter.hints|=kParameterIsInteger; parameter.ranges.def=2; parameter.ranges.min=1; parameter.ranges.max=10; break;
            case pDividerRatio: parameter.name="Divider"; parameter.symbol="divider_ratio";
                parameter.hints|=kParameterIsInteger; parameter.ranges.def=2; parameter.ranges.min=1; parameter.ranges.max=10; break;
            case pMultiplierPhase: parameter.name="Multiplier Phase"; parameter.symbol="multiplier_phase";
                parameter.hints|=kParameterIsBoolean; parameter.ranges.def=0; parameter.ranges.min=0; parameter.ranges.max=1; break;
            case pDividerPhase: parameter.name="Divider Phase"; parameter.symbol="divider_phase";
                parameter.hints|=kParameterIsBoolean; parameter.ranges.def=0; parameter.ranges.min=0; parameter.ranges.max=1; break;
            case pSquareLevel: parameter.name="Square Wave"; parameter.symbol="square_level"; parameter.unit="%";
                parameter.ranges.def=70.f; parameter.ranges.min=0.f; parameter.ranges.max=100.f; break;
            case pMultiplierLevel: parameter.name="Multiplier Level"; parameter.symbol="multiplier_level"; parameter.unit="%";
                parameter.ranges.def=50.f; parameter.ranges.min=0.f; parameter.ranges.max=100.f; break;
            case pDividerLevel: parameter.name="Divider Level"; parameter.symbol="divider_level"; parameter.unit="%";
                parameter.ranges.def=30.f; parameter.ranges.min=0.f; parameter.ranges.max=100.f; break;
            case pWaveShape: parameter.name="Wave Shape"; parameter.symbol="wave_shape"; parameter.unit="%";
                parameter.ranges.def=60.f; parameter.ranges.min=0.f; parameter.ranges.max=100.f; break;
            case pMaster: parameter.name="Master"; parameter.symbol="master"; parameter.unit="%";
                parameter.ranges.def=70.f; parameter.ranges.min=0.f; parameter.ranges.max=100.f; break;
        }
    }

    float getParameterValue(uint32_t index) const override { return fParams[index]; }
    void setParameterValue(uint32_t index, float value) override { fParams[index] = value; }

    void activate() override {
        fLeft.init();
        fRight.init();
    }

    void run(const float** inputs, float** outputs, uint32_t frames) override {
        const float* inL = inputs[0];
        const float* inR = inputs[1];
        float* outL = outputs[0];
        float* outR = outputs[1];

        const bool enabled = fParams[pEnabled] > 0.5f;
        if(!enabled) {
            for(uint32_t i=0;i<frames;++i) { outL[i]=inL[i]; outR[i]=inR[i]; }
            return;
        }

        const float preAmp01 = clampf(fParams[pPreAmp],0.f,100.f)/100.f;
        const float triggerOffset = 0.6f*(2.f*(clampf(fParams[pTrigger],0.f,100.f)/100.f)-1.f);
        const float loopTrack01 = clampf(fParams[pLoopTrack],0.f,100.f)/100.f;
        const float response01 = clampf(fParams[pResponse],0.f,100.f)/100.f;
        static const float speedScales[3] = {8.f, 1.f, 0.15f}; // Slow/Medium/Fast
        const int loopSpeedIdx = (int)clampf(fParams[pLoopSpeed],0.f,2.f);
        const float loopSpeedScale = speedScales[loopSpeedIdx];
        const float lagTime01 = clampf(fParams[pLagTime],0.f,100.f)/100.f;
        const int multiplierRatio = (int)clampf(fParams[pMultiplierRatio],1.f,10.f);
        const int dividerRatio = (int)clampf(fParams[pDividerRatio],1.f,10.f);
        const bool multiplierPhaseInvert = fParams[pMultiplierPhase] > 0.5f;
        const bool dividerPhaseInvert = fParams[pDividerPhase] > 0.5f;
        const float squareLevel01 = clampf(fParams[pSquareLevel],0.f,100.f)/100.f;
        const float multiplierLevel01 = clampf(fParams[pMultiplierLevel],0.f,100.f)/100.f;
        const float dividerLevel01 = clampf(fParams[pDividerLevel],0.f,100.f)/100.f;
        const float waveShape01 = clampf(fParams[pWaveShape],0.f,100.f)/100.f;
        const float master01 = clampf(fParams[pMaster],0.f,100.f)/100.f;

        for(uint32_t i=0;i<frames;++i) {
            outL[i] = fLeft.process(inL[i], preAmp01, triggerOffset, loopTrack01, response01, loopSpeedScale,
                                     lagTime01, multiplierRatio, dividerRatio, multiplierPhaseInvert, dividerPhaseInvert,
                                     squareLevel01, multiplierLevel01, dividerLevel01, waveShape01, master01, fSr);
            outR[i] = fRight.process(inR[i], preAmp01, triggerOffset, loopTrack01, response01, loopSpeedScale,
                                      lagTime01, multiplierRatio, dividerRatio, multiplierPhaseInvert, dividerPhaseInvert,
                                      squareLevel01, multiplierLevel01, dividerLevel01, waveShape01, master01, fSr);
        }
    }

private:
    float fParams[kParameterCount];
    float fSr;
    SchumannChannel fLeft, fRight;

    DISTRHO_DECLARE_NON_COPYABLE(SchumannPllPlugin)
};

Plugin* createPlugin() { return new SchumannPllPlugin(); }

END_NAMESPACE_DISTRHO

endef

define SCHUMANN_PLL_PLUGIN_MAKEFILE
#!/usr/bin/make -f
NAME      = schumann-pll
FILES_DSP = Plugin.cpp
include ../../Makefile.plugins.mk
TARGETS = lv2_dsp
all: $(TARGETS)

endef

define SCHUMANN_PLL_MANIFEST_TTL
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

<https://github.com/austin-dsp/lv2-plugins/schumann-pll>
    a lv2:Plugin ;
    lv2:binary <schumann-pll_dsp.so> ;
    rdfs:seeAlso <schumann-pll.ttl> .

@prefix pset: <http://lv2plug.in/ns/ext/presets#> .

<https://github.com/austin-dsp/lv2-plugins/schumann-pll#preset-locked-octave-harmony>
    a pset:Preset ;
    rdfs:seeAlso <locked-octave-harmony.ttl> .

<https://github.com/austin-dsp/lv2-plugins/schumann-pll#preset-chaotic-self-oscillation>
    a pset:Preset ;
    rdfs:seeAlso <chaotic-self-oscillation.ttl> .

<https://github.com/austin-dsp/lv2-plugins/schumann-pll#preset-automatic-bassline>
    a pset:Preset ;
    rdfs:seeAlso <automatic-bassline.ttl> .

<https://github.com/austin-dsp/lv2-plugins/schumann-pll#preset-glitchy-unstable-chop>
    a pset:Preset ;
    rdfs:seeAlso <glitchy-unstable-chop.ttl> .

<https://github.com/austin-dsp/lv2-plugins/schumann-pll#preset-synth-lead-voice>
    a pset:Preset ;
    rdfs:seeAlso <synth-lead-voice.ttl> .

endef

define SCHUMANN_PLL_PLUGIN_TTL
@prefix lv2:   <http://lv2plug.in/ns/lv2core#> .
@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs:  <http://www.w3.org/2000/01/rdf-schema#> .
@prefix doap:  <http://usefulinc.com/ns/doap#> .
@prefix units: <http://lv2plug.in/ns/extensions/units#> .

<https://github.com/austin-dsp/lv2-plugins/schumann-pll>
    a lv2:Plugin ;
    doap:name "Schumann PLL Model" ;
    doap:license <https://opensource.org/licenses/MIT> ;
    lv2:project <https://github.com/austin-dsp/lv2-plugins/schumann-pll> ;
    lv2:optionalFeature lv2:hardRTCapable ;
    lv2:port
    [
        a lv2:InputPort, lv2:AudioPort ;
        lv2:index 0 ;
        lv2:symbol "audio_in_l" ;
        lv2:name "Audio Input L" ;
    ] ,
    [
        a lv2:InputPort, lv2:AudioPort ;
        lv2:index 1 ;
        lv2:symbol "audio_in_r" ;
        lv2:name "Audio Input R" ;
    ] ,
    [
        a lv2:OutputPort, lv2:AudioPort ;
        lv2:index 2 ;
        lv2:symbol "audio_out_l" ;
        lv2:name "Audio Output L" ;
    ] ,
    [
        a lv2:OutputPort, lv2:AudioPort ;
        lv2:index 3 ;
        lv2:symbol "audio_out_r" ;
        lv2:name "Audio Output R" ;
    ] ,
    [
        a lv2:InputPort, lv2:ControlPort ;
        lv2:index 4 ;
        lv2:symbol "enabled" ;
        lv2:name "Enabled" ;
        lv2:default 1 ;
        lv2:minimum 0 ;
        lv2:maximum 1 ;
        lv2:portProperty lv2:toggled ;
    ] ,
    [
        a lv2:InputPort, lv2:ControlPort ;
        lv2:index 5 ;
        lv2:symbol "pre_amp" ;
        lv2:name "Pre Amp" ;
        lv2:default 50.0 ;
        lv2:minimum 0.0 ;
        lv2:maximum 100.0 ;
        units:unit units:pc ;
    ] ,
    [
        a lv2:InputPort, lv2:ControlPort ;
        lv2:index 6 ;
        lv2:symbol "trigger" ;
        lv2:name "Trigger" ;
        lv2:default 50.0 ;
        lv2:minimum 0.0 ;
        lv2:maximum 100.0 ;
        units:unit units:pc ;
    ] ,
    [
        a lv2:InputPort, lv2:ControlPort ;
        lv2:index 7 ;
        lv2:symbol "loop_track" ;
        lv2:name "Loop Track" ;
        lv2:default 50.0 ;
        lv2:minimum 0.0 ;
        lv2:maximum 100.0 ;
        units:unit units:pc ;
    ] ,
    [
        a lv2:InputPort, lv2:ControlPort ;
        lv2:index 8 ;
        lv2:symbol "response" ;
        lv2:name "Response" ;
        lv2:default 50.0 ;
        lv2:minimum 0.0 ;
        lv2:maximum 100.0 ;
        units:unit units:pc ;
    ] ,
    [
        a lv2:InputPort, lv2:ControlPort ;
        lv2:index 9 ;
        lv2:symbol "loop_speed" ;
        lv2:name "Loop Speed" ;
        lv2:default 1 ;
        lv2:minimum 0 ;
        lv2:maximum 2 ;
        lv2:portProperty lv2:integer, lv2:enumeration ;
        lv2:scalePoint [ rdfs:label "Slow" ; rdf:value 0 ] ;
        lv2:scalePoint [ rdfs:label "Medium" ; rdf:value 1 ] ;
        lv2:scalePoint [ rdfs:label "Fast" ; rdf:value 2 ] ;
    ] ,
    [
        a lv2:InputPort, lv2:ControlPort ;
        lv2:index 10 ;
        lv2:symbol "lag_time" ;
        lv2:name "Lag Time" ;
        lv2:default 50.0 ;
        lv2:minimum 0.0 ;
        lv2:maximum 100.0 ;
        units:unit units:pc ;
    ] ,
    [
        a lv2:InputPort, lv2:ControlPort ;
        lv2:index 11 ;
        lv2:symbol "multiplier_ratio" ;
        lv2:name "Multiplier" ;
        lv2:default 2 ;
        lv2:minimum 1 ;
        lv2:maximum 10 ;
        lv2:portProperty lv2:integer ;
    ] ,
    [
        a lv2:InputPort, lv2:ControlPort ;
        lv2:index 12 ;
        lv2:symbol "divider_ratio" ;
        lv2:name "Divider" ;
        lv2:default 2 ;
        lv2:minimum 1 ;
        lv2:maximum 10 ;
        lv2:portProperty lv2:integer ;
    ] ,
    [
        a lv2:InputPort, lv2:ControlPort ;
        lv2:index 13 ;
        lv2:symbol "multiplier_phase" ;
        lv2:name "Multiplier Phase" ;
        lv2:default 0 ;
        lv2:minimum 0 ;
        lv2:maximum 1 ;
        lv2:portProperty lv2:toggled ;
    ] ,
    [
        a lv2:InputPort, lv2:ControlPort ;
        lv2:index 14 ;
        lv2:symbol "divider_phase" ;
        lv2:name "Divider Phase" ;
        lv2:default 0 ;
        lv2:minimum 0 ;
        lv2:maximum 1 ;
        lv2:portProperty lv2:toggled ;
    ] ,
    [
        a lv2:InputPort, lv2:ControlPort ;
        lv2:index 15 ;
        lv2:symbol "square_level" ;
        lv2:name "Square Wave" ;
        lv2:default 70.0 ;
        lv2:minimum 0.0 ;
        lv2:maximum 100.0 ;
        units:unit units:pc ;
    ] ,
    [
        a lv2:InputPort, lv2:ControlPort ;
        lv2:index 16 ;
        lv2:symbol "multiplier_level" ;
        lv2:name "Multiplier Level" ;
        lv2:default 50.0 ;
        lv2:minimum 0.0 ;
        lv2:maximum 100.0 ;
        units:unit units:pc ;
    ] ,
    [
        a lv2:InputPort, lv2:ControlPort ;
        lv2:index 17 ;
        lv2:symbol "divider_level" ;
        lv2:name "Divider Level" ;
        lv2:default 30.0 ;
        lv2:minimum 0.0 ;
        lv2:maximum 100.0 ;
        units:unit units:pc ;
    ] ,
    [
        a lv2:InputPort, lv2:ControlPort ;
        lv2:index 18 ;
        lv2:symbol "wave_shape" ;
        lv2:name "Wave Shape" ;
        lv2:default 60.0 ;
        lv2:minimum 0.0 ;
        lv2:maximum 100.0 ;
        units:unit units:pc ;
    ] ,
    [
        a lv2:InputPort, lv2:ControlPort ;
        lv2:index 19 ;
        lv2:symbol "master" ;
        lv2:name "Master" ;
        lv2:default 70.0 ;
        lv2:minimum 0.0 ;
        lv2:maximum 100.0 ;
        units:unit units:pc ;
    ] .

endef

define SCHUMANN_PLL_PRESET_LOCKED_OCTAVE_HARMONY
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
@prefix pset: <http://lv2plug.in/ns/ext/presets#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

<https://github.com/austin-dsp/lv2-plugins/schumann-pll#preset-locked-octave-harmony>
    a pset:Preset ;
    lv2:appliesTo <https://github.com/austin-dsp/lv2-plugins/schumann-pll> ;
    rdfs:label "Locked Octave Harmony" ;
    lv2:port
    [ lv2:symbol "enabled" ; pset:value 1 ] ,
    [ lv2:symbol "pre_amp" ; pset:value 55 ] ,
    [ lv2:symbol "trigger" ; pset:value 50 ] ,
    [ lv2:symbol "loop_track" ; pset:value 60 ] ,
    [ lv2:symbol "response" ; pset:value 65 ] ,
    [ lv2:symbol "loop_speed" ; pset:value 1 ] ,
    [ lv2:symbol "lag_time" ; pset:value 45 ] ,
    [ lv2:symbol "multiplier_ratio" ; pset:value 2 ] ,
    [ lv2:symbol "divider_ratio" ; pset:value 4 ] ,
    [ lv2:symbol "multiplier_phase" ; pset:value 0 ] ,
    [ lv2:symbol "divider_phase" ; pset:value 0 ] ,
    [ lv2:symbol "square_level" ; pset:value 60 ] ,
    [ lv2:symbol "multiplier_level" ; pset:value 55 ] ,
    [ lv2:symbol "divider_level" ; pset:value 45 ] ,
    [ lv2:symbol "wave_shape" ; pset:value 55 ] ,
    [ lv2:symbol "master" ; pset:value 70 ] .

endef

define SCHUMANN_PLL_PRESET_CHAOTIC_SELF_OSCILLATION
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
@prefix pset: <http://lv2plug.in/ns/ext/presets#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

<https://github.com/austin-dsp/lv2-plugins/schumann-pll#preset-chaotic-self-oscillation>
    a pset:Preset ;
    lv2:appliesTo <https://github.com/austin-dsp/lv2-plugins/schumann-pll> ;
    rdfs:label "Chaotic Self-Oscillation" ;
    lv2:port
    [ lv2:symbol "enabled" ; pset:value 1 ] ,
    [ lv2:symbol "pre_amp" ; pset:value 90 ] ,
    [ lv2:symbol "trigger" ; pset:value 75 ] ,
    [ lv2:symbol "loop_track" ; pset:value 95 ] ,
    [ lv2:symbol "response" ; pset:value 10 ] ,
    [ lv2:symbol "loop_speed" ; pset:value 2 ] ,
    [ lv2:symbol "lag_time" ; pset:value 90 ] ,
    [ lv2:symbol "multiplier_ratio" ; pset:value 7 ] ,
    [ lv2:symbol "divider_ratio" ; pset:value 3 ] ,
    [ lv2:symbol "multiplier_phase" ; pset:value 1 ] ,
    [ lv2:symbol "divider_phase" ; pset:value 0 ] ,
    [ lv2:symbol "square_level" ; pset:value 80 ] ,
    [ lv2:symbol "multiplier_level" ; pset:value 85 ] ,
    [ lv2:symbol "divider_level" ; pset:value 70 ] ,
    [ lv2:symbol "wave_shape" ; pset:value 35 ] ,
    [ lv2:symbol "master" ; pset:value 60 ] .

endef

define SCHUMANN_PLL_PRESET_AUTOMATIC_BASSLINE
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
@prefix pset: <http://lv2plug.in/ns/ext/presets#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

<https://github.com/austin-dsp/lv2-plugins/schumann-pll#preset-automatic-bassline>
    a pset:Preset ;
    lv2:appliesTo <https://github.com/austin-dsp/lv2-plugins/schumann-pll> ;
    rdfs:label "Automatic Bassline" ;
    lv2:port
    [ lv2:symbol "enabled" ; pset:value 1 ] ,
    [ lv2:symbol "pre_amp" ; pset:value 65 ] ,
    [ lv2:symbol "trigger" ; pset:value 40 ] ,
    [ lv2:symbol "loop_track" ; pset:value 70 ] ,
    [ lv2:symbol "response" ; pset:value 80 ] ,
    [ lv2:symbol "loop_speed" ; pset:value 0 ] ,
    [ lv2:symbol "lag_time" ; pset:value 20 ] ,
    [ lv2:symbol "multiplier_ratio" ; pset:value 1 ] ,
    [ lv2:symbol "divider_ratio" ; pset:value 8 ] ,
    [ lv2:symbol "multiplier_phase" ; pset:value 0 ] ,
    [ lv2:symbol "divider_phase" ; pset:value 0 ] ,
    [ lv2:symbol "square_level" ; pset:value 20 ] ,
    [ lv2:symbol "multiplier_level" ; pset:value 0 ] ,
    [ lv2:symbol "divider_level" ; pset:value 90 ] ,
    [ lv2:symbol "wave_shape" ; pset:value 30 ] ,
    [ lv2:symbol "master" ; pset:value 75 ] .

endef

define SCHUMANN_PLL_PRESET_GLITCHY_UNSTABLE_CHOP
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
@prefix pset: <http://lv2plug.in/ns/ext/presets#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

<https://github.com/austin-dsp/lv2-plugins/schumann-pll#preset-glitchy-unstable-chop>
    a pset:Preset ;
    lv2:appliesTo <https://github.com/austin-dsp/lv2-plugins/schumann-pll> ;
    rdfs:label "Glitchy Unstable Chop" ;
    lv2:port
    [ lv2:symbol "enabled" ; pset:value 1 ] ,
    [ lv2:symbol "pre_amp" ; pset:value 60 ] ,
    [ lv2:symbol "trigger" ; pset:value 60 ] ,
    [ lv2:symbol "loop_track" ; pset:value 15 ] ,
    [ lv2:symbol "response" ; pset:value 20 ] ,
    [ lv2:symbol "loop_speed" ; pset:value 2 ] ,
    [ lv2:symbol "lag_time" ; pset:value 55 ] ,
    [ lv2:symbol "multiplier_ratio" ; pset:value 3 ] ,
    [ lv2:symbol "divider_ratio" ; pset:value 5 ] ,
    [ lv2:symbol "multiplier_phase" ; pset:value 1 ] ,
    [ lv2:symbol "divider_phase" ; pset:value 1 ] ,
    [ lv2:symbol "square_level" ; pset:value 40 ] ,
    [ lv2:symbol "multiplier_level" ; pset:value 60 ] ,
    [ lv2:symbol "divider_level" ; pset:value 50 ] ,
    [ lv2:symbol "wave_shape" ; pset:value 65 ] ,
    [ lv2:symbol "master" ; pset:value 65 ] .

endef

define SCHUMANN_PLL_PRESET_SYNTH_LEAD_VOICE
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
@prefix pset: <http://lv2plug.in/ns/ext/presets#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

<https://github.com/austin-dsp/lv2-plugins/schumann-pll#preset-synth-lead-voice>
    a pset:Preset ;
    lv2:appliesTo <https://github.com/austin-dsp/lv2-plugins/schumann-pll> ;
    rdfs:label "Synth Lead Voice" ;
    lv2:port
    [ lv2:symbol "enabled" ; pset:value 1 ] ,
    [ lv2:symbol "pre_amp" ; pset:value 45 ] ,
    [ lv2:symbol "trigger" ; pset:value 50 ] ,
    [ lv2:symbol "loop_track" ; pset:value 75 ] ,
    [ lv2:symbol "response" ; pset:value 75 ] ,
    [ lv2:symbol "loop_speed" ; pset:value 1 ] ,
    [ lv2:symbol "lag_time" ; pset:value 40 ] ,
    [ lv2:symbol "multiplier_ratio" ; pset:value 4 ] ,
    [ lv2:symbol "divider_ratio" ; pset:value 1 ] ,
    [ lv2:symbol "multiplier_phase" ; pset:value 0 ] ,
    [ lv2:symbol "divider_phase" ; pset:value 0 ] ,
    [ lv2:symbol "square_level" ; pset:value 10 ] ,
    [ lv2:symbol "multiplier_level" ; pset:value 85 ] ,
    [ lv2:symbol "divider_level" ; pset:value 0 ] ,
    [ lv2:symbol "wave_shape" ; pset:value 80 ] ,
    [ lv2:symbol "master" ; pset:value 70 ] .

endef


export SCHUMANN_PLL_PLUGIN_INFO_H
export SCHUMANN_PLL_PLUGIN_CPP
export SCHUMANN_PLL_PLUGIN_MAKEFILE
export SCHUMANN_PLL_MANIFEST_TTL
export SCHUMANN_PLL_PLUGIN_TTL
export SCHUMANN_PLL_PRESET_LOCKED_OCTAVE_HARMONY
export SCHUMANN_PLL_PRESET_CHAOTIC_SELF_OSCILLATION
export SCHUMANN_PLL_PRESET_AUTOMATIC_BASSLINE
export SCHUMANN_PLL_PRESET_GLITCHY_UNSTABLE_CHOP
export SCHUMANN_PLL_PRESET_SYNTH_LEAD_VOICE

define SCHUMANN_PLL_CONFIGURE_CMDS
	mkdir -p $(@D)/examples/schumann-pll
	printf '%s' "$$SCHUMANN_PLL_PLUGIN_INFO_H" > $(@D)/examples/schumann-pll/DistrhoPluginInfo.h
	printf '%s' "$$SCHUMANN_PLL_PLUGIN_CPP" > $(@D)/examples/schumann-pll/Plugin.cpp
	printf '%s' "$$SCHUMANN_PLL_PLUGIN_MAKEFILE" > $(@D)/examples/schumann-pll/Makefile
endef

define SCHUMANN_PLL_BUILD_CMDS
	$(TARGET_MAKE_ENV) $(TARGET_CONFIGURE_OPTS) $(MAKE) NOOPT=true -C $(@D)/examples/schumann-pll lv2_dsp
endef

define SCHUMANN_PLL_INSTALL_TARGET_CMDS
	mkdir -p $($(PKG)_PKGDIR)/schumann-pll.lv2
	cp $(@D)/bin/schumann-pll.lv2/schumann-pll_dsp.so $($(PKG)_PKGDIR)/schumann-pll.lv2/
	printf '%s' "$$SCHUMANN_PLL_MANIFEST_TTL" > $($(PKG)_PKGDIR)/schumann-pll.lv2/manifest.ttl
	printf '%s' "$$SCHUMANN_PLL_PLUGIN_TTL" > $($(PKG)_PKGDIR)/schumann-pll.lv2/schumann-pll.ttl
	printf '%s' "$$SCHUMANN_PLL_PRESET_LOCKED_OCTAVE_HARMONY" > $($(PKG)_PKGDIR)/schumann-pll.lv2/locked-octave-harmony.ttl
	printf '%s' "$$SCHUMANN_PLL_PRESET_CHAOTIC_SELF_OSCILLATION" > $($(PKG)_PKGDIR)/schumann-pll.lv2/chaotic-self-oscillation.ttl
	printf '%s' "$$SCHUMANN_PLL_PRESET_AUTOMATIC_BASSLINE" > $($(PKG)_PKGDIR)/schumann-pll.lv2/automatic-bassline.ttl
	printf '%s' "$$SCHUMANN_PLL_PRESET_GLITCHY_UNSTABLE_CHOP" > $($(PKG)_PKGDIR)/schumann-pll.lv2/glitchy-unstable-chop.ttl
	printf '%s' "$$SCHUMANN_PLL_PRESET_SYNTH_LEAD_VOICE" > $($(PKG)_PKGDIR)/schumann-pll.lv2/synth-lead-voice.ttl
endef

$(eval $(generic-package))
