hey there - this morning I checked out a nice screencast by Ben Swift and was struck by the if-mod construct he's using. It's a really useful shortcut that saves you from writing a few (possibly distracting) parenthesis, so I tried to recreate it myself.
To recap.. normally with Impromptu if you want to play notes at some specific time expressed in beats with you'd have to set up a metronome first [have a look here for more info about how to use *metro*] and then check for the right beat using the modulo function. For example, something like this will play a central C every first beat of a 4/4 measure:
(define *metro* (make-metro 100)) (define test (lambda (beat) (if (equal? (modulo beat 4) 0) (play dls 60 60 3)) (callback (*metro* (+ beat (* 1/2 1/4))) 'test (+ beat 1/4)))) (test (*metro* 'get-beat 4))
Another way of doing this is by using case. Same approach, but probably faster to code, as it lets you specify 'multiple beats' very easily:
(define test2 (lambda (beat) (case (modulo beat 4) ((0) (play dls 60 60 3)) ((2 5/2) (play dls 67 60 1/2))) (callback (*metro* (+ beat (* 1/2 1/4))) 'test2 (+ beat 1/4)))) (test2 (*metro* 'get-beat 4))
Still quite a few parenthesis though... which, especially when playing live, might mean more chances to mess up! So when I saw Ben's video I realized that a macro usable to facilitate the creation of case/modulo expressions would be quite useful.. Here is how it can be done:
(define-macro (if-mod x y args) `(for-each (lambda (step) (if (equal? (modulo beat ,x) step) ,args)) (if (list? ,y) ,y (list ,y))))
Now, by using the if-mod macro we've just created we can re-write the second example above much more concisely:
(define test2-new (lambda (beat) (if-mod 4 0 (play dls 60 60 3)) (if-mod 4 '(2 5/2) (play dls 67 60 1/2)) (callback (*metro* (+ beat (* 1/2 1/4))) 'test2-new (+ beat 1/4)))) (test2-new (*metro* 'get-beat 4))
That's all! Notice also that the if-mod construct can take either a list of beats or a single one.
Cite this blog post: