//A keyboard window that allows to drop functions on
//any key. In general, you can start and stop the sound of a 
//particular function by pressing the particullar key. The letters //pressed appear on the post window allowing to type sentences 
//that are interpreted as a combination of sounds.

//create the window

(
var w; //  the window object
var arial; 
var varfortxt;
var keyboard = #["`1234567890-=", "QWERTYUIOP[]\\", 
							"ASDFGHJKL;'", "ZXCVBNM,./"];
var offsets = #[42, 48, 57, 117];
var actions; 
var makeKey; 
arial = Font("Arial Bold", 14);
actions = IdentityDictionary.new; 

makeKey = {|char, keyname, bounds|
	var v;
	keyname = keyname ? char.asString;
	bounds = bounds ? (24 @ 24);

	v = SCDragBoth(w, bounds);
	v.font = arial;
	v.string = keyname;
	v.align = \center;
	v.setBoth = false;
	v.acceptDrag = {
		SCView.currentDrag.isKindOf(Function)
	};
	v.action = { 
		("added key action : " ++ keyname).postln;
		if (char.isAlpha) {
			actions[char.toUpper] = v.object;
			actions[char.toLower] = v.object;
		}{
			actions[char] = v.object;
		};
		w.front;
	};
};

w = SCWindow("Drag & Drop functions on any key", Rect(128, 320, 420, 150));
w.view.decorator = FlowLayout(w.view.bounds);
w.view.keyDownAction = {|view, char, modifiers, unicode, keycode|
	 var result;
	result = actions[char].value(char, modifiers);
	if (result.isKindOf(Function)) {
		actions[char] = result;
	};
varfortxt = varfortxt ? char.asString;
char.post;
};

keyboard.do {|row, i| 
	row.do {|key| makeKey.(key) };
	if (i==0) { makeKey.(127.asAscii, "del", 38 @ 24) };
	if (i==2) { makeKey.($\r, "return", 46 @ 24) };
	w.view.decorator.nextLine;
	w.view.decorator.shift(offsets[i]);
};

makeKey.($ , "spacebar", 150 @ 24);
makeKey.(3.asAscii, "enter", 48 @ 24);
w.front;
)

//Drag the following functions on any key
//of the keyboard

//FUNCTION No.1
//impulses with reverb & delay

(
{
	var synth, original;
	original = thisFunction;
	synth = {
var s, z, y;
	s = Mix.ar(Array.fill(10, { Resonz.ar(Impulse.ar(10, [0, MouseX.kr(0, 5)], 0.7), XLine.kr(10000,1000.0.rrand(7000),10), 0.003)}) );
	z = DelayN.ar(s+Dust.ar(1.0.rrand(3),0.4), 0.048);
	y = Mix.ar(Array.fill(7,{ CombL.ar(z, 0.1, LFNoise0.kr(0.1.rand, 0.04, 0.05), 15) })); 
	4.do({ y = AllpassN.ar(y, 0.050, [0.050.rand, 0.050.rand], 1) });
	s+(0.2*y);
DelayN.ar(s, 0.2, 0.2,1, y);
}.play;
	{ synth.free; original }
}
)

//FUNCTION No.2.

//use cursor position & mouse button to  modulate the sound & 
//create envelopes. 

(
{var synth, original;
	original = thisFunction;
	synth = { SinOsc.ar(
		freq: SinOsc.ar(
			freq: MouseX.kr(minval:80,maxval:  
			2000)+MouseButton.kr(12000, (BrownNoise.ar([0.5,0.5])), 1),
			mul: EnvGen.kr(0.1,MouseY.kr(minval:0.01,maxval: 2),1,0.5 ), 
			add: LFNoise0.kr(
				freq: MouseX.kr(minval:5, maxval:15),
				mul: MouseY.kr(20, 500),
				add:(LFNoise0.ar(XLine.kr(1000, 10000, 5), 
				0.25)+rrand(80,100)+MouseButton.kr(80, 
				5500,4)+(BrownNoise.ar([0.5,0.5])))
			)), 
	mul: EnvGen.kr(0.1,MouseX.kr(minval:0.1,maxval: 2),2,1 )
	)}.play;
	{ synth.free; original }}
	)

//FUNCTION No.3.
//(this one is cpu expensive)

(
{
	var synth, original;
		original = thisFunction;
	synth = {var frequency;
Mix.ar({
		frequency = rrand(3000,5000)+MouseX.kr(minval:-1000,maxval:  
			1000);
		Pan2.ar(SinOsc.ar(frequency,
			mul: EnvGen.kr(Env.perc(0.001, frequency/1000), 
				Dust.kr(0.5,0.03), 0.2)),Impulse.kr(30)) }.dup(100))

								}.play;
	{ synth.free; original }
}
)

//[FUNC 3.2]. This is similar the the previous but computationally //very heavy,try it by itself or do not load too much at the same //time, (an added tremolo on the frequency seems to take up too 
//much cpu)...    

