MIDIClient.init;
MIDIIn.connectAll;


/*(
b = Buffer.readChannel(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/hebrewian_rue-stems/hebrewian_rue_r1_lead.wav", channels:[0], bufnum: 0);
b = Buffer.readChannel(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/hebrewian_rue-stems/hebrewian_rue_r1_harm.wav", channels:[0], bufnum: 1);
)

(
Buffer.readChannel(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/crawling-stems/lead.wav", channels:[0], bufnum: 0);
Buffer.readChannel(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/crawling-stems/perc mel solo.wav", channels:[0], bufnum: 1);
Buffer.readChannel(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/crawling-stems/bass.wav", channels:[0], bufnum: 2);
)


(
Buffer.readChannel(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/trax/01-Cantor_Samual_Malavsky_Shomea_Kol_Bichios.wav", channels:[0], bufnum: 0);
Buffer.readChannel(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/trax/02-Cantor_Mordechai_Hershman_Baruch_Hashem_Bayom.wav", channels:[0], bufnum: 1);
Buffer.readChannel(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/trax/03-Acheinu_Kol_Beit_Israel.wav", channels:[0], bufnum: 2);
Buffer.readChannel(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/trax/04-Cantor_Samuel_Malavsky_Zechor.wav", channels:[0], bufnum: 3);
Buffer.readChannel(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/trax/05-Cantor_Zavel_Kwartin_Ribono_Shel_Olam.wav", channels:[0], bufnum: 4);
Buffer.readChannel(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/trax/06-Cantor_Zevulun_Zavel_Kwartin_sings_Tiher.wav", channels:[0], bufnum: 5);
Buffer.readChannel(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/trax/07-Izak_Algazi_Efendi_Kamti_Be_ashmoret.wav", channels:0, bufnum: 6);
)*/



