# Hologram Microcosm - GLIDE Effect (Short Overlapping Loops Shifting in Pitch Over Time)
# Based on Hologram Microcosm User Manual - Micro Loop category.
# Upload at https://builder.mod.audio/buildroot

HOLO_GLIDE_VERSION = 61d38eb638449647fb8395a35c5b8dab7e981ba7
HOLO_GLIDE_SITE = https://github.com/DISTRHO/DPF.git
HOLO_GLIDE_SITE_METHOD = git
HOLO_GLIDE_BUNDLES = holo-glide.lv2

# ---------------------------------------------------------------------------
# DistrhoPluginInfo.h
# ---------------------------------------------------------------------------
define HOLO_GLIDE_PLUGIN_INFO_H
#ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED
#define DISTRHO_PLUGIN_INFO_H_INCLUDED

#define DISTRHO_PLUGIN_BRAND        "Hologram Electronics"
#define DISTRHO_PLUGIN_NAME         "Microcosm Glide"
#define DISTRHO_PLUGIN_URI          "urn:mod-cookbook:holo-glide"
#define DISTRHO_PLUGIN_HAS_UI       0
#define DISTRHO_PLUGIN_IS_RT_SAFE   1
#define DISTRHO_PLUGIN_NUM_INPUTS   2
#define DISTRHO_PLUGIN_NUM_OUTPUTS  2

enum Parameters {
    kActivity = 0,
    kShape,
    kFilter,
    kMix,
    kSubdiv,
    kRepeats,
    kModDepth,
    kSpace,
    kReverbMode,
    kPreset,
    kParameterCount
};

#endif
endef

# ---------------------------------------------------------------------------
# HoloGlidePlugin.cpp
# ---------------------------------------------------------------------------
define HOLO_GLIDE_PLUGIN_CPP
#include "DistrhoPlugin.hpp"
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>

START_NAMESPACE_DISTRHO

static const float kPi    = 3.14159265359f;
static const float kTwoPi = 6.28318530718f;

// Heap delay line with linear interpolation
struct DelayLine {
    float* buf;
    int mask, wpos;
    DelayLine() : buf(nullptr), mask(0), wpos(0) {}
    ~DelayLine() { delete[] buf; }
    void init(int n) {
        int sz=1; while(sz<n) sz<<=1;
        delete[] buf; buf=new float[sz](); mask=sz-1; wpos=0;
    }
    void clear() { if(buf) std::memset(buf, 0, (mask+1)*sizeof(float)); wpos=0; }
    void write(float v) { buf[wpos&mask]=v; ++wpos; }
    float read(float d) const {
        if(d<0.5f) d=0.5f;
        float rd=(float)wpos-d-1.f;
        int ri=(int)rd; float fr=rd-(float)ri;
        return buf[ri&mask]+fr*(buf[(ri+1)&mask]-buf[ri&mask]);
    }
};

// One-pole LP filter
struct OnePoleLP {
    float z1;
    OnePoleLP() : z1(0.f) {}
    void reset() { z1=0.f; }
    float process(float in, float c) { z1=(1.f-c)*in+c*z1; return z1; }
};

// Allpass + comb reverb
struct AllPass {
    float* buf; int mask, wpos;
    AllPass() : buf(nullptr), mask(0), wpos(0) {}
    ~AllPass() { delete[] buf; }
    void init(int n) {
        int sz=1; while(sz<n) sz<<=1;
        delete[] buf; buf=new float[sz](); mask=sz-1; wpos=0;
    }
    float process(float in, float g) {
        float d=buf[wpos&mask]; float w=in+g*d;
        buf[wpos&mask]=w; ++wpos; return d-g*w;
    }
};

struct CombFilter {
    float* buf; int mask, wpos; float z1;
    CombFilter() : buf(nullptr), mask(0), wpos(0), z1(0.f) {}
    ~CombFilter() { delete[] buf; }
    void init(int n) {
        int sz=1; while(sz<n) sz<<=1;
        delete[] buf; buf=new float[sz](); mask=sz-1; wpos=0; z1=0.f;
    }
    float process(float in, float fb, float damp) {
        float out=buf[wpos&mask];
        z1=out*(1.f-damp)+z1*damp;
        buf[wpos&mask]=in+z1*fb; ++wpos; return out;
    }
};

