Overriding 'repeats' with Pbindf and Pchain

Hi all,

I have a general sense of how Pbindf and Pchain work, i.e. that they can “filter” one pattern through another, and that they essentially allow pattern construction based on simpler template patterns. Recently, I encountered something that surprised me.

For example, if I start with a pattern that represents a sequence of 4 notes…

s.boot;

(
p = Pbind(
	\dur, 1/8,
	\degree, Pseq([0,2,3,5], 1),
	\legato, 0.02,
);

p.play;
)

and then use Pbindf to modify the \degree values, I had imagined this new value Pattern would “replace” the old one, but instead it seems like it’s being added to the Pbind, so that both the old and new are present. Thus, the four-note Pseq is still present, and thus the total number of events is still four:

(
q = Pbindf(
	p, 
	\degree, Pseq([4,5,7,9,10,11,12,13], 1)
);

q.play;
)

A similar thing happens with Pchain:

(
p = Pbind(
	\dur, 1/8,
	\degree, Pseq([0,2,3,5], 1),
	\legato, 0.02,
);

q = Pchain(
	Pbind(\degree, Pseq([4,5,7,9,10,11,12,13], 1)),
	p
);

q.play;
)

Is there a way to actually “swap out” one value Pattern for another using these modular pattern-building techniques? Is there something I’m misunderstanding?

Eli

Technically, what happens is that the first stream of four notes is always being evaluated (tts values assigned to the event key and then replaced by the second event pattern) so if the first stream ends it ends the whole chain. Pbindef would do what you want by creating proxies but is kind of different. I’m thinking that if you can save the pattern information as a dictionary and mix the data before creating the event patterns would be the simpler data friendly approach, like:

a = (dur: 1/8, degree: Pseq([0, 2, 3, 5], 1), legato: 0.02);
b = (degree: Pseq([4,5,7,9,10,11,12,13], 1));

a.degree.list.postln; // [ 0, 2, 3, 5 ]
c = a.copy().putAll(b); // I don't want to change original a.
c.postln;
c.degree.list.postln; // [ 4, 5, 7, 9, 10, 11, 12, 13 ]

Pbind(*c.getPairs).play;

I don’t know if there is a quark or something else to that does this

hi Eli,

this doesnt fit to your subject line, but to the “swapping out”, here’s how i’ve settled on doing it
(this is definitely not the only way, nor even the best for all situations):

PbindProxy is your friend.

(
~pbindproxy = PbindProxy(
\dur, 1/8,
\degree, Pseq([0,2,3,5], 1),
\legato, 0.02,
);
~player = ~pbindproxy.play;
)

~pbindproxy.set(\degree, Pseq([4,5,7,9,10,11,12,13], 1))
~pbindproxy.play

The hot-swappiness is more apparent with a loop:

(
~pbindproxy = PbindProxy(
\dur, 1/8,
\degree, Pseq([0,2,3,5], inf),
\legato, 0.02,
);
~player = ~pbindproxy.play;
)

~pbindproxy.set(\degree, Pseq([4,5,7,9,10,11,12,13], inf))
~pbindproxy.set(\degree, Pseq({24.rand - 12}!(2**(1..4).choose), inf))

~pbindproxy.set(\dur, Pseq({2**(-5..-2).choose}!8, inf))

~player.pause
~player.reset
~player.play

cheers,
eddi

1 Like

Thanks both for the replies. I’m aware of proxies, and realize that they are undoubtedly the optimal choice for a dynamic “swap” of one value stream with another. I guess I’d always imagined that Pbindf was doing something similar, but that it is implemented during Pbind construction instead of Pbind performance. Maybe I was subconsciously viewing Pbindef/Pbindf as being similar versions of the same thing.

My understanding now is that Pbindf is a very simple tool which quite literally adds a key-value pair to an existing event pattern, regardless of whether that key already exists. Regarding my original Pbindf example,

(
p = Pbind(
	\dur, 1/8,
	\degree, Pseq([0,2,3,5], 1),
	\legato, 0.02,
);

q = Pbindf(
	p, 
	\degree, Pseq([4,5,7,9,10,11,12,13], 1)
);
)

I’m now imagining the result as being functionally equivalent to this:

(
q = Pbind(
	\dur, 1/8,
	\degree, Pseq([0,2,3,5], 1),
	\legato, 0.02,
	\degree, Pseq([4,5,7,9,10,11,12,13], 1)
);
)

Which certainly helps me understand why the second set of degree values are used for pitch information (by virtue of the fact that they occur later), but the event stream is still limited by the length of the first degree stream. If this is an incorrect characterization of how Pbindf works, someone please let me know!

Eli

1 Like

Pbind construction instead of Pbind performance

There are some things that aren’t easy to see or guess. Patterns’ generated streams (or Streams in general) are lazily evaluated, always, they don’t produce values until “next” is called and that call may happen nested for embedded patterns. I do remember myself not understanding how it works, what yield does, how it could be, because I didn’t know what a coroutine was. Later it took me some more to understand how embedInStream works.

