;;; -*- syntax: Sal2; -*- ; Functional data in patterns. ; You can embed thunks (functions of zero arguments) inside patterns, ; when a thunk is encountered it will be called to produce the value ; to return from the pattern: function ran1() between(48, 60) end function ran2() between(60, 72) end function ran3() between(72, 85) end begin with pat = make-cycle( list(ran1, ran2, ran3) ) next(pat, 30) end ; Creating patterns that mix functions or subpatterns with constant ; data can be a cumbersome. for example if you want to create a cyclic ; pattern whose data included some symbols and a heap subpattern e.g ; {c b c d e f} you might do something like: begin with sub = make-heap({x y z}), pat = make-cycle(concat({a b c}, sub, {d e f})) next(pat, 20) end ; You can use #$ (explicit evaluation) inside {} lists to mix ; subpatterns and data, for example: begin with sub = make-heap({x y z}), pat = make-cycle({a b c #$ sub d e f}) next(pat, 20) end ; here is another example : begin with pat = make-cycle( {c4 #$ make-cycle({ef4}, for: make-cycle( {1 2})) #$ make-cycle({b4}, for: make-cycle( {3 2 0})) d4} ) next(pat, 40) end ; How it works. Without #$ things inside a {} list are contants: begin with sub = make-heap({x y z}), pat = make-cycle({a b c sub d e f}) next(pat, 20) end ; inside {} a #$ evaluates the next thing when the list is created. ; thus, that thing will be replaced by its (evaluated) value. ; Execute this block a few times to see that the second ; element can change each time begin with lis = {a #$ random(128) b} lis end ; If you pass that list to pattern and then generate pattern data you ; will see that random number reappearing in the pattern: begin with lis = {a #$ random(128) c}, pat, dat print("pattern list=", lis) set pat = make-cycle(lis) set dat = next(pat, 30) print("pattern data=", dat ) end ; Embedding expressions in patterns. ; You can also embed EXPRESSIONS inside patterns such that the ; expression will produce a new value each time next() reads it from ; the patter. To do this you must DELAY the evaluation of the ; expression when you create the list of data you pass to the pattern, ; either by making it the return value of a function of zero arguments ; or by using the handy 'promise' function: begin with mylist = { a #$ promise( between(30,90) ) c }, mypat = make-cycle( mylist ) print("mylist=", mylist) print("pattern data=", next(mypat, 30)) end ; probability weights as a function of time. ; lets use embedded expressions to create a weighting whose ; probailities change as a function of x. in this example as x goes ; from 0 to 1 wei goes from 0 to 10 according to env: begin with len = 50, env = {0 1 1 10}, max = len - 1, wei loop for i from 0 below len for x = i / max set wei = interp( x, env) print("weight=", wei) end end ; to use wei as a probablity weight inside a pattern we can use ; promise to delay the evaluation of wei and put that in a pattern ; element. in this pattern the probability of B increases from 0 to 10 ; as x goes from 0 to 1. begin with len = 50, env = {0 0 1 10}, max = len - 1, wei loop with pat = make-weighting( {a {b #$ promise(wei)} c }, for: 1) for i from 0 below len for x = i / max set wei = interp( x, env) print("item=", next(pat)) end end ; here is a musical rendition of this that pulls the notes CAGE out a ; a G-dorian backgroud: process cage (num, env, off) with w = 0, pat1 = make-weighting( {{g3 #$ promise(w)} {a3 #$ promise(w)} bf3 {c4 #$ promise(w)} d4 {e4 #$ promise(w)} f4 {g4 #$ promise(w)}} ), pat2 = make-weighting( {1/4 1/2 #$ make-cycle( 1/8, for: 2) } ) for i below num for k = key(next(pat1)) for r = in-tempo( next(pat2), 60) set w = interp(i / num, env) send("mp:midi", dur: r * 1.25, key: transpose(k, off)) wait r end sprout(list(cage(100, {0 .5 1 6}, -12), cage(100, {0 .5 1 6}, 0), cage(100, {0 .5 1 6}, 12), cage(100, {0 .5 1 6}, 24))) ; The Graph pattern. A graph is a network of nodes, each node contains ; a 'value' (the item to return from the pattern), a 'link' to the ; node that should come next , and an identifier (a unique name for ; the node in the graph). Both the value and the link can be ; subpatterns . node identifiers default to increasing numbers from 1 ; here is how to create a cycle of A B C as a graph (the first node is ; always followed by node 2, the second node by node 3, and node 3 ; returns to node 1: variable pat = make-graph( {{A 2} {B 3} {C 1} }) next(pat, 12) ; an 'alberti bass' figure uing the triad C E G variable pat = make-graph( { {c 3} {e 3} {g #$ make-cycle({2 1}) } }) next(pat, 24) ; a randomizing alberti figure: variable pat = make-graph( { {c 3} {e 3} {g #$ make-weighting({2 1}) } }) next(pat, 24) ; extending the alberit idea to a graph of subpatters: variable pat = make-graph( { {#$ make-cycle({c e g}) 3} {#$ make-cycle({f a c}) 3} {#$ make-cycle({g b d}) #$ make-weighting({2 1}) } } ) next(pat, 48) ; first order markov chant as a graph variable pat = make-graph(list(list("c4", make-weighting({2 5}) ), list("d4", make-weighting({1 3}) ), list("ef4", make-weighting({2 4}) ), list("f4", make-weighting({3 5}) ), list( make-heap({"g4" "a4" "bf4" "c5"}), make-cycle( {1 2 3 4})) )) next(pat, 80) ; play it process play-pat (reps, pat, rate) repeat reps for n = next(pat) if (not( rest?(n))) send("mp:midi", key: key(n), dur: rate * 1.5) end wait rate end ; play chant in 6 8 or 12 note phrases separated by a rest begin with pat1 = make-cycle(list(make-graph(list(list("c4", make-weighting({2 5}) ), list("d4", make-weighting({1 3}) ), list("ef4", make-weighting({2 4}) ), list("f4", make-weighting({3 5}) ), list( make-heap({"g4" "a4" "bf4" "c5"}), make-cycle( {1 2 3 4})) ), make-heap({6 8 12}) ), "r") ) sprout(play-pat( 80, pat1, .2) ) end