################################################################################
#
# BIGMUFF_OPAMP
#
# LV2 plugin built from the MOD Audio Cookbook workflow: DPF source is embedded
# below and compiled against a pinned DPF checkout, producing an .lv2 bundle
# that the builder.mod.audio cloud builder can compile and package for the
# MOD Dwarf.
#
################################################################################

BIGMUFF_OPAMP_VERSION = 61d38eb638449647fb8395a35c5b8dab7e981ba7
BIGMUFF_OPAMP_SITE = https://github.com/DISTRHO/DPF.git
BIGMUFF_OPAMP_SITE_METHOD = git
BIGMUFF_OPAMP_GIT_SUBMODULES = YES
BIGMUFF_OPAMP_INSTALL_STAGING = NO
BIGMUFF_OPAMP_BUNDLES = bigmuff-opamp.lv2

define BIGMUFF_OPAMP_PLUGIN_CPP
#include "DistrhoPlugin.hpp"
#include <cmath>

// ---- Shared Big Muff DSP core (identical across all variant plugins) ----
#include <cmath>

// One-pole highpass (models an input/interstage coupling capacitor)
class OnePoleHP {
public:
    void setup(float sr, float fc) {
        const float x = expf(-2.0f * (float)M_PI * fc / sr);
        a1 = x; b0 = (1.0f + x) * 0.5f; b1 = -(1.0f + x) * 0.5f;
    }
    inline float process(float in) {
        const float out = b0 * in + b1 * xz1 + a1 * yz1;
        xz1 = in; yz1 = out;
        return out;
    }
    void reset() { xz1 = 0.f; yz1 = 0.f; }
private:
    float a1 = 0.f, b0 = 1.f, b1 = 0.f;
    float xz1 = 0.f, yz1 = 0.f;
};

// One-pole lowpass (models a Miller / feedback capacitor limiting stage bandwidth)
class OnePoleLP {
public:
    void setup(float sr, float fc) {
        a = 1.0f - expf(-2.0f * (float)M_PI * fc / sr);
    }
    inline float process(float in) {
        yz1 += a * (in - yz1);
        return yz1;
    }
    void reset() { yz1 = 0.f; }
private:
    float a = 1.f;
    float yz1 = 0.f;
};

// Simple one-pole peaking (parametric) filter used for the Deluxe MIDS EQ section
class PeakingEQ {
public:
    void setup(float sr, float fc, float gainDb, float q) {
        const float A = powf(10.0f, gainDb / 40.0f);
        const float w0 = 2.0f * (float)M_PI * fc / sr;
        const float alpha = sinf(w0) / (2.0f * q);
        const float cosw0 = cosf(w0);
        const float b0 =  1.0f + alpha * A;
        const float b1 = -2.0f * cosw0;
        const float b2 =  1.0f - alpha * A;
        const float a0 =  1.0f + alpha / A;
        const float a1 = -2.0f * cosw0;
        const float a2 =  1.0f - alpha / A;
        nb0 = b0 / a0; nb1 = b1 / a0; nb2 = b2 / a0;
        na1 = a1 / a0; na2 = a2 / a0;
    }
    inline float process(float in) {
        const float out = nb0 * in + nb1 * x1 + nb2 * x2 - na1 * y1 - na2 * y2;
        x2 = x1; x1 = in; y2 = y1; y1 = out;
        return out;
    }
    void reset() { x1 = x2 = y1 = y2 = 0.f; }
private:
    float nb0 = 1.f, nb1 = 0.f, nb2 = 0.f, na1 = 0.f, na2 = 0.f;
    float x1 = 0.f, x2 = 0.f, y1 = 0.f, y2 = 0.f;
};

// Soft clipper approximating a back-to-back diode pair to ground in the transistor's
// feedback loop. vfPos/vfNeg set how much drive is needed before the knee saturates
// (silicon ~0.6V needs more drive than germanium ~0.3V), and hardness sets the knee
// steepness. The output always saturates toward +/-1 (full scale) once driven hard,
// matching how a real clipping stage is run well past its threshold in normal use.
static inline float diodeClip(float x, float vfPos, float vfNeg, float hardness) {
    const float xp = x >= 0.f ? x : -x;
    const float vf = x >= 0.f ? vfPos : vfNeg;
    const float driven = xp * hardness / vf;
    const float shaped = tanhf(driven);
    return x >= 0.f ? shaped : -shaped;
}

