tidalcycles / Tidal

Pattern language
http://tidalcycles.org/
GNU General Public License v3.0
2.28k stars 256 forks source link

Rhythms module #633

Open mxmxyz opened 4 years ago

mxmxyz commented 4 years ago

There is a Scales module which has been growing in size and scope, with several popular (and some less popular) scales to use as convenient shorthands. It's a nice module, because it's self-documenting and so can double as a reference of sorts . I think it would sometimes be handy to also have similar shorthands for rhythms, expressed as Pattern Bools, usable with functions such as struct and while.

I've been collecting some rhythms here; there is probably a better and more systematic way to collect them, although I don't really know where to look for myself. Still, even working with such a small library feels very nice.

jwaldmann commented 4 years ago

interesting! - just an observation: the notation

-- [3-3-4-2-4]
son = "1 0 0 1 0 0 1 0 0 0 1 0 1 0 0 0"

emphasizes that the comment is more readable than the 0/1 sequence. Well, then let's write a function f such that we can define

son = f [3,3,4,2,4]

[EDIT] something like

concatMap (\d -> 1 : replicate (d-1) 0) [3,3,4,2,4]
[1,0,0,1,0,0,1,0,0,0,1,0,1,0,0,0]

For writing long-form sequences, I'd instinctively use spaces to express subdivision

son = "1 0 0 1   0 0 1 0   0 0 1 0   1 0 0 0"

this would bring it nearer to "traditional western notation" - which Toussaint avoids on purpose (see Preface 2nd ed.)

To detect typos in longer sequences, lilypond has the nice safety measure of "bar check" http://lilypond.org/doc/v2.20/Documentation/notation/bars#bar-and-bar-number-checks .

PS: I am interested in rhythms. I am reading Toussaint's book (I just ordered it since you mentioned it in the source). Amazingly, it feels completely disjoint (orthogonal?) to, say, https://hudsonmusic.com/product/the-breakbeat-bible/ . An algebraic approach (describe rhythms with trees) is http://repmus.ircam.fr/jacquemard/publications

I want to have the "break beat elements" available as algebraic operators...

mxmxyz commented 4 years ago

Thanks! Your [Int] -> Pattern Bool function is very useful even by itself, maybe warrants a PR?

jwaldmann commented 4 years ago

it was of type [Int] -> [Int]. This has type [Int] -> Pattern Bool:

fastcat . map pure . concatMap (\d -> True : replicate (d-1) False)

make PR: well, let us include some more ideas from Toussaint's book. You already mentioned hop/jump. Can we do this?

NB: I find the book hard to read (or, too easy to read but too hard to make use of). It looks mathematical but actually it's not. All "definitions" (including "algorithms") are buried in the text, and it's often definition-by-example only, so it's not exactly clear what happens in the general case, what are the actual inputs, and what is the claimed property of the output, and why the claim would be true in general.

For instance, hop/jump (p. 80). The example starts at "5 onsets in a cycle of 12 pulses". Then the "hop width" is 2. Why? Is this width actually the third input of the algorithm? (I would certainly implement it that way.) What are its admissible values? E.g., it probably couldn't be 1 since that would create a cluster (interval of adjacent onsets). But even with hop-width 2, some clusters may appear. Or may they not? Are they allowed in an "odd" pattern? Are we using the def. from p. 75, or p. 77? And so on.

yaxu commented 4 years ago

These rhythms are great but I can't help thinking that baking them into a module seems a bit of a shame. I feel if I used such a rhythm I'd always want to edit it. So perhaps this would be better as part of a snippet library, for fast recall into an editor plugin? One problem is that we have a lot of editor plugins and there isn't a standard for snippets as far as I know..

jwaldmann commented 4 years ago

... want to edit

yes but you could also apply Tidal's combinators/transformers.

I thought that some of these algorithms from Toussaint's book could be added, like euclid is today (as a function, I'm not saying we should invent mini-notation for them).

It can be a first step, and it does not hurt?

On the general question of what to add to a library, cf. https://wiki.haskell.org/Fairbairn_threshold

telephon commented 4 years ago

Perhaps there is a way to get rid of the distinction between library function and user defined function? Something like copy on edit?

jwaldmann commented 4 years ago

Interesting. You mean, when using Tidal, there should be a way to "get the source code for this (library) function" in the editor buffer, to change it?

