b = Buffer.read(s, "/home/lcoogan/snd/samples/freesound/vocals/ymaaela/attribution/330909__ymaaela__discordant-clip.mono.wav");
b = Buffer.read(s, "/home/lcoogan/snd/samples/freesound/vocals/ymaaela/333264__ymaaela__female-vocal-cut-ups-collage.mono.wav");

// with this
b = Buffer.read(s, "/home/lcoogan/snd/releases/desolation-mountain/daven.stems/raoeu.wav");
w = Wavesets.from("/home/lcoogan/snd/releases/desolation-mountain/daven.stems/raoeu.wav");

b.play;

s.boot



// make a wavesets from a soundfile
w = Wavesets.from("/home/lcoogan/snd/samples/freesound/vocals/ymaaela/attribution/330909__ymaaela__discordant-clip.mono.wav");
w = Wavesets.from("/home/lcoogan/snd/samples/freesound/vocals/ymaaela/333264__ymaaela__female-vocal-cut-ups-collage.mono.wav");
w = Wavesets.from("/home/lcoogan/.local/share/SuperCollider/Recordings/SC_250525_194956.wav");

w.dump;        // contains mainly analysis data

w.plot(200, 1);    // plot a single waveset
w.signal.copyRange(w.xings[600], w.xings[601]).plot;

w.plot(600, 1);    // a single
w.plot(600, 5);    // a group of five contiguous wavesets
w.buffer;
w.buffer.play;

// build and add the SynthDefs
Wavesets.prepareSynthDefs;

// eventFor returns an event that can be played directly
w.eventFor(startWs: 600, length: 5, repeats: 2).postln.play;
w.eventFor(startWs: 600, length: 2, playRate: 1, repeats: 5).postln.play;
w.eventFor(startWs: 600, length: 2, playRate: 0.5, repeats: 5).postln.play;
w.eventFor(700, 20, 5, 1).play;

(
fork {
    666.do { |i|
        var ev = w.eventFor(i * 5, 2, 5, exprand(0.5, 1.0));
        ev.put(\pan, 1.0.rand2).play;
        ev.sustain.wait;
    }
};
)



(
b = w.buffer;

Wavesets.prepareSynthDefs;

)

(
var start, length, sustain, repeats = 20;
#start, length, sustain = w.frameFor(150, 5);

(	instrument: \wvst0, bufnum: b.bufnum, amp:1,
	start: start, length: length, sustain: sustain * repeats
).play;
)
)




Quarks.directory;




w.lengths.do({ |len, i|
    ("Waveset %" ++ i ++ ": length = " ++ len ++ ", amp = " ++ w.amps[i]).postln;
});


w.plot(0, 1); // arg[0] = waveset index, arg[1] = waveset n

(
// Normalize
~lens = w.lengths.normalize;
~amps = w.amps.normalize;

// Combine into 2D array of feature vectors
~features = Array.fill(w.lengths.size, { |i|
    [~lens[i] * 5, ~amps[i]]
});

// Very basic binning approach
~k = 10;
~clusters = Dictionary.new;
~features.do { |vec, i|
    var clusterID = ((vec[0] + vec[1]) * 0.5 * ~k).floor;
    ~clusters[clusterID] = (~clusters[clusterID] ?? { [] }) ++ [i];
};

~clusters.keysValuesDo { |k, vals| ("Cluster " ++ k ++ ": " ++ vals).postln; };
)


(
// Play one representative from each cluster
~clusters.keysDo { |k|
    var wsIndex = ~clusters[k][0];  // First waveset in the cluster
    var ev = w.eventFor(startWs: wsIndex, length: 1, repeats: 1);
    ev.put(\pan, 1.0.rand2);  // Random pan for stereo fun
    ev.play;
};
)


(
// Build a map of representative wavesets for each cluster
~reps = Dictionary.new;
~clusters.keysDo { |k|
    ~reps[k] = ~clusters[k][0];  // Could improve: use actual centroid distance
};

// Replace each waveset with its cluster's representative, but keep order
fork {
    w.lengths.size.do { |i|
        var vec = [~lens[i] * 5, ~amps[i]];
        var clusterID = ((vec[0] + vec[1]) * 0.5 * ~k).floor;
        var repIndex = ~reps[clusterID] ?? { i };  // fallback: original if rep not found
        var ev = w.eventFor(repIndex, 1, 1, 1);
        ev.put(\pan, 0).play;
        ev.sustain.wait;
    };
};
)




(
// Subtle morph: vary playback based on cluster
fork {
    w.lengths.size.do { |i|
        var vec = [~lens[i] * 5, ~amps[i]];
        var clusterID = ((vec[0] + vec[1]) * 0.5 * ~k).floor;
        var ev = w.eventFor(i, 1, 1, 1 + (clusterID * 0.01));
        ev.put(\pan, clusterID.linlin(0, ~k, -1, 1));
        ev.play;
        ev.sustain.wait;
    };
};
)









/////////////////////////


(
// the simplest synthdef as implented in *prepareSynthDefs:
SynthDef(\wvst0, { arg out = 0, buf = 0, start = 0, length = 441, playRate = 1, sustain = 1, amp=0.2, pan;
    var phasor = Phasor.ar(0, BufRateScale.ir(buf) * playRate, 0, length) + start;
    var env = EnvGen.ar(Env([amp, amp, 0], [sustain, 0]), doneAction: 2);
    var snd = BufRd.ar(1, buf, phasor) * env;

    OffsetOut.ar(out, Pan2.ar(snd, pan));
}, \ir.dup(8)).add;
)

Synth("wvst0", [\bufnum, b, \start, 0, \length, 600, \sustain, 4, \playRate, -0.4]);

