diff --git a/libs/core/pattern-language.xtm b/libs/core/pattern-language.xtm index 3bc801880..73455f830 100644 --- a/libs/core/pattern-language.xtm +++ b/libs/core/pattern-language.xtm @@ -23,6 +23,122 @@ lst (append (cdr lst) '(0)))))) +(define pattern_atom_duration + (lambda (atm) + (cond ((eq? '| atm) 1) + ((eq? '+ atm) 1/2) + ((eq? '- atm) -1/2) + (else 1)))) + +(define rmap_pattern_length + (lambda (lst) + (apply + (map pattern_atom_duration lst)))) + +(define (modify_duration_by dur lst) + (let ((modifiers (take-while is_modifier? lst))) + (foldl (lambda (d m) + (cond ((eq? '| m) (+ d dur)) + ((eq? '+ m) (* 3/2 d)) + ((eq? '- m) (* 1/2 d)))) + dur modifiers))) + +(define skip_leading_modifiers + (lambda (lists) + (map (lambda (l) (drop-while is_modifier? l)) + lists))) + + +(define pattern_special_atoms '(_ | + -)) +(define pattern_modifiers '(+ - |)) + + +(define is_special? + (lambda (atom) (member atom pattern_special_atoms))) + +(define is_modifier? + (lambda (atom) (member atom pattern_modifiers))) + +(define count_specials + (lambda (lst t char) + (length (take-while (lambda (x) (equal? x char)) + (cl:nthcdr (min (cdr t) + (length lst)) + lst))))) + +(define event_locations + (lambda (base-duration lst) + (let loop ((res '()) + (l lst) + (list-pos 1) + (total-dur 0)) + (if (null? l) (reverse res) + (let* ((evt (car l)) + (modifiers (take-while is_modifier? (cdr l))) + (next-l-pos (+ 1 (length modifiers))) + (duration (modify_duration_by base-duration modifiers)) + (elapsed (+ total-dur duration))) + (loop (cons (cons total-dur (cons duration list-pos)) res) + (cl:nthcdr next-l-pos l) + (+ list-pos next-l-pos) + elapsed)))))) + +(define count_specials + (lambda (lst t char) + (length (take-while (lambda (x) (equal? x char)) + (cl:nthcdr (min (cdr t) + (length lst)) + lst))))) + + +(define evaluate_event + (lambda (evt) + (cond ((vector? evt) (map (lambda (x) (eval x (interaction-environment))) + (vector->list l))) + ((and (symbol? evt) (not (is_special? evt))) + (let ((ll (eval evt (interaction-environment)))) + (if (procedure? ll) + (eval `(,ll) (interaction-environment)) + (else ll)))) + evt))) + + +(define make_tmpargs + (lambda (t args) + (map (lambda (l) + (if (list? l) + (set! l (rmap_helper_lst_sym_transform l)) + (if (pair? l) + (set! l ((eval (cdr l)) (rmap_helper_lst_sym_transform (car l)))) + (set! l (list l)))) + (if (< (length l) (cddr t)) + (list-ref l (modulo (- (cddr t) 1) (length l))) + (list-ref l (- (cddr t) 1)))) + args))) + +(define make_targs + (lambda (tmpargs) + (reverse + (foldl (lambda (acc l) + (cond + ((vector? l) (cons (map (lambda (x) (eval x)) (vector->list l)) acc)) + ((and (symbol? l) (not (is_special? l))) + (let ((ll (eval l (interaction-environment)))) + (cons (cond + ((procedure? ll) + (eval `(,ll) (interaction-environment))) + (else + ll)) + acc))) + (else (cons l acc)))) + '() tmpargs)))) + +(define is_position_between? + (lambda (startpos endpos) + (lambda (pos-cell) + (let ((pos (car pos-cell))) + (and (>= pos startpos) + (< pos endpos)))))) + (define rmap_helper (lambda (beats offset func beat dur loopcnt looppos . args) (let* ((lst (rmap_helper_lst_sym_transform (if (list? (car args)) @@ -31,47 +147,25 @@ (caar args) '(_ _ _ _))))) (pos (modulo (- looppos offset) beats)) ;;(modulo (- beat offset) beats)) - (one_beat (/ beats (length lst))) - (lst_positions (range 0 (+ pos beats) one_beat)) + (one_beat (/ beats (rmap_pattern_length lst))) (nextpos (+ pos dur)) - (idx 0) - (f (lambda (old new) - (set! idx (+ idx 1)) - (if (and (>= new pos) (< new nextpos)) - (cons (cons new idx) old) - old))) - (newlst (foldl f '() lst_positions))) + (newlst (filter (is_position_between? pos nextpos) + (event_locations one_beat lst)))) + (map (lambda (t) - (let* ((tmpargs (map (lambda (l) - (if (list? l) - (set! l (rmap_helper_lst_sym_transform l)) - (if (pair? l) - (set! l ((eval (cdr l)) (rmap_helper_lst_sym_transform (car l)))) - (set! l (list l)))) - (if (< (length l) (cdr t)) - (list-ref l (modulo (- (cdr t) 1) (length l))) - (list-ref l (- (cdr t) 1)))) - args)) - (targs (map (lambda (l) - (cond ((vector? l) (map (lambda (x) (eval x)) (vector->list l))) - ((and (symbol? l) (not (member l '(_ |)))) (eval l)) - (else l))) - tmpargs))) + (let* ((tmpargs (make_tmpargs t args)) + (targs (make_targs tmpargs)) + (current-dur (cadr t)) + ) (cond ((or (list? (car tmpargs)) (pair? (car tmpargs))) - (apply rmap_helper one_beat offset func (+ beat (- (car t) pos)) one_beat loopcnt (+ looppos (- (car t) pos)) targs)) - ((member (car tmpargs) '(_ |)) #f) ;; skip these symbols + (apply rmap_helper current-dur offset func (+ beat (- (car t) pos)) current-dur loopcnt (+ looppos (- (car t) pos)) targs)) + ((member (car tmpargs) pattern_special_atoms) #f) ;; skip these symbols (else - ;; this is a messy expression, but it just counts the number of - ;; '| symbols *after* the current value, and adds them to the - ;; duration - (let ((note-dur (* one_beat - (+ 1 (length (take-while (lambda (x) (equal? x '|)) - (cl:nthcdr (min (cdr t) (length (car args))) (car args)))))))) - (apply callback - (- (*metro* (+ beat (- (car t) pos))) *RMAP_HELPER_CALLBACK_OFFSET*) - func (+ beat (- (car t) pos)) note-dur - loopcnt targs)))))) + (apply callback + (- (*metro* (+ beat (- (car t) pos))) *RMAP_HELPER_CALLBACK_OFFSET*) + func (+ beat (- (car t) pos)) current-dur + loopcnt targs))))) newlst)))) ;; rhythm map @@ -260,6 +354,81 @@ (list a x))) (if (list? a) a b))))) +;; Some .abc style note functions. The plain names are quantised to the +;; current *scale* by rmap_helper. Capitalised note names are in octave 3, +;; lowercase names in octave 4. In full .abc lower or higher octaves can be +;; specified by tacking , or ' respectively onto the low/high note names, but +;; I'm lazy. + +;; Quantised notes +(define (a) (qnt a4)) +(define (b) (qnt b4)) +(define (c) (qnt c4)) +(define (d) (qnt d4)) +(define (e) (qnt e4)) +(define (f) (qnt f4)) +(define (g) (qnt g4)) + +(define (A) (qnt a3)) +(define (B) (qnt b3)) +(define (C) (qnt c3)) +(define (D) (qnt d3)) +(define (E) (qnt e3)) +(define (F) (qnt f3)) +(define (G) (qnt g3)) + +;; sharpened notes; these aren't quantised +(define (^a) a#4) +(define (^b) b#4) +(define (^c) c#4) +(define (^d) d#4) +(define (^e) e#4) +(define (^f) f#4) +(define (^g) g#4) + +(define (^A) a#3) +(define (^B) b#3) +(define (^C) c#3) +(define (^D) d#3) +(define (^E) e#3) +(define (^F) f#3) +(define (^G) g#3) + +;; flattened notes. Not quantised +(define (_a) ab4) +(define (_b) bb4) +(define (_c) cb4) +(define (_d) db4) +(define (_e) eb4) +(define (_f) fb4) +(define (_g) gb4) + +(define (_A) ab3) +(define (_B) bb3) +(define (_C) cb3) +(define (_D) db3) +(define (_E) eb3) +(define (_F) fb3) +(define (_G) gb3) + +;; Naturals, again not quantised +(define (=a) a3) +(define (=b) b3) +(define (=c) c3) +(define (=d) d3) +(define (=e) e3) +(define (=f) f3) +(define (=g) g3) + +(define (=A) a4) +(define (=B) b4) +(define (=C) c4) +(define (=D) d4) +(define (=E) e4) +(define (=F) f4) +(define (=G) g4) + + ;; euclidean rhythms ;; written by Ben in recursive Scheme; Kernighan's aphorism probably applies @@ -320,6 +489,14 @@ ((pred (car remaining)) (loop (cons (car remaining) ret) (cdr remaining))) (else (reverse ret)))))) +(define drop-while + (lambda (pred lst) + (let loop ((ret lst)) + (cond ((null? ret)) + ((pred (car ret)) (loop (cdr ret))) + (else ret))))) + + ;; skip by m with a lst rotation of r ;; for example: (skip 2 -1 (scale 3 8)) (define skip