Pbind Multichannel Expansion

Thanks James I do understand that the expansion is done in Event - as I wrote in the post you were responding to:

…but thanks for the correction (emphatic though it was!) as I did absolutely misspeak! From the beginning user’s point of view though - I should have said that the keys in a Pbind (using default \note type at least) do multi-channel expand (excepting \instrument and \dur).

excellent - thanks for this

regarding semantics I would expect this:

 Pbind(
	\degree, Pn(Pseries(0, 1, 8), inf),
	\dur, [0.1, 0.2]
).play;

to expand (in effect) to:

Ppar([ 
  Pbind(
  	\degree, Pn(Pseries(0, 1, 8), inf),
  	\dur, 0.1
  ),
   Pbind(
  	\degree, Pn(Pseries(0, 1, 8), inf),  
	\dur, 0.2
  )
])

What are the other options you imagine? I think it would be nice for expansion to work everywhere the same (args are flopped before evaluation if that makes sense)

One issue that does spring to mind is values which are arrays - the convention elsewhere is to bubble them to prevent expansion and that would seem like a reasonable enough solution.

indeed

expanding “instrument” would be a win in any case and would solve OPs problem!

expanding dur is a seductive prospect though. would be nice to be able to write voices like

Pbind( *[
  degree: [ Pseq([0, -1, 0),     Pseq([2, 3, 2)],
  dur:    [ Pseq([1.5, 0.5, 1]), Pseq([1, 1, 1)],
  pan:    [-0.2,                 0.2],
])

One way to look at Pbind with arrayed values is: when a key has an array, the array values are distributed over multiple synths, while other single-value keys replicate the same value over multiple synths. One could read that pattern as saying that at any given moment, there is one \degree that is distributed over multiple voices.

In practice, this couldn’t be written in that way because there’s one \degree but multiple timing specifications. Which one should \degree follow? This can’t be resolved with the given syntax, so this isn’t a “proposal” as such. But, one thing that the SC and Pd forums have taught me that everything that is possible to misunderstand will be misunderstood, at some point. If there were an auto-parallelizing Pbind, I would predict that, eventually, somebody would argue “I wrote one \degree pattern and I expect not to hear different degrees playing at the same time” and it takes some thought to grasp – it isn’t immediately obvious – why it doesn’t work that way.

(
p = Pchain(
	Ppar([
		Pbind(\dur, 0.2, \pan, -0.8),
		Pbind(\dur, 0.4, \pan, 0.8)
	]),
	Pbind(
		\degree, Pseg(
			Pn(Pseries(0, 1, 8), inf),
			// oh just for fun, randomize it lol
			Prand([0.2, 0.4], inf),
			\step
		),
		\absTime, Ptime(),
		\amp, 0.5
	)
).play;
)

p.stop;

The other way to look at it is: Each key in Pbind gets one stream. If you parallelize, maybe the stream should be distributed.

(
var degree = Pn(Pseries(0, 1, 8), inf).asStream;

p = Ppar([
	Pbind(
		\degree, degree,
		\dur, 0.2
	),
	Pbind(
		\degree, degree,
		\dur, 0.4
	)
]).play;
)

p.stop;

To sum up 3 viewpoints:

  1. “Parallel” view: A \dur array means that the entire Pbind structure is replicated n times, with different timing.
  2. “One pattern → one concurrent value” view: For keys with fewer than n values, some of the values will be copied into other concurrent voices.
  3. “One pattern → one stream” view: For keys with some m values less than n, you would have only m streams and some of them would evaluate in multiple voices.

I think 3 is unlikely for people to imagine, 2 is somewhat likely but I could see someone guessing this, and 1 is most likely. The point is, though, that stating the semantics in terms of the relationship between patterns and output values, it isn’t so obvious which one is ideal (and I can imagine users at times not wanting auto-parallelizing behavior)… which is where MC-expanding \dur might be a neat trick, but I’m not certain that it’s an improvement over just spelling it out in user code. It might be an improvement, but assuming that “obviously everyone will want #1” may end up not being an improvement.

hjh

Just to propose a different opinion…

Having large monolithic pbinds is incredibly difficult to read and maintain. When there are multiple voices this becomes even worse as you can’t just see one voice at a time, but have to sort through all the subarrays - this is a draw back of multichannel expansion and it occurs in synth contexts too. Having multiple instruments, with potentially different keys will exasperate this and should not be encouraged.

Instead, I think there should be a large section in the documentation detailing and encouraging people to write more modular patterns and use pattern composition to solve there issues. In pbind, there could be a section about the instrument and dur keys that show these issue can be solved with functions and composed from smaller more manageable chunks.

I think there’s one legit use for multiple instruments – “I’ve got these 3 SynthDefs and I want them all to play the same material with the same rhythm together” (kinda like those MIDI piano solos in the Chick Corea Elektric Band albums from the 80s). Currently takes some plumbing to do; I don’t see a problem with making that easier.

I’m still skeptical of folding multiple dur streams into one Pbind, for the reasons you mentioned, and also thinking of the coming efforts to trim some of the fat in the interfaces and reduce the “too many ways to do things.” Would be ok if someone makes an ParallelizingPbind as a quark to try it out (then it’s optional).

hjh

Made a PR that checks there is only one instrument and throws an error telling the user how to fix it.

If some one proposes a better one, I will remove it - this is just a default fix that doesn’t change anything but still provides some useful information to the user.

I wouldn’t mind if, at minimum, someone had a look at First attempt at multiple instruments in the default event prototype · jamshark70/supercollider@3aa6de8 · GitHub before making a decision on that pull request.

(I suppose reductio ad absurdum could lead to someone down the road requesting support for multiple \type values… now this I would be quite comfortable rejecting summarily :laughing: but to play the same material in multiple SynthDefs at the same time strikes me as a reasonable idea.)

hjh

1 Like

Déjà-vu …

Also see this old thread on the list:

https://listarc.cal.bham.ac.uk/lists/sc-users-2018/msg62864.html

@julian made an event type suggestion then:

https://listarc.cal.bham.ac.uk/lists/sc-users-2018/msg62866.html

Actually I spoke too quickly – this is a question that has come up before. It’s easier than multiple instruments, though: change the current “play” function (Event.sc line 480) to “playType” and:

				play: #{
					if(~type.size >= 2) {
						~type.do { |type|
							currentEnvironment.copy.put(\type, type)
							.use { ~playType.() }
						};
					} {
						~playType.();
					}
				},

Of course it’s the user’s responsibility to make sure the shared parameters make sense for all the types.

hjh