Working with groups of co-occuring events

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?

1 Like

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 Events 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.)

1 Like

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:

  • you are using withEvents, which (if I am correct) should apply a function to the input pattern
  • the function that you want to apply is munge, which
    • sorts a list of Events from lowest to highest (I assume with respect of time) based on whole (no real idea about it)
    • groups the resulting list given some predicate (and whole is here again)
    • applies the function coming all the way from withEvents and concatenate the result

but where is eventInversion gone?

1 Like

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.

1 Like

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.

1 Like

Definitely interested in knowing more about your point of view.

1 Like

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...

1 Like

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 :slight_smile:


@mattia.paterna, please tag me when you do, as I'm on the same track, even if behind you :slight_smile:

1 Like