; 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))))))