// ---------------------------------------------------------------------------
// Glide voice: a loop that continuously shifts its playback speed using a
// crossfading pair of read heads (windowed dual-tap), gliding between two
// target speeds. Shape controls the glide contour (sine/triangle/ramp).
// Activity controls how fast the glide sweeps between the two speeds.
// ---------------------------------------------------------------------------
struct GlideVoice {
    float ph1, ph2;         // dual read-head phases 0..1
    float currentSpeed;     // current interpolated speed
    float glidePhase;       // 0..1 position within one glide cycle
    static const int kWin = 2048;

    GlideVoice() : ph1(0.f), ph2(0.5f), currentSpeed(1.f), glidePhase(0.f) {}

    // speedA, speedB: the two speeds to glide between
    // glideRate: how fast to sweep (cycles per second, driven by Activity)
    // shapeN: 0..1 — controls glide contour shape
    //   0.0 = sine (smooth glide)
    //   0.5 = triangle (linear)
    //   1.0 = ramp (slow-build then snap)
    // Returns one output sample read from the delay line at the current gliding speed
    float process(DelayLine& dl, float in, float loopLenSamp,
                  float speedA, float speedB, float glideRate,
                  float shapeN, float sr) {
        // Write input
        dl.write(in);

        // Advance glide phase
        glidePhase += glideRate / sr;
        if(glidePhase >= 1.f) glidePhase -= 1.f;

        // Compute glide envelope (shape of the glide pattern per manual)
        float env;
        if(shapeN < 0.5f) {
            // Sine: smooth sinusoidal glide
            float t = shapeN * 2.f; // 0..1
            float sine = std::sin(glidePhase * kTwoPi) * 0.5f + 0.5f;
            float tri  = (glidePhase < 0.5f) ? (glidePhase*2.f) : (2.f-glidePhase*2.f);
            env = sine*(1.f-t) + tri*t;
        } else {
            // Ramp: slow build then snap back (backwards guitar feel)
            float t = (shapeN - 0.5f) * 2.f; // 0..1
            float tri  = (glidePhase < 0.5f) ? (glidePhase*2.f) : (2.f-glidePhase*2.f);
            float ramp = glidePhase; // linear 0→1
            env = tri*(1.f-t) + ramp*t;
        }

        // Interpolate between speedA and speedB using envelope
        currentSpeed = speedA + (speedB - speedA) * env;

        // Advance dual read-head phases at the current speed ratio
        float speedRatio = 1.f - currentSpeed; // relative to write head
        ph1 += speedRatio / (float)kWin;
        ph2 += speedRatio / (float)kWin;
        if(ph1 >= 1.f) ph1 -= 1.f; if(ph1 < 0.f) ph1 += 1.f;
        if(ph2 >= 1.f) ph2 -= 1.f; if(ph2 < 0.f) ph2 += 1.f;

        float d1 = std::max(0.5f, ph1 * (float)kWin);
        float d2 = std::max(0.5f, ph2 * (float)kWin);

        // Hanning window crossfade between the two read heads
        float w1 = std::sin(ph1 * kPi);
        float w2 = std::sin(ph2 * kPi);
        float s1 = dl.read(d1);
        float s2 = dl.read(d2);
        float ws = w1+w2; if(ws < 0.001f) ws = 0.001f;
        return (s1*w1 + s2*w2) / ws;
    }
};

static const int kMaxVoices = 4;

