# Tallon Electric The Banshee - Chopped Digital Octave Fuzz
# Slaughter to Prevail signature. DSP after Demedash Effects.
# Controls: Dissonance, Square Wave, Octave Level, Chop Speed,
#           Chop (momentary), Trash (latch/momentary), Octave Mode (1/2/1+2),
#           Octave Up toggle.
# Upload at https://builder.mod.audio/buildroot

TALLON_BANSHEE_VERSION = 61d38eb638449647fb8395a35c5b8dab7e981ba7
TALLON_BANSHEE_SITE = https://github.com/DISTRHO/DPF.git
TALLON_BANSHEE_SITE_METHOD = git
TALLON_BANSHEE_BUNDLES = tallon-banshee.lv2

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

#define DISTRHO_PLUGIN_BRAND        "Tallon Electric"
#define DISTRHO_PLUGIN_NAME         "The Banshee"
#define DISTRHO_PLUGIN_URI          "urn:mod-cookbook:tallon-banshee"
#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 {
    kDissonance = 0, // Detuned octave divergence / dissonant fuzz character
    kSquareWave,     // Square wave fuzz blend / harshness
    kOctaveLevel,    // Octave voice output level
    kChopSpeed,      // Chop rate (Hz) - controls gate/stutter frequency
    kChop,           // Chop momentary gate: 0=off, 1=active (momentary only)
    kTrash,          // Trash effect: 0=off, 1=on (adds extra decimation/chaos)
    kOctaveMode,     // 0=Oct1 (-12st), 1=Oct2 (-24st), 2=Oct1+2 (both)
    kOctaveUp,       // Octave Up: 0=off, 1=on (adds +12st voice)
    kVolume,         // Master output volume
    kMix,            // Dry/wet blend
    kParameterCount
};

#endif
endef

# ---------------------------------------------------------------------------
# TallonBansheePlugin.cpp
# ---------------------------------------------------------------------------
define TALLON_BANSHEE_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;

// ---------------------------------------------------------------------------
// Pitch Shifter: windowed dual-tap delay line
// Used for sub-octave down (-12st, -24st) and octave up (+12st)
// ---------------------------------------------------------------------------
struct PitchShifter {
    float* buf; int mask, wpos;
    float ph1, ph2;
    static const int kWin = 2048;

    PitchShifter() : buf(nullptr), mask(0), wpos(0), ph1(0.f), ph2(0.5f) {}
    ~PitchShifter() { delete[] buf; }

    void init() {
        int sz=1; while(sz<8192) sz<<=1;
        delete[] buf; buf=new float[sz](); mask=sz-1; wpos=0;
    }

    float process(float in, float semis) {
        buf[wpos&mask]=in; ++wpos;
        float ratio=std::pow(2.f, semis/12.f);
        float speed=1.f-ratio;
        ph1+=speed/(float)kWin; ph2+=speed/(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);
        float w1=std::sin(ph1*kPi), w2=std::sin(ph2*kPi);
        auto rd=[&](float d)->float {
            float pos=(float)wpos-d-1.f;
            int ri=(int)pos; float fr=pos-(float)ri;
            return buf[ri&mask]+fr*(buf[(ri+1)&mask]-buf[ri&mask]);
        };
        float ws=w1+w2; if(ws<0.001f) ws=0.001f;
        return (rd(d1)*w1+rd(d2)*w2)/ws;
    }
};

// ---------------------------------------------------------------------------
// Square wave fuzz:
// Full-wave rectified hard clip -> square wave character.
// SquareWave knob blends between soft-clip fuzz and hard square wave.
// ---------------------------------------------------------------------------
static inline float squareFuzz(float in, float squareN, float driveN) {
    float drive = 1.f + driveN * 20.f;
    float driven = in * drive;

    // Soft clip (tanh) path
    float soft = std::tanh(driven);

    // Hard square wave: sign of signal
    float sq = (driven >= 0.f) ? 1.f : -1.f;
    // Only engage square when amplitude is above a threshold
    float thresh = 0.1f;
    if (std::fabs(driven) < thresh)
        sq = driven / thresh; // linear in dead zone to avoid clicks

    // Blend soft -> square based on SquareWave knob
    return soft*(1.f-squareN) + sq*squareN;
}

// ---------------------------------------------------------------------------
// Dissonance: adds a pitch-detuned copy of the fuzz signal, creating
// beating/dissonant interference. Uses a short modulated delay to
// create micro-pitch divergence between the two copies.
// Higher Dissonance = more detuning = more chaotic/dissonant character.
// ---------------------------------------------------------------------------
struct DissonanceUnit {
    // Two real pitch-shifted voices (not a short modulated delay) so the
    // "Dissonance" knob produces actual clashing intervals / beat-frequency
    // dissonance rather than a chorus-y wobble.
    PitchShifter pUp, pDown;
    float lfoPhase;

    DissonanceUnit() : lfoPhase(0.f) {}

    void init() { pUp.init(); pDown.init(); }

    float process(float in, float dissonanceN, float sr) {
        if(dissonanceN < 0.005f) return in;

        // Slow LFO adds movement/beating on top of the fixed clashing interval
        float lfoRate = 0.3f + dissonanceN * 4.f;
        lfoPhase += lfoRate/sr;
        if(lfoPhase>=1.f) lfoPhase-=1.f;
        float wobble = std::sin(lfoPhase*kTwoPi) * dissonanceN * 0.4f;

        // Sharp minor-second-ish voice above, wider detuned voice below --
        // both intervals widen with Dissonance for a nastier cluster.
        float semisUp   =  1.0f + dissonanceN * 6.0f + wobble;   // up to ~7 semitones sharp
        float semisDown = -(1.5f + dissonanceN * 4.5f) - wobble; // up to ~6 semitones flat

        float up   = pUp.process(in, semisUp);
        float down = pDown.process(in, semisDown);

        // Grind the clashing voices together with extra saturation for crass edge
        float cluster = up + down;
        cluster = std::tanh(cluster * (1.f + dissonanceN * 2.f));

        // Blend goes all the way to fully wet at max Dissonance
        float blend = 0.4f + dissonanceN * 0.6f;
        return in*(1.f-blend) + cluster*blend;
    }
};

// ---------------------------------------------------------------------------
// Chop gate: hard rhythmic gating at ChopSpeed Hz.
// Momentary only -- active only while Chop parameter is 1.
// When active: signal is gated at ChopSpeed rate with a short fade to
// prevent hard clicks (the "stutter/chop" character of the Banshee).
// ---------------------------------------------------------------------------
struct ChopGate {
    float phase;
    ChopGate() : phase(0.f) {}

    float process(float in, float chopHz, bool active, float sr) {
        if(!active) return in;
        chopHz = std::max(0.5f, std::min(chopHz, 40.f));
        phase += chopHz/sr;
        if(phase>=1.f) phase-=1.f;
        // Square gate with ~5% fade-in to avoid clicks
        float gate = (phase < 0.5f) ? 1.f : 0.f;
        float fadeWidth = 0.05f;
        if(phase < fadeWidth)
            gate = phase/fadeWidth;
        else if(phase >= 0.5f && phase < 0.5f+fadeWidth)
            gate = 1.f - (phase-0.5f)/fadeWidth;
        return in * gate;
    }
};

// ---------------------------------------------------------------------------
// Trash effect: applies bit-reduction + sample-rate decimation to add
// digital grit and lo-fi destruction on top of the fuzz signal.
// ---------------------------------------------------------------------------
struct TrashUnit {
    float holdL, holdR;
    int   holdTimer;

    TrashUnit() : holdL(0.f), holdR(0.f), holdTimer(0) {}

    void process(float& l, float& r, bool active, float sr) {
        if(!active) return;
        // Decimate: hold every N samples (creates lo-fi aliasing)
        int holdN = (int)(sr / 8000.f); // hold at ~8kHz effective rate
        ++holdTimer;
        if(holdTimer >= holdN) {
            holdTimer=0;
            // Bit-crush to ~6 bits
            float levels = 64.f;
            holdL = std::round(l*levels)/levels;
            holdR = std::round(r*levels)/levels;
        }
        l = holdL; r = holdR;
    }
};

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

struct HP1 { float z1; HP1():z1(0.f){} void reset(){z1=0.f;}
    float process(float in, float c){float lp=(1.f-c)*in+c*z1;z1=lp;return in-lp;} };

class TallonBansheePlugin : public Plugin
{
public:
    TallonBansheePlugin()
        : Plugin(kParameterCount, 0, 0),
          fDissonance(30.f), fSquareWave(60.f), fOctaveLevel(70.f),
          fChopSpeed(8.f), fChop(0.f), fTrash(0.f),
          fOctaveMode(0.f), fOctaveUp(0.f),
          fVolume(75.f), fMix(100.f),
          fSr(48000.f)
    {
        fOct1L.init(); fOct1R.init();  // -12 semitones
        fOct2L.init(); fOct2R.init();  // -24 semitones
        fOctUpL.init(); fOctUpR.init(); // +12 semitones
        fDissL.init(); fDissR.init();  // dissonant cluster voices
    }

protected:
    const char* getLabel()       const override { return "The Banshee"; }
    const char* getDescription() const override { return "Chopped digital octave fuzz. Slaughter to Prevail signature by Tallon Electric / Demedash Effects."; }
    const char* getMaker()       const override { return "Tallon Electric"; }
    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('T','B','N','S'); }

    void initParameter(uint32_t index, Parameter& p) override {
        p.hints=kParameterIsAutomatable;
        switch(index) {
        case kDissonance:
            p.name="Dissonance"; p.symbol="dissonance"; p.unit="%";
            p.ranges.def=30.f; p.ranges.min=0.f; p.ranges.max=100.f; break;
        case kSquareWave:
            p.name="Square Wave"; p.symbol="square_wave"; p.unit="%";
            p.ranges.def=60.f; p.ranges.min=0.f; p.ranges.max=100.f; break;
        case kOctaveLevel:
            p.name="Octave Level"; p.symbol="octave_level"; p.unit="%";
            p.ranges.def=70.f; p.ranges.min=0.f; p.ranges.max=100.f; break;
        case kChopSpeed:
            p.name="Chop Speed"; p.symbol="chop_speed"; p.unit="Hz";
            p.ranges.def=8.f; p.ranges.min=0.5f; p.ranges.max=40.f; break;
        case kChop:
            p.name="Chop"; p.symbol="chop";
            p.ranges.def=0.f; p.ranges.min=0.f; p.ranges.max=1.f;
            p.hints|=kParameterIsInteger; break;
        case kTrash:
            p.name="Trash"; p.symbol="trash";
            p.ranges.def=0.f; p.ranges.min=0.f; p.ranges.max=1.f;
            p.hints|=kParameterIsInteger; break;
        case kOctaveMode:
            p.name="Octave Mode"; p.symbol="octave_mode";
            p.ranges.def=0.f; p.ranges.min=0.f; p.ranges.max=2.f;
            p.hints|=kParameterIsInteger; break;
        case kOctaveUp:
            p.name="Octave Up"; p.symbol="octave_up";
            p.ranges.def=0.f; p.ranges.min=0.f; p.ranges.max=1.f;
            p.hints|=kParameterIsInteger; break;
        case kVolume:
            p.name="Volume"; p.symbol="volume"; p.unit="%";
            p.ranges.def=75.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=100.f; p.ranges.min=0.f; p.ranges.max=100.f; break;
        }
    }

    float getParameterValue(uint32_t index) const override {
        switch(index) {
        case kDissonance:  return fDissonance;
        case kSquareWave:  return fSquareWave;
        case kOctaveLevel: return fOctaveLevel;
        case kChopSpeed:   return fChopSpeed;
        case kChop:        return fChop;
        case kTrash:       return fTrash;
        case kOctaveMode:  return fOctaveMode;
        case kOctaveUp:    return fOctaveUp;
        case kVolume:      return fVolume;
        case kMix:         return fMix;
        }
        return 0.f;
    }

    void setParameterValue(uint32_t index, float value) override {
        switch(index) {
        case kDissonance:  fDissonance=value;  break;
        case kSquareWave:  fSquareWave=value;  break;
        case kOctaveLevel: fOctaveLevel=value; break;
        case kChopSpeed:   fChopSpeed=value;   break;
        case kChop:        fChop=value;        break;
        case kTrash:       fTrash=value;       break;
        case kOctaveMode:  fOctaveMode=value;  break;
        case kOctaveUp:    fOctaveUp=value;    break;
        case kVolume:      fVolume=value;      break;
        case kMix:         fMix=value;         break;
        }
    }

    void sampleRateChanged(double newSampleRate) override {
        fSr=(float)newSampleRate;
        fInputHPL.reset(); fInputHPR.reset();
        fOutputLPL.reset(); fOutputLPR.reset();
    }

    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 disN   = fDissonance  * 0.01f;
        float sqN    = fSquareWave  * 0.01f;
        float octLvl = fOctaveLevel * 0.01f;
        float volN   = fVolume      * 0.01f;
        float mixN   = fMix         * 0.01f;
        bool  chop   = (fChop   > 0.5f);
        bool  trash  = (fTrash  > 0.5f);
        bool  octUp  = (fOctaveUp > 0.5f);
        int   octMode= std::max(0,std::min((int)fOctaveMode,2));
        // 0=Oct1(-12st), 1=Oct2(-24st), 2=Oct1+2(both)

        // Input HP: remove sub-bass before fuzz to keep it tight
        float hpC = std::exp(-kTwoPi * 80.f / fSr);
        // Output LP: tame ultra-high aliasing artifacts from digital fuzz
        float lpC = std::exp(-kTwoPi * 10000.f / fSr);

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

            // Input high-pass
            l = fInputHPL.process(l, hpC);
            r = fInputHPR.process(r, hpC);

            // ----------------------------------------------------------------
            // SQUARE WAVE FUZZ (core distortion engine)
            // Drive scales with SquareWave knob -- more square = more drive
            // ----------------------------------------------------------------
            float driveN = 0.4f + sqN * 0.6f;
            float fuzzL = squareFuzz(l, sqN, driveN);
            float fuzzR = squareFuzz(r, sqN, driveN);

            // ----------------------------------------------------------------
            // DISSONANCE (detuned interference on fuzz signal)
            // ----------------------------------------------------------------
            float disL = fDissL.process(fuzzL, disN, fSr);
            float disR = fDissR.process(fuzzR, disN, fSr);

            // ----------------------------------------------------------------
            // OCTAVE VOICES (pitch-shifted from input signal, pre-fuzz clean)
            // The octave voices run from the DRY input (before fuzz), matching
            // the Banshee's architecture where octave tracks the clean signal
            // ----------------------------------------------------------------
            float oct1L=0.f, oct1R=0.f;
            float oct2L=0.f, oct2R=0.f;

            if(octMode == 0 || octMode == 2) {
                // Oct 1: one octave down (-12 semitones)
                oct1L = fOct1L.process(inL[i], -12.f);
                oct1R = fOct1R.process(inR[i], -12.f);
                // Run octave voices through fuzz too (heavier texture)
                oct1L = squareFuzz(oct1L, sqN*0.8f, driveN*0.6f);
                oct1R = squareFuzz(oct1R, sqN*0.8f, driveN*0.6f);
            }
            if(octMode == 1 || octMode == 2) {
                // Oct 2: two octaves down (-24 semitones)
                oct2L = fOct2L.process(inL[i], -24.f);
                oct2R = fOct2R.process(inR[i], -24.f);
                oct2L = squareFuzz(oct2L, sqN*0.9f, driveN*0.5f);
                oct2R = squareFuzz(oct2R, sqN*0.9f, driveN*0.5f);
            }

            // Octave Up (+12 semitones) -- toggle button
            float octUpSigL=0.f, octUpSigR=0.f;
            if(octUp) {
                octUpSigL = fOctUpL.process(inL[i], 12.f);
                octUpSigR = fOctUpR.process(inR[i], 12.f);
                octUpSigL = squareFuzz(octUpSigL, sqN*0.5f, driveN*0.4f);
                octUpSigR = squareFuzz(octUpSigR, sqN*0.5f, driveN*0.4f);
            }

            // Mix octave voices with fuzz (Octave Level knob controls all octave voices)
            float wetL = disL;
            float wetR = disR;
            if(octMode==0) {
                wetL += oct1L * octLvl;
                wetR += oct1R * octLvl;
            } else if(octMode==1) {
                wetL += oct2L * octLvl;
                wetR += oct2R * octLvl;
            } else {
                // Oct1+2: both sub-octave voices (split level)
                wetL += oct1L * octLvl * 0.6f + oct2L * octLvl * 0.5f;
                wetR += oct1R * octLvl * 0.6f + oct2R * octLvl * 0.5f;
            }
            if(octUp) {
                wetL += octUpSigL * octLvl * 0.7f;
                wetR += octUpSigR * octLvl * 0.7f;
            }

            // ----------------------------------------------------------------
            // CHOP: momentary rhythmic hard gate (momentary only per hardware)
            // ----------------------------------------------------------------
            wetL = fChopL.process(wetL, fChopSpeed, chop, fSr);
            wetR = fChopR.process(wetR, fChopSpeed, chop, fSr);

            // ----------------------------------------------------------------
            // TRASH: bit-crush + decimation on top of everything
            // ----------------------------------------------------------------
            fTrashUnit.process(wetL, wetR, trash, fSr);

            // Output LP (tame harsh aliasing)
            wetL = fOutputLPL.process(wetL, lpC);
            wetR = fOutputLPR.process(wetR, lpC);

            // Volume + dry/wet blend
            outL[i] = inL[i]*(1.f-mixN) + wetL*volN*mixN;
            outR[i] = inR[i]*(1.f-mixN) + wetR*volN*mixN;
        }
    }

