a little pattern player

172 views
Skip to first unread message

Andrew Sorensen

unread,
Dec 9, 2016, 3:52:25 AM12/9/16
to extemp...@googlegroups.com
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))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Johannes Kroll

unread,
Dec 9, 2016, 9:49:25 AM12/9/16
to extemp...@googlegroups.com
On Fri, 9 Dec 2016 18:52:24 +1000
Andrew Sorensen <dig...@gmail.com> wrote:

> 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.

Wow, this is very cool, thanks!

digego

unread,
Dec 9, 2016, 10:56:14 PM12/9/16
to Extempore
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; A simple playp arp example

(sys:load "libs/core/pc_ivl.xtm")
(sys:load "libs/core/instruments.xtm")

;; load playp code here

(bind-instrument fmsynth fmsynth_note_c fmsynth_fx)

(bind-func dsp:DSP
  (lambda (in time chan dat)
    (if (< chan 2)
        (fmsynth in time chan dat)
        0.0)))

(dsp:set! dsp)

($ (fmsynth_fx.dfb 0.6:f))
($ (fmsynth_fx.wet 0.0:f))
($ (fmsynth_fx.dwet 0.2:f))

(define ivls (lambda (x y z) (range x (+ x y) z)))

(define id (lambda (x) x))

(define scale (pc:scale 0 'dorian))

;; simple arps
(define arps
  (lambda (beat dur)
    (playp scale 2 fmsynth (random '(0 5 7 12)) (ivls 48 24 3) 80 0.1)
    (callback (*metro* (+ beat (* .5 dur))) 'arps (+ beat dur) dur)))

(arps (*metro* 'get-beat 4) 2)

;; extended arps
;; pattern transforms and lots of modulation
(define arps2
  (lambda (beat dur)
    (playp scale ;; quantize to scale
           2 ;; pattern length
           fmsynth ;; instrument
           (random '(-2 0 5 7 12)) ;; pitch offset
           ((random (list reverse id)) (ivls 48 24 3)) ;; pitch pattern
           '(cosr phase 70 30 (random '(7/4 7/3 7/3 1/3))) ;; volume
           '(trir phase 0.05 0.25 1/17) ;; duration
           '(trir phase 0.0 0.2 1/33) ;; fmsynth mod index
           '(cosr phase 10.0 7.0 (random '(7/4 7/3 7/3 1/3)))) ;; fmsynth harmon ratio
    (callback (*metro* (+ beat (* .5 dur))) 'arps2 (+ beat dur) dur)))

(arps2 (*metro* 'get-beat 4) 2)

Michele Pasin

unread,
Dec 31, 2016, 11:57:29 AM12/31/16
to extemp...@googlegroups.com
Thanks Andrew that's awesome! 

Do you think it's easily adaptable so that it works also with a midi instrument ie using `mplay` instead of `play` ? 


--
You received this message because you are subscribed to the Google Groups "Extempore" group.
To unsubscribe from this group and stop receiving emails from it, send an email to extemporelang+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Andrew Sorensen

unread,
Dec 31, 2016, 10:17:10 PM12/31/16
to extemp...@googlegroups.com
should be fine.  just change play to mplay (with suitable changes)

Michele Pasin

unread,
Jan 1, 2017, 12:17:45 PM1/1/17
to extemp...@googlegroups.com
Thanks - I tried substituting play with mplay but that didn't do it... (see https://gist.github.com/anonymous/ae2c245e621a5a8ebdecc76ec2b95dd6

```
;; ERROR
function(length): argument 1 must be: pair or '()
argument values: (1/4) #<PROC length>
Trace: playp_f <- test

```

The only difference between play-note and play-midi-note is the channel argument right? If yes wouldn't be enough to add it to the function call and then let ,@args do the rest? 

thanks, m

Paul Fisher

unread,
Jan 4, 2017, 9:31:50 PM1/4/17
to Extempore

Brilliant.

Going to take me a bit to work through exactly how it is working. I tried creating something in this spirit a few years back in impromptu but it was nothing like as elegant as this.

Thanks!

Andrew Sorensen

unread,
Jan 5, 2017, 4:23:36 AM1/5/17
to extemp...@googlegroups.com, Michele Pasin
It will be something related to variable args length. In *principle* it will work fine :). It shouldn't be hard to track down.
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.

Michele Pasin

unread,
Jan 6, 2017, 12:00:54 PM1/6/17
to extemp...@googlegroups.com
hello

I've got it working.. not sure how stable it is as I haven't tested it much but here it is:


In essence, I changed two things:

1) Checking for a CPTR symbol as well as a CLOSURE:

Since
(println mididevice)  ;;#<CPTR: 0x7fc32ac67ce0>
(println synth) ;; #<<CLOSURE 0x118d20c80>

So this statement
(if (not (closure? (car args)))
    (begin (set! offset (car args))
           (set! args (cdr args))))

Becomes
(if (not (or (closure? (car args)) (cptr? (car args))))
    (begin (set! offset (car args))
           (set! args (cdr args))))

2) Adding the channel parameter in various places 

E.g.
(mplayp_play_list beat dur pclas inst volume duration channel pitch mod_diff step offset poffset args)))


Cheers, 
m

Andrew Sorensen

unread,
Jan 6, 2017, 5:27:48 PM1/6/17
to extemp...@googlegroups.com, Michele Pasin
Excellent.
Reply all
Reply to author
Forward
0 new messages