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.
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.
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.
… 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.)