private:
    float fDissonance, fSquareWave, fOctaveLevel, fChopSpeed;
    float fChop, fTrash, fOctaveMode, fOctaveUp, fVolume, fMix;
    float fSr;

    PitchShifter   fOct1L, fOct1R;   // -12st
    PitchShifter   fOct2L, fOct2R;   // -24st
    PitchShifter   fOctUpL, fOctUpR; // +12st
    DissonanceUnit fDissL, fDissR;
    ChopGate       fChopL, fChopR;
    TrashUnit      fTrashUnit;
    HP1            fInputHPL, fInputHPR;
    LP1            fOutputLPL, fOutputLPR;

    DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TallonBansheePlugin)
};

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

END_NAMESPACE_DISTRHO
endef

# ---------------------------------------------------------------------------
# Makefile
# ---------------------------------------------------------------------------
define TALLON_BANSHEE_PLUGIN_MAKEFILE
#!/usr/bin/make -f
NAME      = tallon-banshee
FILES_DSP = TallonBansheePlugin.cpp
include ../../Makefile.plugins.mk
TARGETS = lv2_dsp
all: $(TARGETS)
endef

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

<urn:mod-cookbook:tallon-banshee>
    a lv2:Plugin ;
    lv2:binary <tallon-banshee_dsp.so> ;
    rdfs:seeAlso <tallon-banshee.ttl> .
