1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
-- region Lesson 1 SHIFTING TIME
-- Lets start with a rhythm:
d1 $ n "[0 [1 0] 6*2 [3 4*2], 8(5,8)]" # sound "cpu2"
-- That's repeating nicely. Keep it running, then run this:
d1 $ 0.25 <~ (n "[0 [1 0] 6*2 [3 4*2], 8(5,8)]" # sound "cpu2")
-- If you switch between them, you can hear the pattern is shifting in
-- time. The `0.25` means it's shifting by a quarter of a cycle.
-- You only hear any difference between them at the point where you
-- switch to the other one. You're jumping forward / backward in time,
-- but once you're there, nothing has changed. (!)
-- Ok, time travel is difficult to talk about.
-- Lets visualise this, compare these two:
drawLine "a b c d"
drawLine $ 0.25 <~ "a b c d"
-- You can see the a b c d sequence is the same, but in the latter
-- case, the cycle begins on the 'b'.
-- So '<~' has moved us _forward_ into the future. So shouldn't it be
-- '~>', rather than '<~'?? Well, we might have moved into the future,
-- but it's all relative - from the perspective of the pattern, it's
-- moved backwards into the past. Furthermore, while we like to think
-- about us moving forwards into the future, from the perspective of
-- the future, it's moving backwards into the past. Furthermore
-- different human cultures think about time in different ways.
-- Anyway, '~>' does indeed exist, compare these two:
drawLine $ 0.25 <~ "a b c d"
drawLine $ 0.25 ~> "a b c d"
-- Time is most interesting if you keep jumping around
-- For example jump every 3 cycles:
d1 $ every 3 (0.25 <~) $ n "[0 [1 0] 6*2 [3 4*2], 8(5,8)]" # sound "cpu2"
# crush 4
-- Jumping in the other direction has quite a different feel:
d1 $ every 3 (0.25 ~>) $ n "[0 [1 0] 6*2 [3 4*2], 8(5,8)]" # sound "cpu2"
# crush 4
-- You can also use a pattern for the time shift amount:
d1 $ "<0 0.25 0.75>" ~>
(n "[0 [1 0] 6*2 [3 4*2], 8(5,8)]" # sound "cpu2" # crush 4)
-- Even with this straightforward shifting, things quickly start
-- sounding 'random', until your ears lock on to the longer loop..
-- SIDETRACK - a note on syntax..
-- Unfortunately this use of the dollar *doesn't work*:
d1 $ "<0 0.25 0.75>" ~> $ n "[0 [1 0] 6*2 [3 4*2], 8(5,8)]"
# sound "cpu2" # crush 4
-- This is because like all operators, you can't use a dollar to group
-- together a pattern to send to `~>` in this way. haskell gets
-- confused about seeing two operators ('$' and '~>') next to each
-- other.
-- So you have to use parenthesis:
d1 $ "<0 0.25 0.75>" ~> (n "[0 [1 0] 6*2 [3 4*2], 8(5,8)]"
# sound "cpu2" # crush 4)
-- Or another way around this is to wrap the *operator* in
-- parenthesis, then you can use it like a normal function:
d1 $ (~>) "<0 0.25 0.75>" $ n "[0 [1 0] 6*2 [3 4*2], 8(5,8)]"
# sound "cpu2" # crush 4
-- Or wrap the first input and the operator in parenthesis:
d1 $ ("<0 0.25 0.75>" ~>) $ n "[0 [1 0] 6*2 [3 4*2], 8(5,8)]"
# sound "cpu2" # crush 4
-- This all works nicely with chopped-up loops:
d1 $ every 2 ("e" <~) $ every 3 (0.25 <~) $
loopAt 1 $ chop 8 $ sound "break:8"
-- endregion
-- region Lesson 2 Binary patterns
-- The patterns you send to SuperDirt tend to contain values of type
-- String (for words), Double (for decimal numbers) or Int (for whole
-- numbers). One pattern type you probably won't send to SuperDirt is
-- of type Bool - short for Boolean.
-- Boolean values can be either True or False. You've probably seen
-- then used with with 'struct', e.g.:
d1 $ struct "t f t t f t f f" $ sound "snare:4"
-- 'struct' provides structure for the pattern on the right; whenever
-- there's a 't' (i.e., a true value) in the boolean pattern, the
-- snare fires.
-- It works with euclidean syntax too:
d1 $ struct "t(3,8)" $ sound "snare:4"
-- The above creates a new pattern with three events per cycle,
-- according to a Euclidean pattern.
-- Lets have a look at that euclidean pattern:
drawLine $ struct "t(3,8)" "a"
-- So what do you think would happen if you changed that 't' (for
-- true) for an 'f' (for false)? Lets try:
drawLine $ struct "f(3,8)" "a"
-- Lets listen to that structure too:
d1 $ struct "f(3,8)" $ sound "snare:4"
-- You can see and hear that the *inverse* of the Euclidean pattern is
-- played. What was true, is now false, and vice-versa.. It's the
-- 'empty' steps which get the true values, and which we end up
-- hearing.
-- This is clearer if we play a t(3,8) against an inverted f(3,8):
d1 $ stack [struct "t(3,8)" $ sound "kick:4",
struct "f(3,8)" $ sound "snare:4"
]
-- You can hear that the snares are 'filling in' where the kicks
-- aren't playing - they never play at the same time.
-- Filling in patterns like this is a lot of fun, and there's a
-- function called 'stitch' that makes it easier:
d1 $ stitch "t(3,8)" (sound "kick:4") (sound "snare:4")
-- You only have to give the boolean pattern once, 'stitch' takes care
-- of inverting the pattern for the second pattern. It's called
-- 'stitch', because it's like going up and down to stitch two things
-- together.
-- You can make more complicated boolean patterns to quickly get some
-- fun patterns going:
d1 $ stitch "t(<3 5>,8,<0 2 3>)" (sound "kick:4") (sound "hc")
d1 $ stitch "t(<3 5>,<8 8 8 6>,<0 2 4>)" (sound "kick:4") (sound "hc")
-- Actually it'd be less typing do the stitching _inside_ the sound
-- control pattern:
d1 $ sound (stitch "t(<3 5>,<8 8 8 6>,<0 2 4>)" "kick:4" "hc")
-- In the above, I only have to write 'sound' once, because the
-- 'stitch' is working on patterns of words, not patterns of sounds.
-- You can also alternate between patterns of true, and patterns of false
-- values:
drawLine $ struct "<t f>(3,8)" "a"
-- If you prefer you can use '1' or '0' instead of 't' and 'f', the
-- result is exactly the same:
drawLine $ struct "<1 0>(3,8)" "a"
d1 $ struct "<1 0>(3,8)" $ sound "clap"
-- You don't have to use the Euclidean syntax, you can just right them
-- out by hand:
d1 $ stitch "t f t t f f t f" (sound "kick:4") (sound "hc")
-- .. and use the usual mininotation syntax:
d1 $ stitch "t f t [t f]*2 f ~ t f" (sound "kick:4") (sound "hc")
# room 0.2 # sz 0.8
-- With stitch, the rhythmic structure comes from the boolean
-- pattern. It has a synonym friend called 'sew', which instead
-- preserves the structure of the patterns it's sewing together.
-- Lets try it:
d1 $ sew "t f" (sound "kick") (sound "clap:4")
-- Oh! We only hear the kick. That's because the 'f' only switches to
-- the second pattern for the second half of the cycle, and no new
-- 'clap's happen then.
-- If we have four claps spread over the cycle, we hear the second two
-- of them:
d1 $ sew "t f" (sound "kick") (sound "clap:4*4")
-- Sew can be really nice for blending together two more complicated
-- patterns. Lets have a listen to them individually first:
d1 $ chunk 4 (hurry 2) $ n "0 .. 7" # sound "cpu"
d1 $ n "0 .. 7" # sound "cpu2" # speed 1.5 # squiz 2
-- And now sewn:
d1 $ sew (iter 4 "t f")
(chunk 4 (hurry 2) $ n "0 .. 7" # sound "cpu")
(n "0 .. 7" # sound "cpu2" # speed 1.5 # squiz 2)
-- In the above I have a really simple "t f" binary pattern, but use
-- 'iter 4' so that it shifts by a quarter every cycle.. So you get
-- different parts of the sewn patterns coming through.
-- endregion