Week 7 Lesson 1 - Composing patterns together with overlay, stack, append, cat, seqPLoop and seqP

Sorry a bit of a late start to the week. Here's a starter, looking at different ways of composing patterns together. I'll continue this in the next video with a look at the ur function for composing patterns of patterns.

  • 0:00 Intro - comparing composition in music vs composition in (functional) programming
  • 2:30 - using 'overlay' to stick one pattern on top of another
  • 4:22 - using 'stack' to stick any number of patterns on top of one another
  • 5:23 - applying functions / effects to whole stacks
  • 6:06 - (sidetrack) - using 'all' to apply a function to all the patterns that are running
  • 8:30 - append and cat - for alternating between cycles from a pair or a list of patterns
  • 10:35 - fastappend and fastcat
  • 11:50 - seqPLoop - for making a looped sequence of patterns
  • 14:20 - naming patterns within a seqPLoop
  • 15:36 - seqP - for making a single-shot (non looped) sequence of patterns
-- Composing patterns together

-- We've already looked at different ways of composing patterns
-- together. Something as simple as this is a composition:

d1 $ fast "1 2 3 4" $ sound "lt mt ht bd*2"

-- Not a super interesting one, but it composes together a pattern of
-- densities, and a pattern of sounds, to create a new pattern that is
-- more than the sum of its parts.

-- In this lesson though we're going to look at ways to compose what
-- you could call 'independent' patterns, where one isn't used to
-- manipulate the other.

-- Tidal is often used in live situations, but there are some
-- functions that help you assemble multiple patterns into something
-- like a complete 'piece', such as a structured four-minute track.

-- Before we get to that, lets look at some extra-simple ways of
-- composing patterns together.. as they can be surprisingly useful

