(
var bufnum;
bufnum = 100;

SynthDef("grain", { arg gate=0.0, vol=1.0, attack=0.01, decay=0.3,
pos=0.0, rate=1.0;
var snd, env;
snd = Mix(PlayBuf.ar(2, bufnum, rate, startPos:pos, loop: 1)) * 0.2;
env = Linen.kr(gate, attack, vol, decay);
FreeSelfWhenDone.kr(env);
Out.ar(0, snd * env);
}).writeDefFile;

s.sendSynthDef("grain");
s.sendMsg("/b_allocRead", bufnum,
"/System/Library/Sounds/Submarine.aiff");
)

(
var sl1, sl2, sl3, sl4, sl5, sl6, nb1, nb2, nb3, nb4, nb5, nb6;
var volSpec, attackSpec, decaySpec, sepSpec, densitySpec, rateSpec;
var grainPos, grainSep, grainDensity, soundFile, fileLength;
var textWidth;
textWidth = 48;
grainPos = 0.0;
grainSep = 800.0;
grainDensity = 0.01;
soundFile = SoundFile.new;
soundFile.openRead("/System/Library/Sounds/Submarine.aiff");
fileLength = soundFile.numFrames / 2;
fileLength.postln;

volSpec = ControlSpec(0, 5.0, \lin, 0, 1.0).asSpec;
attackSpec = ControlSpec(0.0001, 0.1, \lin, 0.001, 0.02).asSpec;
decaySpec = ControlSpec(0.0001, 0.1, \lin, 0.001, 0.02).asSpec;
sepSpec = ControlSpec(100, 10000, \exp, 0.1, grainSep).asSpec;
densitySpec = ControlSpec(0.0001, 0.5, \exp, 0.0001,
grainDensity).asSpec;
rateSpec = ControlSpec(0.1, 10, \exp, 0.01, 1.0).asSpec;

//*** <GUI>
// create a window
w = SCWindow("Grain", Rect(200, 300, 310, 180));
w.view.background = Color(0.7,0.8,0.6);
w.view.decorator = FlowLayout(w.view.bounds);
SCStaticText(w, Rect(0, 0, textWidth, 15)).string = "volume";
sl1 = SCSlider(w, Rect(0, 0, 200, 24)); sl1.resize = 2; sl1.step =
0.01;
nb1 = SCNumberBox(w, Rect(0, 0, 46, 20)); nb1.resize = 3;
sl1.action = { nb1.string = volSpec.map(sl1.value) };
nb1.action = { sl1.value = volSpec.unmap(nb1.value) };
sl1.value = volSpec.unmap(volSpec.default);
SCStaticText(w, Rect(0, 0, textWidth, 15)).string = "attack";
sl2 = SCSlider(w, Rect(0, 0, 200, 24)); sl2.resize = 2; sl2.step =
0.01;
nb2 = SCNumberBox(w, Rect(0, 0, 46, 20)); nb2.resize = 3;
sl2.action = { nb2.string = attackSpec.map(sl2.value) };
nb2.action = { sl2.value = attackSpec.unmap(nb2.value) };
sl2.value = attackSpec.unmap(attackSpec.default);
SCStaticText(w, Rect(0, 0, textWidth, 15)).string = "decay";
sl3 = SCSlider(w, Rect(0, 0, 200, 24)); sl3.resize = 2; sl3.step =
0.01;
nb3 = SCNumberBox(w, Rect(0, 0, 46, 20)); nb3.resize = 3;
sl3.action = { nb3.string = decaySpec.map(sl3.value) };
nb3.action = { sl3.value = decaySpec.unmap(nb3.value) };
sl3.value = decaySpec.unmap(decaySpec.default);
SCStaticText(w, Rect(0, 0, textWidth, 15)).string = "separate";
sl4 = SCSlider(w, Rect(0, 0, 200, 24)); sl4.resize = 2; sl4.step =
0.01;
nb4 = SCNumberBox(w, Rect(0, 0, 46, 20)); nb4.resize = 3;
sl4.action = { nb4.string = sepSpec.map(sl4.value) };
nb4.action = { sl4.value = sepSpec.unmap(nb4.value) };
sl4.value = sepSpec.unmap(sepSpec.default);
SCStaticText(w, Rect(0, 0, textWidth, 15)).string = "density";
sl5 = SCSlider(w, Rect(0, 0, 200, 24)); sl5.resize = 2; sl5.step =
0.01;
nb5 = SCNumberBox(w, Rect(0, 0, 46, 20)); nb5.resize = 3;
sl5.action = { nb5.string = densitySpec.map(sl5.value) };
nb5.action = { sl5.value = densitySpec.unmap(nb5.value) };
sl5.value = densitySpec.unmap(densitySpec.default);
SCStaticText(w, Rect(0, 0, textWidth, 15)).string = "rate";
sl6 = SCSlider(w, Rect(0, 0, 200, 24)); sl6.resize = 2; sl6.step =
0.01;
nb6 = SCNumberBox(w, Rect(0, 0, 46, 20)); nb6.resize = 3;
sl6.action = { nb6.string = rateSpec.map(sl6.value) };
nb6.action = { sl6.value = rateSpec.unmap(nb6.value) };
sl6.value = rateSpec.unmap(rateSpec.default);
// show the window
w.front;
//*** </GUI>

f = { var id;
grainPos = grainPos + sepSpec.map(sl4.value);
if( grainPos > fileLength, {grainPos = 0.0});
id = UniqueID.next;

s.sendBundle(0.2, ["/s_new", "grain", id, 1, 0, "gate", 1.0, "vol",
volSpec.map(sl1.value), "attack", attackSpec.map(sl2.value), "decay",
decaySpec.map(sl3.value), "pos", grainPos, "rate",
rateSpec.map(sl6.value)]);
g.value(attackSpec.map(sl2.value), id)
};

g = { arg dur, id;
SystemClock.play(
Routine({
dur.wait;
s.sendBundle(0.2, ["/n_set", id, "gate", 0.0]);
})
)
};

Routine({
loop({
f.value;
densitySpec.map(sl5.value).wait;
});
}).play(AppClock);

)