class HoloGlidePlugin : public Plugin
{
    static const int kVoiceBufSamp = 1 << 16; // ~1.36s @ 48kHz

public:
    HoloGlidePlugin()
        : Plugin(kParameterCount, 0, 0),
          fActivity(50.f), fShape(50.f), fFilter(100.f),
          fMix(75.f), fSubdiv(2.f), fRepeats(50.f),
          fModDepth(0.f), fSpace(0.f), fReverbMode(0.f), fPreset(0.f),
          fSr(48000.f),
          fFilterZ1L(0.f), fFilterZ1R(0.f)
    {
        for(int v=0; v<kMaxVoices; ++v) {
            fVoiceBufL[v]=new DelayLine(); fVoiceBufL[v]->init(kVoiceBufSamp);
            fVoiceBufR[v]=new DelayLine(); fVoiceBufR[v]->init(kVoiceBufSamp);
        }
        fRevCombL[0]=new CombFilter(); fRevCombL[0]->init(1557);
        fRevCombL[1]=new CombFilter(); fRevCombL[1]->init(1617);
        fRevCombR[0]=new CombFilter(); fRevCombR[0]->init(1617);
        fRevCombR[1]=new CombFilter(); fRevCombR[1]->init(1557);
        fRevApL=new AllPass(); fRevApL->init(556);
        fRevApR=new AllPass(); fRevApR->init(441);
        // Stagger voice glide phases so overlapping voices are offset
        for(int v=0; v<kMaxVoices; ++v) {
            fVoiceL[v].glidePhase = (float)v / (float)kMaxVoices;
            fVoiceR[v].glidePhase = (float)v / (float)kMaxVoices + 0.1f;
        }
    }

