# Eventide H90 Phaser - All-Pass Filter Modulated Phaser Engine
# Incorporating full H90 manual specifications & enumerated dropdown controls.
# Upload at https://builder.mod.audio/buildroot

H90_PHASER_VERSION = 61d38eb638449647fb8395a35c5b8dab7e981ba7
H90_PHASER_SITE = https://github.com/DISTRHO/DPF.git
H90_PHASER_SITE_METHOD = git
H90_PHASER_BUNDLES = h90-phaser.lv2

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

#define DISTRHO_PLUGIN_BRAND        "Eventide"
#define DISTRHO_PLUGIN_NAME         "H90 Phaser"
#define DISTRHO_PLUGIN_URI          "urn:mod-cookbook:h90-phaser"
#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 {
    kIntensity = 0,
    kType,
    kDepth,
    kSpeed,
    kShape,
    kStages,
    kDepthMod,
    kSpeedMod,
    kModRate,
    kModSource,
    kModDepthMeter,
    kModSpeedMeter,
    kParameterCount
};

#endif
endef

# ---------------------------------------------------------------------------
# H90PhaserPlugin.cpp
# ---------------------------------------------------------------------------
define H90_PHASER_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;

// LFO with 8 standard mod-matrix shapes, independent L/R random state
struct LFO {
    float phase, sah, sahPrev;
    float sahR, sahPrevR;

    LFO() : phase(0.f), sah(0.f), sahPrev(0.f), sahR(0.f), sahPrevR(0.f) {}

    void reset() {
        phase = 0.f;
        sah = 0.f; sahPrev = 0.f;
        sahR = 0.f; sahPrevR = 0.f;
    }

    static float shapeAt(float p, int shape, float sahVal, float sahPrevVal) {
        switch (shape) {
        case 0: return std::sin(p * kTwoPi);
        case 1: return (p < 0.5f) ? (4.f * p - 1.f) : (3.f - 4.f * p);
        case 2: return (p < 0.5f) ? 1.f : -1.f;
        case 3: return 2.f * p - 1.f;
        case 4: return 1.f - 2.f * p;
        case 5: return sahVal;
        case 6: return sahPrevVal + (sahVal - sahPrevVal) * p;
        case 7: { float c = std::cos(p * kTwoPi); return (c > 0.f) ? (c*c*c) : (c*0.25f); }
        default: return std::sin(p * kTwoPi);
        }
    }

    float tick(float rate, float sr, int shape, float phaseOffsetR, float& outR) {
        float outL = shapeAt(phase, shape, sah, sahPrev);

        float pR = phase + phaseOffsetR;
        if (pR >= 1.f) pR -= 1.f;
        if (pR < 0.f)  pR += 1.f;
        outR = shapeAt(pR, shape, sahR, sahPrevR);

        float prevPhase = phase;
        phase += rate / sr;
        if (phase >= 1.f) {
            phase -= 1.f;
            sahPrev = sah;
            sah = ((float)std::rand() / (float)RAND_MAX) * 2.f - 1.f;
        }

        float prevPR = prevPhase + phaseOffsetR;
        if (prevPR >= 1.f) prevPR -= 1.f;
        bool wrappedR = (pR < prevPR);
        if (wrappedR) {
            sahPrevR = sahR;
            sahR = ((float)std::rand() / (float)RAND_MAX) * 2.f - 1.f;
        }

        return outL;
    }
};

// First-order all-pass filter, correct one-multiply (transposed) structure:
//   y[n] = c*x[n] + z1 ;  z1 = x[n] - c*y[n]
// which realizes H(z) = (c + z^-1) / (1 + c*z^-1) and is stable for any
// |c| < 1. The previous version (out = c*(in - z1) + z1; z1 = out) was NOT
// actually an all-pass - it collapses to a one-pole recursion
// y[n] = c*x[n] + (1-c)*y[n-1], which is unstable whenever c is negative
// (pole magnitude |1-c| > 1). Since this plugin's all-pass coefficient goes
// negative for most of its sweep range (down to about -0.99 at low break
// frequencies), that bug made the filter state blow up to infinity almost
// immediately - which is exactly what silences/kills audio downstream.
struct AllPass {
    float z1;
    AllPass() : z1(0.f) {}
    void reset() { z1 = 0.f; }

    float process(float in, float coeff) {
        float out = coeff * in + z1;
        z1 = in - coeff * out;
        return out;
    }
};

// Up to 8 cascaded all-pass stages per channel
static const int kMaxStages = 8;