// One transistor common-emitter clipping stage: gain -> bandwidth-limit -> diode clip
class ClipStage {
public:
    void setup(float sr, float fcLowpass) { lp.setup(sr, fcLowpass); }
    inline float process(float in, float gain, float vfPos, float vfNeg, float hardness) {
        const float driven = lp.process(in * gain);
        return diodeClip(driven, vfPos, vfNeg, hardness);
    }
    void reset() { lp.reset(); }
private:
    OnePoleLP lp;
};

// The classic Big Muff passive "scoop" tone stack: crossfade between a lowpass path
// (bass side of the pot) and a highpass path (treble side), with scoopDepth controlling
// how deep the mid notch is (a shallower depth blends some of the dry mid content back in,
// matching the "less scooped" character of e.g. Civil War / op-amp variants).
class ToneStack {
public:
    void setup(float sr, float fcLow, float fcHigh) {
        lp.setup(sr, fcLow);
        hp.setup(sr, fcHigh);
    }
    inline float process(float in, float tonePot, float scoopDepth) {
        const float lo = lp.process(in);
        const float hi = hp.process(in);
        const float scooped = tonePot * hi + (1.0f - tonePot) * lo;
        return scoopDepth * scooped + (1.0f - scoopDepth) * in;
    }
    void reset() { lp.reset(); hp.reset(); }
private:
    OnePoleLP lp;
    OnePoleHP hp;
};

// Simple RMS-ish envelope follower, used for noise gates and the Deluxe Attack circuit
class EnvFollower {
public:
    void setup(float sr, float attackMs, float releaseMs) {
        aCoef = expf(-1.0f / (0.001f * attackMs * sr));
        rCoef = expf(-1.0f / (0.001f * releaseMs * sr));
    }
    inline float process(float in) {
        const float rectified = in >= 0.f ? in : -in;
        const float coef = rectified > env ? aCoef : rCoef;
        env = coef * env + (1.0f - coef) * rectified;
        return env;
    }
    void reset() { env = 0.f; }
private:
    float aCoef = 0.f, rCoef = 0.f, env = 0.f;
};

START_NAMESPACE_DISTRHO

// Circuit: Big Muff Pi - Op-Amp/IC (V3, 1978-80)
// IC-based clipping stages; more cutting mids, less compression, harder clip knee.
class BigMuffOpAmp : public Plugin {
public:
    BigMuffOpAmp()
        : Plugin(3, 0, 0)
    {
        sampleRateChanged(getSampleRate());
    }

protected:
    const char* getLabel() const override { return "BMOpAmp"; }
    const char* getDescription() const override { return "Big Muff Pi - Op-Amp/IC (V3, 1978-80) - MOD Audio Cookbook"; }
    const char* getMaker() const override { return "MODAudioCookbook"; }
    const char* getLicense() const override { return "ISC"; }
    uint32_t getVersion() const override { return d_version(1, 0, 0); }
    int64_t getUniqueId() const override { return d_cconst('B','M','3','1'); }

    void initParameter(uint32_t index, Parameter& parameter) override {
        switch (index) {
        case 0:
            parameter.hints  = kParameterIsAutomatable;
            parameter.name   = "Sustain";
            parameter.symbol = "sustain";
            parameter.ranges.def = 0.5f; parameter.ranges.min = 0.0f; parameter.ranges.max = 1.0f;
            break;
        case 1:
            parameter.hints  = kParameterIsAutomatable;
            parameter.name   = "Tone";
            parameter.symbol = "tone";
            parameter.ranges.def = 0.5f; parameter.ranges.min = 0.0f; parameter.ranges.max = 1.0f;
            break;
        case 2:
            parameter.hints  = kParameterIsAutomatable;
            parameter.name   = "Volume";
            parameter.symbol = "volume";
            parameter.ranges.def = 0.7f; parameter.ranges.min = 0.0f; parameter.ranges.max = 1.0f;
            break;
        }
    }

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

    void sampleRateChanged(double newSampleRate) override {
        const float sr = (float)newSampleRate;
        hpIn.setup(sr, 60.0f);
        clip1.setup(sr, 9500.0f);
        clip2.setup(sr, 9200.0f);
        tone.setup(sr, 220.0f, 1400.0f);
        lpOut.setup(sr, 10000.0f);
    }

    void activate() override {
        hpIn.reset(); clip1.reset(); clip2.reset(); tone.reset(); lpOut.reset();
    }

