# Eventide H9 Resonator - tuned resonant comb filters
# Upload at https://builder.mod.audio/buildroot

H9_RESONATOR_VERSION = 61d38eb638449647fb8395a35c5b8dab7e981ba7
H9_RESONATOR_SITE = https://github.com/DISTRHO/DPF.git
H9_RESONATOR_SITE_METHOD = git
H9_RESONATOR_BUNDLES = h9-resonator.lv2

define H9_RESONATOR_PLUGIN_CPP
#include "DistrhoPlugin.hpp"
#include <cmath>
#include <cstring>

START_NAMESPACE_DISTRHO

static const float MIDI_A4=440.f;
static inline float noteToHz(float n){return MIDI_A4*powf(2.f,(n-69.f)/12.f);}

// Tuned resonant comb filter
struct TunedComb {
    float buf[65536];
    int   wp;
    float fb,lz;
    TunedComb():wp(0),fb(0.9f),lz(0.f){memset(buf,0,sizeof(buf));}
    void reset(){memset(buf,0,sizeof(buf));wp=0;lz=0.f;}
    float tick(float in,float freqHz,float sr,float damp){
        int len=(int)(sr/freqHz);
        if(len<2)len=2; if(len>65534)len=65534;
        int rp=(wp-len)&65535;
        float d=buf[rp];
        lz=d*(1.f-damp)+lz*damp;
        buf[wp&65535]=in+lz*fb;
        wp++;
        return d;
    }
};

struct P1{float z;P1():z(0.f){}void reset(){z=0.f;}
    float lp(float x,float a){return z=x*(1.f-a)+z*a;}};

class H9ResonatorPlugin : public Plugin {
public:
    H9ResonatorPlugin():Plugin(kParameterCount,0,0),
      fSR(44100.f),
      pNote1(60.f),pNote2(64.f),pNote3(67.f),pNote4(72.f),
      pLevel1(0.8f),pLevel2(0.8f),pLevel3(0.8f),pLevel4(0.8f),
      pDecay(0.7f),pDamp(0.3f),pMix(0.5f)
    {}
protected:
    const char* getLabel()const override{return"H9Resonator";}
    const char* getDescription()const override{return"Eventide H9 Resonator: four tuned resonant comb filters. Plucked strings, bells, metallic resonance.";}
    const char* getMaker()const override{return"MOD Cookbook";}
    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','R','S');}

    void initParameter(uint32_t i,Parameter&p)override{
        switch(i){
        case kNote1: p.hints=kParameterIsAutomatable;p.name="Note 1";p.symbol="note1";
            p.ranges.def=60.f;p.ranges.min=24.f;p.ranges.max=96.f;break;
        case kNote2: p.hints=kParameterIsAutomatable;p.name="Note 2";p.symbol="note2";
            p.ranges.def=64.f;p.ranges.min=24.f;p.ranges.max=96.f;break;
        case kNote3: p.hints=kParameterIsAutomatable;p.name="Note 3";p.symbol="note3";
            p.ranges.def=67.f;p.ranges.min=24.f;p.ranges.max=96.f;break;
        case kNote4: p.hints=kParameterIsAutomatable;p.name="Note 4";p.symbol="note4";
            p.ranges.def=72.f;p.ranges.min=24.f;p.ranges.max=96.f;break;
        case kLevel1: p.hints=kParameterIsAutomatable;p.name="Level 1";p.symbol="level1";
            p.ranges.def=0.8f;p.ranges.min=0.f;p.ranges.max=1.f;break;
        case kLevel2: p.hints=kParameterIsAutomatable;p.name="Level 2";p.symbol="level2";
            p.ranges.def=0.8f;p.ranges.min=0.f;p.ranges.max=1.f;break;
        case kLevel3: p.hints=kParameterIsAutomatable;p.name="Level 3";p.symbol="level3";
            p.ranges.def=0.8f;p.ranges.min=0.f;p.ranges.max=1.f;break;
        case kLevel4: p.hints=kParameterIsAutomatable;p.name="Level 4";p.symbol="level4";
            p.ranges.def=0.8f;p.ranges.min=0.f;p.ranges.max=1.f;break;
        case kDecay: p.hints=kParameterIsAutomatable;p.name="Decay";p.symbol="decay";
            p.ranges.def=0.7f;p.ranges.min=0.f;p.ranges.max=1.f;break;
        case kDamp: p.hints=kParameterIsAutomatable;p.name="Damp";p.symbol="damp";
            p.ranges.def=0.3f;p.ranges.min=0.f;p.ranges.max=1.f;break;
        case kMix: p.hints=kParameterIsAutomatable;p.name="Mix";p.symbol="mix";
            p.ranges.def=0.5f;p.ranges.min=0.f;p.ranges.max=1.f;break;
        default:break;}
    }
    float getParameterValue(uint32_t i)const override{
        switch(i){
        case kNote1:return pNote1;case kNote2:return pNote2;
        case kNote3:return pNote3;case kNote4:return pNote4;
        case kLevel1:return pLevel1;case kLevel2:return pLevel2;
        case kLevel3:return pLevel3;case kLevel4:return pLevel4;
        case kDecay:return pDecay;case kDamp:return pDamp;
        case kMix:return pMix;default:return 0.f;}
    }
    void setParameterValue(uint32_t i,float v)override{
        switch(i){
        case kNote1:pNote1=v;break;case kNote2:pNote2=v;break;
        case kNote3:pNote3=v;break;case kNote4:pNote4=v;break;
        case kLevel1:pLevel1=v;break;case kLevel2:pLevel2=v;break;
        case kLevel3:pLevel3=v;break;case kLevel4:pLevel4=v;break;
        case kDecay:pDecay=v;updateFb();break;
        case kDamp:pDamp=v;break;case kMix:pMix=v;break;default:break;}
    }
    void activate()override{
        fSR=(float)getSampleRate();
        for(int i=0;i<4;++i){cL[i].reset();cR[i].reset();}
        updateFb();
    }
    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 notes[4]={pNote1,pNote2,pNote3,pNote4};
        float levels[4]={pLevel1,pLevel2,pLevel3,pLevel4};
        for(uint32_t i=0;i<frames;++i){
            float in=(inL[i]+inR[i])*0.5f;
            float sumL=0.f,sumR=0.f;
            for(int c=0;c<4;++c){
                float hz=noteToHz(notes[c]);
                // Slight stereo detune for width
                float rL=cL[c].tick(in,hz,fSR,pDamp*0.8f)*levels[c];
                float rR=cR[c].tick(in,hz*1.002f,fSR,pDamp*0.8f)*levels[c];
                // Odd resonators go left, even go right (like MuRF)
                sumL+=rL; sumR+=rR;
            }
            sumL*=0.25f; sumR*=0.25f;
            outL[i]=inL[i]*(1.f-pMix)+sumL*pMix;
            outR[i]=inR[i]*(1.f-pMix)+sumR*pMix;
        }
    }
