Last active
May 7, 2025 06:31
-
-
Save scztt/33e5ca33cad184078942294869faee70 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
( | |
SynthDef(\default, { | |
var env, sig, freq, velocity; | |
velocity = \velocity.kr(80).linlin(0, 127, 0, 1); | |
env = Env.adsr( | |
\attackTime.kr(0.05), | |
\decayTime.kr(0.2), | |
\sustainLevel.kr(-2.dbamp), | |
\releaseTime.kr(0.5), | |
curve:-2 | |
).kr(gate:\gate.kr(1), doneAction:2); | |
freq = \freq.kr(440); | |
freq = freq * [ | |
0, | |
env.linlin(0, 1, 0.1, 0.3) | |
].midiratio; | |
sig = Saw.ar(freq).sum; | |
sig = BLowPass.ar( | |
sig, | |
env.linexp( | |
0, 1, | |
velocity.linexp(0, 1, 80, 300), | |
velocity.linexp(0, 1, 700, 4200), | |
) * \filt.kr(0.75), | |
0.8 | |
); | |
sig = Pan2.ar(sig, \pan.kr(0), \amp.kr(1) * env); | |
OffsetOut.ar(\out.kr(0), sig) | |
}).add | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Event.addEventType(\pattern, { | |
var pat, event, outerEvent, recursionLevel, instrument, embeddingLevel, freq, rest; | |
var args, defaults, timingOffset; | |
~pattern = ~pattern ?? { ~instrument }; | |
if (~pattern.isKindOf(Function)) { | |
defaults = ~pattern.def.prototypeFrame; | |
args = ~pattern.def.argNames.collect { | |
|name, i| | |
currentEnvironment[name].value ?? { defaults[i] } | |
}; | |
pat = ~pattern.value(*args); | |
} { | |
if (~pattern.isKindOf(Symbol)) { | |
~pattern = Pdef(~pattern); | |
}; | |
pat = ~pattern.value; | |
}; | |
if (pat.isKindOf(Event)) { | |
Error("Event patterns must be wrapped in a Ref or a function when passed in as an \instrument argument").throw; | |
}; | |
if(pat.notNil) { | |
if (pat.isKindOf(PatternProxy)) { | |
pat = pat.pattern; // optimization. outer pattern takes care for replacement | |
}; | |
// preserve information from outer pattern, but not delta. | |
if(~transparency ? true) { | |
outerEvent = currentEnvironment.copy; | |
// outerEvent[\instrument] = nil; | |
outerEvent[\pattern] = nil; | |
outerEvent[\type] = nil; | |
outerEvent[\parentType] = nil; | |
outerEvent[\timingOffset] = nil; | |
outerEvent[\addToCleanup] = nil; | |
outerEvent[\removeFromCleanup] = nil; | |
} { | |
outerEvent = Event.default; | |
}; | |
outerEvent.put(\delta, nil); // block delta modification by Ppar | |
outerEvent.put(\instrument, ~synthDef); | |
timingOffset = ~timingOffset.value ? 0; | |
// not sure why we DON'T need to account for positive timingOffset here, | |
// but if we do it breaks.... | |
if (~gatePattern ? true) { | |
pat = pat.finDur(~sustain.value - timingOffset.min(0)); | |
}; | |
pat = Pevent(pat, outerEvent); | |
if (timingOffset < 0) { | |
pat = pat.asStream; | |
pat.fastForward(timingOffset.neg, 0, outerEvent); | |
pat = pat.asEventStreamPlayer(outerEvent); | |
pat.play(thisThread.clock, quant:0.0); | |
} { | |
pat.play(thisThread.clock, outerEvent, 0.0) | |
} | |
} | |
}, Event.parentEvents.default.copy.putAll((legato:1))); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Couple notes: | |
// [1] Pdef | |
// I'll wrap my Pbinds in Pdef, because if you run the code again it will | |
// just swap out the playing pattern rather than start it again, so you | |
// can make adjustments on the fly.&&(function, adverb) | |
// Pdef just defines a pattern with a name, so: | |
// Pdef(\foo, Pbind()) | |
// defines a Pbind pattern named \foo. You can do: | |
// Pdef(\foo).play; | |
// Pdef(\foo).stop; | |
// And you can combine Pdef(\foo, Pbind(...)).play in one block of code | |
// [2] default Synth | |
// I added attached a better default synth in default.scd, its just less | |
// annoying when testing vs the built-in one. You can run that code to | |
// add it. | |
// [2] \type, \pattern | |
// If you specify the \type key as \pattern, then each event in your Pbind | |
// will play the Pbind specified in the \pattern key more or less AS IF | |
// it were a note. I'll give some examples of how this works, but conceptually | |
// it will be as if you replaced the note with a pattern. | |
// [3] Duration stuff | |
// You may know this, but it's important for the examples.... | |
// Event durations use three related keys: \dur, \legato, and \sustain. | |
// | |
// \dur (required) is the time until the *next event* | |
// \sustain (optional) is HOW LONG the current event plays. If it's less | |
// than \dur, the event fill finish before the next one - if it's | |
// longer, then they will overlap | |
// \legato (optional) is how long the event plays IN RELATION to the \dur | |
// so, (\dur, 4, \legato, 0.5) will play for 2 seconds, and the next | |
// event comes in 4. | |
// So, \sustain is defined as \legato * \dur. You ONLY specify one of | |
// \legato or \sustain, whichever makes more sense for how you want to | |
// set time on your events. | |
// [4] Scale stuff | |
// You also may know but I'll use three of the many note-related keys: | |
// | |
// \octave (this ones obvious) | |
// \degree (steps in a scale) | |
// \scale (the scale you're using - by default, diatonic major) | |
// You can list scales and the notes in them with this code snippet: | |
// Scale.names.do { |name| "Scale.% %".format(name, Scale.all[name].degrees).postln } | |
// If don't want to be locked to a scale and just want to manage that yourself | |
// you can use \scale, Scale.diatonic to get all 12 degrees. | |
// You can also specify \midinote to bypass both \octave and \degree | |
( | |
// Simple and slow, with overlaps | |
Pdef(\test, | |
Pbind( | |
// timing | |
\dur, 4, | |
\legato, 1.5, | |
// notes | |
\scale, Scale.augmented, | |
\octave, 3, | |
\degree, Pseq([1, -1, 3, 5], inf), | |
// timbre | |
\attackTime, 2, \decayTime, 2, \releaseTime, 1, | |
\filt, Pwhite(0.2, 0.6) | |
) | |
).play | |
) | |
( | |
// Pattern type. This will replace each 4 beat note | |
// with a pattern that plays repeated 1/4 beat notes | |
Pdef(\test, | |
Pbind( | |
\type, \pattern, | |
\pattern, { | |
Pbind( | |
\dur, 1/4, | |
\attackTime, 1/8, \decayTime, 1/8, \releaseTime, 0.2 | |
) | |
}, | |
// timing | |
\dur, 4, | |
\legato, 1.5, | |
// notes | |
\scale, Scale.augmented, | |
\octave, 3, | |
\degree, Pseq([1, -1, 3, 5], inf), | |
// timbre | |
\attackTime, 2, \decayTime, 2, \releaseTime, 1, | |
\filt, Pwhite(0.2, 0.6) | |
) | |
).play; | |
// Note that: the inner pattern inherits the keys from the outer pattern. | |
// So, if we dont's specify anything, our note is the same just repeated. | |
// Notice that I'm overriding the \attackTime though! | |
// \pattern HAS to be inside a {function} for some reasons that may be clear later | |
) | |
( | |
// \type can itself be a pattern, so you can alternate | |
// between single events and phrases | |
Pdef(\test, | |
Pbind( | |
\type, Pseq([\note, \pattern], inf), | |
\pattern, { | |
Pbind( | |
\dur, 1/4, | |
\attackTime, 1/8, \decayTime, 1/8, \releaseTime, 0.2 | |
) | |
}, | |
// timing | |
\dur, 4, | |
\legato, 1.5, | |
// notes | |
\scale, Scale.augmented, | |
\octave, 3, | |
\degree, Pseq([1, -1, 3, 5], inf), | |
// timbre | |
\attackTime, 2, \decayTime, 2, \releaseTime, 1, | |
\filt, Pwhite(0.2, 0.6) | |
) | |
).play; | |
) | |
( | |
// Inner patterns can be arbitrarily complex - its just another pattern. | |
// Note that BEFORE my \filt was coming from the outer pattern, so it | |
// was the same for all notes. But now I'm overriding it, so it's different | |
// for each note. | |
Pdef(\test, | |
Pbind( | |
\type, \pattern, | |
\pattern, { | |
Pbind( | |
\dur, Prand([1, 2, 4], inf) * 1/6, | |
\filt, Pwhite(0.2, 0.6), | |
\attackTime, 1/8, \decayTime, 1/8, \releaseTime, 0.2 | |
) | |
}, | |
// timing | |
\dur, 4, | |
\legato, 2, | |
// notes | |
\scale, Scale.augmented, | |
\octave, 3, | |
\degree, Pseq([1, -1, 3, 5], inf), | |
// timbre | |
\attackTime, 2, \decayTime, 2, \releaseTime, 1, | |
\filt, Pwhite(0.2, 0.6) | |
) | |
).play; | |
) | |
( | |
// Inner patterns can be arbitrarily complex - its just another pattern. | |
// Note that BEFORE my \filt was coming from the outer pattern, so it | |
// was the same for all notes of the inner pattern. But now I'm overriding | |
// it, so it's different for each note. | |
// Arguments to the \pattern function come from keys in the | |
// outer event. This is good to capture something like \degree and build it | |
// into another pattern. but there are other uses also... | |
Pdef(\test, | |
Pbind( | |
\type, \pattern, | |
\pattern, { | |
|degree| | |
Pbind( | |
\dur, Prand([1, 2, 4], inf) * 1/6, | |
\degree, degree + Pshuf([0, 3, 6]).repeat, | |
\filt, Pwhite(0.1, 0.8), | |
\attackTime, 0.01, \decayTime, 1/8, \releaseTime, 0.2 | |
) | |
}, | |
// timing | |
\dur, 4, | |
\legato, 2, | |
// notes | |
\scale, Scale.hexSus, | |
\octave, 3, | |
\degree, Pseq([0, 3, 5, -2], inf), | |
// timbre | |
\attackTime, 2, \decayTime, 2, \releaseTime, 1, | |
\filt, Pwhite(0.2, 0.6) | |
) | |
).play; | |
) | |
( | |
// I'm using a \steps key now - this doesn't have any specific meaning, | |
// I'm JUST using it as a parameter to my inner pattern to change the | |
// steps in my degree pattern. I can modulate this in the outside pattern. | |
// Important to know that the OUTER pattern value stays the same for the | |
// duration of the inner pattern, e.g. if I get \steps, 2, then my inner | |
// pattern has a fixed steps 2 for the whole time it plays (4*2 beats) | |
Pdef(\test, | |
Pbind( | |
\type, \pattern, | |
\pattern, { | |
|degree, steps| | |
Pbind( | |
\dur, Prand([1, 2, 4], inf) * 1/6, | |
\degree, degree + (steps * Pshuf([0, 1, 2]).repeat), | |
\filt, Pwhite(0.1, 0.8), | |
\attackTime, 0.01, \decayTime, 1/8, \releaseTime, 0.2 | |
) | |
}, | |
// timing | |
\dur, 4, | |
\legato, 2, | |
// notes | |
\scale, Scale.hexSus, | |
\octave, 3, | |
\degree, Pseq([0, 3, 5, -2], inf), | |
\steps, Prand([2, 3, 4], inf), | |
// timbre | |
\attackTime, 2, \decayTime, 2, \releaseTime, 1, | |
\filt, Pwhite(0.2, 0.6) | |
) | |
).play; | |
) | |
( | |
// The inner pattern will play for a duration according to \dur, \sustain, \legato. | |
// So, a version with differing durs. You can change \legato to 1 to hear it without overlaps. | |
// Note that i also added an additional duration modulation parameter, \durStep | |
Pdef(\test, | |
Pbind( | |
\type, Prand([\note, \pattern, \pattern], inf), | |
\pattern, { | |
|degree, steps, durStep| | |
Pbind( | |
\dur, durStep * Prand([1, Rest(1)], inf), | |
\sustain, 1/4, | |
\timingOffset, Prand([0, 0, 1/64], inf), | |
\degree, degree + (steps * Pshuf([0, 1, 2]).repeat), | |
\filt, Pexprand(0.02, 1), | |
\attackTime, 0.01, \decayTime, 1/16, \releaseTime, 2.5 | |
) | |
}, | |
// timing | |
\dur, Prand([4, 2], inf), | |
\durStep, Pshuf([2/16, 4/16]).repeat, | |
\legato, 2, | |
// notes | |
\scale, Scale.phrygian, | |
\octave, Prand([3, 4, 5, 5, 5], inf), | |
\degree, Pseq([0, 3, 5, -2], inf), | |
\steps, Prand([2, -3, -1, 5], inf), | |
// timbre | |
\attackTime, 2, \decayTime, 2, \releaseTime, 1, | |
\filt, Pwhite(0.2, 0.6) | |
) | |
).play; | |
) | |
( | |
// \pattern can also be a pattern of \symbols - if THIS is the case, | |
// then the pattern that gets played is pulled from a Pdef with the same | |
// name. So, this is pretty much equivalent to what we had before, but | |
// expressed differently. | |
Pdef(\innerPattern, { | |
|degree, steps, durStep| | |
Pbind( | |
\dur, durStep * Prand([1, Rest(1)], inf), | |
\sustain, 1/4, | |
\timingOffset, Prand([0, 0, 1/64], inf), | |
\degree, degree + (steps * Pshuf([0, 1, 2]).repeat), | |
\filt, Pexprand(0.02, 1), | |
\attackTime, 0.01, \decayTime, 1/16, \releaseTime, 2.5 | |
) | |
}); | |
Pdef(\test, | |
Pbind( | |
\type, Prand([\note, \pattern, \pattern], inf), | |
\pattern, \innerPattern, | |
// timing | |
\dur, Prand([4, 2], inf), | |
\durStep, Pshuf([2/16, 4/16]).repeat, | |
\legato, 2, | |
// notes | |
\scale, Scale.phrygian, | |
\octave, Prand([3, 4, 5, 5, 5], inf), | |
\degree, Pseq([0, 3, 5, -2], inf), | |
\steps, Prand([2, -3, -1, 5], inf), | |
// timbre | |
\attackTime, 2, \decayTime, 2, \releaseTime, 1, | |
\filt, Pwhite(0.2, 0.6) | |
) | |
).play; | |
) | |
( | |
// The cooler version of this is that you can string together | |
// sequences of different patterns... | |
// Original | |
Pdef(\a, { | |
|degree, steps, durStep| | |
Pbind( | |
\dur, durStep * Prand([1, Rest(1)], inf), | |
\sustain, 1/4, | |
\timingOffset, Prand([0, 0, 1/64], inf), | |
\degree, degree + (steps * Pshuf([0, 1, 2]).repeat), | |
\filt, Pexprand(0.02, 1), | |
\attackTime, 0.01, \decayTime, 1/16, \releaseTime, 2.5 | |
) | |
}); | |
// High notes | |
Pdef(\b, { | |
|degree, steps, durStep| | |
Pbind( | |
\dur, 3/8, | |
\sustain, 1, | |
\timingOffset, Prand([0, 0, 1/64], inf), | |
\octave, 7, | |
\degree, degree + (steps * Pshuf([0, 1, 2]).repeat), | |
\filt, Pexprand(0.02, 1), | |
\attackTime, 0.01, \decayTime, 1/16, \releaseTime, 2.5 | |
) | |
}); | |
// Slow chord | |
Pdef(\c, { | |
|degree, steps, durStep| | |
Pbind( | |
\dur, 4, | |
\legato, 1, | |
\degree, degree + (steps * [-2, 0, 1]), | |
\filt, Pexprand(0.01, 0.4), | |
\attackTime, 3, \decayTime, 1, \releaseTime, 2.5 | |
) | |
}); | |
Pdef(\test, | |
Pbind( | |
\type, Prand([\note, \pattern, \pattern], inf), | |
\pattern, Pshuf([\a, \a, \a, \b, \c]).repeat, | |
// timing | |
\dur, Prand([4, 2], inf), | |
\durStep, Pshuf([2/16, 4/16]).repeat, | |
\legato, 3, | |
// notes | |
\scale, Scale.phrygian, | |
\octave, Prand([3, 4, 5, 5, 5], inf), | |
\degree, Pseq([0, 3, 5, -2], inf), | |
\steps, Prand([2, -3, -1, 5], inf), | |
// timbre | |
\attackTime, 2, \decayTime, 2, \releaseTime, 1, | |
\filt, Pwhite(0.2, 0.6) | |
) | |
).play; | |
) | |
// Final comments: | |
// | |
// - There's no reason why you can't have your inner pattern ALSO be a | |
// \type, \pattern, meaning two levels of recursion. Complexity builds fast tho. | |
// | |
// - Pdefs can be re-defined WHILE theyre playing, so there's a cool livecoding | |
// or exploration workflow where you have your outer pattern triggering different | |
// Pdef patterns like my last example, and then you just change things about | |
// each of the individual \a, \b, \c patterns while its playing (and reun the definition code) | |
// and theyll get updated without stopping playback. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment