Impromptu: If-mod macro


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.

Implementing if-mod

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:


Michele Pasin. Impromptu: If-mod macro. Blog post on www.michelepasin.org. Published on Oct. 29, 2009.

Comments via Github:


See also: