// ===================================================
// SuperCollider: GrainFM Examples and Use Cases
// ===================================================

// Load this file into SuperCollider and evaluate each section as needed

// ---------------------------------------------------
// 1. Tonal Ambient Pad (Stable Pitch, Gentle Movement)
// ---------------------------------------------------
(
SynthDef(\grainFMPadClean, {
    |out=0, freq=440, amp=0.3, dur=10|
    var trig = Impulse.kr(15); // grains per second
    var snd;

    snd = GrainFM.ar(
        numChannels: 2,
        trigger: trig,
        dur: 0.2,
        carfreq: freq,
        modfreq: freq * 2, // simple ratio
        index: 1.5,
        pan: LFNoise1.kr(0.2),
        envbufnum: -1
    );

    snd = snd * EnvGen.kr(Env.linen(1, dur-2, 1), doneAction: 2) * amp;
    Out.ar(out, snd);
}).add;
)

// Example usage:
Synth(\grainFMPadClean, [\freq, 330, \dur, 12]);

// ---------------------------------------------------
// 2. Cloudy Texture (Randomized Frequencies)
// ---------------------------------------------------
(
SynthDef(\grainFMCloud, {
    |out=0, amp=0.2, dur=8|
    var trig = Dust.kr(25);
    var freq = TExpRand.kr(200, 800, trig);
    var snd;

    snd = GrainFM.ar(
        numChannels: 2,
        trigger: trig,
        dur: 0.1,
        carfreq: freq,
        modfreq: freq * 1.5,
        index: TRand.kr(1, 10, trig),
        pan: TRand.kr(-1, 1, trig),
        envbufnum: -1
    );

    snd = snd * EnvGen.kr(Env.linen(1, dur-2, 1), doneAction: 2) * amp;
    Out.ar(out, snd);
}).add;
)

Synth(\grainFMCloud);

// ---------------------------------------------------
// 3. Evolving Drone (Slow Modulation of Grain Rate & Pitch)
// ---------------------------------------------------
(
SynthDef(\grainFMEvolvingDrone, {
    |out=0, baseFreq=100, amp=0.3, dur=20|
    var trig = Impulse.kr(LFNoise1.kr(0.1).range(5, 30));
    var freq = LFNoise1.kr(0.05).range(baseFreq, baseFreq * 4);
    var modfreq = freq * LFNoise1.kr(0.02).range(0.5, 2);
    var snd;

    snd = GrainFM.ar(
        numChannels: 2,
        trigger: trig,
        dur: 0.15,
        carfreq: freq,
        modfreq: modfreq,
        index: 3,
        pan: LFNoise1.kr(0.3),
        envbufnum: -1
    );

    snd = snd * EnvGen.kr(Env.linen(2, dur-4, 2), doneAction: 2) * amp;
    Out.ar(out, snd);
}).add;
)

Synth(\grainFMEvolvingDrone);

// ---------------------------------------------------
// 4. Percussive Metallic Hits
// ---------------------------------------------------
(
SynthDef(\grainFMHit, {
    |out=0, freq=400, amp=0.4|
    var trig = Impulse.kr(1);
    var snd;

    snd = GrainFM.ar(
        numChannels: 2,
        trigger: trig,
        dur: 0.05,
        carfreq: freq,
        modfreq: freq * 3,
        index: 12,
        pan: 0,
        envbufnum: -1
    );

    snd = snd * EnvGen.kr(Env.perc(0.01, 0.3), doneAction: 2) * amp;
    Out.ar(out, snd);
}).add;
)

Synth(\grainFMHit);

// ---------------------------------------------------
// 5. Blending with a Pure Tone (Stable Pitch)
// ---------------------------------------------------
(
SynthDef(\grainFMWithTone, {
    |out=0, freq=440, amp=0.3, dur=10|
    var trig = Impulse.kr(20);
    var grain = GrainFM.ar(2, trig, 0.15, freq, freq * 2, 2, LFNoise1.kr(0.2), -1);
    var tone = SinOsc.ar(freq) * 0.1;
    var sig = Mix([grain, tone]);

    sig = sig * EnvGen.kr(Env.linen(1, dur-2, 1), doneAction: 2) * amp;
    Out.ar(out, sig);
}).add;
)

Synth(\grainFMWithTone);

// ---------------------------------------------------
// 6. MIDI-Controlled GrainFM Synth (For Sequencing)
// ---------------------------------------------------
(
SynthDef(\grainFMMidi, {
    |out=0, freq=440, amp=0.3, gate=1|
    var trig = Impulse.kr(10);
    var snd;

    snd = GrainFM.ar(
        numChannels: 2,
        trigger: trig,
        dur: 0.2,
        carfreq: freq,
        modfreq: freq * 2,
        index: 2,
        pan: LFNoise1.kr(0.2),
        envbufnum: -1
    );

    snd = snd * EnvGen.kr(Env.asr(0.01, 1, 1), gate, doneAction: 2) * amp;
    Out.ar(out, snd);
}).add;
)

(
MIDIIn.connectAll;

~grainFMResponder = NoteOnResponder({
    |src, chan, num, vel|
    var freq = num.midicps;
    Synth(\grainFMMidi, [\freq, freq, \amp, vel/127]);
});
)

// ---------------------------------------------------
// You can now play GrainFM like an instrument from a MIDI controller!
// ---------------------------------------------------
