// see:
// http://www.sfu.ca/~truax/gran.html
//---- granular synthesis
// RedGrain
//-- prepare some buffers
~length= 3; //segment lengths in seconds - should be the same for positions to scan correctly
~b1= RedBuffer(s, "sounds/playground.snd", 0.1, ~length);
~b2= RedBuffer(s, "sounds/playground.snd", 0.3, ~length);
~b3= RedBuffer(s, "sounds/playground.snd", 0.5, ~length);
//-- create and start the granulator object
r= RedGrain(s);
r.buf_(~b1);
r.start;
//offset halfway into source buffer
r.pos_(0.5);
//
r.delta_(0.1);
r.amp_(5.5);
r.pos_(0.51);
r.pan_(0.5);
r.rate_(1.0);
r.dur_(0.001);
//switch buffer
r.buf_(~b2);
//cycle through buffers quickly 20 times
(
fork{
20.do{|i| r.buf_([~b1, ~b2, ~b3].wrapAt(i)); 0.1.wait};
});
//cycle through buffers and randomise playback rate
(
fork{
20.do{|i|
r.buf_([~b1, ~b2, ~b3].wrapAt(i));
r.rate_(2.0.rand2);
0.1.wait;
}
});
//now for something completely different
(
r.rate_(-1.7);
r.pos_(0);
r.dur_(0.2);
r.delta_({0.2.rrand(0.3)}); //delta can be a function
)
//and all of the controllers can be functions
(
r.buf_({[~b1, ~b2, ~b3].choose});
r.rate_({8.0.rand2});
r.pos_({0.1.rand});
r.dur_({0.05.rrand(0.15)});
r.pan_({1.0.rand2});
r.amp_({0.5.rrand(1.0)});
r.delta_({0.01.rrand(0.2)});
)
r.start;
//quantise playbackrate
r.rate_({10.rand.round(5)});
r.rate_({10.rand.round(4)});
r.rate_({10.rand.round(3)});
//back to static amp and deltatime
r.amp_(0.3);
r.delta_(0.005); //default
r.dur_(0.1);
r.buf_(~b1);
r.pos_(0);
//take out more and more and stop
(
fork{
30.do{|i| r.delta_(r.delta*1.1); 0.2.wait};
r.stop;
});
r.stop;
r.free;
//// RedAGrain
// server side granulator...
//-- prepare some buffers
~length= 3; //segment lengths in seconds - should be the same for positions to scan correctly
~b1= RedBuffer(s, "sounds/playground.snd", 0.1, ~length);
~b2= RedBuffer(s, "sounds/playground.snd", 0.3, ~length);
~b3= RedBuffer(s, "sounds/playground.snd", 0.5, ~length);
~length= 1; //segment lengths in seconds - should be the same for positions to scan correctly
~b1= RedBuffer(s, "sounds/1-left.snd", 0.1, ~length);
~b2= RedBuffer(s, "sounds/1-left.snd", 0.3, ~length);
~b3= RedBuffer(s, "sounds/1-left.snd", 0.5, ~length);
//-- create and start the granulator object
r= RedAGrain(s);
r.start;
//again note that all lfo synths must send audiorate
//and be careful to free lfo synths before creating new ones - to not loose handle
//-- init lfo synth
(
//create lfo synth for trigger - here 30 hz
a= SynthDef("trig", {Out.ar(r.trigBus.index, Impulse.ar(30))}).play;
//create lfo synth for bufnum - static at ~b1 bufnum
b= SynthDef("buf", {Out.ar(r.bufBus.index, DC.ar(~b1.bufnum))}).play;
//create lfo synth for rate - slowly rising
c= SynthDef("rate", {Out.ar(r.rateBus.index, LFSaw.ar(0.1, 0, 0.1, 1))}).play;
//create lfo synth for position - static at 0
d= SynthDef("pos", {Out.ar(r.posBus.index, DC.ar(0))}).play;
//create lfo synth for duration - slow drunk walk
e= SynthDef("dur", {Out.ar(r.durBus.index, LFNoise2.ar(1, 0.1, 0.11))}).play;
//create lfo synth for amplitude control - static at 0.4
f= SynthDef("pan", {Out.ar(r.panBus.index, DC.ar(0.4))}).play;
//create lfo synth for amplitude control - static at 0.4
g= SynthDef("amp", {Out.ar(r.ampBus.index, DC.ar(0.4))}).play;
)
//-- trigBus
//change trigger control lfo to another dusty one
a.free;
a= SynthDef("trig", {Out.ar(r.trigBus.index, Dust.ar(10))}).play;
//change to static fast trigger
a.free;
a= SynthDef("trig", {Out.ar(r.trigBus.index, Impulse.ar(140))}).play;
//let mouseX control trigger rate
a.free;
a= SynthDef("trig", {Out.ar(r.trigBus.index, Impulse.ar(MouseX.kr(2, 200)))}).play;
//-- bufBus
//change buffer to ~b2
b.free;
b= SynthDef("buf", {Out.ar(r.bufBus.index, DC.ar(~b2.bufnum))}).play;
//play from the 3 buffers in cycle
b.free;
b= SynthDef("buf", {Out.ar(r.bufBus.index, Demand.ar(Impulse.ar(4), 0, Dseq([~b1.bufnum, ~b2.bufnum, ~b3.bufnum], inf)))}).play;
//quick drunk walk change buffers (little bit dangerous if ~b1-3 not following each other here)
b.free;
b= SynthDef("buf", {Out.ar(r.bufBus.index, Demand.ar(Impulse.ar(100), 0, Dbrown(~b1.bufnum, ~b3.bufnum, 1, inf)))}).play;
//slow back and forth change of buffers - 4 changes per segment length
b.free;
b= SynthDef("buf", {Out.ar(r.bufBus.index, Demand.ar(Impulse.ar(~length.reciprocal*4), 0, Dseq([~b1.bufnum, ~b2.bufnum, ~b3.bufnum, ~b2.bufnum], inf)))}).play;
//faster back and forth change of buffers - 32 changes per segment length
b.free;
b= SynthDef("buf", {Out.ar(r.bufBus.index, Demand.ar(Impulse.ar(~length.reciprocal*32), 0, Dseq([~b1.bufnum, ~b2.bufnum, ~b3.bufnum, ~b2.bufnum], inf)))}).play;
//-- rateBus
//varying rate with drunk walk
c.free;
c= SynthDef("rate", {Out.ar(r.rateBus.index, LFNoise2.ar(1, 0.3, 1))}).play;
//halfspeed backwards static rate
c.free;
c= SynthDef("rate", {Out.ar(r.rateBus.index, DC.ar(-0.5))}).play;
//falling rate during segment length
c.free;
c= SynthDef("rate", {Out.ar(r.rateBus.index, LFSaw.ar(~length.reciprocal.neg, 0, 0.1, 1))}).play;
//-- posBus
//set position to normal speed scanning forwards
d.free;
d= SynthDef("pos", {Out.ar(r.posBus.index, LFSaw.ar(~length.reciprocal, 0, 0.5, 0.5))}).play;
//position slow scanning forwards
d.free;
d= SynthDef("pos", {Out.ar(r.posBus.index, LFSaw.ar(0.4, 0, 0.5, 0.5))}).play;
//position slow scanning backwards
d.free;
d= SynthDef("pos", {Out.ar(r.posBus.index, LFSaw.ar(-0.5, 0, 0.5, 0.5))}).play;
//mouseX controls position
d.free;
d= SynthDef("pos", {Out.ar(r.posBus.index, K2A.ar(MouseX.kr(0, 1, lag:1)))}).play;
//mouseXY control + a little random
d.free;
d= SynthDef("pos", {Out.ar(r.posBus.index, LFNoise2.ar(120, MouseY.kr(0, 0.1), MouseX.kr(0, 1, lag:1)))}).play;
//back to static position
d.free;
d= SynthDef("pos", {Out.ar(r.posBus.index, DC.ar(0))}).play;
//-- durBus
//change to short static duration
e.free;
e= SynthDef("dur", {Out.ar(r.durBus.index, DC.ar(0.02))}).play;
//random duration between 0.01 and 0.41
e.free;
e= SynthDef("dur", {Out.ar(r.durBus.index, LFNoise0.ar(4, 0.2, 0.21))}).play;
//mouseY controls duration
e.free;
e= SynthDef("dur", {Out.ar(r.durBus.index, K2A.ar(MouseY.kr(0, 0.6)))}).play;
//slow phasor control duration
e.free;
e= SynthDef("dur", {Out.ar(r.durBus.index, LFSaw.ar(0.4, 0, 0.04, 0.05))}).play;
//-- panBus
//sine panning
f.free;
f= SynthDef("pan", {Out.ar(r.panBus.index, SinOsc.ar(4))}).play;
//slow random panning
f.free;
f= SynthDef("pan", {Out.ar(r.panBus.index, LFNoise2.ar(0.3))}).play;
//random discrete panning at fast rate
f.free;
f= SynthDef("pan", {Out.ar(r.panBus.index, Demand.ar(Impulse.ar(100), 0, Dseq([-0.9, 0, 0.9], inf)))}).play;
//-- ampBus
//fast jumps in amplitude
g.free;
g= SynthDef("amp", {Out.ar(r.ampBus.index, LFNoise0.ar(40, 0.2, 0.2).max(0))}).play;
//fade out during each segment
g.free;
g= SynthDef("amp", {Out.ar(r.ampBus.index, LFSaw.ar(~length.reciprocal.neg, 0, 0.5, 0.5))}).play;
//-- more complex example
[a, b, c, d, e, f, g].do(_.free);
(
//master trigger controlling the other lfos
a= SynthDef("trig", {Out.ar(r.trigBus.index, Impulse.ar(LFNoise1.ar(0.5, 10, 80)))}).play;
b= SynthDef("buf", {Out.ar(r.bufBus.index, DC.ar(~b1.bufnum))}).play;
c= SynthDef("rate", {Out.ar(r.rateBus.index, Demand.ar(InFeedback.ar(r.trigBus.index), 0, Dseq([1, 2], inf)))}).play;
d= SynthDef("pos", {Out.ar(r.posBus.index, Demand.ar(InFeedback.ar(r.trigBus.index), 0, Dseq([Dseries(0, 0.002, 900)], inf)))}).play;
e= SynthDef("dur", {Out.ar(r.durBus.index, Demand.ar(InFeedback.ar(r.trigBus.index), 0, Dseq([Dseries(0.01, 0.001, 100)], inf)))}).play;
f= SynthDef("pan", {Out.ar(r.panBus.index, Demand.ar(InFeedback.ar(r.trigBus.index), 0, Dseq([-0.9, 0, 0.9], inf)))}).play;
g= SynthDef("amp", {Out.ar(r.ampBus.index, DC.ar(0.4))}).play;
)
//-- clean up and stop
[a, b, c, d, e, f, g, ~b1, ~b2, ~b3].do(_.free);
r.free
////
// Controller
//prepare some buffers
~length= 1.8; //segment lengths in seconds - should be the same for positions to scan correctly
~b1= RedBuffer(s, "sounds/1-left.snd", 0.0, ~length);
~b2= RedBuffer(s, "sounds/1-left.snd", 0.1, ~length);
~b3= RedBuffer(s, "sounds/1-left.snd", 0.2, ~length);
//create and start a grain object
r= RedGrain(s);
r.buf_(~b1);
r.start;
r.stop;
//create and start the controller object
g= RedGrainController(r);
g.start;
//for density - position and index is passed in as arguments
g.density_(0.1); //10% on and 90% off
g.density_(0.75); //75% on and 25% off
g.density_({|o| o}); //from 0 to 100% -depend on position
g.density_({|o, i| [0, 0.3].asSpec.map(i%1000/1000)}); //slowly from 0% to 30%
g.density_({|o, i| i%2}); //only every second ie 50%
g.density_(1); //all on
//for positionStart - position and index is passed in as arguments
g.positionStart_({|o| o}); //no change
g.positionStart_({|o, i| [0, 0.2.rand, 0.5].foldAt(i)});//pattern with random element
g.positionStart_({|o| o+0.0015.rand2}); //minimal drunk
g.positionStart_({|o| o+0.01.rand2}); //little drunk
g.positionStart_({|o| o+0.05.rand2}) //more drunk
g.positionStart_({1.0.rand}) //total random offset start
g.positionStart_(0.1); //freeze
g.positionStart_({|o| if(0.03.coin, {o+0.1}, {o})}); //small chance to skip forward
//for positionSpeed - position and index is passed in as arguments
g.positionSpeed_({|o| if(o<0.5, {0.5}, {1.1})}); //first half 50%, rest 110%
g.positionSpeed_(0.4); //slow forward
g.positionSpeed_(0); //freeze
g.positionSpeed_(-1); //static backwards
g.positionSpeed_({|o| [1, 2, 'exp'].asSpec.map(o)}); //accelerate
g.positionSpeed_({|o, i| #[0.5, 1.25, 5].wrapAt(i)}); //pattern
g.positionSpeed_({|o| #[0.1, 1, 1.5].wrapAt(o%0.5*50)});//3 positions
//for grainBuf - buffer, position and index is passed in as arguments
g.grainBuf_(~b2); //static buffer
g.grainBuf_({[~b1, ~b2, ~b3].choose}); //randomise buffer
g.grainBuf_({[~b1, ~b3].wchoose(#[0.3, 0.7])}); //weighted random
g.grainBuf_({|buf, o, i| [~b2, ~b1, ~b2, ~b2, ~b3].wrapAt(i/50%5)});//pattern
g.grainBuf_({|buf, o, i| [~b1, ~b2, ~b3].wchoose(FloatArray[0.1, 1.0, 0.0].interpolate([0.0, 0.0, 1.0], o).normalizeSum)});//interpolate -buffer depend on position
g.grainBuf_({|buf, o, i| [~b1, ~b2, ~b3].wchoose(FloatArray[0.2, 0.8, 0.0].interpolate([0.0, 0.0, 1.0], i%9000/9000).normalizeSum)}) //interpolate weights slowly
//for grainRate - rate, position and index is passed in as arguments
g.grainRate_(0.9); //transpose
g.grainRate_({1.0+0.01.rand2}); //rate fluctuate
g.grainRate_({|rate, o, i| o*0.5+0.75}); //transpose upwards -depend on position
//for grainDur - duration, position and index is passed in as arguments
g.grainDur_(0.03); //static short
g.grainDur_(0.3); //static long duration
g.grainDur_({0.2.rand}); //randomise duration
g.grainDur_({|dur, o| #[0.01, 0.4, 'exp'].asSpec.map(o)});//from short to long -depend on position
g.grainDur_({|dur| dur= (dur+0.0001).min(0.6)}); //grows and grows (independent of position)
g.grainDur_({|dur, o, i| #[0.01, 0.02, 0.1].foldAt(i)});//pattern
//for grainAmp - amplitude, position and index is passed in as arguments
g.grainAmp_(0.1); //quite quiet
g.grainAmp_(1); //unity gain
g.grainAmp_({1.0.rand}); //randomise amplitude for each grain
g.grainAmp_({|amp, o, i| o.rand}); //rising random -depend on position
g.grainAmp_({|amp, o, i| #[0, 0, 0, 1, 0, 0, 1, 1].wrapAt(i/5)});//pattern
//for grainPan - panning, position and index is passed in as arguments
g.grainPan_(-1); //static full left
g.grainPan_({1.0.rand2}); //random panning for each grain
g.grainPan_({|pan, o, i| sin(o*2*pi)}); //sine -depend on position
g.grainPan_({|pan, o, i| sin(i%1000/1000*2*pi)}); //sine (not dependent on position)
g.grainPan_({|pan, o, i| #[0, 0, 0, -1, 0, 0, 1, -1].wrapAt(i)});//pattern
//set up a dictionary to control the gain of the different buffers
(
~dict= (
~b1.bufnum : 0.2,
~b2.bufnum : 1,
~b3.bufnum : 0.2
));
g.gain_(~dict);
g.grainBuf_(~b2); //loud
g.grainBuf_(~b1); //soft
//stop and clear
g.stop;
r.stop;
(
r.free;
~b1.free;
~b2.free;
~b3.free;
)
////