// play back a soundfile with different effects
// dewdrop_world, http://www.dewdrop-world.net
// code is released under the LGPL, http://creativecommons.org/licenses/LGPL/2.1/

// on init: load soundfile, prepare mixerchannel and effect synthdefs
// 	on play: start the buffer playback synth
// 	on stop: release the buffer playback synth
// on free: release the buffer and mixerchannel

/**** basic template, don't run this!
PR(\abstractProcess).v.clone({
	~prep = {
	};
	
	~asPattern = {
	};
	
	~stopCleanup = {
	};
	
	~freeCleanup = {
	};
}) => PR(\bufPlayer);
****/

(
PR(\abstractProcess).v.clone({
	~event = (eventKey: \singleSynthPlayer);
	~soundfile = "sounds/a11wlk01.wav";
	
		// these will be compiled into synthdefs in ~prep
	~effects = [
		(name: \ringmod1, func: { |sig, freq|
			SinOsc.ar(freq, 0, sig)
		}, args: { [\freq, exprand(100, 2000)] }),

		(name: \ringmod2, func: { |sig, freq, lofreq, hifreq|
			SinOsc.ar(LFNoise1.kr(freq).range(lofreq, hifreq), 0, sig)
		}, args: { [\freq, rrand(0.1, 1.6), \lofreq, exprand(100, 2000),
			\hifreq, exprand(100, 2000)] }),

		(name: \comb1, func: { |sig, delay, dur|
			CombN.ar(sig, delay + 0.01, delay, dur)
		}, args: { [\delay, exprand(50, 500).reciprocal, \dur, rrand(0.05, 1.0)] }),

		(name: \comb2, func: { |sig, freq, lodelay, hidelay, dur|
			CombL.ar(sig, hidelay + 0.01, LFNoise1.kr(freq).exprange(lodelay, hidelay), dur)
		}, args: { var d1 = exprand(50, 500).reciprocal, d2 = exprand(50, 500).reciprocal;
			[\freq, rrand(0.2, 14.0), \lodelay, min(d1, d2), \hidelay, max(d1, d2),
			\dur, rrand(0.05, 1.0)] }),

		(name: \echo, func: { |sig, delay, dur|
			CombN.ar(sig, delay + 0.01, delay, dur)
		}, args: { [\delay, rrand(0.4, 1.0), \dur, rrand(4.0, 12.0)] })
	];
	
	~prep = {
		~buffer = Buffer.read(s, ~soundfile);
		SynthDef(\bufPlayer1, { |outbus, bufnum, gate = 1|
			var	basefreq = BufDur.ir(bufnum).reciprocal * 0.94,
				trig = Impulse.kr(basefreq + LFNoise1.kr(1, basefreq * 0.5, basefreq * 0.9)),
				env = EnvGen.kr(Env(#[1, 1, 0], #[1, 0.2], -4, 1), gate, doneAction:2),
				sig = PlayBuf.ar(1, bufnum, BufRateScale.ir(bufnum), trig,
					TRand.kr(0, BufFrames.ir(bufnum) * 0.5, trig));
			Out.ar(outbus, Pan2.ar(sig * env, LFNoise1.kr(1)))
		}).send(Server.default);
		~bufplayer = \bufPlayer1;
		
			// build effects by wrapping effect function in a fade in/out envelope
			// also include random panning
		~effects.do({ |fxdef|
			SynthDef(fxdef.name, { |gate = 1, fadetime = 4, holdtime = 4, outbus|
				var	sig = In.ar(outbus, 2),
					wetenv = EnvGen.kr(Env(#[0, 1, 1, 0], [fadetime, holdtime, fadetime],
						\lin), gate, doneAction:2),
					wetsig;
				wetsig = SynthDef.wrap(fxdef[\func], nil, [sig]);
				sig = XFade2.ar(sig, Limiter.ar(wetsig, 0.9), wetenv.madd(2, -1));
				ReplaceOut.ar(outbus, sig);
			}).send(Server.default);
		});
		
		~fade = Pwhite(2.0, 8.0, inf);
		~hold = Pwhite(5.0, 16.0, inf);
		~dfactor = Pwhite(0.4, 0.7, inf);
		
		~chan = MixerChannel(\bufPlayer, inChannels:2, outChannels:2);
	};
	
		// bufplayer synth needs to be created every time I play
		// therefore reset is required every time
	~alwaysReset = true;
	
	~asPattern = {
		Pseq([
			Pfuncn({
				~bufsynth.isNil.if({  // does my runtime resource exist?
					~chan.server.makeBundle(~leadTime / thisThread.clock.tempo, {
						~bufsynth = ~chan.play(~bufplayer, 
							[\bufnum, ~buffer.bufnum]);
					});
				});
				(play: 0, delta: rrand(2.0, 5.0))	// dummy event
			}, 1),
			Pbind(
				\chan, ~chan,
				\isFx, true,
				\fxdef, Pfunc({ ~effects.choose }),
				\instrument, Pfunc({ |ev| ev[\fxdef].name }),
				\fadetime, BPStream(\fade),
				\holdtime, BPStream(\hold),
				\delta, Pfunc({ |ev| (ev[\holdtime] + (ev[\fadetime] * 2)) })
					* BPStream(\dfactor)
			).collect({ |ev|
				ev[\fxdef].args.pairsDo({ |key, value|
					ev.put(key, value)
				});
				ev
			});
		], 1)
	};
	
	~stopCleanup = {
		~bufsynth !? { ~bufsynth.set(\gate, 0); ~bufsynth = nil; };
	};
	
	~freeCleanup = {
		[~chan, ~buffer].free;
		~effects.do({ |fxdef| fxdef.name.free });
	};
}) => PR(\fxOverlap);
)

PR(\fxOverlap) => BP(\test);
BP(\test).play;

Pwhite(0.1, 0.4, inf) =>.dfactor BP(\test);
Pwhite(0.4, 0.8, inf) =>.dfactor BP(\test);

BP(\test).stop;
BP(\test).free;

BP(\test).listVars