    ~HoloGlidePlugin() {
        for(int v=0; v<kMaxVoices; ++v) { delete fVoiceBufL[v]; delete fVoiceBufR[v]; }
        delete fRevCombL[0]; delete fRevCombL[1];
        delete fRevCombR[0]; delete fRevCombR[1];
        delete fRevApL; delete fRevApR;
    }

protected:
    const char* getLabel()       const override { return "Microcosm Glide"; }
    const char* getDescription() const override { return "Short overlapping loops shifting in pitch over time. Hologram Microcosm Glide effect."; }
    const char* getMaker()       const override { return "Hologram Electronics"; }
    const char* getHomePage()    const override { return "https://mod.audio"; }
    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('H','G','L','D'); }

    void initParameter(uint32_t index, Parameter& p) override {
        p.hints = kParameterIsAutomatable;
        switch(index) {
        case kActivity:
            p.name="Activity"; p.symbol="activity"; p.unit="%";
            p.ranges.def=50.f; p.ranges.min=0.f; p.ranges.max=100.f; break;
        case kShape:
            p.name="Shape"; p.symbol="shape"; p.unit="%";
            p.ranges.def=50.f; p.ranges.min=0.f; p.ranges.max=100.f; break;
        case kFilter:
            p.name="Filter"; p.symbol="filter"; p.unit="%";
            p.ranges.def=100.f; p.ranges.min=0.f; p.ranges.max=100.f; break;
        case kMix:
            p.name="Mix"; p.symbol="mix"; p.unit="%";
            p.ranges.def=75.f; p.ranges.min=0.f; p.ranges.max=100.f; break;
        case kSubdiv:
            p.name="Subdiv"; p.symbol="subdiv";
            p.ranges.def=2.f; p.ranges.min=0.f; p.ranges.max=4.f;
            p.hints|=kParameterIsInteger; break;
        case kRepeats:
            p.name="Repeats"; p.symbol="repeats"; p.unit="%";
            p.ranges.def=50.f; p.ranges.min=0.f; p.ranges.max=100.f; break;
        case kModDepth:
            p.name="Mod Depth"; p.symbol="mod_depth"; p.unit="%";
            p.ranges.def=0.f; p.ranges.min=0.f; p.ranges.max=100.f; break;
        case kSpace:
            p.name="Space"; p.symbol="space"; p.unit="%";
            p.ranges.def=0.f; p.ranges.min=0.f; p.ranges.max=100.f; break;
        case kReverbMode:
            p.name="Reverb Mode"; p.symbol="reverb_mode";
            p.ranges.def=0.f; p.ranges.min=0.f; p.ranges.max=3.f;
            p.hints|=kParameterIsInteger; break;
        case kPreset:
            p.name="Preset"; p.symbol="preset";
            p.ranges.def=0.f; p.ranges.min=0.f; p.ranges.max=3.f;
            p.hints|=kParameterIsInteger; break;
        }
    }

    float getParameterValue(uint32_t index) const override {
        switch(index) {
        case kActivity:   return fActivity;
        case kShape:      return fShape;
        case kFilter:     return fFilter;
        case kMix:        return fMix;
        case kSubdiv:     return fSubdiv;
        case kRepeats:    return fRepeats;
        case kModDepth:   return fModDepth;
        case kSpace:      return fSpace;
        case kReverbMode: return fReverbMode;
        case kPreset:     return fPreset;
        }
        return 0.f;
    }

    void setParameterValue(uint32_t index, float value) override {
        switch(index) {
        case kActivity:   fActivity=value; break;
        case kShape:      fShape=value; break;
        case kFilter:     fFilter=value; break;
        case kMix:        fMix=value; break;
        case kSubdiv:     fSubdiv=value; break;
        case kRepeats:    fRepeats=value; break;
        case kModDepth:   fModDepth=value; break;
        case kSpace:      fSpace=value; break;
        case kReverbMode: fReverbMode=value; break;
        case kPreset:
            for(int v=0; v<kMaxVoices; ++v) {
                fVoiceBufL[v]->clear(); fVoiceBufR[v]->clear();
                fVoiceL[v].glidePhase = (float)v/(float)kMaxVoices;
                fVoiceR[v].glidePhase = (float)v/(float)kMaxVoices + 0.1f;
            }
            fPreset=value; break;
        }
    }

    void sampleRateChanged(double newSampleRate) override {
        fSr=(float)newSampleRate;
        fFilterZ1L=0.f; fFilterZ1R=0.f;
    }

    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];

        float activityN  = fActivity * 0.01f;
        float shapeN     = fShape * 0.01f;
        float mixN       = fMix * 0.01f;
        float repeatsN   = fRepeats * 0.01f;
        float modDepthN  = fModDepth * 0.01f;
        float spaceN     = fSpace * 0.01f;
        int   preset     = (int)fPreset;
        int   reverbMode = (int)fReverbMode;

        // Activity: controls glide rate (cycles per second)
        // Low activity = slow, dreamy glide; high = fast pitch-shift cycling
        float glideRate = 0.05f + activityN * 2.f; // 0.05..2.05 Hz

        // Subdiv adjusts the loop capture length
        static const float kSubdivMult[5] = {0.25f, 0.5f, 1.0f, 2.0f, 4.0f};
        int subdivIdx = std::max(0, std::min((int)fSubdiv, 4));
        float loopLenSamp = 0.5f * kSubdivMult[subdivIdx] * fSr;
        loopLenSamp = std::max(256.f, std::min(loopLenSamp, (float)(kVoiceBufSamp-1)));

        // Reverb
        static const float kRevFB[4]   = {0.72f, 0.80f, 0.88f, 0.94f};
        static const float kRevDamp[4] = {0.15f, 0.35f, 0.20f, 0.10f};
        float revFB   = kRevFB[reverbMode];
        float revDamp = kRevDamp[reverbMode];

        // Filter
        float filterFC = 200.f + fFilter*0.01f*18000.f;
        float filterC  = std::exp(-kTwoPi * std::min(filterFC, fSr*0.45f) / fSr);
        bool  doFilter = (fFilter < 99.f);

        // Pitch modulation depth (secondary of Repeats knob):
        // adds an additional wobble on top of the glide
        float pitchModAmt = modDepthN * 0.02f;

        // Preset speed pairs per manual:
        // A: half speed ↔ normal (0.5 ↔ 1.0)
        // B: double speed ↔ half speed (2.0 ↔ 0.5)
        // C: normal ↔ double speed (1.0 ↔ 2.0)
        // D: both directions simultaneously (two voice pairs, one up one down)
        float speedA_lo, speedA_hi, speedB_lo, speedB_hi;
        int numVoices;
        switch(preset) {
        case 0: // A: half → normal
            speedA_lo=0.5f; speedA_hi=1.0f;
            speedB_lo=0.5f; speedB_hi=1.0f;
            numVoices=2; break;
        case 1: // B: double → half
            speedA_lo=0.5f; speedA_hi=2.0f;
            speedB_lo=0.5f; speedB_hi=2.0f;
            numVoices=2; break;
        case 2: // C: normal → double
            speedA_lo=1.0f; speedA_hi=2.0f;
            speedB_lo=1.0f; speedB_hi=2.0f;
            numVoices=2; break;
        case 3: // D: both directions simultaneously
            speedA_lo=0.5f; speedA_hi=2.0f; // upward glide pair
            speedB_lo=2.0f; speedB_hi=0.5f; // downward glide pair (inverted)
            numVoices=4; break;
        default:
            speedA_lo=0.5f; speedA_hi=1.0f;
            speedB_lo=0.5f; speedB_hi=1.0f;
            numVoices=2; break;
        }

        for(uint32_t i=0; i<frames; ++i) {
            float l=inL[i], r=inR[i];

            float wetL=0.f, wetR=0.f;

            if(preset < 3) {
                // Presets A-C: two overlapping voices gliding in the same direction
                // Voice 0 and 1 are staggered in phase so they crossfade smoothly
                for(int v=0; v<2; ++v) {
                    float gL = fVoiceL[v].process(*fVoiceBufL[v], l, loopLenSamp,
                                                   speedA_lo, speedA_hi, glideRate, shapeN, fSr);
                    float gR = fVoiceR[v].process(*fVoiceBufR[v], r, loopLenSamp,
                                                   speedA_lo, speedA_hi, glideRate, shapeN, fSr);
                    // Window each voice by its glide phase for smooth overlap
                    float win = std::sin(fVoiceL[v].glidePhase * kPi);
                    win = std::max(0.1f, win); // never fully silent
                    wetL += gL * win;
                    wetR += gR * win;
                }
                wetL *= 0.5f; wetR *= 0.5f;
            } else {
                // Preset D: four voices — two gliding up, two gliding down simultaneously
                for(int v=0; v<2; ++v) {
                    // Up pair
                    float gUL = fVoiceL[v].process(*fVoiceBufL[v], l, loopLenSamp,
                                                    speedA_lo, speedA_hi, glideRate, shapeN, fSr);
                    float gUR = fVoiceR[v].process(*fVoiceBufR[v], r, loopLenSamp,
                                                    speedA_lo, speedA_hi, glideRate, shapeN, fSr);
                    // Down pair (v+2, inverted speed range)
                    float gDL = fVoiceL[v+2].process(*fVoiceBufL[v+2], l, loopLenSamp,
                                                      speedB_lo, speedB_hi, glideRate, shapeN, fSr);
                    float gDR = fVoiceR[v+2].process(*fVoiceBufR[v+2], r, loopLenSamp,
                                                      speedB_lo, speedB_hi, glideRate, shapeN, fSr);
                    float winU = std::max(0.1f, std::sin(fVoiceL[v].glidePhase * kPi));
                    float winD = std::max(0.1f, std::sin(fVoiceL[v+2].glidePhase * kPi));
                    wetL += gUL*winU + gDL*winD;
                    wetR += gUR*winU + gDR*winD;
                }
                wetL *= 0.25f; wetR *= 0.25f;
            }

            // LP filter
            if(doFilter) {
                fFilterZ1L = (1.f-filterC)*wetL + filterC*fFilterZ1L;
                fFilterZ1R = (1.f-filterC)*wetR + filterC*fFilterZ1R;
                wetL = fFilterZ1L; wetR = fFilterZ1R;
            }

            // Reverb (Space)
            if(spaceN > 0.001f) {
                float rvL=0.f, rvR=0.f;
                rvL += fRevCombL[0]->process(wetL, revFB, revDamp);
                rvL += fRevCombL[1]->process(wetL, revFB*0.92f, revDamp);
                rvR += fRevCombR[0]->process(wetR, revFB, revDamp);
                rvR += fRevCombR[1]->process(wetR, revFB*0.92f, revDamp);
                rvL = fRevApL->process(rvL*0.5f, 0.5f);
                rvR = fRevApR->process(rvR*0.5f, 0.5f);
                wetL = wetL*(1.f-spaceN) + rvL*spaceN;
                wetR = wetR*(1.f-spaceN) + rvR*spaceN;
            }

            outL[i] = l*(1.f-mixN) + wetL*mixN;
            outR[i] = r*(1.f-mixN) + wetR*mixN;
        }
    }

