Week 4 lesson 1 - continuous patterns - sine, square, tri, saw and random functions

Ok, lets have a look at some continuous functions! This is quite a large topic (hence the longer video, partly also because I got sidetracked playing with binary patterns) but will help for getting stuck into randomness later in the week.

This video is basically all about waveforms (apart from that binary sidetrack).

  • 0:28 - basic waveforms, a.k.a. continuous patterns
  • 3:16 - sinewave patterns
  • 4:09 - changing the range of waveforms with addition or the range cycle
  • 5:33 - waveforms have no rhythmic structure, so no events! Dealing with that using segment
  • 7:46 - Giving waveforms rhythmic structure with the 'struct' function and binary patterns (I get sidetracked into binary patterns here)
  • 10:56 - Slowing down waveforms
  • 12:16 - square waves
  • 12:30 - triangle waves
  • 12:45 - sawtooth waves
  • 12:55 - using waveforms in different control patterns, e.g. reverb (room and sz)
  • 15:00 - doing arithmetic on waveforms, e.g. multipling them together
  • 16:25 - random waveforms #1 - rand
  • 17:46 - random waveform #2 - perlin
-- 'Continuous functions' provide different kinds of waveforms.
-- There's a nice graphic showing sine, square, triangle and sawtooth
-- waves here: https://en.wikipedia.org/wiki/Waveform

-- Here's what the sine waveform sounds like applied to sample playback
-- speed:
d1 $ sound "bd*32" # speed sine

-- and to panning:
d1 $ sound "bd*32" # pan sine

-- and to waveshape distortion (gets loud):
d1 $ sound "bd*32" # shape sine

-- You can manipulate continuous patterns just like other kinds of
-- patterns, for example slowing down:
d1 $ sound "bd*32" # shape (slow 2 sine)

-- The waveforms all move between 0 and 1. So at its lowest point, sine
-- will be 0, and at its highest point it will be 1. Having a value
-- near 0 can be problematic with 'speed', as you can end up with
-- sounds played very slowly that take a long time to complete.

-- To get around this you can add to the sine:
d1 $ sound "bd*32" # speed (sine + 0.5)

-- Or use the 'range' function:
d1 $ sound "bd*32" # speed (range 0.5 1.5 sine)

-- Lets listen to triangle, sawtooth and square waves:
d1 $ sound "bd*32" # speed (range 0.5 1.5 tri)

d1 $ sound "bd*32" # speed (range 0.5 1.5 saw)

d1 $ sound "bd*32" # speed (range 0.5 1.5 square)

-- What happens if you put the continuous pattern on the left?
-- Remember that with '#', the rhythmic structure comes from the
-- left. Try this:
d1 $ speed (range 0.5 1.5 sine) # sound "bd"

-- Silence! Why's that?
-- It's because continuous functions don't actually contain any
-- events. They have values which continually change, without
-- triggering anything.

-- If we want to trigger events in a continuous pattern, we have
-- to explicitly sample values from it. One way to do that is with
-- the 'segment' function:
d1 $ speed (segment 32 $ range 0.5 2.5 sine) # sound "bd"

-- The above samples 32 values per cycle, generating discrete
-- events from them.

-- Another way to do this is with 'binary' or 'boolean' patterns,
-- using the 'struct' function:
d1 $ speed (struct "t(3,8)" $ slow 2 $ range 0.5 2.5 sine)
  # sound "bd"

-- 't' stands for 'true'. So that euclidean rhythm is used to sample
-- events from the continuous sine function. We'll return to
-- binary patterns in another video.

-- You can also add or multiply continous patterns together:
d1 $ sound "bd*32" # speed (range 0.5 2.5 (sine + (slow 2 saw)))

d1 $ sound "bd*32" # speed (range 0.5 2.5 (sine * (slow 2 saw)))

-- I slowed the 'saw' down in the above patterns, so you end
-- up with a sine wave that rises in pitch over two cycles.

-- In Tidal, random functions are also often continous.
-- For example, rand works like sine, saw etc, but returns random
-- values:
d1 $ sound "bd(5,8)" # speed (range 1 3 rand)

-- Perlin is similar, but returns 'perlin noise'. In Tidal, this
-- means that the pattern smoothly transitions between random values,
-- every cycle:
d1 $ sound "bd(5,8)" # speed (range 1 3 perlin)

-- Lets try that with some reverb:
d1 $ sound "bd(7,16)"
   # room 0.7
   # sz (range 0.4 1 $ slow 4 perlin)

Next lesson:


Thanks Alex!
You got me with that last video screenshot, I've been battling to control the waveforms the way I wanted, so I watched, got distracted then was reminded when I saw the screenshot again that I hadn't actually resolved my problem after all that :stuck_out_tongue:

Anyway, using range for wave control (second item you spoke about) was exactly what I was looking for!

1 Like

Alex this is rad, thank you. So much fun to be had!

Can you explain more about how iter 4 works in the video example?

Also, these oscillators are all unipolar, right? So there's no need to , for instance, (sine + 1)/2 to get it scaled into the 0-1 range? And is that true with the rand and perlin objects, or are they bipolar? Also, is the rate of the oscillator in Hz defined by the cps?

