Patterns, realtime updating arrays in Prand (or ListPattern more generally)

Does any one know how to achieve something like this…

~chord_tones = [1, 2, 3];
~chord_tones = 10 + [1, 2, 3];
~chord_tones = [1, 2, 3, 5];

Pbind(
   \dur, 0.1,
   \freq, Prand( Pfunc({ ~chord_tones }) )
).trace.play

ERROR: ListPattern (Prand) requires a non-empty collection; received a Pfunc.

I’ve tried Pdefn

Prand(Pdefn(\f), inf)
ERROR: ListPattern (Prand) requires a non-empty collection; received Pdefn('f').

The current ListPattern implementations directly index (by number) into the list member variable, so you can’t use a “list generator” as in those examples.

You could probably use an array-proxy that supports numeric indexing – i.e., List. List does allow you to replace its array, so it could be pressed into service as effectively an array Ref.

Changing the size while a list pattern is iterating is likely to do weird things though (maybe only in Pseq, or maybe also Place).

Btw the reason for the error message is a size check, not a class check – Pseq([], inf) will infloop – so we require at least one item.

hjh

What about this ?

\note, Pfunc { ~chord_tones.choose }

You could also check the PLx pattern suite from miSCellaneous_lib, there’s a PLrand.

That solution doesn’t work for me as Prand is just an example, I want to use patterns operations on the array, replace and resize it at run time.

What does PLx do?

If this is the only reason, it might be worth having the pattern terminate instead of loop.

I’ll look into an array proxy, thanks!

It does exactly that :slight_smile:

Amazing :heart_eyes: thanks! (And then some characters)

One already exists – I mentioned it.

l = List[0, 2, 4, 2, 3, 1, -1, -3];

p = Pbind(\degree, Pseq(l, inf), \dur, 0.25).play;

l.array = [8, 5, 6, 3, 4, 1, 2, -3];

l.array = [1, 2, 3];

l.array = Array.rand(11, -3, 7);

p.stop;

hjh

1 Like

Thanks!

It works quite well for my use case of Prand, but in other patterns it has some odd behaviour.

Ptuple and Pshuf never gets updated with the new values.

Changing Pseq wraps weirdly when the sizes are different, but does correct itself.

~a = List[1, 2, 3, 4, 5]

Pdef(\x, Pbind(
	\dur, 1,
	\seq, Pseq(~a, inf).trace,
	\rand, Prand(~a, inf),
	\shuf, Pshuf(~a, inf),
	\tupl, Ptuple(~a, inf),
)).
~a.array = [\a, \b, \c]
1
2
3
4
5
1
2
3
4
-> List[a, b, c]
b
a // should go c here, presumably the index is wrapping? From here behaviour is correct.
b
c
a 
b
c

Changing Pseq to an empty array does one of two things. In most cases, it halts the pattern — this is good!
However, if the pattern has just played the final element it will crash the interpreter.

fork {
	~a = List[1, 2, 3];
	
	Pdef(\x, Pbind(
		\dur, 1,
		\seq, Pseq(~a, inf),
	)).trace.play;
	
	2.5.wait; // 2 < t <= 3
	
	~a.array = [];
	
	1.wait;
	"survived".postln;	
}

I wonder if these issues could be fixed. Then we could just make this work?

An alternate approach may be to write the pattern to generate indices into an array, and wrap that in a pattern that explicitly supports changing the values to which the indices refer – “if you want to change the source data, here is a single way that’s explicitly designed to support that, and you have control over it” where the current contract is “at your own risk.”

It’s actually the Pshuf problem that gave me this idea. The only way to do Pshuf and change the array is to shuffle indices, rather than shuffling the array. Then I thought, well, if we generalize that and use that approach for the whole class of problems, it could all be fixed in one place.

Ptuple would be trickier because it needs to make a stream for every new array element.

Yes, I suspected so (and even included that in an earlier message).

hjh

With PLx (miSCellaneous_lib) it could look like this:

~a = [1, 2, 3, 4, 5];


Pdef(\x, Pbind(
	\dur, 1,
	\seq, PLseq(\a, inf),
	\rand, PLrand(\a, inf),
	\shuf, PLshuf(\a, inf),
	// and all other PLx patterns ...
)).trace.play


// replace while running

~a = [\a, \b, \c]

As James says, Ptuple is a trickier case. I have not implemented a PLtuple as I had no clear use case in mind. What would one want to replace, numbers, Patterns ?

WIth the versatile Pn & Plazy combo and Streams you could achieve something:


~a = [1, 2, 3, 4, 5];

// Pfuncn defaults to repeats  1

Pdef(\x, Pbind(
	\dur, 1,
	\tuple, Pn(Plazy { Ptuple(~a.collect(Pfuncn(_))) })
)).trace.play

// replace while running

~a = [\a, \b, \c]
~a = [\a, \b, Pseq([0, 1], inf).iter]
~a = [\a, \b, Pseq([2, 3], inf).iter]

WIth a helper function it can be done more smoothly

~myPtuple = {  |sym| Pn(Plazy { Ptuple(currentEnvironment[sym].collect(Pfuncn(_))) }) } 

~a = [1, 2, 3, 4, 5];

Pdef(\x, Pbind(
	\dur, 1,
	\tuple, ~myPtuple.(\a)
)).trace.play

~a = [\a, \b, \c]
~a = [\a, \b, Pseq([0, 1], inf).iter]
~a = [\a, \b, Pseq([2, 3], inf).iter]

1 Like