################################################################################
#
# ZOOM-SLICER (Zoom MS-70CDR Rhythmic Audio Gate for MOD Dwarf)
# Standalone True Stereo Pattern Gated Modulation Specification & Source Package
#
################################################################################

ZOOM_SLICER_VERSION = 1.0.0
ZOOM_SLICER_SITE_METHOD = local
ZOOM_SLICER_SITE = $(_ZOOM_SLICER_SITE_ENV)
ZOOM_SLICER_SOURCE =

ZOOM_SLICER_BUNDLES = zoom-slicer.lv2

define ZOOM_SLICER_CPP_DATA
#include <lv2/core/lv2.h>
#include <cmath>
#include <stdint.h>
#include <stdlib.h>

#define SLICER_URI "https://modaudio.com/plugins/zoom-slicer"

enum Ports {
    SLICE_INPUT_L  = 0,
    SLICE_INPUT_R  = 1,
    SLICE_OUTPUT_L = 2,
    SLICE_OUTPUT_R = 3,
    SLICE_RATE     = 4,
    SLICE_DEPTH    = 5,
    SLICE_PATTERN  = 6,
    SLICE_MIX      = 7
};

typedef struct {
    const float* input_l;
    const float* input_r;
    float* output_l;
    float* output_r;
    const float* rate;
    const float* depth;
    const float* pattern;
    const float* mix;

    double sample_rate;
    double phase;
} ZoomSlicer;

static LV2_Handle instantiate(const LV2_Descriptor* descriptor, double rate, const char* bundle_path, const LV2_Feature* const* features) {
    ZoomSlicer* self = (ZoomSlicer*)calloc(1, sizeof(ZoomSlicer));
    if (!self) return NULL;
    self->sample_rate = rate;
    self->phase = 0.0;
    return (LV2_Handle)self;
}

static void connect_port(LV2_Handle instance, uint32_t port, void* data) {
    ZoomSlicer* self = (ZoomSlicer*)instance;
    switch (port) {
        case SLICE_INPUT_L:  self->input_l  = (const float*)data; break;
        case SLICE_INPUT_R:  self->input_r  = (const float*)data; break;
        case SLICE_OUTPUT_L: self->output_l = (float*)data;       break;
        case SLICE_OUTPUT_R: self->output_r = (float*)data;       break;
        case SLICE_RATE:     self->rate     = (const float*)data; break;
        case SLICE_DEPTH:    self->depth    = (const float*)data; break;
        case SLICE_PATTERN:  self->pattern  = (const float*)data; break;
        case SLICE_MIX:      self->mix      = (const float*)data; break;
    }
}

