[Pattern] Gradually empty a random list which is also played repeatedly

Hi!

I am not sure if this topic has already showed up here in the forum, if so please share the link.

I would like to have a list of degrees (e.g. chromatic scale) in which I selecting a sub list randomly and looping through it:

// melodies, not chords
[1,2,3,4], [1,2,3,4], [1,2,3,4]
[5,1,4,11], [5,1,4,11], [5,1,4,11] 
[10, 2, 7, 8], [10, 2, 7, 8], [10, 2, 7, 8]

At each big cycle, let say after 100 values, the chromatic scale drops down one of its values randomly. However, the emptying process should have memory, because it will be like emptying a Bag and not like selecting different smaller random lists every time:

[0,1,2,3,4,5,6,7,8,9,10,11]
[0,1,2,3,4,5,6,7,9,10,11]
[0,1,2,4,5,6,7,9,10,11]
[0,2,4,5,6,7,9,10,11]
[0,2,4,5,6,7,9,11]
[0,2,4,6,7,9,11]

I managed to find this solution:

(
Pbind(
	\note, Pn(
		Plazy(
			{var x = (0..12)@|@dup({rrand(0,12)},4);
				Pn(Pshuf(x,3), 1);
		}),
		inf),
	\dur, 1/8
).play;
)

And then I nest several of this into Pseq().

What are the other ways to this?
Is it possible to do so with a single Pattern (something like Pshuf)?
For debugging, is it possible to trace separetely what the Plazy, Pn and Pshuf are producing? E.g. have them posting their output with a marker or linebreak in the post window?

Thanks a lot!

I’d use normal variables for this and pattern composition.

(
var degrees = (0..12);

Pdef(\removeIndex, Pbind(
	\remove, Pfunc({  
		if(degrees.size > 1,
			{ degrees.removeAt( degrees.size.rand ) });
		degrees.postln;
	}),
	\dur, 0
));

Pdef(\melody, Pbind(
	\note, Pfunc({ degrees.choose }),
	\dur, Pseq( [0.25, 0.25, 0.5])
));

Pdef(\phrase, 
	Pseq( [  
		Pdef(\melody), 
		Pfin(1, Pdef(\removeIndex)) // ensure called only once as \dur is 0
	], inf)
);

Pdef(\phrase).trace.play
)

I think this does what you have described?

As an aside, I’ve never seen someone use @|@ before, had to look that up.

1 Like

Thanks a lot!

In this case, the only possible solution is to assign the degrees list to a variable? Or would it be possible to use a repetitive Pattern that produces a random list?

For that you will need a pattern wizard, which I am not.

The problem is that you need to store and edit state. Sharing state isn’t too hard, but mutating over time it can be a little more complex. Whilst there are downsides to what I’ve posted, I think it is reasonably readable.

1 Like

hahahaha let’s see if Sauron from middle-earth will show up to solve this problem with the ring.

Your solution is great! I am just curious about the other possibilities

I am just curious about the other possibilities

If you only need next (i.e. not reset) then perhaps a routine is fine?

var z = r { var x = (0 .. 11); inf.do { x.yield; x.remove(x.choose) } };
12.do { z.next.postln }

i.e. in pattern form:

var z = p { var x = (0 .. 11); inf.do { x.yield; x.remove(x.choose) } }.asStream;
12.do { z.next.postln }

Else perhaps tiny edit to implement reset:

var z = { var x = (0 .. 11); Pfunc { x.remove(x.choose); x } { x = (0 .. 11) } }.value.asStream;
12.do { z.next.postln }; z.reset; 12.do { z.next.postln }
1 Like

There’s not a predefined pattern for this, to my knowledge.

I like Prout for this, because it lets you keep internal values in a local scope.

(
Pbind(
	\note, Prout { |inval|
		var array = (0..12);
		while { array.size > 0 } {
			// inval handling is explained in
			// https://doc.sccode.org/Tutorials/A-Practical-Guide/PG_Ref01_Pattern_Internals.html

			// run one sub-pattern
			inval = Pshuf(array, 1).embedInStream(inval);

			// modify internal state and loop back
			array.remove(array.choose);
		};
		inval  // not strictly needed here but might be in other contexts
	},
	\dur, 1/8
).trace.play;
)

A potentially useful extension might be this:

+ Pattern {
	collectEmbed { |func|
		^Prout { |inval|
			block { |break|
				this.asStream.do { |item|
					if(item.isNil) { break.(inval) };
					item = func.value(item, inval);
					if(item.isNil) { break.(inval) };  // not a typo
					inval = item.embedInStream(inval);
				};
			};
		}
	}
}

But manipulating the state still reads a bit awkwardly (Penvir is an alternate way to have local state):

(
Pbind(
	\note, Penvir(
		(array: (0..12)),
		Pfunc {
			var out = ~array.copy;
			~array.remove(~array.choose);
			if(out.notEmpty) { out } { nil }
		}
	).collectEmbed { |array| Pshuf(array, 1) },
	\dur, 1/8
).trace.play;
)

hjh

1 Like