################################################################################
#
# ZOOM-DIRTYGATE (Zoom MS-70CDR Gated Glitch Synth Fuzz for MOD Dwarf)
# Standalone True Stereo Industrial Noise Gate Synth Specification
#
################################################################################

ZOOM_DIRTYGATE_VERSION = 1.0.0
ZOOM_DIRTYGATE_SITE_METHOD = local
ZOOM_DIRTYGATE_SITE = $(_ZOOM_DIRTYGATE_SITE_ENV)
ZOOM_DIRTYGATE_SOURCE =

ZOOM_DIRTYGATE_BUNDLES = zoom-dirtygate.lv2

define ZOOM_DIRTYGATE_CPP_DATA
#include <lv2/core/lv2.h>
#include <cmath>
#include <stdint.h>
#include <stdlib.h>

#define DIRTYGATE_URI "https://modaudio.com/plugins/zoom-dirtygate"

enum Ports {
    GATE_INPUT_L  = 0,
    GATE_INPUT_R  = 1,
    GATE_OUTPUT_L = 2,
    GATE_OUTPUT_R = 3,
    GATE_THRESH   = 4,
    GATE_GLITCH   = 5,
    GATE_MIX      = 6
};

typedef struct {
    const float* input_l;
    const float* input_r;
    float* output_l;
    float* output_r;
    const float* threshold;
    const float* glitch;
    const float* mix;

    float env_l;
    float env_r;
    float phase_l;
    float phase_r;
} ZoomDirtyGate;

static LV2_Handle instantiate(const LV2_Descriptor* descriptor, double rate, const char* bundle_path, const LV2_Feature* const* features) {
    ZoomDirtyGate* self = (ZoomDirtyGate*)calloc(1, sizeof(ZoomDirtyGate));
    if (!self) return NULL;
    self->env_l = 0.0f; self->env_r = 0.0f;
    self->phase_l = 0.0f; self->phase_r = 0.0f;
    return (LV2_Handle)self;
}

static void connect_port(LV2_Handle instance, uint32_t port, void* data) {
    ZoomDirtyGate* self = (ZoomDirtyGate*)instance;
    switch (port) {
        case GATE_INPUT_L:  self->input_l   = (const float*)data; break;
        case GATE_INPUT_R:  self->input_r   = (const float*)data; break;
        case GATE_OUTPUT_L: self->output_l  = (float*)data;       break;
        case GATE_OUTPUT_R: self->output_r  = (float*)data;       break;
        case GATE_THRESH:   self->threshold = (const float*)data; break;
        case GATE_GLITCH:   self->glitch    = (const float*)data; break;
        case GATE_MIX:      self->mix       = (const float*)data; break;
    }
}

static void run(LV2_Handle instance, uint32_t sample_count) {
    ZoomDirtyGate* self = (ZoomDirtyGate*)instance;
    
    float mix_val = *(self->mix) * 0.01f;
    float gate_th = *(self->threshold) * 0.01f * 0.1f; // tracking sensitivity
    float glitch_amt = *(self->glitch) * 0.01f;

    for (uint32_t i = 0; i < sample_count; ++i) {
        float in_l = self->input_l[i];
        float in_r = self->input_r[i];

        // Track energy
        self->env_l += 0.05f * (fabsf(in_l) - self->env_l);
        self->env_r += 0.05f * (fabsf(in_r) - self->env_r);

        float wet_l = 0.0f;
        float wet_r = 0.0f;

        // Left Channel Gate Processing
        if (self->env_l > gate_th) {
            self->phase_l += 0.01f + (self->env_l * 0.2f);
            if (self->phase_l > 1.0f) self->phase_l -= 1.0f;
            // Generate a square wave sub-harmonic fuzz tone
            float sub = (self->phase_l > 0.5f) ? 0.5f : -0.5f;
            wet_l = (in_l * (1.0f - glitch_amt)) + (sub * glitch_amt);
            wet_l = (wet_l > 0.4f) ? 0.4f : (wet_l < -0.4f ? -0.4f : wet_l); // Hard clip
        }

        // Right Channel Gate Processing
        if (self->env_r > gate_th) {
            self->phase_r += 0.01f + (self->env_r * 0.2f);
            if (self->phase_r > 1.0f) self->phase_r -= 1.0f;
            float sub = (self->phase_r > 0.5f) ? 0.5f : -0.5f;
            wet_r = (in_r * (1.0f - glitch_amt)) + (sub * glitch_amt);
            wet_r = (wet_r > 0.4f) ? 0.4f : (wet_r < -0.4f ? -0.4f : wet_r); // Hard clip
        }

        self->output_l[i] = (wet_l * mix_val) + (in_l * (1.0f - mix_val));
        self->output_r[i] = (wet_r * mix_val) + (in_r * (1.0f - mix_val));
    }
}

