Dynamic manipulation of pattern length (and start/end points)

I feel like I'm missing something really obvious in the documentation, but here goes β€”

I'm interested in exploring the live manipulation of pattern length, pattern start/end points, and step durations within a pattern...something akin to Mark Fell's techniques. I've lately been having fun with linger and trunc, but I haven't found a way of dynamically playing just a subset of a pattern. I'm sure there are many ways of approaching this, but I feel like there was a function I came across once that would do exactly what I have in mind, I just haven't been able to dig it up.

Mods, feel free to merge this in a pre-existing topic.

1 Like

bite ?

The bite function allows you to slice each cycle into a given number of equal sized bits, and then pattern those bits by number. It's similar to slice , but is for slicing up patterns, rather than samples. The following slices the pattern into four bits, and then plays those bits in turn.

also possibly, chew

6 Likes

That looks perfect, thanks!

okay interesting, i actually never thought about this. i use bite a lot but i'm not sure it solves the "dynamic" aspect to this problem. we need to be able to offset the start/end points of a pattern without losing the pulse.

5 Likes

Great point, and that’s my feeling after having played around with bite for a while last night. Perhaps this points to a challenge with having a global cps.

I love linger β€” maybe there's a future variation where you can simply loop a portion of a pattern with two arguments: one for the start point and one for the end.

Something like

d1
$ linger (0.25, 0.5)
$ n "1 2 3 4 5 6 7 8"
# s "midi"

would just loop the following as many times as possible within a single cycle.

$ n "3 4"

:thinking:

1 Like

okay so i am thinking fit' might get us a little closer to this. it's not perfect (meaning it doesn't do exactly what i am envisioning) but it might have potential.

the first argument is how many cycles you want to apply the fitting to. in the example below i want one cycle that's running through 8 steps of a major scale.

the second argument gets us closer to this "dynamic" end-point. in the example i am telling it to only play six notes of the total eight notes before starting the pattern up again but it's not great because you can't pattern the values.

the third/fourth arguments are tricky because it kind of just needs to be a placeholder for the total steps in your looping sequence.

d1 $ fit' 1 6 "[0 .. 7]" "[0 .. 7]" $ n (scale "major" "[0 .. 7]") # s "superpiano"

i tried making a little custom function to build dynamic lists but this clearly exceeds my Haskell proficiency because i get an error :frowning:

let step n x p = fit' 1 x "[1 .. (n - 1)]" "[1 .. (n - 1)]" $ p

where n is the number of steps you want in your sequence and x is the dynamic end point (has to be less than or equal to n).

if anyone has suggestions on doing the math to create those dynamic lists, i'd love to know how!

1 Like

love this! maybe it's just as simple as that. would certainly be much easier to use compared to fit', as well as dynamic since you can pattern the arcs.

1 Like

There are good suggestions already for getting towards this, but in the end this is a tricky one. As tidal is quite oriented around cycles as a reference point, it's difficult to work really flexibly with steps. I've been learning Konnakol recently, which has really flexible approach to time, and gives an interesting perspective on this.

In strudel there is a nice 'reset'/'restart' function that I'll backport to tidal soon, that gets further, being able to reset to the start of a cycle or pattern in a really flexible way.

Longer term though, tidal's mininotation needs to evolve into a fully flexible, step-based representation. I have a plan to work on this over the summer.

16 Likes

Very exciting stuff! Thanks for sharing.

1 Like

wow, cool, i second this! something to look forward to!

yes please

You can do this using zoom
https://tidalcycles.org/docs/patternlib/tour/time/#zoom

this automatically adjusts how fast the events are occuring, so if you would want to have a function more similar to linger you could do:

lingerAt :: (Time, Time) -> Pattern a -> Pattern a
lingerAt a@(t1,t2) p = slow (pure $ t2 - t1) $ zoom a p
3 Likes

this is sick.

THANK YOU :pray:

1 Like

maybe lingerIn would be a better name when I think about it :slight_smile:

would also cool to extend it to the signature

lingerIn :: (Pattern Time, Pattern Time) -> Pattern a -> Pattern a

I'll look into that

1 Like

yes, that would be lovely as well. if i recall, the so-called "arc" functions can be tricky to make patternable?

i've never thought about it before but I came up with this:

tParamTup :: ((a,b) -> c -> Pattern d) -> (Pattern a, Pattern b) -> c -> Pattern d
tParamTup f = uncurry $ tParam2 $ curry f
zoom' = tParamTup zoom

it seems to work, but I'd be grateful if you could test it aswell.

If it works, I think it should definitely be included in standard tidal and all similar functions that are not patternable yet should be made patternable :slight_smile:

I've only more recently been realising how powerful it is to pattern everything

2 Likes

this is working great! wow, major progress -- thank you!

d1 $ someCycles (zoom' ("<0.25 0.5>", "<0.75 1>")) $ n (scale "major" "[0 .. 7]") # s "superpiano"

works the way i would imagine it to.

i tried modifying your original lingerAt function to include the new zoom' but am getting an error:

lingerAt :: (Time, Time) -> Pattern a -> Pattern a
lingerAt a@(t1,t2) p = slow (pure $ t2 - t1) $ zoom' a p
  • Couldn't match type Ratio Integer' with Pattern Time'
    Expected type: (Pattern Time, Pattern Time)
    Actual type: (Time, Time)
    • In the first argument of zoom', namely a' In the second argument of ($)', namely `zoom' a p'
      In the expression: slow (pure $ t2 - t1) $ zoom' a p
1 Like

you need to modify it a little:

lingerIn :: (Pattern Time,  Pattern Time) -> Pattern a -> Pattern a
lingerIn a@(t1,t2) p = slow (t2 - t1) $ zoom' a p

thanks for testing!

2 Likes

so far so good! :heart_eyes:

1 Like

Just great, the only issue I faced is that if the first parameter (for all the 3 functions) is not zero, it does not sound, e.g.:

this sounds

d1 $ lingerIn ("0", (segment 8 $ slow 4 sine)) $ s "numbers"

this don't

d1 $ lingerIn ("0.1", (segment 8 $ slow 4 sine)) $ s "numbers"
1 Like