Advice on substituting values in pattern on the fly

I am looking for advice on how to temporarily substitute values in a pattern without loosing temporal order. The below code does the job but feels slightly awkward, or maybe it is just me. I am thinking there must be a more sc native way of doing it with the pattern lib but I can’t think of how.

The example shows what I am after and in more general terms:

  • temporarily substituting values in a pattern
  • The receiving pattern is agnostic of substitutes and does not need to be set up in a specific way for a a sub to happen (or very minimally so) - in my example I had to introduce the Pdefn(\val).

I can think of other ways like Pchaining, using a Prout or a Pfunc to filter the pattern etc. I would like to avoid deciding on eg. naming conventions in the receiving pattern and have as much freedom (with a short syntax) as possible.

I have previously used Pdefn(\val…).quant_(1); wait.3, Pdefn(\val…).quant_(1), but then you are using a routine to play a routine which again feels sub optimal.

Any suggestions?

(
Pdef(\test,
	Pbind(
		\type, \rest,
		\dur, 0.5,
		\pat, Pseq((0..9), inf),
		\value, Pdefn(\val, Pkey(\pat)).trace,
	)
).play
)

(
var newPat = Pseq([100, 101, 102]).asStream;
Pdefn(\val, Pfunc{|ev| 
	var val = newPat.next;
	if (val.notNil) { val } { ev.pat }
}).trace
)

/// or if you wanted to clean up a bit...

(
var newPat = Pseq([100, 101, 102]).asStream;
Pdefn(\val, Pfunc{|ev| 
	var val = newPat.next;
	if (val.notNil) { val } { Pdefn(\val, Pkey(\pat)); ev.pat }
}).trace
)

Pdef(\test).clear

Some time ago I made a value pattern that can “hijack” values in another value pattern based on a predicate (see Phijack). Not sure if it is 100% what you need, but it looks related at least.

  • Phijack takes a predicate (a function that returns a boolean) and two value patterns.
  • The first value pattern provides normal values, and the second value pattern makes “hijacker” values.
  • Whenever the predicate is false, it uses a value produced from the normal value pattern.
  • Whenever the predicate is true, it uses a value produced from the hijacker pattern instead (replacing the value produced by the first pattern).
  • What is so special about it? Well: the original value pattern keeps proceeding with time (i.e. values that get hijacked are lost) - making sure that the overall length of the pattern is not affected.
  • The hijacker pattern on the other hand only proceeds whenever the predicate is true, so it acts more like a hat from which you draw the next value whenever the predicate is true.
1 Like

Yes that is pretty much it! Can I ask what mechanism you are using under the hood to achieve this?

I’ve implemented the method embedInStream. I’m calling “asStream” on the value patterns, and then I’m also calling .next on those streams whenever I want to retrieve the next value from it.

	embedInStream {
		| inVal |
		var nextval;
		var stream = pattern.asStream;
		var hijackstream = hijacker_pattern.asStream;
		inf.do({
			| idx |
			// get next value for normal pattern
			nextval = stream.next;
			if (nextval.isNil) { ^nextval; };
			if (predicate.value(nextval, idx)) {
				// if predicate is true, replace it with one from the hijacking pattern
				var replacement = hijackstream.next;
				if (replacement.notNil) {
					replacement.yield;
				} {
					// unless the hijacker ran out of values, in which case we keep returning normal values
					nextval.yield;
				}
			} {
				// if predicate is false, do not replace
				nextval.yield;
			};
		});
	}

Yes that is more or less the approach I am using. Currently I am experimenting with having simultaneous patterns playing chained into to ‘mute modules’ - this approach makes it easier to not just hijack certain values but to replace several beats with a new pattern with new dur-values without having to worry about the old patterns loosing their temporal order.