Repeating a random pattern

Hello

I’m sure it’s trivial but I haven’t found out how to repeat a random number of times a random pattern from a list of patterns.

Example : I have two patterns Pseq([1,2]) and Pseq([3,4]) and I want to choose one of them, and repeat it 2 or 4 times.

I’ve tried Pn and Pstutter but neither of them does what I want

Pn(
	Prand(
		[Pseq([1,2]), Pseq([3,4])], 
	),
	Prand([2,4])
).asStream.nextN(10)
-> [ 3, 4, nil, nil, nil, nil, nil, nil, nil, nil ]
Pstutter(
	Prand([2,4]),
	Prand(
		[Pseq([1,2]), Pseq([3,4])], 
	)
).asStream.nextN(10)
-> [ 3, 3, nil, nil, nil, nil, nil, nil, nil, nil ]

Any clue ?

Thanks !

The simplest what comes to my mind is the straight translation of your verbal formulation:

Pn([Pseq([1,2]), Pseq([3,4])].choose, [2, 4].choose).asStream.nextN(10)

By the way Pattern / Streams and repetitions is by far no trivial topic. I have encountered a number of examples, where the variant of repetition / embedding can be easily thought and formulated, but is clumsy to express in classical Pattern notation.
This is a hint that even the most flexible syntax (and SC patterns are definitely very flexible) tends to lead to certain solutions and makes others less likely to be used …

Thanks a lot @dkmayer for your fast and simple answer !

I was expecting to repeat that process several times.
Based on your solution, I would do

Pn(
	Plazy(
		{
			Pn([Pseq([1,2]), Pseq([3,4])].choose, [2, 4].choose.debug("nb"));
		}
), inf).asStream.nextN(20)

which seems to work

nb: 4
nb: 2
nb: 4
-> [ 1, 2, 1, 2, 1, 2, 1, 2, 3, 4, 3, 4, 1, 2, 1, 2, 1, 2, 1, 2 ]

but maybe there’s something clever ?

Thanks again !

Exactly, Plazy is then the tool of choice to embed it.

However, thinking about this, the above solution isn’t very flexible. Suppose you wouldn’t only want to choose (which is easy to write with method ‘choose’), but to generate a sequence of Patterns with Patterns itself and then repeat them, controlled with a sequence, which should maybe also be generated by a Pattern. How to do that ?
You could use Ref objects to force a Pattern to output Patterns, or, in other words, inhibit their normal embedding. This Stream of ref’ed Patterns can then be used in a Plazy:

// last example

(
x = Pstutter(
	Prand([2, 4], inf),
	Prand([`Pseq([1, 2]), `Pseq([3, 4])], inf)
).iter;

Pn(Plazy { x.next.value }).iter.nextN(20);
)

// check helper stream alone
// x.next

So more refined sequences of this type can be generated:

(
y = Pstutter(
	Pseq([Prand([1, 2]), Prand([3, 5])], inf),
	Pseq([ `Pseq([1, 2]), `Prand([3, 4]) ], inf)
).iter;

Pn(Plazy { y.next.value }).iter.nextN(30);
)

Insterestingly I don’t remember ever having thought about this kind of using Refs in combination with Patterns, it might open some nice possibilities.
Also it might be worth writing a Pattern for this special type of repetition.

// provisional easy implementation, might need improvements

save as .sc file in Extensions and recompile

PpatStutter : Pstutter {
	*new { |n, pattern|
		^super.new.n_(n).pattern_(pattern)
	}

	embedInStream { | event |
		var str = Pstutter(n, pattern).iter;
		inf.value(event).do { event = str.next.value.embedInStream(event) };
		^event;
	}
}

Then we can write

(
PpatStutter(
	Pseq([Prand([1, 2]), Prand([3, 5])], inf),
	Pseq([ `Pseq([1, 2]), `Prand([3, 4]) ], inf)
).iter.nextN(30)
)

(
PpatStutter(
	Prand([2, 4], inf),
	Prand([`Pseq([1, 2]), `Pseq([3, 4])], inf)
).iter.nextN(30)
)

There’s a catch here – something that is poorly documented.

Let’s take Pseries(start, step, length) as an example: beginning with a number start, and producing length values, repeatedly add step.

A given series of numbers can have only one starting point, and one length.

But, within that series, conceivably the step could change. To support that, you’re allowed to provide a pattern for step. When the Pseries streams out, it automatically converts step into a stream.

Pseries(0, 1, 5).asStream.all
-> [ 0, 1, 2, 3, 4 ]

Pseries(0, Pwhite(1, 3, inf), 5).asStream.all
-> [ 0, 3, 5, 6, 8 ]

So, what if you use a pattern for the starting point?

Pseries(Pwhite(0, 7, inf), 1, 5).asStream.all
-> [ a Pwhite, a Pbinop, a Pbinop, a Pbinop, a Pbinop ]

Every time Pseries streams out, it needs one and only one starting point. So it does not automatically asStream, and you get the results of the math operations on the pattern objects themselves (basically, nonsense).

Pseries(Pwhite(0, 7, inf).asStream, 1, 5).asStream.all
-> [ 7, 8, 9, 10, 11 ]

^^ If you supply a stream explicitly, then it does as you expect.

One step further, to show why streams are useful here:

Pn(
	Pseries(  // <-- return values
		Pseries(0, 1, inf).asStream,  // <-- `start` stream
		1, 4
	),
	5
).asStream.all
-> [ 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 6, 4, 5, 6, 7 ]

^^ The start stream remembers its state between invocations of the “return” pattern. Without the explicit asStream, that state is forgotten.

