Skip to content

Instantly share code, notes, and snippets.

@lambdamusic
Created January 6, 2017 16:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lambdamusic/89d891fa79079c39c61799ef663769bc to your computer and use it in GitHub Desktop.
Save lambdamusic/89d891fa79079c39c61799ef663769bc to your computer and use it in GitHub Desktop.
Play pattern for midi instruments
;;;;;;;;;;;;;;; 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 for MIDI Instruments ;;;;;;;;;;;;;;
(define mplayp_play_list
(let ((lst_idx (range 0 1000)))
(lambda (beat dur pclas inst vols durs channel 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)
(mplayp_play_list beat dur pclas inst volume
duration channel x mod_diff (/ step (length lst)) (+ t offset) poffset args)
(if (> x 0)
(begin
(set! phase (+ mod_diff t offset))
(eval
`(mplay ,phase ;(+ mod_diff t offset)
,inst
,(pc:quantize (+ x poffset) pclas)
,volume
,duration
,channel
,@args))))))
lst
lst_idx
(range 0 step (/ step (length lst))))))))
(define mplayp_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 '())
(channel 0)
(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 (or (closure? (car args)) (cptr? (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)))
(if (not (or (closure? (car args)) (cptr? (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! channel (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)))
(mplayp_play_list beat dur pclas inst volume duration channel pitch mod_diff step offset poffset args)))
(begin
(set! phase (+ mod_diff offset))
(if (and (>= mod_diff 0)
(< mod_diff dur)
(> pitch 0))
(eval `(mplay ,phase ;(+ mod_diff offset)
,inst
,(pc:quantize (+ pitch poffset) pclas)
,volume
,duration
,channel
,@args))
(begin #f)))))))))
;; this is what you *actually* call
(define-macro (mplayp . args)
`(mplayp_f beat dur ,@args))
; remember
; BEAT and DUR arguments must be defined for mplayp to work
"""
Updates:
1) Checking for a CPTR symbol as well as a CLOSURE:
(println mididevice) ;;#<CPTR: 0x7fc32ac67ce0>
(println synth) ;; #<<CLOSURE 0x118d20c80>
So this
(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 eg
(mplayp_play_list beat dur pclas inst volume duration channel pitch mod_diff step offset poffset args)))
EXAMPLE
;; SETUP
(pm_print_devices)
(define mididevice (pm_create_output_stream 4)) ; eg Device 4
(play-midi-note (now) mididevice (random 36 42) 90 44100 0)
;; TESTING
(define loop
(lambda (beat dur)
(mplayp 2 mididevice -24 '(80 72 70 (75 77)) 80 dur 0)
(mplayp 4 mididevice '(80 72 70 (75 77)) 80 dur 1)
(mplayp 8 mididevice '(80 72 70 (75 77)) 80 dur 2)
(callback (*metro* (+ beat (* 1/2 dur))) 'loop (+ beat dur) dur)))
(loop (*metro* 'get-beat 1) 1/4)
"""
;; from Andrew https://mail.google.com/mail/u/0/#label/LSTs-AlgoMusic%2FLST-extempore/158e2c894316d1b9
;
;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.
;
;https://youtu.be/ARRIVEGROEg
;
;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.
;
;https://youtu.be/ARRIVEGROEg
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment