Need help patterning with sequences of random values

I was going to post a function I use in custom functions, but realized that there are probably better ways of doing this and I wanted see if anyone had any ideas.

I really like being able to generate random persistent sequences, for example, shuffling a pattern and having that particular shuffle repeat. I made a custom function I call lock which uses repeatCycles with an arbitrarily large number.

lock n offset p = slow n $ repeatCycles 10000 $ fast n $ (offset  <~) p

This works in that it 'locks in' any randomness in a loop that is n cycles long, and allows me vary the random pattern by changing the offset value.

I'm not crazy about the repeatCycles 10000 which feels like a code smell, but more importantly, it doesn't let me use patterns for the offset values in a predictable way. I might find two different offset vales that produce interesting sequences, for example 0 and 1, and set it to either value, but I can't set it to alternate between the two sequences produced by those offsets.

E.g., I can make two patterns:

d1 $ lock 1 "0"  $ s "arpy*4" # n (irand 8)

and

d1 $ lock 1 "1"  $ s "arpy*4" # n (irand 8)

but

d1 $ lock 2 "<0 1>"  $ s "arpy*4" # n (irand 8)

won't alternate between the two. I can easily see the reason why: the random values used in the 2nd cycle won't be the same as the random values in the first cycle, but if I loop every cycle then I can't pattern any variation.

Does anyone have any suggestion for how I can accomplish this (I've also experimented with using qtrigger in the function but either I'm doing it wrong or it's not what I need)? Or alternatively, other strategies for getting repeatable bits of randomness that can patterned together. Thanks!

2 Likes

I'd be really interested in understanding how to work in this way as well.

There's a loopFirst function (and related timeLoop) that can build what you want. For example, lock could be written as

lock n offset = timeLoop n . (offset <~)

which then produces

tidal> lock 1 0 $ s "arpy*4" # n (irand 8)

(0>¼)|n: 2.0f, s: "arpy"
(¼>½)|n: 4.0f, s: "arpy"
(½>¾)|n: 1.0f, s: "arpy"
(¾>1)|n: 3.0f, s: "arpy"

tidal> lock 1 1 $ s "arpy*4" # n (irand 8)

(0>¼)|n: 5.0f, s: "arpy"
(¼>½)|n: 6.0f, s: "arpy"
(½>¾)|n: 4.0f, s: "arpy"
(¾>1)|n: 3.0f, s: "arpy"

tidal> lock 1 "0 1" $ s "arpy*4" # n (irand 8)

(0>¼)|n: 2.0f, s: "arpy"
(¼>½)|n: 4.0f, s: "arpy"
(½>¾)|n: 4.0f, s: "arpy"
(¾>1)|n: 3.0f, s: "arpy"
1 Like

Thanks @bgold. timeLoop is worlds better than repeatCycles 10000 in terms of hackiness. But unfortunately it has the same behaviour across cycles. I should have been clearer stating my problem: (as with repeatCycles 10000) I am able to pattern the offset values within a single cycle, but not across multiple cycles.

E.g., I get 4 sequences A,B,C, and D with 4 different offset values:

-- A: ibdk
d1 $ lock 1 0 $ s "alphabet*4" # n (irand 26) 

-- B: rbfk
d1 $ lock 1 1 $ s "alphabet*4" # n (irand 26) 

-- C: xbez
d1 $ lock 1 2 $ s "alphabet*4" # n (irand 26) 

-- D: rlas
d1 $ lock 1 3 $ s "alphabet*4" # n (irand 26)

If I want to alternate between A and C, I can't use lock 2 "<0 2>", that will produce A D. Because C is occurring in the 2nd cycle I need to offset its offset value by 1:

-- A C
d1 $ lock 2 "<0 1>" $ s "alphabet*4" # n (irand 26)

While reversing the order to C A would require:

-- C A
d1 $ lock 2 "<2 -1>" $ s "alphabet*4" # n (irand 26)

This comes out the same with either definition of lock

It's not tough math, but especially if I want to sequence together more than 2 cycles, it's big investment of cognitive load.

Ah, ok, I think I get it now. The issue is the periodicity of the "offset" pattern. I don't know if there's a way around the math, but it's possible build it into the lock function so you don't have to think about it

lock n offset = timeLoop n . ((offset |- (slow n $ run n)) <~)

d1 $ lock 1 0 $ s "alphabet*4" # n (irand 26)
-- "I P D K"

d1 $ lock 1 2 $ s "alphabet*4" # n (irand 26)
-- "X C E Z"

d1 $ lock 2 "<0 2>" $ s "alphabet*4" # n (irand 26)
-- alternates each cycle

The thing to remember is that in the last example, since you want a two-cycle offset pattern, that you need to use lock 2, but I think that's not too tricky to remember once you get the hang of it.

This function probably does strange stuff if you spread events over cycle boundaries, but I think that's unavoidable.

2 Likes

Hey, do you have any links to an explanation of timeLoop? I couldn't find anything in the tidal documentation.

@bgold Perfect! That's what I was trying to get. Thanks for help!