w8.tidal 7.06 KB
-- 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