    void run(const float** inputs, float** outputs, uint32_t frames) override {
        const float sustain = params[0];
        const float toneP   = params[1];
        const float volume  = params[2];
        const float gain1 = 3.6f + sustain * 9.0f;
        const float gain2 = 3.8f + sustain * 9.5f;

        for (uint32_t i = 0; i < frames; ++i) {
            float x = hpIn.process(inputs[0][i]);
            float s1 = clip1.process(x, gain1, 0.6f, 0.6f * 1.0f, 2.4f);
            float s2 = clip2.process(s1, gain2, 0.6f, 0.6f * 1.0f, 2.4f);
            float toned = tone.process(s2, toneP, 0.55f);
            float out = lpOut.process(toned * volume * 1.1f);
            outputs[0][i] = out;
        }
    }

private:
    float params[3] = {0.5f, 0.5f, 0.7f};
    OnePoleHP hpIn;
    ClipStage clip1, clip2;
    ToneStack tone;
    OnePoleLP lpOut;

    BigMuffOpAmp(const BigMuffOpAmp&) = delete;
    BigMuffOpAmp& operator=(const BigMuffOpAmp&) = delete;
};

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

END_NAMESPACE_DISTRHO

endef
export BIGMUFF_OPAMP_PLUGIN_CPP

define BIGMUFF_OPAMP_PLUGIN_INFO_H
#ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED
#define DISTRHO_PLUGIN_INFO_H_INCLUDED

#define DISTRHO_PLUGIN_BRAND   "MODAudioCookbook"
#define DISTRHO_PLUGIN_NAME    "Big Muff Pi - Op-Amp/IC (V3, 1978-80)"
#define DISTRHO_PLUGIN_URI     "https://mod.audio/cookbook/bigmuff/bigmuff-opamp"
#define DISTRHO_PLUGIN_NUM_INPUTS  1
#define DISTRHO_PLUGIN_NUM_OUTPUTS 1
#define DISTRHO_PLUGIN_IS_RT_SAFE  1
#define DISTRHO_PLUGIN_WANT_STATE 0
#define DISTRHO_PLUGIN_HAS_UI     0

#endif

endef
export BIGMUFF_OPAMP_PLUGIN_INFO_H

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

<https://mod.audio/cookbook/bigmuff/bigmuff-opamp>
    a lv2:Plugin , lv2:DistortionPlugin ;
    doap:name "Big Muff Pi - Op-Amp/IC (V3, 1978-80)" ;
    doap:license <http://opensource.org/licenses/isc> ;
    lv2:project <https://mod.audio/cookbook/bigmuff/bigmuff-opamp> ;
    lv2:optionalFeature lv2:hardRTCapable ;
    lv2:port
    [
        a lv2:AudioPort , lv2:InputPort ;
        lv2:index 0 ;
        lv2:symbol "in" ;
        lv2:name "In" ;
    ] ,
    [
        a lv2:AudioPort , lv2:OutputPort ;
        lv2:index 1 ;
        lv2:symbol "out" ;
        lv2:name "Out" ;
    ] ,
    [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 2 ;
        lv2:symbol "sustain" ;
        lv2:name "Sustain" ;
        lv2:default 0.5 ;
        lv2:minimum 0.0 ;
        lv2:maximum 1.0 ;
    ] ,
    [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 3 ;
        lv2:symbol "tone" ;
        lv2:name "Tone" ;
        lv2:default 0.5 ;
        lv2:minimum 0.0 ;
        lv2:maximum 1.0 ;
    ] ,
    [
        a lv2:InputPort , lv2:ControlPort ;
        lv2:index 4 ;
        lv2:symbol "volume" ;
        lv2:name "Volume" ;
        lv2:default 0.7 ;
        lv2:minimum 0.0 ;
        lv2:maximum 1.0 ;
    ] .

endef
export BIGMUFF_OPAMP_PLUGIN_TTL

define BIGMUFF_OPAMP_MANIFEST_TTL
@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://mod.audio/cookbook/bigmuff/bigmuff-opamp>
    a lv2:Plugin ;
    lv2:binary <bigmuff-opamp_dsp.so> ;
    rdfs:seeAlso <bigmuff-opamp.ttl> .

<https://mod.audio/cookbook/bigmuff/bigmuff-opamp#preset-clean-boost>
    a pset:Preset ;
    lv2:appliesTo <https://mod.audio/cookbook/bigmuff/bigmuff-opamp> ;
    rdfs:label "Clean Boost" ;
    rdfs:seeAlso <presets/clean-boost.ttl> .

<https://mod.audio/cookbook/bigmuff/bigmuff-opamp#preset-classic-muff>
    a pset:Preset ;
    lv2:appliesTo <https://mod.audio/cookbook/bigmuff/bigmuff-opamp> ;
    rdfs:label "Classic Muff" ;
    rdfs:seeAlso <presets/classic-muff.ttl> .