private:
    void updateFb(){float fb=0.80f+pDecay*0.18f;for(int i=0;i<4;++i){cL[i].fb=fb;cR[i].fb=fb;}}
    float fSR;
    float pNote1,pNote2,pNote3,pNote4;
    float pLevel1,pLevel2,pLevel3,pLevel4;
    float pDecay,pDamp,pMix;
    TunedComb cL[4],cR[4];
    enum Parameters{kNote1=0,kNote2,kNote3,kNote4,
        kLevel1,kLevel2,kLevel3,kLevel4,
        kDecay,kDamp,kMix,kParameterCount};
    DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(H9ResonatorPlugin)
};
Plugin*createPlugin(){return new H9ResonatorPlugin();}
END_NAMESPACE_DISTRHO
endef

define H9_RESONATOR_PLUGIN_INFO_H
#ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED
#define DISTRHO_PLUGIN_INFO_H_INCLUDED
#define DISTRHO_PLUGIN_BRAND "MOD Cookbook"
#define DISTRHO_PLUGIN_NAME  "H9 Resonator"
#define DISTRHO_PLUGIN_URI   "urn:mod-cookbook:h9-resonator"
#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{kNote1=0,kNote2,kNote3,kNote4,
    kLevel1,kLevel2,kLevel3,kLevel4,
    kDecay,kDamp,kMix,kParameterCount};
#endif
endef

define H9_RESONATOR_PLUGIN_MAKEFILE
#!/usr/bin/make -f
NAME = h9-resonator
FILES_DSP = H9ResonatorPlugin.cpp
include ../../Makefile.plugins.mk
TARGETS = lv2_dsp
all: $(TARGETS)
endef

define H9_RESONATOR_MANIFEST_TTL
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<urn:mod-cookbook:h9-resonator>
    a lv2:Plugin,lv2:FilterPlugin;
    lv2:binary <h9-resonator_dsp.so>;
    rdfs:seeAlso <h9-resonator.ttl>.
endef