(
b = Buffer.read(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/harm/01 Ritual for Harmonica.wav", bufnum:0);
b = Buffer.read(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/harm/02 Chant - Harmonica.wav", bufnum:1);
b = Buffer.read(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/harm/03 Spheres 1.wav", bufnum:2);
b = Buffer.read(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/harm/04 Spheres 2.wav", bufnum:3);
b = Buffer.read(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/harm/05 Spheres 3.wav", bufnum:4);
b = Buffer.read(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/harm/06 The Lady of Shalott.wav", bufnum:5);
b = Buffer.read(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/harm/07 Pan and Apollo.wav", bufnum:6);
b = Buffer.read(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/harm/08 The King's Ears.wav", bufnum:7);
b = Buffer.read(s, "/home/lcoogan/snd/live/2025-04-26.Basic_City_Brewery/harm/09 Rip Van Winkle.wav", bufnum:8);
)




(
SynthDef(\granulator_compressor, { |bufnum, tFreq=20, overlap=2, rate=1, tFreqMF=0, tFreqMD=0,
    rateMF=0, rateMD=0, posRateMD=0, posRateMF=0, granDur=4|

    var dry, gran, env, bufFrames, tFreqMod, rateMod, posRateMod, playTrig;
    var t, samplePhasor, phasor, bufrd;
    var in, e, o, r, c, bias, lookahead, kneelag, drywet, compressed;

	    var attack = \attack.kr(0.01);
    var release = \release.kr(0.1);

    // === GRANULATOR SECTION ===

    bufFrames = BufFrames.ir(bufnum);

    tFreqMod = SinOsc.ar(tFreqMF, Rand(0.0, 2pi)) * tFreqMD;
    rateMod = SinOsc.ar(rateMF, Rand(0.0, 2pi)) * rateMD;
    posRateMod = SinOsc.ar(posRateMF, Rand(0.0, 2pi)) * posRateMD;

    playTrig = Impulse.ar(\playTrFr.kr(0.5));

    tFreq = tFreq + tFreqMod.dup;
    rate = rate + rateMod.dup;

    t = Impulse.ar(tFreq);

    samplePhasor = EnvGen.ar(Env([0, 0, bufFrames], [0, bufFrames / SampleRate.ir], curve: 0), gate: playTrig);
    phasor = EnvGen.ar(Env([0, 0, 1], [0, granDur], curve: 0), gate: playTrig);

    bufrd = BufRd.ar(
        numChannels: 1,
        bufnum: bufnum,
        phase: samplePhasor,
        interpolation: 4
    );

    bufrd = LeakDC.ar(bufrd);

    gran = GrainBufJ.ar(
        numChannels: 1,
        trigger: t,
        dur: tFreq.reciprocal * overlap,
        sndbuf: bufnum,
        rate: rate,
        pos: phasor + posRateMod.dup,
        interp: 2,
        pan: 0,
        envbufnum: -1,
        maxGrains: 512,
        loop: 0
    ) * EnvGen.ar(Env.perc(0.001, granDur * \grainGate.kr(1), curve: \granCurve.kr(-4)), gate: playTrig);

    dry = bufrd * \dry.kr(1.0) + gran * \wet.kr(0.5); // summed dry + wet
    dry = LeakDC.ar(dry);

    // === COMPRESSOR SECTION ===



    in = dry * \trim.kr(0).dbamp; // input to compressor

    e = in.mean;
    e = HPF.ar(e, \hpf.kr(50));
    e = EnvDetect.ar(e, attack, release);

    o = e.ampdb.excess(\threshold.kr(-6));
    r = \ratio.kr(4);
    c = (max(o, 0.0)) * (r.reciprocal - 1);

    kneelag = attack * \knee.kr(0.0);
    c = c.lag(kneelag);
    c = c.dbamp;

    lookahead = \lookahead.kr(0);
    in = DelayC.ar(in, 0.5, lookahead.lag(0.4));
    compressed = in * c;
    compressed = compressed * \gain.kr(0).dbamp;
    bias = K2A.ar(\bias.kr(0.0));

    compressed = Select.ar(\saturate.kr(1), [compressed, (compressed + bias).softclip - bias]);
    compressed = LeakDC.ar(compressed);

    drywet = \dry_wet.kr(1);
    compressed = Mix([
        compressed * drywet,
        DelayC.ar(dry * (1 - drywet), 0.5, lookahead.lag(0.4))
    ]);

    Out.ar(0, compressed);

}).add;

x = Synth(\granulator_compressor, [\bufnum, b]);
)
x.free;



(
var paramMap = (
    0: [\wet, 0, 1],
    1: [\bufnum, 0, 4],
    2: [\rate, -2, 2],
    3: [\posRate, 0, 4],
    4: [\tFreq, 0, 80],
    5: [\tFreqMF, 0, 80],
    6: [\tFreqMD, 0, 20],
    7: [\rateMF, 0, 80],
    8: [\rateMD, 0, 2],
    9: [\posRateMF, 0, 80],
    10: [\posRateMD, 0, 2],
    11: [\playTrFr, 0, 1],
    12: [\granDur, 0, 8],
    13: [\granCurve, -10, 10],
    14: [\grainGate, 0, 1],
    15: [\overlap, 0.001, 40, \exp],

    16: [\attack, 0.0000001, 0.1, \exp],
    17: [\release, 0.0000001, 0.4, \exp],
    18: [\threshold, -120, 0], // flipped key/value to array

    19: [\trim, 0, 60],
    20: [\gain, -40, 40],
    21: [\ratio, 1, 20, \exp],
    22: [\dry_wet, 0, 1],
    23: [\hpf, 10, 1000], // fixed missing bracket
    24: [\knee, 0.0, 10], // fixed missing bracket
    25: [\lookahead, 0.0, 1], // fixed missing bracket
    26: [\saturate, \switch], // you may want to define how to handle \switch
    27: [\bias, 0.0, 1.0]
);



MIDIIn.control = { |src, chan, num, val|
    if (chan == 2) {
        var mapping = paramMap[num];
        if (mapping.notNil) {
            var param = mapping[0];
            var min = mapping[1];
            var max = mapping[2];
            var scaleType = mapping[3] ?? \lin; // default to lin if not specified
            var mappedVal = (scaleType == \exp)
                .if { val.linexp(0, 127, min, max) }
                { val.linlin(0, 127, min, max) };
            x.set(param, mappedVal);
            ("Setting " ++ param ++ " to " ++ mappedVal).postln;
        };
    };
};
)


