Key points:
(synths: List[], params: Dictionary.new) // Encapsulation of synths and parameters
Env variables, with synth IDs as keys to parameters in a Dictionary.
~valid = { |param| ... } // Parameter validation function
~transform = { |param| ... } // Parameter transformation function
Implements higher-order functions that operate on Events (parameters), returning Boolean and new Event, respectively.
Collection Methods: select and difference for declarative/functional synth filtering.
synths.select { |synth| ~valid.(params[synth.nodeID]) } // Filter valid synths
synths.difference(valid) // Set operation for removal
Each synth’s state is maintained in parallel data structures (List and Dictionary).
~lims = (
freq: (min: 80, max: 8000),
amp: (min: 0.002, max: 0.08),
pan: (min: -0.8, max: 0.8),
prob: 0.8
);
~valid = { |param|
param.notNil and: {
param[\freq].notNil and:
param[\amp].notNil and:
{param[\freq] < 750} and:
{param[\amp] < 0.13} and:
~lims.prob.coin
}
};
~transform = { |param|
(
freq: (param[\freq] * rrand(0.5, 2.0)).clip(~lims.freq.min, ~lims.freq.max),
amp: (param[\amp] * rrand(0.9, 1.1)).clip(~lims.amp.min, ~lims.amp.max),
pan: if(param[\pan].notNil) { param[\pan] * ~lims.pan.min } { 0 }
)
};
SynthDef(\sine, { |freq=400, amp=0.1, pan=0, gate=1|
var sig, env, wet;
sig = Mix.ar([
SinOsc.ar(freq, 0, 0.3),
SinOsc.ar(freq * 2.02, 0, 0.2),
SinOsc.ar(freq * 0.501, 0, 0.15)
]);
env = EnvGen.kr(Env.asr(2.9, 1, 1.3), Lag.kr(gate, 0.4), doneAction: 2);
sig = sig * env * Lag.kr(amp, 0.1);
sig = LPF.ar(sig, freq * 4);
sig = sig * (1 + LFNoise2.kr(0.5, 0.1));
wet = CombC.ar(sig, 0.3, LFNoise1.kr(0.2).range(0.1, 0.3), 2) * LFNoise2.kr(0.1).range(0.2, 0.5);
sig = (sig + (wet * 0.3)) * 0.7;
Out.ar(0, Pan2.ar(sig, pan));
}).add;
~makeParam = {
(
freq: exprand(100, 400),
amp: exprand(~lims.amp.min, ~lims.amp.max),
pan: rrand(~lims.pan.min, ~lims.pan.max)
)
};
~init = {
var synths = List[];
var params = Dictionary.new;
30.do {
var param = ~makeParam.value;
var synth = Synth(\sine, param.asPairs);
synths.add(synth);
params[synth.nodeID] = param;
};
(synths: synths, params: params)
};
~manage = { |synths, params|
var valid = synths.select { |synth|
~valid.(params[synth.nodeID])
};
synths.difference(valid).do { |synth|
["Removing", synth.nodeID].postln;
synth.set(\gate, 0);
};
valid.do { |synth|
var param = ~transform.(params[synth.nodeID]);
["New ", synth.nodeID, param].postln;
synth.set(*param.asPairs);
params[synth.nodeID] = param;
};
(synths: valid, params: params)
};
s.waitForBoot {
var state = ~init.value;
Routine({
inf.do {
state = ~manage.(state.synths, state.params);
10.0.rand.wait;
}
}).play;
};