Hereās a refactoring of your original example that captures some of the modularity of PD youāre talking about, but does it on the event / pattern side - which is something thatās very much outside the capabilities of PD.
( // only change here is the addition of a \release arg
SynthDef (\simple_sine) {
var env = EnvGen.kr (Env.perc(0.01, \release.kr(1)), doneAction:2);
var sig = SinOsc.ar (\freq.ir (440)) * env;
sig = Pan2.ar (sig, \pan.ir (0), \amp.ir (0.2));
OffsetOut.ar (0, sig);
}.add;
(
~minor_eleven = [ 0, 3, 7, 10, 14, 17 ];
~major_thirteen = [ 0, 4, 7, 14, 18, 21 ];
~chord_shapes = [ ~minor_eleven, ~major_thirteen ];
~chords = Array.fill (4) {|i| 60 - i + ~chord_shapes[i % 2] };
Pdef(\rollChord, {
|chordIndex, rollDelta, count|
Pbind(
\instrument, \simple_sine,
\amp, Pfunc({ rrand(-30, -20).dbamp }),
\midinote, Pser(~chords[chordIndex], count),
\dur, Pfunc({ rollDelta * rrand(0.5, 1) }),
)
});
Pdef(\rollBase, Pbind(
\instrument, \rollChord,
\type, \phrase,
\legato, 1,
\rollDelta, 0.1,
\count, 16,
\chordIndex, 1,
\release, 1,
\chords, ~chords,
\dur, Pkey(\rollDelta, inf) * Pkey(\count) / 2,
));
Pdef(\fast, Pbind(
\rollDelta, 0.02,
\dur, Pkey(\rollDelta, inf) * Pkey(\count) / 2,
));
Pdef(\slow, Pbind(
\rollDelta, 0.5,
\dur, Pkey(\rollDelta, inf) * Pkey(\count) / 2,
));
Pdef(\fastSlow, Pbind(
\rollDelta, Pn(Penv([0.15, 0.25, 0.15], [30, 30]), inf),
\dur, Pkey(\rollDelta, inf) * Pkey(\count) / 2,
));
Pdef(\clipped, Pbind(
\count, Pfunc({ rrand(4, 8) })
));
Pdef(\long, Pbind(
\dur, 8,
\count, inf,
));
Pdef(\strum, Pbind(
\dur, 4,
\count, 8,
\release, 10,
\rollDelta, 0.05
));
Pdef(\chordSequence, Pbind(
\chordIndex, Pstutter(2, Pseq([
0, 1, 2, 3, 2, 3, 2, 2, 1
], inf))
));
Pdef(\rollA, Pdef(\slow) <> Pdef(\clipped) <> Pdef(\chordSequence) <> Pdef(\rollBase));
Pdef(\rollB, Pdef(\long) <> Pdef(\fast) <> Pdef(\chordSequence) <> Pdef(\rollBase));
Pdef(\rollC, Pdef(\strum) <> Pdef(\chordSequence) <> Pdef(\rollBase));
Pdef(\roll).source = Pdef(\rollA);
Pdef(\roll).play;
)
This breaks several of the gestural properties apart into separate Pdefs. Then, it uses the chain operator <>
to compose several of these abstractions together into a composite Pdef, and plays it. You can change the source of your final Pdef(\roll)
by switching the source = ...
line. In addition, you can make modifications to any of the Pdefs being composed and the changes will make their way into your already-playing output Pdef. For example, adding a \release, 5
key to Pdef(\slow)
will hold the note longer in a really nice way.
This is obviously more verbose than your original, but itās factored into semantically meaningful chunks. With experimentation, you can build out a library of interesting Pdefs for a particular SynthDef or scenario, and then work at a compositional level more like the Pdef(\rollA)
synths at the bottom. If you are smart about using Pkey
in your base patterns, you can parameterize them to some extent. For example:
Pdef(\fastBase, Pbind(
\rollDelta, 0.5 / Pkey(\howFast),
\dur, Pkey(\rollDelta, inf) * Pkey(\count) / 2,
));
Pdef(\fast, Pbind(\howFast, 4) <> Pdef(\fastBase));
Pdef(\faster, Pbind(\howFast, 6) <> Pdef(\fastBase));
Pdef(\fastest, Pbind(\howFast, 8) <> Pdef(\fastBase));