Thanks! :smiley:

1 Like

Yes they all go from 0-1 these days. In earlier versions this was less standard, and you might still see sine1 in some videos for unipolar, but now they're all unipolar, to match with the range of gain, pan, shape etc. And yes periods are 1 cycle.


Will deal with iter and friends properly soon but iter 4 will shift time by one quarter every cycle. You can use it with any pattern.

1 Like

Spicy! And thanks for the other response too.

Hi Alex :wink: Thanks a lot for this tutorial! I have a question regarding these two lines of code:

d1 $ speed (segment 32 $ range 0.5 2.5 sine) # sound "bd"

and this

d1 $ sound "bd" # speed (range 0.5 1.5 sine)

I can't understand the difference in the execution. That is, I take the structure from the left side, but why is the operation sounds the same?

Many thanks :wink:

Hi, they shouldn't sound the same, the first one will have 32 events per cycle, and the second only one.

It's not until you have 32 'bd' kicks in the second example that they sound the same:

d1 $ sound "bd*32" # speed (range 0.5 2.5 sine)

I also needed to adjust the range of the sine.

Thanks;) regarding the position of the speed settings, does it change anything? I mean, put speed after a$(so in the first part) or after the#is important? Sorry for this simple question but I'm struggling a bit in here

First of all, a simple example

d1 $ sound "bd" # speed 2


d1 $ speed 2 # sound "bd"

These have exactly the same result.

This would also be the same, because although 'speed' is given twice, when you use #, the value is taken from the right.

d1 $ speed 5 # sound "bd" # speed 2

The next one would also be the same. There are two events on the right, but with #, the rhythm comes from the bd on the left. That bd starts at the start of the cycle, when the 2 of speed is active, so you end up with a single bd with a speed of 2.

d1 $ sound "bd" # speed "2 5"

The $ does something quite different from #. The # is combining the sound "bd" and speed "2" patterns into a single pattern. The $ is just there to make sure everything it 'worked out' on its right, before being passed to d1 to be sent to superdirt.

I talk about combining patterns with # and friends here:

and I talk about $ here:

Does that help @antonelse?

1 Like

SUPER clear Alex :wink: thanks a lot for the effort !! :slight_smile:

1 Like

Wow, this can get really fun (and crazy!)...

d1 $ speed (segment "128 64 256 16" $ range 0.5 "<64 128 16 32>" sine) # "breaks125" # legato 1 # pan (fast 4 (sine))

Hi! Very nice video, full of interesting stuff. I plan to watch the videos from the start soon in order to try and come to terms with what I've already forgotten and what I missed. My issues are generally related to combining elements and creating variety, for instance having different speeds for different parts of the n pattern, I will post concrete examples in "thoughs on the course".

Here, I tried something which didn't work and I'm wondering how to get it working :

d3 $ speed (struct "t <(3,7) (3,8)>" $ range 0.5 1 sine) #sound "dr:8"

The error comes from putting two euclidean patterns between '<' and '>' and although it didn't seem far fetched as you showed <t f t t f>(3,8) I didn't crack this. Any tip welcome and thanks for all this, a concrete project is starting to form and I'm very excited.

P.S. while watching W4L2 I found this nice solution:
d2 $ fast 2 $ n (struct "t(<2 5>,<9 7>)" $ (irand 24)+ 12) # sound "rash"

Yes the parser isn't clever enough for t<(3,7) (3,8)> but t(3,<7 8>) does indeed work

Thanks for the answer.
A new door opens :
d1 $ sound (struct "t(5,8,<0 2>)" $ choose ["bd", "arpy", "kick"])
Could you please elaborate on it?

I tried to simplify the example without choose but I didn't get how to make n patterns with a series of samples which are not in the same folder ( I' suspect I simply haven't memorized this)

d1 $ sound (struct "t(5,8,<3 8>)" $ sound "bd" "arpy" "kick"

P.S. : Eventually tried to use drawLine with little success :

drawLine $ "x(5,8,<3 8>"

Is there any way to create custom wave shapes? Almost like with ADSR types of values?


Is this what you mean?

d1 $ struct "t(5,8,<3 8>)" $ sound "bd arpy kick"

You were just missing a closing bracked here:

drawLine $ "x(5,8,<3 8>)"

Hm you can specify points and then interpolate between with smooth..

E.g. this would be like tri

smooth "0 1"

Ah, that's a neat way to go about it...

d1 $ s "ec*8" # speed (smooth "0 1@5 0.5 0.2")

...has some interesting results


Oh boy! So much fun applying the lessons learned so far.
setcps (75/60/4)

xfadeIn 1 8 $ jux (iter 2) $ loopAt 6 $ chop 1 $ striate 64 $ speed (struct "t(2,7)" $ range 0.7 1 sine) # sound "bev" # gain 0.7 # delay 1 # delaytime 0.675 # delayfb 0.8 # room 0.2

d2 $ juxBy 0 (iter 4) $ rev $ struct "t(5,8)" $ sound "tabla" # n "<21 24> <16 6> <4 12>" # gain 0.8

1 Like