This would require source code to be available, which normally isn't. After cabal install tidal, ghci uses object (that is, machine code) files.

If ghci was started in tidals's source directory, then source code is present. But then we need to think about naming (for the copy of the function) and scoping (if it's using other functions that are not exported).

That's certainly possible, but would require a larger effort (design, implementation, installation), and would be worthy of a separate issue.

I thought the original topic (Toussaint's rhythm functions) is a nice student programming exercise. (But I don't have students right now - will have in October.)

yaxu commented 4 years ago

Maybe #682 could help with this - an API for expanding code.

Would be nice to be able to expand

and so on

lwlsn commented 4 years ago

Just came across this, seems like it's doing something like what's described in this issue:

https://github.com/lvm/tidal-drum-patterns

lvm commented 4 years ago

Hi, Yes. I've been working on these patterns lately basically for the same reason I created the Scales and Chords modules a while back (because I'm lazy and don't want to type the same thing over and over and over again). PS, that's why I created this PR days ago :-)

Re: Technical aspects of how to implement this: Tried to keep this as simple as possible. In the past (circa 2016) it was possible to write patterns as n "1 0 1 0" # s "sample", not sure if this is still the case, but found about mask / struct and both seem to do the trick. There are many patterns that can be written as Euclidean rhythms but that would be a bit annoying to parse [0] later.

[0] Sidenote: "t ~ [~ t] ~" is much more simple to convert to other format (for example: a SuperCollider Dict) than x(6,8).

TylerMclaughlin commented 4 years ago

Hi! quite a while since I've posted on here.

Toussaint's book is awesome imo. I think being able to compactly express sparse rhythms like the son and bossa nova timelines would be really powerful.

Following up on @jwaldmann 's function implementing @mxmxyz 's notation, the following works like a charm (highly hacky charm).

let timeline p = parseBP_E (map (\c -> if c==',' then ' '; else c) $ show $ concatMap (\d -> 1 : replicate (d-1) 0) p)

Example:
d1 $ (# sound "sd") $ gain (timeline [3, 4, 3, 2, 3] )

Not patternable though.

lvm commented 4 years ago

@TylerMclaughlin The only downside to that is that you can't use invert (or functions that are applied to pattern... am i right? it's been a while since i used tidal. there's a high chance i'm wrong), other than that -which i'm sure that in the case of being true, it can be converted to a Pattern Something (sic) somehow- looks really useful.

TylerMclaughlin commented 4 years ago

@lvm , you can do anything like rev $ timeline [3, 4, 3, 2, 3] or plyWith 2 (euclid 3 8) $ amp (timeline [3,3, 2, 3, 3]) but what you can't do is pattern the list of integers with something like timeline [3, <4 5>, 3, 2, 3]. It's got the same syntax as choose.

I've seen before but haven't used invert. inv won't work on what i posted because it's [Int] -> Pattern, but it definitely does work on @jwaldmann 's [Int] -> Pattern Bool version.

TylerMclaughlin commented 4 years ago

today learned it's way tidier to use fastcat and map pure instead of the hacky show / parseBP_E combo :D

better 0 and 1s version: let timeline p = (fastcat . map pure . concatMap (\d -> 1 : replicate (d-1) 0)) p
and @jwaldmann 's
let tlbool p = (fastcat . map pure . concatMap (\d -> True : replicate (d-1) False) p

Since the bool version might be more versatile than the 1s and 0s version, maybe it would make sense to consider a more general As and Bs timeline function:

let tlab a b p = (fastcat . map pure . concatMap (\d -> a : replicate (d-1) b)) p

a and b can be midi note numbers, or instrument/sample names.

you can get really fun linear rhythms:

d1 $ s (tlab "hh" "bd" [3,4,2,3,4])

bgold-cosmos commented 4 years ago

Just a note, if you get rid of the map pure you can actually just have A and B as patterns, and Haskell should usually figure out the correct type. For example:

tlab a b = fastcat . concatMap (\d -> a : replicate (d-1) b)

d1 $ s (tlab "hh" "bd" [3,4,2,3,4]

still works fine but now you can do

d1 $ s (tlab "hh*2" "bd" [3,4,2,3,4]

Patternizing the list is a little harder - have to think about what something like tlab "hh" "bd" ["3 4", 3, 4] should even mean, but I suspect there's probably a way to do it.