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