The time-lib library also contains a very useful function called make-metre. Like the make-metro function, the make-metre function returns a closure which can subsequently be called. make-metre returns a closure that returns #t or #f based on a simple query - given an accumulated beat are we on a certain metric pulse? A practical demo should make this a little clearer.
First though, a brief explanation of make-metre initial arguments. First argument is a list of numerators and the second argument is a single denominator. What this implies is that make-metre can work with a series of revolving metre’s. For example, (make-metre ‘(4) 1.0) gives us 4 * 1.0 metric pulses (recurring 4/4 bars); (make-metre ‘(3) 0.5) gives us 3 * 0.5 metric pulses (recurring 3/8 bars); (make-metre ‘(2 3) 0.5) gives us 2 * 0.5 then 3 * 0.5 metric pulses (recurring series of 2/8 3/8 2/8 3/8 2/8 3/8 ... ).
Let’s try an example. Note that we are only going to play the first beat of each ‘bar’.
(define *metro* (make-metro 90))
;; a 2/8 3/8 2/8 cycle
(define *metre* (make-metre '(2 3 2) 0.5))
;; play first beat of each 'bar'
(define metre-test
(lambda (time)
(if (*metre* time 1.0)
(play-note (*metro* time) drums *gm:side-stick* 80 10000 9))
(callback (*metro* (+ time 0.4)) 'metre-test (+ time 0.5))))
(metre-test (*metro* 'get-beat 1.0))
Well that was easy. Let’s complicate things just a little by adding a second metre. We’ll play the side stick for one metre and the snare for the second metre.
;; classic 2 against 3
;;
(define *metro* (make-metro 180))
;; 3/8
(define *metre1* (make-metre '(3) .5))
;; 2/8
(define *metre2* (make-metre '(2) .5))
;; play first beat of each 'bar'
(define metre-test
(lambda (time)
(if (*metre1* time 1.0)
(play-note (*metro* time) drums *gm:side-stick* 80 10000 9))
(if (*metre2* time 1.0)
(play-note (*metro* time) drums *gm:snare* 60 10000 9))
(callback (*metro* (+ time 0.4)) 'metre-test (+ time 0.5))))
(metre-test (*metro* 'get-beat 1.0))
The French composer Olivier Messiaen is well known for (amongst many things) symmetrical metric structures. Let’s take his lead and build up a relatively complex poly-symmetric drum pattern. Again we’re are going to work with two (poly) competing metric structures - both of which will be symmetric ( 2/8 3/8 4/8 3/8 2/8 ) and (3/8 5/8 7/8 5/8 3/8). Note that because the second metric structure is uneven in length we should get some nice phasing effects (a la Steve Reich). I’m also going to add some hi-hats to give it a constant pulse.
;; messiaen drum kit
(define *metro* (make-metro 140))
(define *metre1* (make-metre '(2 3 4 3 2) .5))
(define *metre2* (make-metre '(3 5 7 5 3) .5))
;; play first beat of each 'bar'
(define metre-test
(lambda (time)
(play-note (*metro* time) drums
(random (cons .8 *gm:closed-hi-hat*) (cons .2 *gm:open-hi-hat*))
(+ 40 (* 20 (cos (* 2 3.441592 time))))
(random (cons .8 500) (cons .2 2000)) 9)
(if (*metre1* time 1.0)
(begin (play-note (*metro* time) drums *gm:snare* 80 10000 9)
(play-note (*metro* time) drums *gm:pedal-hi-hat* 80 100000 9)))
(if (*metre2* time 1.0)
(begin (play-note (*metro* time) drums *gm:kick* 80 100000 9)
(play-note (*metro* time) drums *gm:ride-bell* 100 100000 9)))
(callback (*metro* (+ time 0.2)) 'metre-test (+ time 0.25))))
(metre-test (*metro* 'get-beat 1.0))
Here’s a short recording kit_one.mp3 - I’m not sure what Messiaen would think of this :)
There are a couple of things to note in the previous example. First our old oscillating volume is back in the hi-hat parts. We are also using a weighted random for the first time for both the choice of hi-hat pitch and the length of the hi-hat sound. Also notice that we are moving around our callback faster than before - but this does not affect our metre’s as long as our time increment has a suitable ratio to both metre’s.
Let’s keep going with this idea and add bass, bells and keyboard parts to accentuate the pattern.
;; messiaen drum kit
(define *metro* (make-metro 140))
(au:set-global-tempo 140)
(define *metre1* (make-metre '(2 3 4 3 2) .5))
(define *metre2* (make-metre '(3 5 7 5 3) .5))
;; play first beat of each 'bar'
(define metre-test
(lambda (time degree)
(play-note (*metro* time) drums
(random (cons .8 *gm:closed-hi-hat*) (cons .2 *gm:open-hi-hat*))
(+ 40 (* 20 (cos (* 2 3.141592 time))))
(random (cons .8 500) (cons 2 2000))
9)
(play-note (*metro* time) bells
(pc:random 90 107 (pc:diatonic 9 '- degree))
(+ 50 (* 25 (cos (* .125 3.141592 time))))
100)
(if (*metre1* time 1.0)
(begin (play-note (*metro* time) drums *gm:snare* 80 10000 9)
(play-note (*metro* time) drums *gm:pedal-hi-hat* 80 100000 9)
(play-note (*metro* time) bass
(+ 60 (car (pc:diatonic 9 '- degree)))
60
10000)))
(if (*metre2* time 1.0)
(begin (play-note (*metro* time) drums *gm:kick* 100 100000 9)
(play-note (*metro* time) drums *gm:ride-bell* 100 100000 9)
(for-each (lambda (p)
(play-note (*metro* time) keys p 70 10000))
(pc:make-chord 65 80 3 (pc:diatonic 9 '- degree)))))
(callback (*metro* (+ time 0.2)) 'metre-test (+ time 0.25)
(if (= 0.0 (fmod time 8.0))
(random (cdr (assoc degree '((i vii vii vi)
(n v)
(vi n)
(v vi i)
(vii i)))))
degree))))
(metre-test (*metro* 'get-beat 1.0) 'i)
Here is a short example from my machine kit_two.mp3. In this example we have used many of the techniques picked up in previous tutorials, so take some time and have a good look through this code. There are plenty of things here for you to further extend and try out on your own.
We are now going to take a look at how we can control parameter changes over time.