private:
    float fActivity, fShape, fFilter, fMix, fSubdiv;
    float fRepeats, fModDepth, fSpace, fReverbMode, fPreset;
    float fSr;
    float fFilterZ1L, fFilterZ1R;

    GlideVoice  fVoiceL[kMaxVoices];
    GlideVoice  fVoiceR[kMaxVoices];
    DelayLine*  fVoiceBufL[kMaxVoices];
    DelayLine*  fVoiceBufR[kMaxVoices];
    CombFilter* fRevCombL[2];
    CombFilter* fRevCombR[2];
    AllPass*    fRevApL;
    AllPass*    fRevApR;

    DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(HoloGlidePlugin)
};

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

END_NAMESPACE_DISTRHO
endef

# ---------------------------------------------------------------------------
# Makefile
# ---------------------------------------------------------------------------
define HOLO_GLIDE_PLUGIN_MAKEFILE
#!/usr/bin/make -f
NAME      = holo-glide
FILES_DSP = HoloGlidePlugin.cpp
include ../../Makefile.plugins.mk
TARGETS = lv2_dsp
all: $(TARGETS)
endef

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

<urn:mod-cookbook:holo-glide>
    a lv2:Plugin ;
    lv2:binary <holo-glide_dsp.so> ;
    rdfs:seeAlso <holo-glide.ttl> .
