; creates a rhythm of a given length with a
; simple gestalt grouping. Rhythmic atoms are
; selected from the list provided in the function
; or additionally as a single list argument to the function
;
; (define rlist (make-rhythm 1.0 (list (/ 1 3) 0.5 1.0)))
;
(define make-rhythm
(lambda (beats . args)
(define rlist
(if (list? (car args))
(car args)
(cl:remove 'bad
(map (lambda (i)
(if (null? args)
(if (<= i beats) i 'bad)
(if (or (< i (car args))
(> i (cadr args)))
'bad
i)))
(list .25 (/ 1 3) .5 (/ 2 3) .75 1.0 1.25 (/ 4 3) 1.5 1.75 2.0 2.5 3.0 3.5 4.0)))))
(let loop ((cnt 0)
(rl (list (random rlist))))
(cond ((null? rl)
(loop 0 (list (random rlist))))
((> cnt 50) (loop 0 (list (random rlist))))
((= (apply + rl) beats)
(reverse rl))
((> (apply + rl) beats)
(loop (+ cnt 1) (cdr rl)))
((< (random) .35)
(loop (+ cnt 1) (cons (car rl) rl)))
(else (loop (+ cnt 1) (cons (random rlist)
rl)))))))
; accepts an associative list as the timeline argument
; returns an event at a given time from the alist
(define make-timeline
(lambda (timeline)
(if (null? timeline)
'()
(lambda (time)
(let loop ((lst (reverse timeline)))
(cond ((null? lst) '())
((>= time (caar lst))
(cdar lst))
(else (loop (cdr lst)))))))))
; creates a meter where metre is a list of numerators
; and base is a shared denominator (relative to impromptu beats. i.e. 1 = crotchet, 0.5 = eighth etc.)
;
; e.g. (define *metre* (make-metre '(2 3 2) 0.5)) = 2/8 3/8 2/8 rotating cycle.
;
; then call meter with time and beat
; if beat matches time then #t else #f
;
; e.g. give the above define
; (*metre* 2.5 1.0) => #t because 0.0 = 1, 0.5 = 2, 1.0 = 1, 1.5 = 2, 2.0 = 3, 2.5 = 1, 3.0 = 2 and repeat.
(define make-metre
(lambda (metre base)
(let ((metre-length (apply + metre)))
(lambda (time beat)
(if (= (let loop ((qtime (fmod (/ time base) metre-length))
(lst metre)
(valuea (car metre))
(valueb 0))
(if (< qtime valuea)
(+ 1.0 (- qtime valueb))
(loop qtime (cdr lst) (+ valuea (cadr lst)) (+ valueb (car lst)))))
beat)
#t
#f)))))
; creates a metronome object
; metro is basically a linear function that returns
; a time in absolute samples when given a time in beats.
;
; metro is instantiated with a starting tempo.
; you can call the metro with the following symbols
;
; 'get-time ; which is also the default
; 'get-beat
; 'get-tempo
; 'set-tempo
; 'dur
;
(define make-metro
(lambda (start-tempo . args)
(let* ((offset (if (null? args) (now) (car args)))
(mark offset)
(total-beats 0.0)
(g-tempo (/ 60 start-tempo))
(beat-pos (lambda (x1 y1 x2 y2)
(let* ((m (if (= 0 (- x2 x1)) 0 (/ (- y2 y1) (- x2 x1))))
(c (- y1 (* m x1))))
(lambda (time)
(+ (* time m) c)))))
(samp-env (beat-pos 0 0 1.0 (* g-tempo *samplerate*))))
(lambda (sym . args)
(cond ((number? sym)
(+ (samp-env sym) offset))
((equal? sym 'get-time)
(+ (samp-env (car args)) offset))
((equal? sym 'set-tempo)
(let ((time (if (null? (cdr args)) (now) (cadr args)))
(val (* *samplerate* g-tempo 0.125)))
(set! time (+ time (- val (fmod (- time mark) val))))
(set! total-beats (+ total-beats (/ (- time mark)
(* *samplerate* g-tempo))))
(set! g-tempo (/ 60 (car args)))
(set! mark time)
(set! samp-env (beat-pos total-beats
(samp-env total-beats)
(+ total-beats 1.0)
(+ (samp-env total-beats) (* g-tempo *samplerate*))))
(car args)))
((equal? sym 'get-tempo) (* (/ 1.0 g-tempo) 60))
((equal? sym 'dur) (* *samplerate* g-tempo (car args)))
((equal? sym 'get-beat)
(let ((val (+ total-beats
(/ (- (now) mark)
(* *samplerate* g-tempo))))
(quantize (if (null? args) 1.0 (car args))))
(+ val (- quantize (fmod val quantize)))))
(else 'bad-method-name))))))