endef

# ---------------------------------------------------------------------------
# LV2 TTL Plugin Configuration
# ---------------------------------------------------------------------------
define TALLON_BANSHEE_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:tallon-banshee>
    a lv2:Plugin , lv2:DistortionPlugin ;
    doap:name "The Banshee" ;
    doap:license <http://opensource.org/licenses/MIT> ;
    doap:maintainer [ foaf:name "Tallon Electric" ] ;
    rdfs:comment "Chopped digital octave fuzz. Slaughter to Prevail signature by Tallon Electric / Demedash Effects." ;

    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 "dissonance" ; lv2:name "Dissonance" ;
        lv2:default 30.0 ; lv2:minimum 0.0 ; lv2:maximum 100.0 ;
        units:unit units:pc
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 5 ; lv2:symbol "square_wave" ; lv2:name "Square Wave" ;
        lv2:default 60.0 ; lv2:minimum 0.0 ; lv2:maximum 100.0 ;
        units:unit units:pc
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 6 ; lv2:symbol "octave_level" ; lv2:name "Octave Level" ;
        lv2:default 70.0 ; lv2:minimum 0.0 ; lv2:maximum 100.0 ;
        units:unit units:pc
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 7 ; lv2:symbol "chop_speed" ; lv2:name "Chop Speed" ;
        lv2:default 8.0 ; lv2:minimum 0.5 ; lv2:maximum 40.0 ;
        units:unit units:hz
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 8 ; lv2:symbol "chop" ; lv2:name "Chop" ;
        lv2:default 0 ; lv2:minimum 0 ; lv2:maximum 1 ;
        lv2:portProperty lv2:integer , lv2:enumeration ;
        lv2:scalePoint [ rdfs:label "Off (Release)" ; rdf:value 0 ] ;
        lv2:scalePoint [ rdfs:label "On (Hold)" ;    rdf:value 1 ] ;
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 9 ; lv2:symbol "trash" ; lv2:name "Trash" ;
        lv2:default 0 ; lv2:minimum 0 ; lv2:maximum 1 ;
        lv2:portProperty lv2:integer , lv2:enumeration ;
        lv2:scalePoint [ rdfs:label "Off" ; rdf:value 0 ] ;
        lv2:scalePoint [ rdfs:label "On" ;  rdf:value 1 ] ;
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 10 ; lv2:symbol "octave_mode" ; lv2:name "Octave Mode" ;
        lv2:default 0 ; lv2:minimum 0 ; lv2:maximum 2 ;
        lv2:portProperty lv2:integer , lv2:enumeration ;
        lv2:scalePoint [ rdfs:label "Oct 1 (-1 Octave)" ;       rdf:value 0 ] ;
        lv2:scalePoint [ rdfs:label "Oct 2 (-2 Octaves)" ;      rdf:value 1 ] ;
        lv2:scalePoint [ rdfs:label "Oct 1 + 2 (Both)" ;        rdf:value 2 ] ;
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 11 ; lv2:symbol "octave_up" ; lv2:name "Octave Up" ;
        lv2:default 0 ; lv2:minimum 0 ; lv2:maximum 1 ;
        lv2:portProperty lv2:integer , lv2:enumeration ;
        lv2:scalePoint [ rdfs:label "Off" ; rdf:value 0 ] ;
        lv2:scalePoint [ rdfs:label "On" ;  rdf:value 1 ] ;
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 12 ; lv2:symbol "volume" ; lv2:name "Volume" ;
        lv2:default 75.0 ; lv2:minimum 0.0 ; lv2:maximum 100.0 ;
        units:unit units:pc
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 13 ; lv2:symbol "mix" ; lv2:name "Mix" ;
        lv2:default 100.0 ; lv2:minimum 0.0 ; lv2:maximum 100.0 ;
        units:unit units:pc
    ] .