Back to your example: In a single invocation of Pn, there can be one and only one number of repeats. So Prand([2, 4]) needs .asStream.

Pn(
	Prand(
		[Pseq([1, 2]), Pseq([3, 4])], 
	),
	Prand([2, 4]).asStream
).asStream.nextN(10)
-> [ 3, 4, 3, 4, nil, nil, nil, nil, nil, nil ]  // did twice, OK

This does actually all make sense, except that sclang does nothing syntactically to distinguish which arguments are which type. You just have to “know.” That’s definitely a point of confusion when getting started with patterns.

hjh

1 Like

Sorry for the late reply @dkmayer and @jamshark70, your two last solutions are a bit difficult to understand for me ! I’m working on it today and will try to get it ! Thanks again !

Based on the title, I thought this thread might have some solutions for what I’m trying to do - that is, to repeat the events of a randomly generated pattern, rather than just re-evaluating (i.e., calling .next on the stream of) the embedded pattern. I have scoured the documentation and am looking through Stream.sc and Patterns.sc and I have not had any success so far.

I’m doing some timing manipulation of the durations and number of events in the embedded Pattern to change up timing, so I don’t necessarily know how many events I should be embedded ahead of time. I’m using Psync in my pattern to repeat, and since this creates a pattern of finite length, and I thought this could be wrapped in Pn to simply repeat the truncated bars, but this does not appear to be the case. Instead, this just re-evaluates the pattern that yielded the events, rendering the extra plumbing pointless.

The Stream documentation has an example of a FuncStream that resets the random seed in its reset function. I guess that could be part of a solution, but it seems a bit hacky and messy to set up.

Pscratch from ddwPatterns also seems to have some potential, but it seems like the indeterminate number of events could get in the way here and I’m not quite sure how to wire it up. The idea of event memory is what seems particularly useful about this class.

I attempted to implement a version of such a memory only for value streams using Plazy - I had to do the aforementioned reseeding trick (i.e., resetting the repeated sequence), but it did work. However, I haven’t figured out how to do this for event streams. Anyway, here’s the closest thing I have so far:

(
var seed = 413;
var rand2 = Pfin(3, Pwhite(0, 100)).asStream;
rand2.randSeed_(seed);
Pn(Plazy({ 
	var next = rand2.next; 
	next.postln;
	if(next == nil, {
		'Out of values, resetting'.postln;
		rand2.reset;
		rand2.randSeed_(seed);
		next = rand2.next;
		next.postln;
		next;
	}, {
		next	
	});
})).asStream.nextN(10)
)

Any suggestions appreciated :slight_smile:

Sorry if reviving an old thread is a no-no; it seemed like the best approach.

See PSloop from miSC lib. Or else you need to do what it does, i.e. save the values if you want to repeat the same.

.) PSloop: Pattern to derive loops from a given Pattern

1 Like

Excellent! This gets me most of the way there. I still need to figure out how to sync it - it doesn’t seem to play nicely with a finite Psync pattern, but maybe there’s some other way to set up an accumulator or use an event key or the like. Thanks!

Here’s my crude version of a “Pfin-style” auto-repeater

(
~pbuf = { |pat, n, rreps = 1|
	if (n < 1) { nil } { Plazy {
		var buf = Array(n), i = -1;
		Prout({ |ev|
			// recording mode
			while { i = i+1; i < n } {
				buf = buf.add(ev);
				ev = ev.yield;
			};
			// replay mode
			rreps do: {buf do: _.yield};
		})
		<> Pclutch(pat, Pfunc {i < n});
	}}
}
)

Some tests

~pbuf.(Pseq([5, 7, 11, 345]), 3).iter.all
// -> [ 5, 7, 11, 5, 7, 11 ]

// 0 == rreps is Pfin basically
~pbuf.(Pseq([5, 7, 11, 345]), 2, 0).iter.all
// -> [ 5, 7 ]

~pbuf.(Pseq([5, 7, 11, 345]), 2, 4).iter.all
// -> [ 5, 7, 5, 7, 5, 7, 5, 7, 5, 7 ]


~pbuf.(Pbind(\degree, (:1..8)), 2).iter.all(())
// -> [ ( 'degree': 1 ), ( 'degree': 2 ), ( 'degree': 1 ), ( 'degree': 2 ) ]

~pbuf.(Pbind(\freq, 100 * Pwhite(1, 9)), 2).iter.all(())
// -> [ ( 'freq': 600 ), ( 'freq': 200 ), ( 'freq': 600 ), ( 'freq': 200 ) ]

As extra/exercise you could further condition the “clutch” so it can resume recording on some flag or some other variations…

If you want to “productize” this for event patterns, you do have worry about what happens with cleanups (see EventStreamCleanup) in those repeats and cut-offs.

2 Likes

Even better, something I can wrap my head around. Cheers!

Hey, I found this thread because it was exactly what I searched for, but I did not understand anything in this thread.

After I sat with this for a good long while I made a little pattern thingy which gave me what I wanted. Now I’m curious if this does the thing you wanted also?

(
Pdef(0,
    Pseed(19, // Change me and reevaluate.
        Psync(
            Pbind(*[
                freq: 40 * Plprand(1, 13),
                dur: 1 / Pstutter(Phprand(5, 11), Pwhite(5, 11)),
                release: 8,
            ]), 1, 1.5, // 2nd number in this line is loop length. 
        )
    )
).play(quant:1);
)
2 Likes

To be honest, I don’t remember exactly what I was going for now, but I tried your example and I like this approach!

1 Like