<https://mod.audio/cookbook/bigmuff/bigmuff-opamp#preset-full-sustain>
    a pset:Preset ;
    lv2:appliesTo <https://mod.audio/cookbook/bigmuff/bigmuff-opamp> ;
    rdfs:label "Full Sustain" ;
    rdfs:seeAlso <presets/full-sustain.ttl> .

<https://mod.audio/cookbook/bigmuff/bigmuff-opamp#preset-scooped-lead>
    a pset:Preset ;
    lv2:appliesTo <https://mod.audio/cookbook/bigmuff/bigmuff-opamp> ;
    rdfs:label "Scooped Lead" ;
    rdfs:seeAlso <presets/scooped-lead.ttl> .

<https://mod.audio/cookbook/bigmuff/bigmuff-opamp#preset-bright-rasp>
    a pset:Preset ;
    lv2:appliesTo <https://mod.audio/cookbook/bigmuff/bigmuff-opamp> ;
    rdfs:label "Bright Rasp" ;
    rdfs:seeAlso <presets/bright-rasp.ttl> .

endef
export BIGMUFF_OPAMP_MANIFEST_TTL

define BIGMUFF_OPAMP_PLUGIN_MAKEFILE
#!/usr/bin/make -f
# Makefile for bigmuff-opamp (MOD Audio Cookbook / DPF) #
# ------------------------------------------------ #

include ../../Makefile.base.mk

NAME = bigmuff-opamp

all: lv2_dsp

BUILD_DIR = ../../build/bigmuff-opamp

FILES_DSP = \
	DistrhoPluginBigMuffOpAmp.cpp

include ../../Makefile.plugins.mk

TARGETS += lv2_dsp

all: $(TARGETS)

endef
export BIGMUFF_OPAMP_PLUGIN_MAKEFILE

define BIGMUFF_OPAMP_PRESET_CLEAN_BOOST_TTL
@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://mod.audio/cookbook/bigmuff/bigmuff-opamp#preset-clean-boost>
    a pset:Preset ;
    lv2:appliesTo <https://mod.audio/cookbook/bigmuff/bigmuff-opamp> ;
    rdfs:label "Clean Boost" ;
    lv2:port
        [ lv2:symbol "sustain" ; pset:value 0.15 ] ,
        [ lv2:symbol "tone" ; pset:value 0.55 ] ,
        [ lv2:symbol "volume" ; pset:value 0.55 ] .

endef
export BIGMUFF_OPAMP_PRESET_CLEAN_BOOST_TTL

define BIGMUFF_OPAMP_PRESET_CLASSIC_MUFF_TTL
@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://mod.audio/cookbook/bigmuff/bigmuff-opamp#preset-classic-muff>
    a pset:Preset ;
    lv2:appliesTo <https://mod.audio/cookbook/bigmuff/bigmuff-opamp> ;
    rdfs:label "Classic Muff" ;
    lv2:port
        [ lv2:symbol "sustain" ; pset:value 0.5 ] ,
        [ lv2:symbol "tone" ; pset:value 0.5 ] ,
        [ lv2:symbol "volume" ; pset:value 0.7 ] .

endef
export BIGMUFF_OPAMP_PRESET_CLASSIC_MUFF_TTL

define BIGMUFF_OPAMP_PRESET_FULL_SUSTAIN_TTL
@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://mod.audio/cookbook/bigmuff/bigmuff-opamp#preset-full-sustain>
    a pset:Preset ;
    lv2:appliesTo <https://mod.audio/cookbook/bigmuff/bigmuff-opamp> ;
    rdfs:label "Full Sustain" ;
    lv2:port
        [ lv2:symbol "sustain" ; pset:value 0.95 ] ,
        [ lv2:symbol "tone" ; pset:value 0.45 ] ,
        [ lv2:symbol "volume" ; pset:value 0.8 ] .

endef
export BIGMUFF_OPAMP_PRESET_FULL_SUSTAIN_TTL

define BIGMUFF_OPAMP_PRESET_SCOOPED_LEAD_TTL
@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://mod.audio/cookbook/bigmuff/bigmuff-opamp#preset-scooped-lead>
    a pset:Preset ;
    lv2:appliesTo <https://mod.audio/cookbook/bigmuff/bigmuff-opamp> ;
    rdfs:label "Scooped Lead" ;
    lv2:port
        [ lv2:symbol "sustain" ; pset:value 0.75 ] ,
        [ lv2:symbol "tone" ; pset:value 0.5 ] ,
        [ lv2:symbol "volume" ; pset:value 0.75 ] .

endef
export BIGMUFF_OPAMP_PRESET_SCOOPED_LEAD_TTL

