Tidal is currently cycle-based, apart from in some parts of the mini-notation
So if you append a-b-c-
to "d-e-"
you get a-b-c-d--e--
i.e., the relative cycle duration is preserved, not the beat duration
A simple append operation that gives
a-b-c-d-e-
isn't really possible because a pattern is a function of time, rather than a sequence of beats, so you can't count the beats ahead of time.
However mininotation does have some beat-wise functionality, like
"{a b c, d e}"
gives this over two cycles:
a-b-c-a-b-c
d-e-d-e-d-e
whereas the cycle-wise
"[a b c, d e]"
gives
a-b-c-
d--e--
The {}
beat-wise approach is possible because the mininotation parser can count the elements in each subpattern and use that information when combining the subpatterns. But an equivalent haskell-level function Pattern a -> Pattern a -> Pattern a
to combine patterns beat-wise isn't really possible.
But what if we changed the representation of patterns so that mini-notation-like beat-wise compositioln was supported in tidal itself?
Potential approaches:
- Adding a field to the Pattern datatype constructor to store the number of steps in the cycle, and keeping it updated as far as possible through pattern transformations
e.g.
data Pattern a = Pattern {query :: Span -> [Event a],
beats :: Maybe Rational
}
The beats could then be used to combine patterns beat-wise.
What to call this? Tactus? Tatum? Tempo? Beats?
If we cat a four-beat a-b-
with a three-beat c--
to make the pattern [a-b-c--]
, how many beats are in that? 7 I guess?
- Adding a field to the
Pattern
datatype constructor to store the 'period' of the cycle, and keeping it updated as far as possible through pattern transformations
Similar and related to the above, but for representing the relative duration of a cycle in the pattern, rather than assuming everything has the same cycle duration. I suppose '1.' would be for finding the tactus (clapping rate) or beat-rate of the rhythm in order to align the beats of two patterns accordingly, and this would be for finding the wider cycle period for the pattern in order to concatenate them or align cycles.
Well there are probably different concepts that are being conflated here..
- Adding a separate datatype for representing rhythm as a sequence.
Something like:
data Rhythm a = Atom a
| Silence
| Subsequence {rSteps :: [Step a] }
| StackCycles {rRhythms :: [Rhythm a]}
| StackSteps {rPerCycle :: Rational,
rRhythms :: [Rhythm a]
}
| Patterning {rFunction :: Pattern a -> Pattern a,
rRhythm :: Rhythm a
}
This is a different approach to 1. and 2., involving representing sequences as a tree structure of lists that can be converted to a pattern. That would allow different kinds of transformations. Perhaps it would allow stateful lazy lists as well, like l-systems, but it's unclear how that could be efficiently converted to a stateless pattern..