Wavesets 

analyses soundfiles into wavesets and keeps all waveset data.
This can support a variety of waveset based instruments.

By Trevor Wishart's definition, a waveset is a segment of 
an audio signal between one non-positive to positive zero 
crossing and the next. [ see T. Wishart : Audible Design. ]
Please note that this definition only applies to mono signals.

The Wavesets Class keeps a dictionary of all analysed wavesets.

Wavesets.all;		// a dictionary of all analyzed wavesets.

Load a file into a signal by hand and analyse it:

(
		// minLength is the shortest allowed waveset length.
		// anything shorter will be merged with the next waveset.
Wavesets.minLength = 5;	

f = SoundFile.new; 
f.openRead("sounds/a11wlk01.wav");
f.readData(g = Signal.newClear(f.numFrames));
//	g.dump;
		// create a new instance with a name and signal:
w = Wavesets(\apollo, g);
//	w.dump;
)

or do it directly from a file : 

w = Wavesets.from("sounds/a11wlk01.wav");
g = w.signal;

w.dump;

w.xings;
w.numXings;
w.lengths;
w.amps;			

w.fracXings;		// calculate fractional zerocrossing points.
				// this is a bit more accurate and can sound a bit smoother.
w.fracLengths;	
				
		// some statistics which can be useful:	
w.minSet;			// shorted waveset
w.maxSet;			// longest
w.avgLength;		
w.sqrAvgLength;	// not sure this is useful.
w.minAmp;			// softest
w.maxAmp;			// loudest
w.avgAmp;			
w.sqrAvgAmp;			

	// a single waveset:
g.copyRange(w.xings[0], w.xings[1] - 1).postln.plot;

g.copyRange(w.xings[600], w.xings[601] - 1).plot;

	// 5 
g.copyRange(w.xings[600], w.xings[605]).plot;

	// 20
g.copyRange(w.xings[600], w.xings[620]).plot;

	// load soundfile into buffer:
Server.default = s = Server.internal.boot;

	// read soundfile into buffer 0
b = Buffer.read(s, "sounds/a11wlk01.wav", 0, -1, bufnum: 0);

	// play one waveset (or waveset group:
(
{ 	var startFr, endFr, dur; 
	startFr = w.xings[800]; 
	endFr = w.xings[820];
	
	dur = endFr - startFr / f.sampleRate;
	dur.postln;
	BufRd.ar(1, 0, Line.ar(startFr, endFr, dur , doneAction: 2)) 
}.play;
)

		// timestretch * 3
(
{ 	var startFr, endFr, dur, stretch; 

	startFr = w.xings[800]; 
	endFr = w.xings[820];
	stretch = 3;
	
	dur = endFr - startFr / f.sampleRate;	
	dur = dur * stretch;
	dur.postln;
	BufRd.ar(1, 0, Line.ar(startFr, endFr, dur, doneAction: 2)) 
}.play;
)

	loop buffer segments with a Phasor:
(
{ 	BufRd.ar(1, 0, 
		Phasor.ar(0, BufRateScale.kr(0), w.xings.at(100), w.xings.at(105))
	) 
}.scope;
)	

g.copyRange(w.xings.at(100), w.xings.at(105)).plot;

	// some Phasor examples:
{ Phasor.ar(0, 1, 0, 50) * 0.01 }.scope;
{ Phasor.ar(0, 0.2, 0, 50) * 0.01 }.scope;
{ Phasor.ar(0, -0.2, 0, 50) * 0.01 }.scope;

	// loop wavesets, continuous
(
x = { arg start = 0, end = 10, rate = 1; 
	BufRd.ar(1, 0, 
		Phasor.ar(0, rate * BufRateScale.kr(0), start, end)
	)
}.play;
)

x.set(\start, 0, \end, 5);
x.set(\start, w.xings.at(100), \end, w.xings.at(101));
x.set(\start, w.xings.at(101), \end, w.xings.at(102));
x.set(\start, w.xings.at(100), \end, w.xings.at(200));
x.set(\rate, 0.25);
x.set(\rate, 1);
x.set(\rate, -1);

(
t = Task ({ 
	var start, len = 5; 
		100.do ({
			start = rrand (0, 1000). postln;
			x.set(\start, w.xings.at(start), \end, w.xings.at(start + len));
			wait (0.15);	
		})
}).play;
)

To end playing a waveset(or group) after a precise number of repetitions, 
a SynthDef with a hard cutoff envelope:
// note that adding an offset outside phasor seems to work better; 
// Phasor.ar(0, rate, start, end) was sometimes off by ca. 10 samples or so.
// I have no explanation for this yet.

(
SynthDef("waveset", { arg out = 0, bufnum = 0, start = 0, end, rate = 1, dur = 1, amp=0.2; 
	var phasor;
	phasor = Phasor.ar(0, BufRateScale.kr(bufnum) * rate, 0, end - start) + start;
	OffsetOut.ar(out, 
		// [ BufRd.ar(1, bufnum, phasor), phasor - start * 0.01 - 1 ] // debug
		BufRd.ar(1, bufnum, phasor)
	 	* EnvGen.ar(Env([amp, amp, 0], [dur, 0]), doneAction: 2)
	 );
}).send(s);
)