endef

# ---------------------------------------------------------------------------
# LV2 TTL Plugin Configuration
# ---------------------------------------------------------------------------
define HOLO_GLIDE_PLUGIN_TTL
@prefix doap:  <http://usefulinc.com/ns/doap#> .
@prefix foaf:  <http://xmlns.com/foaf/0.1/> .
@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 units: <http://lv2plug.in/ns/extensions/units#> .

<urn:mod-cookbook:holo-glide>
    a lv2:Plugin , lv2:ModulatorPlugin ;
    doap:name "Microcosm Glide" ;
    doap:license <http://opensource.org/licenses/MIT> ;
    doap:maintainer [ foaf:name "Hologram Electronics" ] ;
    rdfs:comment "Short overlapping loops shifting in pitch over time. Hologram Microcosm Glide effect." ;

    lv2:port [
        a lv2:InputPort , lv2:AudioPort ;
        lv2:index 0 ; lv2:symbol "in_l" ; lv2:name "Input L"
    ] , [
        a lv2:InputPort , lv2:AudioPort ;
        lv2:index 1 ; lv2:symbol "in_r" ; lv2:name "Input R"
    ] , [
        a lv2:OutputPort , lv2:AudioPort ;
        lv2:index 2 ; lv2:symbol "out_l" ; lv2:name "Output L"
    ] , [
        a lv2:OutputPort , lv2:AudioPort ;
        lv2:index 3 ; lv2:symbol "out_r" ; lv2:name "Output R"
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 4 ; lv2:symbol "activity" ; lv2:name "Activity" ;
        lv2:default 50.0 ; lv2:minimum 0.0 ; lv2:maximum 100.0 ;
        units:unit units:pc
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 5 ; lv2:symbol "shape" ; lv2:name "Shape" ;
        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 "filter" ; lv2:name "Filter" ;
        lv2:default 100.0 ; lv2:minimum 0.0 ; lv2:maximum 100.0 ;
        units:unit units:pc
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 7 ; lv2:symbol "mix" ; lv2:name "Mix" ;
        lv2:default 75.0 ; lv2:minimum 0.0 ; lv2:maximum 100.0 ;
        units:unit units:pc
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 8 ; lv2:symbol "subdiv" ; lv2:name "Subdiv" ;
        lv2:default 2 ; lv2:minimum 0 ; lv2:maximum 4 ;
        lv2:portProperty lv2:integer , lv2:enumeration ;
        lv2:scalePoint [ rdfs:label "1/4" ; rdf:value 0 ] ;
        lv2:scalePoint [ rdfs:label "1/2" ; rdf:value 1 ] ;
        lv2:scalePoint [ rdfs:label "1x" ;  rdf:value 2 ] ;
        lv2:scalePoint [ rdfs:label "2x" ;  rdf:value 3 ] ;
        lv2:scalePoint [ rdfs:label "4x" ;  rdf:value 4 ] ;
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 9 ; lv2:symbol "repeats" ; lv2:name "Repeats" ;
        lv2:default 50.0 ; lv2:minimum 0.0 ; lv2:maximum 100.0 ;
        units:unit units:pc
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 10 ; lv2:symbol "mod_depth" ; lv2:name "Mod Depth" ;
        lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 100.0 ;
        units:unit units:pc
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 11 ; lv2:symbol "space" ; lv2:name "Space" ;
        lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 100.0 ;
        units:unit units:pc
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 12 ; lv2:symbol "reverb_mode" ; lv2:name "Reverb Mode" ;
        lv2:default 0 ; lv2:minimum 0 ; lv2:maximum 3 ;
        lv2:portProperty lv2:integer , lv2:enumeration ;
        lv2:scalePoint [ rdfs:label "A - Bright Room" ; rdf:value 0 ] ;
        lv2:scalePoint [ rdfs:label "B - Dark Medium" ; rdf:value 1 ] ;
        lv2:scalePoint [ rdfs:label "C - Large Hall" ;  rdf:value 2 ] ;
        lv2:scalePoint [ rdfs:label "D - Ambient" ;     rdf:value 3 ] ;
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 13 ; lv2:symbol "preset" ; lv2:name "Preset" ;
        lv2:default 0 ; lv2:minimum 0 ; lv2:maximum 3 ;
        lv2:portProperty lv2:integer , lv2:enumeration ;
        lv2:scalePoint [ rdfs:label "A - Half to Normal Speed" ;              rdf:value 0 ] ;
        lv2:scalePoint [ rdfs:label "B - Double to Half Speed" ;              rdf:value 1 ] ;
        lv2:scalePoint [ rdfs:label "C - Normal to Double Speed" ;            rdf:value 2 ] ;
        lv2:scalePoint [ rdfs:label "D - Both Directions Simultaneously" ;    rdf:value 3 ] ;
    ] .