Later characterization is correct. Roughly, Pbind takes the value of every key and makes a stream from it, then within a loop it calls next on every estream and sets the value of the inevent for each key and yields the event. Filter (event) patterns take that yielded event and process the keys and yield the modified data. Event stream player takes the resulting event of the chain and calls playAndDelta on them which returns the wait time for the clock. And repeats, until an inner stream returns nil from its next call and that value passes up through the chain of embedded streams.

Thanks for the clarification. The gist of how Patterns and Streams work, as described above, has been reasonably clear to me for awhile.

This was a hastily-written phrase meant to refer to the Pattern/Stream distinction — I can see how it might imply confusion on my part.

I should clarify that this question arose because I’m in the midst of writing pedagogical materials, which is causing me to dive deeper into the nuts & bolts of SC more than I have in the past, and I’m discovering little corners where my knowledge is incomplete. But, now I’m starting to second guess myself — if some tiny detail of Pbindf is something I wasn’t aware of for 10 years, then it probably doesn’t belong in a pedagogical resource intended as a practical resource for newer users.

Not to get too off-topic, but with regard to ‘embedInStream’ — I have a general idea of how it works for Routines, e.g.:

(
~f0 = {
	"one".postln;
	1.yield;
	"two".postln;
	1.yield
};

~f1 = {
	"three".postln;
	1.yield;
	"four".postln;
	1.yield
};
)

(
//serial
r{
	r(~f0).embedInStream;
	r(~f1).embedInStream;
}.play;
)

(
//parallel
r{
	r(~f0).play;
	r(~f1).play;
}.play;
)

But is there a practical context where a user would want to use ‘embedInStream’ with event patterns? If so, what would that look like? Or is ‘embedInStream’ mainly used internally in Pattern contexts?

Eli

But is there a practical context where a user would want to use ‘embedInStream’ with event patterns? If so, what would that look like? Or is ‘embedInStream’ mainly used internally in Pattern contexts?

For me, the short answer is either that it is not commonly necessary or isn’t the usual or common practice when building patches or sequences, is a bit more advanced as a topic. Because, regarding patterns the library seems to be made to use higher level abstractions that internally create or combine streams, it is possible to use the library without knowing some of the internals. However, to explain routines and clocks it is necessary to explain the programming constructs and make clear the behaviour, either with intuitive or technical/theoretical explanations to some extent. That is a decision to make, where to cut and what.

So, and just because I like to discuss the topic, in your examples you use the behaviour serial or parallel to explain what happens, which is perfectly fine, but the internal constructs are a bit different because parallelism implies scheduling each one in a clock. I would explain some of the magic happening there somehow, to make clear the different constructs, but not all. Consider this, consider this:

(
a = {
"one".postln;
1.yield;
"two".postln;
1.yield;
};

b = {
"three".postln;
1.yield;
"four".postln;
1.yield;
};
)

(
// sequential embedded (this might be worth to explain if using embedInStream
c = {
r(a).asStream.do({ arg i; i.yield });
r(b).asStream.do({ arg i; i.yield });
};
)

z = r(c).asStream;
z.next;

(
// parallel why not (Ptuple does this), mind burning
d = {
var ra = r(a).asStream;
var rb = r(b).asStream;
[ra.next, rb.next].yield;
[ra.next, rb.next].yield;
};
)

z = r(d).asStream;
z.next;

Side note, if someone curious enough goes to see the implementation in Pattern for asStream and embedInStream it will find something impossible (mind burning), they are recursive, something is wrong, that is because one of them has to be overridden in subclasses. And… cut! Is not possible to explain everything, I just wanted to make music. I hope you enjoy it.

embedInStream is, as Lucas said, mostly internal. In most ordinary cases, a user would not need to call it directly.

One exception is Prout – say, I want to have n numbers from a counter and then embed a sequence:

Prout { |inval|
    var counter = 0;
    loop {
        rrand(3, 7).do {
            inval = counter.yield;
            counter = counter + 1;
        };
        inval = (Pseq([0, 2, 3, 5, 9], 1) - 7).embedInStream(inval);
    };
}

But you don’t really need Prout for this case: Pseq([Pfin({ rrand(3, 7) }, Pseries(0, 1, inf).asStream), Pseq([0, 2, 3, 5, 9], 1) - 7], inf).

inval isn’t used in that example, but for a Prout processing events and embedding any Pbinds, you would have to handle inval as shown (Pbind will not work at all without it). So I think it’s a good habit to write it in all cases, even if it’s not strictly necessary.

hjh

Appreciate the reply and clarification very much.

