Before getting on to working with longer samples, here's something about the every function. It's a nice clear example of how functions work, and gives us the opportunity to start to get a feel for how parenthesis and $ works. I also go through how to add an effect as a function.
I had a lot of problems with corrupted subtitle files which I won't go into.. and only after editing the subtitles noticed that my friend the vertical grey oblong decided to join me in the video. They're not really in the way so I decided not to reshoot it all, hope they don't get too distracting!
Here's the worksheet:
-- every
-- 'every' is one of a family of Tidal functions, that takes another
-- function as one of its inputs.
-- Let's say we had a simple pattern like this:
d1 $ sound "bd sd ~ cp"
-- ... and we wanted to speed it up like this:
d1 $ fast 2 $ sound "bd sd ~ cp"
-- ... but only one cycle out of three.
-- Here's how we'd use 'every' to do that:
d1 $ every 3 (fast 2) $ sound "bd sd ~ cp"
-- You can read this as "every 3rd cycle, make 'sound "bd sd ~ cp"',
-- go faster by a factor of two."
-- We'll take this apart to work out why we sometimes use (), and
-- sometimes '$' later. First, lets look at more, practical examples
-- of using 'every'.
-- We can use every with any function that takes single pattern as
-- input (and returns a transformed version as output). For example,
-- we can use 'hurry' instead of fast:
d1 $ every 3 (hurry 2) $ sound "bd sd [~ bd] [cp bd*2]"
-- Or use 'rev':
d1 $ every 3 (rev) $ sound "bd sd [~ bd] [cp bd*2]"
-- Because 'rev' is a single word, we don't actually need to put it in
-- parenthesis:
d1 $ every 3 rev $ sound "bd sd [~ bd] [cp bd*2]"
-- Here's a trick with using effects as functions..
-- Lets look at this:
d1 $ sound "bd sd [~ bd] [cp bd*2]"
# squiz "5"
-- We can treat the '# speed 5' bit as a function. If you think about
-- it, it does something to a pattern, just like 'fast 2' does.
-- So.. does this work?
d1 $ every 3 (# squiz 5) $ sound "bd sd [~ bd] [cp bd*2]"
-- Yes it does!
-- You can also add more than one 'every' manipulation, giving them
-- different periods for their first input, to create longer form
-- variety:
d1 $ every 3 (# squiz 5) $ sound "bd sd [~ bd] [cp bd*2]"
d1 $ every 2 (hurry 2) $ every 3 (# squiz 5) $ sound "bd sd [~ bd] [cp bd*2]"
-- keep going..
d1 $ every 4 rev $ every 2 (hurry 2) $ every 3 (# squiz 5)
$ sound "bd sd [~ bd] [cp bd*2]"
-- In Tidal, the pattern that a function is manipulating is generally
-- its final input, which makes it easy to 'chain together' functions
-- like this.
-- Ok as promised, lets go back to our original, simple example:
d1 $ every 3 (fast 2) $ sound "bd sd ~ cp"
-- Lets go through the three 'inputs' (also sometimes called
-- 'parameters' or 'arguments') for every.
-- [a] 3 - how often a function is applied
-- [b] fast 2 - the function that is applied
-- [c] sound "bd sd ~ cp" - the pattern that it's applied to.
-- Looking again at this pattern, you can see that the inputs are
-- given in three different ways:
d1 $ every 3 (fast 2) $ sound "bd sd ~ cp"
-- '3' is just on its own. It's a single number so tidal has no
-- problem knowing it's a single input.
-- 'fast 2' is in parenthesis '(fast 2)'. Then the word 'fast' and
-- number '2' are grouped together into a function, _before_ being
-- passed to 'every' as its second input.
-- 'sound "bd sd ~ cp"' has $ in front. We *could* have done this
-- instead:
d1 $ every 3 (fast 2) (sound "bd sd ~ cp")
-- That works fine, but '$' does the same kind of job. It passes
-- what's on its left, to the function on its right, as a single
-- parameter. '$' has really low priority, which means everything on
-- its right is worked out first before being passed to the left.
d1 $ every 3 (fast 2) $ sound "bd sd ~ cp"
-- This saves you from having to match up ( and ) around a function's
-- final input. It doesn't work with anything other than the final
-- input, so unfortunately this _doesn't_ work
d1 $ every 3 $ fast 2 $ sound "bd sd ~ cp"
-- The above would work out 'fast 2 $ sound "bd sd ~ cp"' first, and
-- would then try to pass that to 'every' as its second parameter,
-- which doesn't make sense to tidal, so it returns an error.
-- Note that when Tidal makes an error, if there was already a
-- pattern running, it will keep that going. If you're live coding
-- in front of an audience, you probably don't want an error to
-- result in silence!
I liked that one too 23/66 for my percentages @tedthetrumpet
@yaxu I would still call it snappy, not because of the time (13m is still a good length imo) but because you covered the content in exactly the level of detail required (imo of course!)
very cool. it becomes harder and harder to keep count of what's happening, which is funny and tricky in the case of weird rhythms and so on. i think i need to go back to the first lesson and write down everything you teach, i feel like i already forgot a lot!
hurry needs two inputs - the speed factor (the amount you're 'hurrying' by) and the pattern you're hurrying.
rev only needs one input - the pattern you're reversing.
Once you give hurry its first input, that speed factor, it only has one input left - the pattern to hurry. So you can say that hurry 2 is of the same 'type' as rev.. Both take a pattern as input and return a transformed pattern as output.. and that's the type of function that every wants for its second input.
Here's the three inputs for every again: every (number of cycles) (function that takes pattern as input, and returns pattern as output) (the pattern to work on)
.. and then its output is the transformed pattern.
You can actually ask haskell what type of thing a function or value is with :t. For example:
You can see that rev and hurry 2 are similar, but rev deals with Pattern as and hurry 2 with ControlPatterns. Pattern a means a pattern of any type, so rev is more general than hurry 2 - it can reverse patterns of numbers (such as "1 2 3"), patterns of words (such as "bd bd"), or patterns of superdirt controls (such as sound "bd bd"). hurry 2` only works with patterns of superdirt controls.
:t every
every :: Pattern Int -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
You can compare this with my description of it earlier.. Let me label the different inputs and outputs so you can compare:
every ([a]number of cycles) ([b]function that takes pattern as input, and returns pattern as output) ([c]the pattern to work on) .. and then returns [d]the transformed pattern.
every :: [a]Pattern Int -> [b](Pattern a -> Pattern a) -> [c]Pattern a -> [d]Pattern a
This can get a bit high minded, but you can understand how functions work just from working with examples, it comes with practice. If you want to look into the underlying principles though, then "partial application" and "currying" are good search terms.
d1 $ every 2 (hurry 2) $ every 2 (# squiz 2) $ "bd sd"
more compact like this:
d1 $ every 2 ((hurry 2).(# squiz 2)) $ "bd sd"
If you are only using functions like fast, slow, striate, hurry, stut (functions that don't need a # inside the every-function) you can omit the brackets
d1 $ every 2 (hurry 2 . striate 16) $ "bd sd"
You can do this with as many functions as you like
Yep indeed! If you're feeling used to # and $ and up for a challenge, then . is a good one to get your head around. It's a general Haskell thing, but in Tidal it's useful for putting two pattern-transforming functions in the place of one.
Yes that's right! d1 takes a pattern as input, and doesn't really have an output, apart from the 'action' of using the pattern to schedule events to send to superdirt.
Hello Yaxu, sorry for the delay but I'm studying the course now and not in live. It's very clear but I need an explanation on why in every we are using direct function like (rev) (hurry 2) (speed 2) and for crush; we need to put # on left (# crush 2). Even if crush is returning a Control Pattern like the others functions.
Hi, the delay is no problem, some people are still just joining the course and I'm happy to answer questions any time.
This is a bit tricky.
Firstly, it's true that rev takes a pattern as input, and returns a new version of that pattern, with each cycle reversed. This will work with any kind of pattern, whether it's a pattern of numbers or a control pattern. It will just return a pattern of the same type as you give it.
crush and speed work a bit differently. They both take a pattern of numbers as input, and return a control pattern as output. So crush 2 is a control pattern. So is speed 2.
That's the difference - rev is a function that works on patterns, crush 2 is itself just a control pattern.
For its second input, every needs a function that works on patterns, like rev. crush 2 isn't a function that works on patterns, it is itself a kind of pattern.
Ok so we want to give every a function that applies the crush 2 control pattern to another pattern, but every 2 (crush 2) $ sound "bd sn" doesn't work. What is the function that combines one control pattern to another?
The answer is #. When we write sound "bd sn" # crush 2, it's the # that is combining the two control patterns sound "bd sn" and crush 2.
so every 2 (# crush 2) $ sound "bd sn" is what we want. It says, every second cycle (every 2), combine (#) a crush control pattern (crush 2) with the base pattern (sound "bd sn")
(remember that $ here is just making sure that what is on its right is worked out in full before being passed to the function on it's left. We could also do every 2 (# crush 2) (sound "bd sn") for this.)
Ok thanks @yaxu . It's very clear now. I reformulate to test if i have well understood. (fast 2) , (slow 2) , (hurry 2) are function that take a pattern/control pattern in input and return a pattern/control pattern, but (crush 2) or (speed 2) are control pattern only. So we need # to get the sound pattern on the right, combine it with the control pattern and return the result to the second parameter of the function "every"
d1 $ every 4 (const $ sound "[bd sn bd hh]") $ sound "bd [bd*2 sn:2] [bd ~]"
The const function takes two inputs, and simply returns the first one. So if you give it just one input like this: const $ sound "[bd sn bd hh]" you end up with a new function that takes one more input, but ignores it and just returns the input it already has. That's the function that's being passed to every 4, so every fourth repetition, the pattern on the right is replaced with the first input to const. Hope that makes some sense!