endef

export HOLO_GLIDE_PLUGIN_CPP HOLO_GLIDE_PLUGIN_INFO_H HOLO_GLIDE_PLUGIN_MAKEFILE
export HOLO_GLIDE_MANIFEST_TTL HOLO_GLIDE_PLUGIN_TTL

# ---------------------------------------------------------------------------
# Buildroot Platform Compilation Commands
# ---------------------------------------------------------------------------
define HOLO_GLIDE_CONFIGURE_CMDS
	mkdir -p $(@D)/examples/holo-glide
	printf '%s' "$$HOLO_GLIDE_PLUGIN_CPP"      > $(@D)/examples/holo-glide/HoloGlidePlugin.cpp
	printf '%s' "$$HOLO_GLIDE_PLUGIN_INFO_H"   > $(@D)/examples/holo-glide/DistrhoPluginInfo.h
	printf '%s' "$$HOLO_GLIDE_PLUGIN_MAKEFILE" > $(@D)/examples/holo-glide/Makefile
endef

define HOLO_GLIDE_BUILD_CMDS
	$(TARGET_MAKE_ENV) $(TARGET_CONFIGURE_OPTS) $(MAKE) NOOPT=true -C $(@D)/examples/holo-glide lv2_dsp
endef

define HOLO_GLIDE_INSTALL_TARGET_CMDS
	mkdir -p $($(PKG)_PKGDIR)/holo-glide.lv2
	cp $(@D)/bin/holo-glide.lv2/holo-glide_dsp.so $($(PKG)_PKGDIR)/holo-glide.lv2/
	printf '%s' "$$HOLO_GLIDE_MANIFEST_TTL" > $($(PKG)_PKGDIR)/holo-glide.lv2/manifest.ttl
	printf '%s' "$$HOLO_GLIDE_PLUGIN_TTL" > $($(PKG)_PKGDIR)/holo-glide.lv2/holo-glide.ttl
endef

$(eval $(generic-package))
