That is exactly what I was looking for, thank you!
@yaxu let's assume for a second there is no inversion in Tidal: how would then be possible to make @loopology's function work?
That is exactly what I was looking for, thank you!
@yaxu let's assume for a second there is no inversion in Tidal: how would then be possible to make @loopology's function work?
Moved to Innards as it's a bit involved..
I'd generalise the type so it works with any number, as in Tidal notes aren't integers but floats.
inversion :: Num a => Int -> [a] -> [a]
inversion _ [] = []
inversion n xs = drop n xs ++ [ x + 12 | x <- take n xs ]
Then I'd get it to with with lists of Tidal events rather than values:
eventInversion :: Num a => Int -> [Event a] -> [Event a]
eventInversion _ [] = []
eventInversion n xs = drop n xs ++ (fmap (fmap (+12)) $ take n xs )
I used fmap to work in the values inside events, and for brevity used fmap on the list too instead of the list comprehension.
Then I made this function that allows you to group any co-occuring events and work on them together:
withFriends :: ([Event a] -> [Event b]) -> Pattern a -> Pattern b
withFriends f pat = withEvents munge pat
where munge es = concatMap (f) (groupBy (\a b -> whole a == whole b) $ sortOn whole es)
Then bring it together:
invert :: Num b => Int -> Pattern b -> Pattern b
invert = withFriends . eventInversion
d1 $ n (invert 2 "c'maj e'min'4") $ sound "superpiano"
It's complicated because tidal patterns are functions from time to events, so you have to make a function that intercepts values on the way out of the pattern, grouping together ones happening at the same time.
Here's a less attractive version of inversion
that works with negative inversions:
inversion :: Num a => Int -> [Event a] -> [Event a]
inversion _ [] = []
inversion n xs = map (\i -> fmap (+ (fromIntegral $ (i `div` l)*12)) (xs !! (i `mod` l))) $ take l [n ..]
where l = length xs
Thanks for the explanation @yaxu, there are quite many things that I am not able to understand.
I will try to summarise them, feel free to skip if you think we are going down the rabbit hole and perhaps it is better to wait.
fmap
is used here because Event
is a Functor
therefore we can't really apply standard functions such as take
and +12
right?
So here I understand that when we stack
something as in the case of chords, Tidal creates an Event
for every single note in the stack and these Event
s happen to play in the same Part
, i.e. they are scheduled to be simultaneous?
(Plus I got a bit lost inside the function TBH.)
Yep! The fmap is there to do something to the value inside the event.
Yes, so if you played two chords at the same time, this function would treat them as a single chord.
At this point tidal isn't creating any events, but composing a function for creating events.
Yes there's a lot going on here..
I can see couple of things in there though:
withEvents
, which (if I am correct) should apply a function to the input patternmunge
, which
Event
s from lowest to highest (I assume with respect of time) based on whole
(no real idea about it)whole
is here again)withEvents
and concatenate the resultbut where is eventInversion
gone?
I think this relates to what I was saying somewhere else in the club about the struggle to making things work.
What I experience is that I sometimes (hopefully) learn something new in Haskell, e.g. list comprehension. A lot of ideas come on how to tweak this list into that - but then I try to write something and it almost never works because either the function input is not what it should be or the output is not what Tidal wants.
Oops that should be -
invert = withFriends . eventInversion
A lot of haskell documentation focuses on working with lazy lists, which isn't often useful with tidal. Generally they're awesome but I'd say that lists are a kind of code smell with tidal.
Definitely interested in knowing more about your point of view.
It's just that Tidal paradigm is about functions of time, not data structures like lists.. About defining behaviour and not procedures.
Thanks a lot for taking the time and energy to explain this, Alex @yaxu. And thanks to Mattia @mattia.paterna for asking. That's going to give me a lot to think about...
you're welcome @loopology!
I've been trying really hard to get deeper into the innards and every time I see this kind of discussion I jump in straight away.
@yaxu I apologise in advance because I'll probably be asking many more times in the next weeks about Tidal paradigm as it is not clear yet to me
@mattia.paterna, please tag me when you do, as I'm on the same track, even if behind you