Synth("waveset", [\bufnum, 0, \start, 0, \end, 5000, \dur, 1]);

(
var a=100, e = 102, rep = 1, dur, rate = 0.5;
var startframe, endframe;

startframe = w.xings[a];
endframe = w.xings[e];
dur = (endframe - startframe) * rep / rate / f.sampleRate; 

Synth("waveset", [
	\bufnum, 0, 
	\start, startframe, 
	\end, endframe, 
	\rate, rate,
	\dur, dur, 
	\amp, 3
]);
)

(
Task({ 
	1300.do({ arg i; 
		var a=100, e = 102, rep = 1, dur, rate = 1;
		var startframe, endframe;
		
		a = 100 + i; 
		e = a + 2; 
		rep = 4; 
		
		startframe = w.xings[a];
		endframe = w.xings[e];
		dur = (endframe - startframe) * rep / rate / f.sampleRate; 
			
			// better timing with sendBundle that with Synth("waveset", ...);
		s.sendBundle(0.2, [ "s_new", 
			"waveset", -1, 0, 0, 
			\bufnum, 0, 
			\start, startframe, 
			\end, endframe, 
			\rate, rate,
			\dur, dur, 
			\amp, 0.5
		]);
		(dur ).wait; 
	});
}).play;
)

		// compare fractional and integer xings; 
		// fractional does better looping and finer pitch gradations.
(
Task({ 
	100.do({ arg i; 
		var a=100, e = 102, rep = 1, dur, rate = 1;
		var startframe, endframe;
		
		a = 600 + i; 
		e = a + 5; 
		rep = 10; 
		
			// integer
		startframe = w.fracXings[a]; 		endframe = w.fracXings[e];
		
			// uncomment for fractional xings:
		startframe = w.xings[a];			endframe = w.xings[e];

		dur = (endframe - startframe) * rep / rate / f.sampleRate; 
		
		s.sendBundle(0.2, [ "s_new", 
			"waveset", -1, 0, 0, 
			\bufnum, 0, 
			\start, startframe, 
			\end, endframe, 
			\rate, rate,
			\dur, dur, 
			\amp, 0.5
		]);
		(dur ).wait; 
	});
}).play;
)

		// waveset timestretch
(
Task({ 
	270.do({ arg i; 
		var a=100, e = 102, rep = 1, dur, rate = 1;
		var startframe, endframe;
		
		a = (i * 5); 
		e = a + 5; 
		rep = 4; 
		
		startframe = w.xings[a];
		endframe = w.xings[e];
		dur = (endframe - startframe) * rep / rate / f.sampleRate; 
		
		s.sendBundle(0.2, [ "s_new", 
			"waveset", -1, 0, 0, 
			\bufnum, 0, 
			\start, startframe, 
			\end, endframe, 
			\rate, rate,
			\dur, dur, 
			\amp, 0.5
		]);
		(dur * 1).wait; 
	});
}).play;
)

	// single wavesets x 10;
(
Task({ 
	var start, end, startFr, endFr, dur, repeats;
	1400.do({ arg i; 
		repeats = 10;
		start = i * 1;
		end = start + 1; 
		startFr = w.fracXings[start]; 
		endFr = w.fracXings[end] - 1; 	
		dur = endFr - startFr / f.sampleRate * repeats; 
		
		s.sendBundle(0.2, [ "s_new", 
			"waveset", -1, 0, 0, 
			\bufnum, 0, 
			\start, startFr, 
			\end, endFr, 
			\rate, 1,
			\dur, dur
		]);
				// with pauses
		(dur * 2).wait; 
	});
}).play;
)

	// play them sorted by (integer) waveset length: 
w.lengths.plot;
o = w.lengths.order;
(
Task({ 
	var start, end, startFr, endFr, dur, repeats;
	var order; 
	order = w.lengths.order;	// nach der laenge
	order.do({ arg indexXing; 
		repeats = 10;
		start = indexXing;
		end = start + 1; 
		startFr = w.xings[start]; 
		endFr = w.xings[end]; 	
		dur = endFr - startFr * repeats / f.sampleRate; 
		
		s.sendBundle(0.5, [ "s_new", 
			"waveset", -1, 0, 0, 
			\bufnum, 0, 
			\start, startFr, 
			\end, endFr, 
			\rate, 1,
			\dur, dur
		]);
		(dur).wait; 
	});
}).play;
)

// same with fractional lengths:
w.fracLengths.plot;
o = w.fracLengths.order;

(
Task({ 
	var start, end, startFr, endFr, dur, repeats;
	var order; 
	order = w.fracLengths.order;	// by fractional waveset length
	order.do({ arg indexXing; 
		repeats = 10;
		start = indexXing;
		end = start + 1; 
		startFr = w.fracXings[start]; 
		endFr = w.fracXings[end]; 	
		dur = endFr - startFr * repeats / f.sampleRate; 
		
		s.sendBundle(0.5, [ "s_new", 
			"waveset", -1, 0, 0, 
			\bufnum, 0, 
			\start, startFr, 
			\end, endFr, 
			\rate, 1,
			\dur, dur
		]);
		(dur).wait; 
	});
}).play;
)

// hopefully more examples to come.