The one thing that still partially eludes me is the concept and purpose of the singular argument declared in the context of a Stream (usually named inval). My rough guess is that it’s the value that Streams pass from one to another when yielding/taking control. No idea if this is accurate. I’ve never really found myself in a practical situation that required this specific handling of inval as in the Prout example above, and I don’t fully understand why it’s necessary to store the yield/embedInStream value in inval. I understand that not storing the yielded value in inval breaks something, but I don’t understand what or why.

Possible to shed some light here? Is there a help document somewhere that solidly discusses/explains the purpose of inval?

1 Like

http://doc.sccode.org/Tutorials/A-Practical-Guide/PG_Ref01_Pattern_Internals.html#Streams

hjh

value that Streams pass from one to another when yielding/taking control.

That’s not a correct assumption that leads to the rest of the problems. The value that streams pass to each other is the value passed to the “next()” method call. For example, event streams must receive an event each time “next” is called from the outermost stream. On the first call to “next” evaluates the function/method up to the first yield. The yielded value is the return value of the “next” call. The next “next” can pass a new value to the stream which is the return value of the “yield” method call within the stream. That is tricky indeed. To store the return value of the “yield” call in “inval” is just a variable reutilization.

James’s guide explains a lot of it, but I think there are other implicit pitfalls which I understood only after doing comparisons with other languages that have the concept more explicitly implemented. That is definitely not for teaching the basics of patterns and streams. To explain it and why would take me quite a while but I can give (all) you my cheatsheet:

// inval is the value passed in to next calls.
q = r({ arg inval; inval = inval.yield; inval.postln; }).asStream;
q.next('hi'); // yield inval value passed as parameter.
q.next('lo'); // print inval and yield nil (end of stream).

// Objects are infinite streams.
q = 1.asStream;
q.next; // inf

// Objects are not infinite when their stream is embedded.
q = r({ 1.embedInStream() }).asStream;
q.next;
q.next; // nil

p = Psimple1();
q = p.asStream; // Pattern.asStream returns Routine({ arg inval; this.embedInStream(inval) })
q.next;

p = Psimple2([1, 2, 3]);
q = p.asStream;
q.next;
q.next;
q.next;
q.next; // nil

p = Psimple2([Psimple1()]);
q = p.asStream;
q.next;
q.next;
q.next;
q.next; // prints not nil not this and yield 10

// [psimple.sc](http://psimple.sc) -----------------------------------------------------

Psimple1 : Pattern {
embedInStream { // inval val is not defined, ignored.
1.yield;
2.yield;
3.yield;
// this are the return value of the method call,
// different from the yield values of the routine
// created with it. When embedInStream ends the
// stream return nil, that is implicit.
^'not nil not this';
// ^nil;
// ^this
}
}

Psimple2 : ListPattern {
*new { arg list, repeats=1;
^super.new(list, repeats)
}

embedInStream { arg inval;

repeats.do({ arg j;
list.do({ arg item;
// 1. Reutilization of inval variable.
// 2. Yield process happens within embedInStream.
// 3. The embedded stream must receive inval to
// propagate it, the embedded stream is evaluated
// first with the value passed to the next method.
inval = item.embedInStream(inval);
inval.postln; // prints the return value of embedInStream.
});
});

^inval; // return value of embedInStream
}
}

// [psimple.sc](http://psimple.sc) -----------------------------------------------------
2 Likes

As a side note, if objects were infinite when embedded, then Pseq([1, 2, 3]) would output 1, 1, 1, 1 … so this may look like a gotcha but it’s kinda necessary.

hjh

In reality, when a Routine yields a value, its execution is interrupted after calling yield , but before yield returns.

Thanks James. This is a piece of information I had been missing for many years. Initially, something like

inval = yield(inval * 2)

inside a Routine looks like inval doubles on each loop, and it’s hard to shake this visual gut feeling. But the idea of interruption between call and return makes the flow of data very clear to me now.

Eli

I’m not sure this matters in the broader context, but…

That expression will double at each iteration if yield is identity!

var r = {arg x; {x = (x * 2).yield}.loop}.r;
var x = 1;
9.do({x = r.next(x).postln})

yield is an ordinary message send, it behaves just like any other message send.

Routine>>next is as well, they’re symmetric.

yield looks a bit different because the context to switch to is implicit, but they do the same thing.

We can see the symmetry by swapping the operations above from one location to the other.

var r = {arg x; {x = x.yield.postln}.loop}.r;
var x = 1;
9.do({x = r.next(x * 2)})

Or we can do both at both!

var r = {arg x; {x = (x * 2).yield.postln}.loop}.r;
var x = 1;
9.do({x = r.next(x * 2).postln})

Co-routines are a bit subtle, but yield and next are just ordinary message sends.

Mutating the argument variable in a loop is perhaps a bit confusing.

We can also write it out longhand, giving names to each step.

var f = {arg x; var y = x * 2; var z = y.yield; f.value(z)};
var r = Routine.new(f);
1.to(9).collect({arg i; var j = r.next(i); i -> j})
1 Like