define H9_RESONATOR_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 rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<urn:mod-cookbook:h9-resonator>
    a lv2:Plugin,lv2:FilterPlugin;
    doap:name "H9 Resonator";
    doap:license <http://opensource.org/licenses/MIT>;
    doap:maintainer [foaf:name "MOD Cookbook";foaf:homepage <https://mod.audio>];
    rdfs:comment "Eventide H9 Resonator: four tuned resonant comb filters for plucked strings, bells and metallic resonance.";
    lv2:port [
        a lv2:InputPort,lv2:AudioPort;lv2:index 0;lv2:symbol "in_l";lv2:name "Audio In L"
    ],[a lv2:InputPort,lv2:AudioPort;lv2:index 1;lv2:symbol "in_r";lv2:name "Audio In R"
    ],[a lv2:OutputPort,lv2:AudioPort;lv2:index 2;lv2:symbol "out_l";lv2:name "Audio Out L"
    ],[a lv2:OutputPort,lv2:AudioPort;lv2:index 3;lv2:symbol "out_r";lv2:name "Audio Out R"
    ],[a lv2:InputPort,lv2:ControlPort;lv2:index 4;lv2:symbol "note1";lv2:name "Note 1";
        lv2:default 60.0;lv2:minimum 24.0;lv2:maximum 96.0
    ],[a lv2:InputPort,lv2:ControlPort;lv2:index 5;lv2:symbol "note2";lv2:name "Note 2";
        lv2:default 64.0;lv2:minimum 24.0;lv2:maximum 96.0
    ],[a lv2:InputPort,lv2:ControlPort;lv2:index 6;lv2:symbol "note3";lv2:name "Note 3";
        lv2:default 67.0;lv2:minimum 24.0;lv2:maximum 96.0
    ],[a lv2:InputPort,lv2:ControlPort;lv2:index 7;lv2:symbol "note4";lv2:name "Note 4";
        lv2:default 72.0;lv2:minimum 24.0;lv2:maximum 96.0
    ],[a lv2:InputPort,lv2:ControlPort;lv2:index 8;lv2:symbol "level1";lv2:name "Level 1";
        lv2:default 0.8;lv2:minimum 0.0;lv2:maximum 1.0
    ],[a lv2:InputPort,lv2:ControlPort;lv2:index 9;lv2:symbol "level2";lv2:name "Level 2";
        lv2:default 0.8;lv2:minimum 0.0;lv2:maximum 1.0
    ],[a lv2:InputPort,lv2:ControlPort;lv2:index 10;lv2:symbol "level3";lv2:name "Level 3";
        lv2:default 0.8;lv2:minimum 0.0;lv2:maximum 1.0
    ],[a lv2:InputPort,lv2:ControlPort;lv2:index 11;lv2:symbol "level4";lv2:name "Level 4";
        lv2:default 0.8;lv2:minimum 0.0;lv2:maximum 1.0
    ],[a lv2:InputPort,lv2:ControlPort;lv2:index 12;lv2:symbol "decay";lv2:name "Decay";
        lv2:default 0.7;lv2:minimum 0.0;lv2:maximum 1.0
    ],[a lv2:InputPort,lv2:ControlPort;lv2:index 13;lv2:symbol "damp";lv2:name "Damp";
        lv2:default 0.3;lv2:minimum 0.0;lv2:maximum 1.0
    ],[a lv2:InputPort,lv2:ControlPort;lv2:index 14;lv2:symbol "mix";lv2:name "Mix";
        lv2:default 0.5;lv2:minimum 0.0;lv2:maximum 1.0
    ].
endef

export H9_RESONATOR_PLUGIN_CPP H9_RESONATOR_PLUGIN_INFO_H H9_RESONATOR_PLUGIN_MAKEFILE
export H9_RESONATOR_MANIFEST_TTL H9_RESONATOR_PLUGIN_TTL

define H9_RESONATOR_CONFIGURE_CMDS
	mkdir -p $(@D)/examples/h9-resonator
	printf '%s' "$$H9_RESONATOR_PLUGIN_CPP"      > $(@D)/examples/h9-resonator/H9ResonatorPlugin.cpp
	printf '%s' "$$H9_RESONATOR_PLUGIN_INFO_H"   > $(@D)/examples/h9-resonator/DistrhoPluginInfo.h
	printf '%s' "$$H9_RESONATOR_PLUGIN_MAKEFILE" > $(@D)/examples/h9-resonator/Makefile
endef
define H9_RESONATOR_BUILD_CMDS
	$(TARGET_MAKE_ENV) $(TARGET_CONFIGURE_OPTS) $(MAKE) NOOPT=true -C $(@D)/examples/h9-resonator lv2_dsp
endef
define H9_RESONATOR_INSTALL_TARGET_CMDS
	mkdir -p $($(PKG)_PKGDIR)/h9-resonator.lv2
	cp $(@D)/bin/h9-resonator.lv2/h9-resonator_dsp.so $($(PKG)_PKGDIR)/h9-resonator.lv2/
	printf '%s' "$$H9_RESONATOR_MANIFEST_TTL" > $($(PKG)_PKGDIR)/h9-resonator.lv2/manifest.ttl
	printf '%s' "$$H9_RESONATOR_PLUGIN_TTL"   > $($(PKG)_PKGDIR)/h9-resonator.lv2/h9-resonator.ttl
endef
$(eval $(generic-package))
