We generally encourage the DIY approach when it comes to compositional tools in Extempore, but here's a little pattern player that I thought some of you might find useful. It wont be added to mainline, so stick it in your personal lib somewhere. I've included a link to a brief 5min video - just the absolute basics.
It's quite powerful but will probably take you some time to get your head around. Particularly with the rates of change. onbeat? is something to keep in mind - I've also added that macro in case you don't have something similar. (onbeat? 1 8 (println "HI")). On beat 1 of 8 print "HI". (onbeat? 2 3 (println "HI")) etc..
The basic form of the pattern player is (where <> are optional)
(playp <pitch-class> pattern-length instrument <pitch-offset> pitch-pattern volume duration . args)
A pitch pattern is just a list. As it should be ;) As a pattern is *just* a list, you can apply any transformation to that list (or sublists) that you desire.
The list is evenly divided into the pattern-length, which is specified in beats. A pattern can contain sub patterns (i.e. sub lists), where a sublist is a pattern that takes the exact duration that it replaces. i.e. '(60 (67 65) 63) with a pattern-length of 3 would result in a rhythm of 1 1/2 1/2 1. A pattern length of 2 would result in 2/3 1/3 1/3 2/3. etc..
You can optionally provide patterns for volume and duration, but they must match the pitch-pattern. You can also optionally quote volume and duration - which allows for update rates different from the TR rate. There is also a 'hidden' phase that can be applied to volume and duration - for use as offsets for functions like cosr rampr etc.. I wont explain any of that further here - ask questions if your interested, and get stuck on something.
Other than that volume and duration behave as with play. Also 'args' behaves just as it does with play.
What is worth paying attention to is that the pattern runs independently of the temporal recursion rate, but the update rate is obviously tied to the TR rate. In other words, while the pattern maybe independent of the update rate, everything else you do in the TR is still tied to the TR rate. This is actually quite a useful compositional tool, but might take a little practice to get familiar with.
Here is a *very* cursory video introduction. Although this example is pitched, the same code obviously works just as well for purely rhythmic material.
In your own setup use custom definitions for shorter entry - like using (define R repeat) (define Rnd random) (define I invert) (define J jumble) (define Rev reverse) and add pitch definitions (define c4 60) etc.. This will make your patterns much quicker to write and easier to read. i.e. (c3 (R d3 2) (Rnd c5 c6)). I went long form in the video for clarity ;)
Cheers,
Andrew.
;;;;;;;;;;;;;;; On Beat ;;;;;;;;;;;;;;;;;;;;;
(define-macro (onbeat? b of t . f)
(if (null? f)
`(if (= (modulo beat ,of) (- ,b 1))
,t)
`(if (= (modulo beat ,of) (- ,b 1))
,t ,(car f))))
;;;;;;;;;;;;;;;; Pattern Player ;;;;;;;;;;;;;;
(define playp_play_list
(let ((lst_idx (range 0 1000)))
(lambda (beat dur pclas inst vols durs lst mod_diff step offset poffset args)
(let ((duration 0) (volume 0)
(phase 0))
(for-each (lambda (x idx t)
(if (symbol? x) (set! x (eval x)))
(if (list? durs)
(if (and (symbol? (car durs))
(defined? (car durs))
(or (closure? (eval (car durs)))
(procedure? (eval (car durs)))
(macro? (eval (car durs)))))
(set! duration durs)
(if (= (length durs) (length lst))
(set! duration (list-ref durs idx))
(set! duration step)))
(set! duration durs))
(if (list? vols)
(if (and (symbol? (car vols))
(defined? (car vols))
(or (closure? (eval (car vols)))
(procedure? (eval (car vols)))
(macro? (eval (car vols)))))
(set! volume vols)
(if (= (length vols) (length lst))
(set! volume (list-ref vols idx))
(set! volume 80)))
(set! volume vols))
(if (list? x)
(playp_play_list beat dur pclas inst volume
duration x mod_diff (/ step (length lst)) (+ t offset) poffset args)
(if (> x 0)
(begin
(set! phase (+ mod_diff t offset))
(eval
`(play ,phase ;(+ mod_diff t offset)
,inst
,(pc:quantize (+ x poffset) pclas)
,volume
,duration
,@args))))))
lst
lst_idx
(range 0 step (/ step (length lst))))))))
(define playp_f
(lambda (beat dur . args)
(let ((pclas '(0 1 2 3 4 5 6 7 8 9 10 11))
(offset 0)
(poffset 0)
(inst '())
(data '())
(vols '())
(durs '())
(datal 0)
(cycle 0)
(step 0))
;; check for quantizer list
(if (list? (car args))
(begin (set! pclas (car args))
(set! args (cdr args))))
;; now cycle
(if (closure? (car args))
(set! cycle dur)
(begin
(set! cycle (car args))
(set! args (cdr args))))
;; if no instrument must be an offset
(if (not (closure? (car args)))
(begin (set! offset (car args))
(set! args (cdr args))))
;; now instrument (which should be a closure!)
(set! inst (car args))
(set! args (cdr args))
;; if not pitch list must be offset
(if (not (list? (car args)))
(begin (set! poffset (car args))
(set! args (cdr args))))
;; now must be pitch list
(set! data (car args))
(set! args (cdr args))
(set! datal (length data))
(set! vols (car args))
(set! args (cdr args))
(set! durs (car args))
(set! args (cdr args))
(set! step (/ cycle datal))
(let ((local_beat (modulo beat cycle))
(mod_diff 0)
(volume vols)
(phase 0.0)
(duration durs)
(pitch 0))
(dotimes (i (* 2 datal))
(set! mod_diff (- (* i step) local_beat))
(set! pitch (list-ref data (modulo i datal)))
(if (symbol? pitch) (set! pitch (eval pitch)))
(if (list? durs)
(if (and (symbol? (car durs))
(defined? (car durs))
(or (closure? (eval (car durs)))
(procedure? (eval (car durs)))
(macro? (eval (car durs)))))
(set! duration durs)
(if (= (length durs) datal)
(set! duration (list-ref durs (modulo i datal)))
(set! duration step))))
(if (list? vols)
(if (and (symbol? (car vols))
(defined? (car vols))
(or (closure? (eval (car vols)))
(procedure? (eval (car vols)))
(macro? (eval (car vols)))))
(set! volume vols)
(if (= (length vols) datal)
(set! volume (list-ref vols (modulo i datal)))
(set! volume 80))))
(if (list? pitch)
(begin
(if (and (>= mod_diff 0)
(< mod_diff dur)
(not (null? pitch)))
(playp_play_list beat dur pclas inst volume duration pitch mod_diff step offset poffset args)))
(begin
(set! phase (+ mod_diff offset))
(if (and (>= mod_diff 0)
(< mod_diff dur)
(> pitch 0))
(eval `(play ,phase ;(+ mod_diff offset)
,inst
,(pc:quantize (+ pitch poffset) pclas)
,volume
,duration
,@args))
(begin #f)))))))))
;; this is what you *actually* call
(define-macro (playp . args)
`(playp_f beat dur ,@args))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;