This is a listing of notable features of cl-patterns. For a listing of new features relative to SuperCollider’s patterns system, see sc-differences.org.
- multiple sound server backends are supported:
- SuperCollider (via the cl-collider library)
- Incudine
- ALSA MIDI (via the cl-alsaseq library)
- event keys that are different representations of the same concept are automatically converted between each other. For example, if you set the
amp
of an event and then try to get itsdb
, cl-patterns converts the amplitude to decibels for you:(db (event :amp 0.5)) ;=> -6.0205994 (amp (event :db -3)) ;=> 0.70794576
See special-keys.org for a full listing of such keys.
- pbind has “special” keys which alter the functionality of the pattern or pstream.
For consistency, they’re typically named after other patterns, i.e. pfin
, pfindur
, psync
, etc.
See special-keys.org for a full listing.
- it’s possible to embed an event’s values into another from inside a pattern. For example:
(next-n (pbind :foo (pseq '(1 2 3 4)) :embed (pseq (list (event) (event :bar 1 :baz 2) (event :qux 3)))) 3) ((EVENT :FOO 1) (EVENT :FOO 2 :BAR 1 :BAZ 2) (EVENT :FOO 3 :QUX 3))
- all pstreams keep a history of previous values that can be referred back to at any time using
pstream-elt
. - patterns that have
repeat
orlength
arguments accept any pattern as “gate” patterns:(let* ((foo 1) (bar (as-pstream (pseq '(1 2 3) (pfunc (lambda () foo)))))) (print (next-n bar 10)) ;=> (1 2 3 1 2 3 1 2 3 1) (setf foo 0) (print (next-n bar 3))) ;=> (2 3 NIL)
- patterns keep track of their “parents”:
(defparameter *pat* (pbind :foo (pseq '(1 2 3)))) (defparameter *pseq* (getf (slot-value *pat* 'cl-patterns::pairs) :foo)) (pattern-parent *pseq*) ;=> #<PBIND {1003480F53}> (eq *pat* (pattern-parent *pseq*)) ;=> T
- the “children” of patterns can also be easily found with the
pattern-children
function:;; get just the children that are patterns: (pattern-children (pbind :foo (pseq (list 1 2 3)) :bar 4)) ;; => (#<PSEQ (1 2 3) :INF 0>) ;; ...or get all children even if they aren't patterns: (pattern-children (pbind :foo (pseq (list 1 2 3)) :bar 4) :class t) ;; => (#<PSEQ (1 2 3) :INF 0> 4)
- conditions that occur during pattern execution give you the option to remove the task from the clock, or just skip processing of the event.
- alternatively, you can set a condition handler in the clock to automatically remove the task or skip the event, recording the error and stack trace to a slot:
(setf (clock-condition-handler *clock*) 'remove-task) (play (pbind :x (p/ 1 (pseq '(440 220 0))))) ;; ...and then the error occurs: ;; WARNING: Task had condition #<DIVISION-BY-ZERO {1002EF08E3}>; invoked CL-PATTERNS::REMOVE-TASK restart and pushed the condition to #<CL-PATTERNS::CLOCK :tempo 110/60 :beat 2.0>'s caught-conditions slot. (car (clock-caught-conditions *clock*)) ;; => (:CONDITION #<DIVISION-BY-ZERO {10043ABA93}>)
- alternatively, you can set a condition handler in the clock to automatically remove the task or skip the event, recording the error and stack trace to a slot:
- SuperCollider backend: if a node is supplied as a value for a
pbind
, the synth metadata for that node is used to set itsinput-bus
oroutput-bus
as the value instead:(setf (synthdef-metadata :fx :input-bus) (bus-audio :chanls 2)) (defparameter *fx* (proxy :fx (let* ((sig (in.ar (synthdef-metadata :fx :input-bus) 2)) (sig (comb-c.ar sig 0.2 (range (lf-noise1.kr 1) 0.04 0.2)))) sig))) (pb :fx-test :instrument :kik :dur (p/ 1 (pwhite 1 16)) :midinote (pwhite 0 127) :pan (pwhite -1.0 1.0) :out *fx* :pfindur 4) (play :fx-test)
…in the future, this will be even simpler. :)
metadata
slot for patterns
Hash table associated with each pattern for storing additional data. Access with the pattern-metadata
function. This is used by the midifile functionality to include track names/information/etc, but it can be used to store any kind of arbitrary pattern data.
- Process pattern outputs with arbitrary functions before they are sent to backends using the
*post-pattern-output-processors*
list:(push (lambda (event pstream) (declare (ignore pstream)) (when (position :foo (keys event)) (incf (event-value event :foo))) event) *post-pattern-output-processors*) (next-n (pbind :foo (pseq (list 1 2 3))) 3) ;; => ((EVENT :FOO 2) (EVENT :FOO 3) (EVENT :FOO 4))
…by default, cl-patterns::remap-instrument-to-parameters
is the function in this list, which can be used to map from specified instrument
values to other event keys:
(setf (instrument-mapping :bar) (list :test 3 :instrument :qux))
(next (pbind :instrument :bar))
;; => (EVENT :TEST 3 :INSTRUMENT :QUX)
- Various unit conversion functions (i.e.
midinote-freq
to convert from a midi note number to a frequency in Hz) that also work on UGens when used in cl-collider synthdefs. see conversions.lisp for the conversion functions.