Rest(1) for \buf key is triggering a sample

I’m messing around with some examples from the Pattern Guide and stumbled upon a oddity.
The Rest() in this code seems to trigger a sample.

Pdefn(\bd1, Pseq([d[\k][0], Rest(1)], inf));
Pdefn(\sn1, Pseq([Rest(2), d[\s][0] ], inf));

(
Pbindef(\drums1, 
		\instrument, \bplay, 
	    \buf,  Pnsym([
			\bd1 , \sn1
	              ]),
		\dur, 1,
		\amp, 0.3,
		\rate, 1,
	).play;
)
(
SynthDef(\bplay,
    {arg out = 0, buf = 0, rate = 1, amp = 0.5, pan = 0, pos = 0, rel=15;
        var sig,env ;
        sig = Pan2.ar(PlayBuf.ar(2, buf, BufRateScale.ir(buf) * rate, 1, BufDur.kr(buf) * pos * 44100, doneAction:2 ), pan);
        env = EnvGen.ar(Env.linen(0.0, rel, 0), doneAction: 2);
        sig = sig * env;
        sig = sig * amp;
        Out.ar(out, sig);
}).add;
)

Edit: It seems to pick the sample from the same subfolder in the dictionary. So, not Rest(1), but d[\s][1] for instance.

What is going wrong here?

Your bplay synthdef is passing its \buf control through to PlayBuf’s bufnum parameter. Deep in the bowls of the SC library, it is calling .asControlInput assuming that it will get a bufnum back. This is what allows you to pass a buffer rather than a bufnum. Rest(1) also implements asControlInput which returns the value/duration of the rest. Rest(1).asControlInput returns 1, Rest(2).asControlInput returns 2. These cause PlayBuf to play those buffers.

If you evaluate these it might make sense…

d[\k][0].asControlInput
d[\k][0].bufnum
{ PlayBuf.ar(2, d[\k][0]) }.play;
{ PlayBuf.ar(2, d[\k][0].bufnum) }.play;
{ PlayBuf.ar(2, d[\k][0].asControlInput) }.play;
Rest(1).asControlInput;
{ PlayBuf.ar(2, 1) }.play;
{ PlayBuf.ar(2, Rest(1)) }.play;
{ PlayBuf.ar(2, Rest(1).asControlInput) }.play;

Try creating an empty buffer and storing it in d[\rest] and then using that in the Pdefn.

d[\rest] = Buffer.alloc(s, 1, 2);
Pdefn(\bd1, Pseq([d[\k][0], d[\rest]], inf));

It doesn’t look like it’s possible to use Pif (or the ilk) and Pkey on the \dur element to access the \buf to see if it’s a rest. That wouldn’t really work anyway since you have two parallel patterns and really wouldn’t want to have both of them rest.

Someone with more SC experience than me might be able to figure out how to write a Synthdef that checks the current \buf control to see if it’s a rest and then either use PlayBuf or an empty signal.

1 Like

The problem here is that an entire event may be a rest, or not a rest, but there’s currently no way to use Rest to mark part of an event as a rest while allowing the remainder to play.

Pnsym([\bd1 , \sn1]) is effectively the same thing as Ptuple([Pnsym(\bd1), Pnsym(\sn1)]). Ptuple produces arrays, so \buf gets arrays such as [d[\k][0], Rest(2)].

If an event key is directly a Rest, then the event is a rest. But if the Rest is contained in an array belonging to the key, then the event is not a rest.

(buf: Rest(1), dur: 1).isRest
-> true

(buf: [0, Rest(1)], dur: 1).isRest
-> false

Since the event is not a rest, it will play.

The solution would be to parallelize not the \buf, but the entire pattern.

Pdef(\drums, Ppar(
	[\bd1, \sn1].collect { |key|
		Pbind(
			\instrument, \bplay, 
			\buf, Pnsym(key),
			\dur, 1,
			\amp, 0.3,
			\rate, 1,
		)
	}
));

… which would take a little more work to get the Pbindef reprogrammability back, but it does split the separate drums into separate events.

This analysis is almost right. The one difference is that SynthDef arguments must be numbers. It’s impossible for the server to receive and decode a Rest object.

The asControlInput translation does take place, but it’s at the time of building the OSC message to the server.

// start with an event
e = (buf: [0, Rest(2)], dur: 1, amp: 0.3, rate: 1);

// get a list of arg pairs
a = e.use { SynthDescLib.at(\bplay).msgFunc.valueEnvir };
-> [ buf, [ 0, Rest(2) ], rate, 1, amp, 0.3 ]

// arrays should expand to multiple synths
a = a.flop;
-> [ [ buf, 0, rate, 1, amp, 0.3 ], [ buf, Rest(2), rate, 1, amp, 0.3 ] ]

// and sanitize for OSC transmission
a.collect { |args| args.asOSCArgArray }
-> [ [ buf, 0, rate, 1, amp, 0.3 ], [ buf, 2, rate, 1, amp, 0.3 ] ]

(To be really technically correct, the translation for UGen arguments (as in the PlayBuf examples) is asUGenInput.)

hjh

1 Like

Alternately, it might work just to delete Rests from the buf array:

\buf, Pnsym([\bd1, \sn1]).collect { |row| row.reject(_.isRest) },

hjh

1 Like