-- First, there's `overlay` that simply plays the two given patterns
-- at the same time:
d1 $ overlay (fast "1 2 3 4" $ sound "lt mt ht ~")
             (sound "clap:4(3,8)" # speed 2)

-- Similar to this is `stack`, which lets you overlay any number of
-- patterns on top of each other. People tend to use this rather than
-- `overlay`, as it's more flexible:
d1 $ stack [(fast "1 2 3 4" $ sound "lt mt ht ~"),
            (sound "clap:4(3,8)" # speed 2),
            sound "[kick:5(5,8), snare:3(7,16,3)]"
           ]

-- The above composes a list of three patterns together. You can see that
-- a list is given using square brackets ('[' and ']'), with the patterns
-- in the list separated by commas (','). You have to remember *not* to
-- put a comma at the end of the list, only between the elements.

-- The above might not seem too useful, as you could do the same with
-- separate patterns. This sounds exactly the same as the above:
d1 $ fast "1 2 3 4" $ sound "lt mt ht ~"
d2 $ sound "clap:4(3,8)" # speed 2
d3 $ sound "[kick:5(5,8), snare:3(7,16,3)]"

-- Remember though that stack combines everything into a single
-- pattern. This is useful as you can manipulate all those patterns as
-- one. For example:
d1 $ chunk 4 (hurry 2) $
  stack [(fast "1 2 3 4" $ sound "lt mt ht ~"),
         (sound "clap:4(3,8)" # speed 2),
         sound "[kick:5(5,8), snare:3(7,16,3)]"
        ]

-- Or adding a parameter that applies to the whole stack:
d1 $ stack [(fast "1 2 3 4" $ sound "lt mt ht ~"),
            (sound "clap:4(3,8)" # speed 2),
            sound "[kick:5(5,8), snare:3(7,16,3)]"
           ] # squiz "<0 2>"

-- So `overlay` and `stack` stack things up, so that they happen at
-- the same time. Howabout sticking things together over time, so they
-- happen one after another?

-- Like overlay and stack, there is one function, 'append' for
-- composing two patterns together, and another, 'cat' for composing a
-- list of patterns together.

-- For two patterns:
d1 $ append (fast "1 2 3 4" $ sound "lt mt ht ~")
            (sound "clap:4(3,8)" # speed 2)

-- For a list of patterns:
d1 $ cat [fast "1 2 3 4" $ sound "lt mt ht ~",
          sound "clap:4(3,8)" # speed 2,
          sound "[kick:5(5,8), snare:3(7,16,3)]"
         ]

-- Again, you'll see `cat` used more often than `append`.

-- `append` and `cat` maintain the original 'density' of the patterns,
-- taking one cycle per cycle.

-- There are variants `fastappend` and `fastcat`, that take a cycle
-- from each of the patterns, and squash them all into a single cycle:

-- For two patterns:
d1 $ fastappend (fast "1 2 3 4" $ sound "lt mt ht ~")
  (sound "clap:4(3,8)" # speed 2)

-- For a list of patterns:
d1 $ fastcat [fast "1 2 3 4" $ sound "lt mt ht ~",
              sound "clap:4(3,8)" # speed 2,
              sound "[kick:5(5,8), snare:3(7,16,3)]"
             ]

-- That's fine, but what if you don't want to loop between patterns a
-- cycle at a time, but have something between a `stack` and a `cat`,
-- where you can have the patterns overlap? `seqPLoop` is one answer.

-- With `seqPLoop`, you say when each pattern starts and stops.
-- Lets first emulate the `cat` from earlier, by having each
-- pattern last one cycle.
d1 $ seqPLoop [(0, 1, fast "1 2 3 4" $ sound "lt mt ht ~"),
               (1, 2, sound "clap:4(3,8)" # speed 2),
               (2, 3, sound "[kick:5(5,8), snare:3(7,16,3)]")
              ]

-- Now let's adjust the starts and stops, so the first two overlap by
-- a pattern, then there's a gap of a cycle before the last one plays:
d1 $ seqPLoop [(0, 2, fast "1 2 3 4" $ sound "lt mt ht ~"),
               (1, 3, sound "clap:4(3,8)" # speed 2),
               (5, 6, sound "[kick:5(5,8), snare:3(7,16,3)]")
              ]

-- If you want to use the same pattern more than once, you can give it a name
--, like this:
let florence = fast "1 2 3 4" $ sound "lt mt ht ~"
in
d1 $ seqPLoop [(0, 2, florence),
               (1, 3, sound "clap:4(3,8)" # speed 2),
               (3, 4, sound "[kick:5(5,8), snare:3(7,16,3)]"),
               (3, 5, florence # coarse 5)
              ]

-- If you don't want the pattern sequence to loop, then use
-- seqP. You'll need to use something like `qtrigger`, so it starts
-- from cycle 0
d1 $ qtrigger 1 $ seqP [(0, 2, fast "1 2 3 4" $ sound "lt mt ht ~"),
                        (1, 3, sound "clap:4(3,8)" # speed 2),
                        (5, 6, sound "[kick:5(5,8), snare:3(7,16,3)]")
                       ]
12 Likes

tidal becomes more of an integral part of my workflow with every week

thank you, alex!

3 Likes

yesss! been trying to understand seqP for weeks now :slight_smile: thanks alex!
before i started this course a (haskell knowing) friend of mine gave me a little introduction and later sent me snippets of code to try out, but i wasn't able to keep seqP in my memory (<- brain)

1 Like

I've added an index now to the post now..

Thanks for the feedback, glad it was helpful! I was a bit unsure about it, I was pretty tired when I made it..

1 Like

And don't forget that you can stack seqPLoops:

d3 $ stack [
    seqPLoop [
    (0, 2, s "[bd*2] hh bd sn:8"),
    (1, 3, s "[hh*4] bd bd sn:8"),
    (2, 4, s "bd bd sn:8 sn:8"),
    (4, 5, s "bd sn:8 [bd*2] sn:8")
    ],
    seqPLoop [
    (0, 2, s "jvbass(3,8)"),
    (2, 4, s "jvbass(5,8)"),
    (4, 5, s "jvbass*4" # speed 1.1)
    ]
]
13 Likes

Hi, has anyone had the same problem with formatting their stacks/tracks, where you aren't able to trigger patterns that spread across lines

In the video lessons, and elsewhere online I see stuff like this a lot:

d1 $ s "bd sd"
# crush 3

d2 $ stack [( n "0 1 2 3" # s "cpu"),
( fast "2 3" $ s "bd")]

It looks really handy for legibility spacing out your patterns over multiple lines, especially for stacks/appends/etc, but I can't seem to get it to trigger all at once.

Shift plus enter only triggers the current line I'm on, and draws no connection between multiple lines when I've tried this formatting.

I'm sure this is something very silly that I've been missing or out of the loop on but if someone could help, I'd definitely appreciate it!!!!

Hi @camcclellan8142, try ctrl- or cmd-enter. More info here:
Week 1 lesson 1 - Tidal interaction

1 Like

I was copying what you were doing in the video with all, all $ (chunk 4 (hurry 2)), but it does not seem to have any effect? Does not throw an error either?

This pattern is amazing!

I tried something using this

setcps(135/60/3)

d1 $ stack [(s "jvbass:3(3,8)"),
            (s "jvbass:5(5,8)")
            ]

d2 $ s "[bd sn]*2" # room 0.3 # sz 0.3

d3 $ jux (rev) $ s "ht(3, 8)" # squiz 2

d4 $ seqPLoop [(0, 2, s "superpiano(3, 8)"),
              (1, 3, fast 2 $ n "e'maj7 d'maj" # s "superpiano(5, 8)" # legato 2)
              ] # room 0.3 # sz 0.1 # lpf 2000

d5 $ stack [
    seqPLoop [
    (0, 2, s "[bd*2] hh bd sn:8"),
    (1, 3, s "[hh*4] bd bd sn:8"),
    (2, 4, s "bd bd sn:8 sn:8"),
    (4, 5, s "bd sn:8 [bd*2] sn:8")
    ],
    seqPLoop [
    (0, 2, s "jvbass(3,8)"),
    (2, 4, s "jvbass(5,8)"),
    (4, 5, s "jvbass*4" # speed 1.1)
    ]
]

I'm still a beginner and hope to see some advice/insight regarding what can I do build on it more.

2 Likes

Hello! For some reason, the combination 'qtrigger 1 $ seqP 'only works for me when I use it on d1. In any other stream, it doesn't sound! does anyone have an idea why this could be happening? thank you and yayyyy SeqP <3 <3 <3

I have found it! now I understand that the value of the trigger relates to the stream it's applied to. So for d3 it should be: d3 $ qtrigger 3
:slight_smile:

Yes! One day I will fix it so you don't have to give this number..