define BIGMUFF_OPAMP_PRESET_BRIGHT_RASP_TTL
@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://mod.audio/cookbook/bigmuff/bigmuff-opamp#preset-bright-rasp>
    a pset:Preset ;
    lv2:appliesTo <https://mod.audio/cookbook/bigmuff/bigmuff-opamp> ;
    rdfs:label "Bright Rasp" ;
    lv2:port
        [ lv2:symbol "sustain" ; pset:value 0.8 ] ,
        [ lv2:symbol "tone" ; pset:value 0.9 ] ,
        [ lv2:symbol "volume" ; pset:value 0.65 ] .

endef
export BIGMUFF_OPAMP_PRESET_BRIGHT_RASP_TTL


define BIGMUFF_OPAMP_CONFIGURE_CMDS
	mkdir -p $(@D)/examples/bigmuff-opamp
	mkdir -p $(@D)/examples/bigmuff-opamp/presets
	printf '%s' "$$BIGMUFF_OPAMP_PLUGIN_CPP" > $(@D)/examples/bigmuff-opamp/DistrhoPluginBigMuffOpAmp.cpp
	printf '%s' "$$BIGMUFF_OPAMP_PLUGIN_INFO_H" > $(@D)/examples/bigmuff-opamp/DistrhoPluginInfo.h
	printf '%s' "$$BIGMUFF_OPAMP_PLUGIN_TTL" > $(@D)/examples/bigmuff-opamp/bigmuff-opamp.ttl
	printf '%s' "$$BIGMUFF_OPAMP_MANIFEST_TTL" > $(@D)/examples/bigmuff-opamp/manifest.ttl
	printf '%s' "$$BIGMUFF_OPAMP_PLUGIN_MAKEFILE" > $(@D)/examples/bigmuff-opamp/Makefile
	printf '%s' "$$BIGMUFF_OPAMP_PRESET_CLEAN_BOOST_TTL" > $(@D)/examples/bigmuff-opamp/presets/clean-boost.ttl
	printf '%s' "$$BIGMUFF_OPAMP_PRESET_CLASSIC_MUFF_TTL" > $(@D)/examples/bigmuff-opamp/presets/classic-muff.ttl
	printf '%s' "$$BIGMUFF_OPAMP_PRESET_FULL_SUSTAIN_TTL" > $(@D)/examples/bigmuff-opamp/presets/full-sustain.ttl
	printf '%s' "$$BIGMUFF_OPAMP_PRESET_SCOOPED_LEAD_TTL" > $(@D)/examples/bigmuff-opamp/presets/scooped-lead.ttl
	printf '%s' "$$BIGMUFF_OPAMP_PRESET_BRIGHT_RASP_TTL" > $(@D)/examples/bigmuff-opamp/presets/bright-rasp.ttl
endef

define BIGMUFF_OPAMP_BUILD_CMDS
	$(TARGET_MAKE_ENV) $(TARGET_CONFIGURE_OPTS) $(MAKE) NOOPT=true -C $(@D)/examples/bigmuff-opamp lv2_dsp
endef

define BIGMUFF_OPAMP_INSTALL_TARGET_CMDS
	cp $(@D)/examples/bigmuff-opamp/manifest.ttl $(@D)/bin/bigmuff-opamp.lv2/manifest.ttl
	cp $(@D)/examples/bigmuff-opamp/bigmuff-opamp.ttl $(@D)/bin/bigmuff-opamp.lv2/bigmuff-opamp.ttl
	mkdir -p $(@D)/bin/bigmuff-opamp.lv2/presets
	cp $(@D)/examples/bigmuff-opamp/presets/clean-boost.ttl $(@D)/bin/bigmuff-opamp.lv2/presets/clean-boost.ttl
	cp $(@D)/examples/bigmuff-opamp/presets/classic-muff.ttl $(@D)/bin/bigmuff-opamp.lv2/presets/classic-muff.ttl
	cp $(@D)/examples/bigmuff-opamp/presets/full-sustain.ttl $(@D)/bin/bigmuff-opamp.lv2/presets/full-sustain.ttl
	cp $(@D)/examples/bigmuff-opamp/presets/scooped-lead.ttl $(@D)/bin/bigmuff-opamp.lv2/presets/scooped-lead.ttl
	cp $(@D)/examples/bigmuff-opamp/presets/bright-rasp.ttl $(@D)/bin/bigmuff-opamp.lv2/presets/bright-rasp.ttl
	mkdir -p $(TARGET_DIR)/usr/lib/lv2
	cp -r $(@D)/bin/bigmuff-opamp.lv2 $(TARGET_DIR)/usr/lib/lv2/
endef

$(eval $(generic-package))