endef

export TALLON_BANSHEE_PLUGIN_CPP TALLON_BANSHEE_PLUGIN_INFO_H TALLON_BANSHEE_PLUGIN_MAKEFILE
export TALLON_BANSHEE_MANIFEST_TTL TALLON_BANSHEE_PLUGIN_TTL

# ---------------------------------------------------------------------------
# Buildroot Platform Compilation Commands
# ---------------------------------------------------------------------------
define TALLON_BANSHEE_CONFIGURE_CMDS
	mkdir -p $(@D)/examples/tallon-banshee
	printf '%s' "$$TALLON_BANSHEE_PLUGIN_CPP"      > $(@D)/examples/tallon-banshee/TallonBansheePlugin.cpp
	printf '%s' "$$TALLON_BANSHEE_PLUGIN_INFO_H"   > $(@D)/examples/tallon-banshee/DistrhoPluginInfo.h
	printf '%s' "$$TALLON_BANSHEE_PLUGIN_MAKEFILE" > $(@D)/examples/tallon-banshee/Makefile
endef

define TALLON_BANSHEE_BUILD_CMDS
	$(TARGET_MAKE_ENV) $(TARGET_CONFIGURE_OPTS) $(MAKE) NOOPT=true -C $(@D)/examples/tallon-banshee lv2_dsp
endef

define TALLON_BANSHEE_INSTALL_TARGET_CMDS
	mkdir -p $($(PKG)_PKGDIR)/tallon-banshee.lv2
	cp $(@D)/bin/tallon-banshee.lv2/tallon-banshee_dsp.so $($(PKG)_PKGDIR)/tallon-banshee.lv2/
	printf '%s' "$$TALLON_BANSHEE_MANIFEST_TTL" > $($(PKG)_PKGDIR)/tallon-banshee.lv2/manifest.ttl
	printf '%s' "$$TALLON_BANSHEE_PLUGIN_TTL" > $($(PKG)_PKGDIR)/tallon-banshee.lv2/tallon-banshee.ttl
endef

$(eval $(generic-package))