class H90PhaserPlugin : public Plugin
{
public:
    H90PhaserPlugin()
        : Plugin(kParameterCount, 0, 0),
          fIntensity(75.f), fType(0.f), fDepth(60.f), fSpeed(0.5f), fShape(0.f),
          fStages(4.f), fDepthMod(0.f), fSpeedMod(0.f), fModRate(0.3f), fModSource(0.f),
          fSr(48000.f), fFeedbackL(0.f), fFeedbackR(0.f)
    {}

protected:
    const char* getLabel()       const override { return "H90 Phaser"; }
    const char* getDescription() const override { return "Series of all-pass filters creating sweeping phase notches. Eventide H90 Phaser emulation."; }
    const char* getMaker()       const override { return "Eventide"; }
    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','9','0','H'); }

    void initParameter(uint32_t index, Parameter& p) override {
        p.hints = kParameterIsAutomatable;
        switch (index) {
        case kIntensity:
            p.name = "Intensity"; p.symbol = "intensity"; p.unit = "%";
            p.ranges.def = 75.f; p.ranges.min = 0.f; p.ranges.max = 100.f; break;
        case kType:
            p.name = "Type"; p.symbol = "type";
            p.ranges.def = 0.f; p.ranges.min = 0.f; p.ranges.max = 3.f;
            p.hints |= kParameterIsInteger; break;
        case kDepth:
            p.name = "Depth"; p.symbol = "depth"; p.unit = "%";
            p.ranges.def = 60.f; p.ranges.min = 0.f; p.ranges.max = 100.f; break;
        case kSpeed:
            p.name = "Speed"; p.symbol = "speed"; p.unit = "Hz";
            p.ranges.def = 0.5f; p.ranges.min = 0.02f; p.ranges.max = 10.f; break;
        case kShape:
            p.name = "Shape"; p.symbol = "shape";
            p.ranges.def = 0.f; p.ranges.min = 0.f; p.ranges.max = 7.f;
            p.hints |= kParameterIsInteger; break;
        case kStages:
            p.name = "Stages"; p.symbol = "stages";
            p.ranges.def = 4.f; p.ranges.min = 2.f; p.ranges.max = 8.f;
            p.hints |= kParameterIsInteger; break;
        case kDepthMod:
            p.name = "Depth Mod"; p.symbol = "depth_mod"; p.unit = "%";
            p.ranges.def = 0.f; p.ranges.min = -100.f; p.ranges.max = 100.f; break;
        case kSpeedMod:
            p.name = "Speed Mod"; p.symbol = "speed_mod"; p.unit = "%";
            p.ranges.def = 0.f; p.ranges.min = -100.f; p.ranges.max = 100.f; break;
        case kModRate:
            p.name = "Mod Rate"; p.symbol = "mod_rate"; p.unit = "Hz";
            p.ranges.def = 0.3f; p.ranges.min = 0.01f; p.ranges.max = 10.f; break;
        case kModSource:
            p.name = "Mod Source"; p.symbol = "mod_source";
            p.ranges.def = 0.f; p.ranges.min = 0.f; p.ranges.max = 7.f;
            p.hints |= kParameterIsInteger; break;
        // Output meters (not settable): let you watch the secondary LFO's
        // actual effect on depth/speed live in the MOD UI, to confirm the
        // modulation is really happening rather than being too slow/subtle
        // to notice by ear. depth_mod/speed_mod/mod_rate at defaults means
        // 3.3s per cycle - these meters make that undeniable either way.
        case kModDepthMeter:
            p.name = "Mod Depth Meter"; p.symbol = "mod_depth_meter";
            p.hints |= kParameterIsOutput;
            p.ranges.def = 0.f; p.ranges.min = 0.f; p.ranges.max = 1.f; break;
        case kModSpeedMeter:
            p.name = "Mod Speed Meter"; p.symbol = "mod_speed_meter"; p.unit = "Hz";
            p.hints |= kParameterIsOutput;
            p.ranges.def = 0.f; p.ranges.min = 0.f; p.ranges.max = 20.f; break;
        }
    }

    float getParameterValue(uint32_t index) const override {
        switch (index) {
        case kIntensity: return fIntensity;
        case kType:      return fType;
        case kDepth:     return fDepth;
        case kSpeed:     return fSpeed;
        case kShape:     return fShape;
        case kStages:    return fStages;
        case kDepthMod:  return fDepthMod;
        case kSpeedMod:  return fSpeedMod;
        case kModRate:   return fModRate;
        case kModSource: return fModSource;
        case kModDepthMeter: return fModDepthMeter;
        case kModSpeedMeter: return fModSpeedMeter;
        }
        return 0.f;
    }