static void run(LV2_Handle instance, uint32_t sample_count) {
    ZoomSlicer* self = (ZoomSlicer*)instance;
    
    float mix_val = *(self->mix) * 0.01f;
    float depth_val = *(self->depth) * 0.01f;
    int pat = (int)(*(self->pattern) + 0.5f);
    
    double phase_inc = (2.0 * M_PI * (*self->rate)) / self->sample_rate;

    for (uint32_t i = 0; i < sample_count; ++i) {
        float in_l = self->input_l[i];
        float in_r = self->input_r[i];

        self->phase += phase_inc;
        if (self->phase >= 2.0 * M_PI) self->phase -= 2.0 * M_PI;

        // Break the phase down into 4 distinct step quarters (Rhythmic pattern block)
        int step = (int)((self->phase / (2.0 * M_PI)) * 4.0f);
        
        float gain_l = 1.0f;
        float gain_r = 1.0f;

        if (pat == 0) {
            // Pattern 0: Straight classic hard chopping gating (on/off/on/off)
            float v = (step % 2 == 0) ? 1.0f : (1.0f - depth_val);
            gain_l = v; gain_r = v;
        } else {
            // Pattern 1: Interspersed ping-pong rhythmic slicer pattern
            if (step == 0) { gain_l = 1.0f; gain_r = 0.1f; }
            else if (step == 1) { gain_l = 0.1f; gain_r = 0.1f; }
            else if (step == 2) { gain_l = 0.1f; gain_r = 1.0f; }
            else { gain_l = 1.0f; gain_r = 1.0f; }
            
            // Factor in depth scaling smoothly
            gain_l = 1.0f - ((1.0f - gain_l) * depth_val);
            gain_r = 1.0f - ((1.0f - gain_r) * depth_val);
        }

        float wet_l = in_l * gain_l;
        float wet_r = in_r * gain_r;

        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 = { SLICER_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_SLICER_MANIFEST_TTL
@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<https://modaudio.com/plugins/zoom-slicer> a lv2:Plugin ; lv2:binary <zoom-slicer.so> ; rdfs:seeAlso <zoom-slicer.ttl> .
endef

define ZOOM_SLICER_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#> .
@prefix toggles: <http://lv2plug.in/ns/ext/port-props#> .

<https://modaudio.com/plugins/zoom-slicer>
    a lv2:ModulatorPlugin, lv2:Plugin ;
    doap:name "Zoom Slicer Gate" ;
    mod:brand "Custom" ;
    mod:label "zoom-slicer" ;

    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 "rate" ; lv2:name "Slicer Tempo Speed" ;
        lv2:default 3.0 ; lv2:minimum 0.2 ; lv2:maximum 15.0 ; units:unit units:hz ;
    ] , [
        a lv2:InputPort, lv2:ControlPort ; lv2:index 5 ; lv2:symbol "depth" ; lv2:name "Chop Intensity" ;
        lv2:default 90.0 ; lv2:minimum 0.0 ; lv2:maximum 100.0 ; units:unit units:pc ;
    ] , [
        a lv2:InputPort, lv2:ControlPort ; lv2:index 6 ; lv2:symbol "pattern" ; lv2:name "Pattern: Gate / PngPong" ;
        lv2:default 0.0 ; lv2:minimum 0.0 ; lv2:maximum 1.0 ;
        lv2:portProperty lv2:integer, toggles:hasToggle ;
    ] , [
        a lv2:InputPort, lv2:ControlPort ; lv2:index 7 ; lv2:symbol "mix" ; lv2:name "Mix" ;
        lv2:default 100.0 ; lv2:minimum 0.0 ; lv2:maximum 100.0 ; units:unit units:pc ;
    ] .
endef

export ZOOM_SLICER_CPP_DATA ZOOM_SLICER_MANIFEST_TTL ZOOM_SLICER_TTL

define ZOOM_SLICER_CONFIGURE_CMDS
	mkdir -p $(@D)/src
	mkdir -p $(@D)/zoom-slicer.lv2
	printf '%s' "$$ZOOM_SLICER_CPP_DATA" > $(@D)/src/zoom-slicer.cpp
endef

define ZOOM_SLICER_BUILD_CMDS
	$(TARGET_CXX) $(TARGET_CXXFLAGS) $(TARGET_CPPFLAGS) $(TARGET_LDFLAGS) -O3 -Wall -fPIC -shared \
		-I$(@D) `pkg-config --cflags lv2` $(@D)/src/zoom-slicer.cpp \
		-o $(@D)/zoom-slicer.lv2/zoom-slicer.so -lm
endef

define ZOOM_SLICER_INSTALL_TARGET_CMDS
	mkdir -p $($(PKG)_PKGDIR)/zoom-slicer.lv2
	$(INSTALL) -m 0755 $(@D)/zoom-slicer.lv2/zoom-slicer.so $($(PKG)_PKGDIR)/zoom-slicer.lv2/
	printf '%s' "$$ZOOM_SLICER_MANIFEST_TTL" > $($(PKG)_PKGDIR)/zoom-slicer.lv2/manifest.ttl
	printf '%s' "$$ZOOM_SLICER_TTL"          > $($(PKG)_PKGDIR)/zoom-slicer.lv2/zoom-slicer.ttl
endef

$(eval $(generic-package))