static void cleanup(LV2_Handle instance) {	free(instance); }
static const LV2_Descriptor descriptor = { DIRTYGATE_URI, instantiate, connect_port, NULL, run, NULL, cleanup, NULL };
LV2_SYMBOL_EXPORT const LV2_Descriptor* lv2_descriptor(uint32_t index) { return (index == 0) ? &descriptor : NULL; }
endef

define ZOOM_DIRTYGATE_MANIFEST_TTL
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<https://modaudio.com/plugins/zoom-dirtygate> a lv2:Plugin ; lv2:binary <zoom-dirtygate.so> ; rdfs:seeAlso <zoom-dirtygate.ttl> .
endef

define ZOOM_DIRTYGATE_TTL
@prefix doap:  <http://usefulinc.com/ns/doap#> .
@prefix lv2:   <http://lv2plug.in/ns/lv2core#> .
@prefix mod:   <http://moddevices.com/ns/mod#> .
@prefix units: <http://lv2plug.in/ns/extensions/units#> .

<https://modaudio.com/plugins/zoom-dirtygate>
    a lv2:DynamicsPlugin, lv2:Plugin ;
    doap:name "Zoom DirtyGate Synth" ;
    mod:brand "Custom" ;
    mod:label "zoom-dirtygate" ;

    lv2:port [
        a lv2:AudioPort, lv2:InputPort ; lv2:index 0 ; lv2:symbol "in_l" ; lv2:name "In L" ;
    ] , [
        a lv2:AudioPort, lv2:InputPort ; lv2:index 1 ; lv2:symbol "in_r" ; lv2:name "In R" ;
    ] , [
        a lv2:AudioPort, lv2:OutputPort ; lv2:index 2 ; lv2:symbol "out_l" ; lv2:name "Out L" ;
    ] , [
        a lv2:AudioPort, lv2:OutputPort ; lv2:index 3 ; lv2:symbol "out_r" ; lv2:name "Out R" ;
    ] , [
        a lv2:InputPort, lv2:ControlPort ; lv2:index 4 ; lv2:symbol "thresh" ; lv2:name "Gate Threshold" ;
        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 "glitch" ; lv2:name "Glitch Distortion" ;
        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 "mix" ; lv2:name "Mix" ;
        lv2:default 70.0 ; lv2:minimum 0.0 ; lv2:maximum 100.0 ; units:unit units:pc ;
    ] .
endef

export ZOOM_DIRTYGATE_CPP_DATA ZOOM_DIRTYGATE_MANIFEST_TTL ZOOM_DIRTYGATE_TTL

define ZOOM_DIRTYGATE_CONFIGURE_CMDS
	mkdir -p $(@D)/src
	mkdir -p $(@D)/zoom-dirtygate.lv2
	printf '%s' "$$ZOOM_DIRTYGATE_CPP_DATA" > $(@D)/src/zoom-dirtygate.cpp
endef

define ZOOM_DIRTYGATE_BUILD_CMDS
	$(TARGET_CXX) $(TARGET_CXXFLAGS) $(TARGET_CPPFLAGS) $(TARGET_LDFLAGS) -O3 -Wall -fPIC -shared \
		-I$(@D) `pkg-config --cflags lv2` $(@D)/src/zoom-dirtygate.cpp \
		-o $(@D)/zoom-dirtygate.lv2/zoom-dirtygate.so -lm
endef

define ZOOM_DIRTYGATE_INSTALL_TARGET_CMDS
	mkdir -p $ Ferguson_PKGDIR $($(PKG)_PKGDIR)/zoom-dirtygate.lv2
	$(INSTALL) -m 0755 $(@D)/zoom-dirtygate.lv2/zoom-dirtygate.so $($(PKG)_PKGDIR)/zoom-dirtygate.lv2/
	printf '%s' "$$ZOOM_DIRTYGATE_MANIFEST_TTL" > $($(PKG)_PKGDIR)/zoom-dirtygate.lv2/manifest.ttl
	printf '%s' "$$ZOOM_DIRTYGATE_TTL"          > $($(PKG)_PKGDIR)/zoom-dirtygate.lv2/zoom-dirtygate.ttl
endef

$(eval $(generic-package))