    void setParameterValue(uint32_t index, float value) override {
        switch (index) {
        case kIntensity: fIntensity = value; break;
        case kType:      fType = value; break;
        case kDepth:     fDepth = value; break;
        case kSpeed:     fSpeed = value; break;
        case kShape:     fShape = value; break;
        case kStages:    fStages = value; break;
        case kDepthMod:  fDepthMod = value; break;
        case kSpeedMod:  fSpeedMod = value; break;
        case kModRate:   fModRate = value; break;
        case kModSource: fModSource = value; break;
        }
    }

    void sampleRateChanged(double newSampleRate) override {
        fSr = (float)newSampleRate;
        fLFO.reset();
        fModLFO.reset();
        fFeedbackL = 0.f;
        fFeedbackR = 0.f;
        for (int s = 0; s < kMaxStages; ++s) {
            fApL[s].reset();
            fApR[s].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 intensity = fIntensity * 0.01f;
        float depthAmt = fDepth * 0.01f;
        int type = (int)fType;
        int shape = (int)fShape;
        int modShape = (int)fModSource;
        int numStages = (int)fStages;
        if (numStages < 2) numStages = 2;
        if (numStages > kMaxStages) numStages = kMaxStages;

        // Feedback amount and inversion per type
        // Positive: non-inverted feedback mix
        // Negative: inverted feedback mix
        // Feedback: feedback only, no feed-forward
        // Bi-phase: moderate inverted feedback, wider sweep
        float fbAmt = 0.f;
        bool invertFB = false;
        bool fbOnly = false;
        switch (type) {
        case 0: fbAmt = 0.45f; invertFB = false;  fbOnly = false; break; // Positive
        case 1: fbAmt = 0.45f; invertFB = true;   fbOnly = false; break; // Negative
        case 2: fbAmt = 0.70f; invertFB = false;  fbOnly = true;  break; // Feedback
        case 3: fbAmt = 0.35f; invertFB = true;   fbOnly = false; break; // Bi-phase
        }

        float depthModAmt = fDepthMod * 0.01f;
        float speedModAmt = fSpeedMod * 0.01f;

        // Secondary modulator: control-rate LFO that wiggles Depth/Speed
        float modLfoDummy;
        float modVal = fModLFO.tick(fModRate, fSr / (float)frames, modShape, 0.f, modLfoDummy);

        float modDepth = depthAmt * (1.f + modVal * depthModAmt);
        modDepth = std::max(0.f, std::min(modDepth, 1.f));

        float modSpeed = fSpeed * (1.f + modVal * speedModAmt);
        modSpeed = std::max(0.01f, std::min(modSpeed, 20.f));

        fModDepthMeter = modDepth;
        fModSpeedMeter = modSpeed;

        // All-pass break frequency range: 200Hz–4000Hz swept by LFO
        float freqMin = 200.f;
        float freqRange = 200.f + modDepth * 3800.f;

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

            float lfoOutR;
            float lfoOutL = fLFO.tick(modSpeed, fSr, shape, 0.f, lfoOutR);
            float sweepNormL = lfoOutL * 0.5f + 0.5f;
            float sweepNormR = lfoOutR * 0.5f + 0.5f;

            // All-pass break frequency for this sample
            float fcL = freqMin + freqRange * sweepNormL;
            float fcR = freqMin + freqRange * sweepNormR;
            fcL = std::max(40.f, std::min(fcL, fSr * 0.45f));
            fcR = std::max(40.f, std::min(fcR, fSr * 0.45f));

            // All-pass coefficient: c = (tan(pi*fc/sr) - 1) / (tan(pi*fc/sr) + 1)
            float tanL = std::tan(kPi * fcL / fSr);
            float tanR = std::tan(kPi * fcR / fSr);
            float coeffL = (tanL - 1.f) / (tanL + 1.f);
            float coeffR = (tanR - 1.f) / (tanR + 1.f);

            // Input with feedback
            float fbSignL = invertFB ? -fFeedbackL : fFeedbackL;
            float fbSignR = invertFB ? -fFeedbackR : fFeedbackR;
            float inWithFBL = l + fbSignL * fbAmt;
            float inWithFBR = r + fbSignR * fbAmt;

            // Cascade all-pass stages
            float apOutL = inWithFBL;
            float apOutR = inWithFBR;
            for (int s = 0; s < numStages; ++s) {
                apOutL = fApL[s].process(apOutL, coeffL);
                apOutR = fApR[s].process(apOutR, coeffR);
            }

            fFeedbackL = apOutL;
            fFeedbackR = apOutR;

            float wetL, wetR;
            if (fbOnly) {
                // Feedback type: only the phase-shifted signal
                wetL = apOutL;
                wetR = apOutR;
            } else {
                // Standard: mix phase-shifted signal with dry
                wetL = l + apOutL;
                wetR = r + apOutR;
            }

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

private:
    float fIntensity, fType, fDepth, fSpeed, fShape;
    float fStages, fDepthMod, fSpeedMod, fModRate, fModSource;
    float fSr;
    float fFeedbackL, fFeedbackR;
    float fModDepthMeter = 0.f, fModSpeedMeter = 0.f;

    LFO fLFO;
    LFO fModLFO;
    AllPass fApL[kMaxStages];
    AllPass fApR[kMaxStages];

    DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(H90PhaserPlugin)
};

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

END_NAMESPACE_DISTRHO
endef

# ---------------------------------------------------------------------------
# Makefile
# ---------------------------------------------------------------------------
define H90_PHASER_PLUGIN_MAKEFILE
#!/usr/bin/make -f
NAME      = h90-phaser
FILES_DSP = H90PhaserPlugin.cpp
include ../../Makefile.plugins.mk
TARGETS = lv2_dsp
all: $(TARGETS)
endef

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

<urn:mod-cookbook:h90-phaser>
    a lv2:Plugin ;
    lv2:binary <h90-phaser_dsp.so> ;
    rdfs:seeAlso <h90-phaser.ttl> .
endef

# ---------------------------------------------------------------------------
# LV2 TTL Plugin Configuration with Custom Enumerations Dropdowns
# ---------------------------------------------------------------------------
define H90_PHASER_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:h90-phaser>
    a lv2:Plugin , lv2:PhaserPlugin ;
    doap:name "H90 Phaser" ;
    doap:license <http://opensource.org/licenses/MIT> ;
    doap:maintainer [ foaf:name "Eventide" ] ;
    rdfs:comment "Series of all-pass filters creating sweeping phase notches. Eventide H90 Phaser emulation." ;

    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 "intensity" ; lv2:name "Intensity" ;
        lv2:default 75.0 ; lv2:minimum 0.0 ; lv2:maximum 100.0 ;
        units:unit units:pc
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 5 ; lv2:symbol "type" ; lv2:name "Type" ;
        lv2:default 0 ; lv2:minimum 0 ; lv2:maximum 3 ;
        lv2:portProperty lv2:integer , lv2:enumeration ;
        lv2:scalePoint [ rdfs:label "Positive" ;  rdf:value 0 ] ;
        lv2:scalePoint [ rdfs:label "Negative" ;  rdf:value 1 ] ;
        lv2:scalePoint [ rdfs:label "Feedback" ;  rdf:value 2 ] ;
        lv2:scalePoint [ rdfs:label "Bi-Phase" ;  rdf:value 3 ] ;
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 6 ; lv2:symbol "depth" ; lv2:name "Depth" ;
        lv2:default 60.0 ; lv2:minimum 0.0 ; lv2:maximum 100.0 ;
        units:unit units:pc
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 7 ; lv2:symbol "speed" ; lv2:name "Speed" ;
        lv2:default 0.5 ; lv2:minimum 0.02 ; lv2:maximum 10.0 ;
        units:unit units:hz
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 8 ; lv2:symbol "shape" ; lv2:name "Shape" ;
        lv2:default 0 ; lv2:minimum 0 ; lv2:maximum 7 ;
        lv2:portProperty lv2:integer , lv2:enumeration ;
        lv2:scalePoint [ rdfs:label "Sine" ;         rdf:value 0 ] ;
        lv2:scalePoint [ rdfs:label "Triangle" ;     rdf:value 1 ] ;
        lv2:scalePoint [ rdfs:label "Square" ;       rdf:value 2 ] ;
        lv2:scalePoint [ rdfs:label "Ramp Up" ;      rdf:value 3 ] ;
        lv2:scalePoint [ rdfs:label "Ramp Down" ;    rdf:value 4 ] ;
        lv2:scalePoint [ rdfs:label "S&H" ;          rdf:value 5 ] ;
        lv2:scalePoint [ rdfs:label "Smooth Random" ; rdf:value 6 ] ;
        lv2:scalePoint [ rdfs:label "Peak" ;         rdf:value 7 ] ;
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 9 ; lv2:symbol "stages" ; lv2:name "Stages" ;
        lv2:default 4 ; lv2:minimum 2 ; lv2:maximum 8 ;
        lv2:portProperty lv2:integer , lv2:enumeration ;
        lv2:scalePoint [ rdfs:label "2" ; rdf:value 2 ] ;
        lv2:scalePoint [ rdfs:label "4" ; rdf:value 4 ] ;
        lv2:scalePoint [ rdfs:label "6" ; rdf:value 6 ] ;
        lv2:scalePoint [ rdfs:label "8" ; rdf:value 8 ] ;
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 10 ; lv2:symbol "depth_mod" ; lv2:name "Depth Mod" ;
        lv2:default 0.0 ; lv2:minimum -100.0 ; lv2:maximum 100.0 ;
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 11 ; lv2:symbol "speed_mod" ; lv2:name "Speed Mod" ;
        lv2:default 0.0 ; lv2:minimum -100.0 ; lv2:maximum 100.0 ;
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 12 ; lv2:symbol "mod_rate" ; lv2:name "Mod Rate" ;
        lv2:default 0.3 ; lv2:minimum 0.01 ; lv2:maximum 10.0 ;
        units:unit units:hz
    ] , [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 13 ; lv2:symbol "mod_source" ; lv2:name "Mod Source" ;
        lv2:default 0 ; lv2:minimum 0 ; lv2:maximum 7 ;
        lv2:portProperty lv2:integer , lv2:enumeration ;
        lv2:scalePoint [ rdfs:label "Sine" ;         rdf:value 0 ] ;
        lv2:scalePoint [ rdfs:label "Triangle" ;     rdf:value 1 ] ;
        lv2:scalePoint [ rdfs:label "Square" ;       rdf:value 2 ] ;
        lv2:scalePoint [ rdfs:label "Ramp Up" ;      rdf:value 3 ] ;
        lv2:scalePoint [ rdfs:label "Ramp Down" ;    rdf:value 4 ] ;
        lv2:scalePoint [ rdfs:label "S&H" ;          rdf:value 5 ] ;
        lv2:scalePoint [ rdfs:label "Smooth Random" ; rdf:value 6 ] ;
        lv2:scalePoint [ rdfs:label "Peak" ;         rdf:value 7 ] ;
    ] , [
        a lv2:OutputPort , lv2:ControlPort ;
        lv2:index 14 ; lv2:symbol "mod_depth_meter" ; lv2:name "Mod Depth Meter" ;
        lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0
    ] , [
        a lv2:OutputPort , lv2:ControlPort ;
        lv2:index 15 ; lv2:symbol "mod_speed_meter" ; lv2:name "Mod Speed Meter" ;
        lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 20.0 ;
        units:unit units:hz
    ] .
endef

export H90_PHASER_PLUGIN_CPP H90_PHASER_PLUGIN_INFO_H H90_PHASER_PLUGIN_MAKEFILE
export H90_PHASER_MANIFEST_TTL H90_PHASER_PLUGIN_TTL

# ---------------------------------------------------------------------------
# Buildroot Platform Compilation Commands
# ---------------------------------------------------------------------------
define H90_PHASER_CONFIGURE_CMDS
	mkdir -p $(@D)/examples/h90-phaser
	printf '%s' "$$H90_PHASER_PLUGIN_CPP"      > $(@D)/examples/h90-phaser/H90PhaserPlugin.cpp
	printf '%s' "$$H90_PHASER_PLUGIN_INFO_H"   > $(@D)/examples/h90-phaser/DistrhoPluginInfo.h
	printf '%s' "$$H90_PHASER_PLUGIN_MAKEFILE" > $(@D)/examples/h90-phaser/Makefile
endef

define H90_PHASER_BUILD_CMDS
	$(TARGET_MAKE_ENV) $(TARGET_CONFIGURE_OPTS) $(MAKE) NOOPT=true -C $(@D)/examples/h90-phaser lv2_dsp
endef

define H90_PHASER_INSTALL_TARGET_CMDS
	mkdir -p $($(PKG)_PKGDIR)/h90-phaser.lv2
	cp $(@D)/bin/h90-phaser.lv2/h90-phaser_dsp.so $($(PKG)_PKGDIR)/h90-phaser.lv2/
	printf '%s' "$$H90_PHASER_MANIFEST_TTL" > $($(PKG)_PKGDIR)/h90-phaser.lv2/manifest.ttl
	printf '%s' "$$H90_PHASER_PLUGIN_TTL" > $($(PKG)_PKGDIR)/h90-phaser.lv2/h90-phaser.ttl
endef

$(eval $(generic-package))