(
{
	var synth, original;   
		original = thisFunction;
	synth = {var frequency;
Mix.ar({
		frequency = rrand(3000,5000)+MouseX.kr(minval:-1000,maxval:  
			1000);
		Pan2.ar(SinOsc.ar(frequency-5000+SinOsc.ar(freq: 10, mul: 50),
			mul: EnvGen.kr(Env.perc(0.001, frequency/900), 
				Dust.kr(0.05), 0.2)),Impulse.kr(30)) }.dup(100))
								}.play;
	{ synth.free; original }
}
)

//FUNCTION No.4. 
//noisy 

(
{
	var synth, original;
	original = thisFunction;
	synth = {  RHPF.ar(BrownNoise.ar([0.5,0.5]+SinOsc.ar(MouseY.kr(80,16000), 0, 0.5), -0.49).max(0) * 20, MouseY.kr(0.2,10), 1)  }.play;
	{ synth.free; original }
}
)

//FUNCTION No.5.
//a simple function with two random generators for freq every time //the function is triggered 

(
{
	var synth, original;
	original = thisFunction;
	synth = { Mix.new( Array.fill(8, { SinOsc.ar(MouseX.kr(80, 100) + 1000.0.rand, 0, 0.05) } ),SinOsc.ar(1000.0.rand,0.1), 0, 0.5); 
}.play;
	{ synth.free; original }
}
)

//FUNCTION No.6.
// here is a modified code on fiiltering
// firstly, run the following once.

(
SynthDef("moto-rev", { arg out=0;
	var x;
	x = RLPF.ar(LFPulse.ar(SinOsc.kr(0.2, 0, 10, XLine.kr(50,1000,30)), [0,0.1], 0.1), 
		100, 0.1).clip2(0.4);
	Out.ar(out, x);
}).send(s);

SynthDef("combs", { arg out=0;
	var f, zout;
	f = LFSaw.kr(0.4, 0, 24, LFSaw.kr([2.0.rrand(10),7.23], 0, 3, 80)).midicps; 
	zout = CombN.ar(SinOsc.ar(f+MouseX.kr(minval:0,maxval:  
			200), 0, 0.04), 0.2, 0.2, 4); 
	Out.ar(out, zout);
}).send(s);

SynthDef("rlpf",{ arg out=0,ffreq=600,rq=0.1;
	ReplaceOut.ar( out, RLPF.ar( In.ar(out), ffreq,rq) )
}).send(s);

SynthDef("preDelay", { arg out, rate = 25, cfreq = 1400, mfreq = 1200, rq=0.1;
	var zin, zout;

	zin = In.ar(out, 2);
	cfreq = Lag3.kr(cfreq, 0.1);
	mfreq = Lag3.kr(mfreq+0.0.rrand(500), 0.1);
	rq   = Ramp.kr(rq, 0.1);
	zout = RLPF.ar(zin, LFNoise1.kr(rate, mfreq, cfreq), rq, 10).distort 
					* 0.15;

	ReplaceOut.ar( out , zout ); 
	
}).send(s);

SynthDef("modulate",{ arg out = 0, freq = 1, center = 440, plusMinus = 110;
	Out.kr(out, SinOsc.kr(freq, 0, plusMinus, center));
}).send(s);
)

//////////////////////////////////////////
// You can run the following one by one just to try them

//1. Start the "moto-rev"
y = Synth("moto-rev",["out",0]);

//2. Start the "preDelay"
z = Synth.after(y,"preDelay",["out",0]);

// or you can stop it and resume it
z.run(false);
z.run(true);

//3. The synthdef "combs" after "moto-rev", both write to the bus adding their 
//outputs.
r = Synth.after(y,"combs",["out",0]);

//4. A  resonant low pass filter reading and writing to the same bus
x = Synth.after(z,"rlpf",["out",0]);
//set at 950
x.set("ffreq", 950);

//5. Free them all
x.free;
z.free;
y.free;
r.free;

//we can assign the same exact functions to certain keys (as before):

//assign the "moto-rev" synth
(
{
	var synth, original;
	original = thisFunction;
	synth = {y = Synth("moto-rev",["out",0]);
}.play;
	{ synth.free; original }
}
)

//assign a stop key for "moto-rev" (a different one)
(
{
	var synth, original;
	original = thisFunction;
	synth = {y.free;
}.play;
	{ synth.free; original }
}
)

//assign the "preDelay"
(
{
	var synth, original;
	original = thisFunction;
	synth = {z = Synth.after(y,"preDelay",["out",0]);
	}.play;
	{ synth.free; original }
}
)

//& free it
(
{
	var synth, original;
	original = thisFunction;
	synth = {z.free;
}.play;
	{ synth.free; original }
}
)

//assign "combs"

(
{
	var synth, original;
	original = thisFunction;
	synth = {r = Synth.after(y,"combs",["out",0]);
}.play;
	{ synth.free; original }
}
)

// & free it

(
{
	var synth, original;
	original = thisFunction;
	synth = {r.free;
}.play;
	{ synth.free; original }
}
)

//Now you can assign functions (preferebly that are not cpu 
//expensive) on every key and produce soundscapes by 
//typing words that will appear on the post window.