Regarding your last sub-question,
Also, is there a way to reference synths created by an event ? So that, when they have sustain: inf
as argument, I can free them on a later time.
Yes, by "indexing" the Event
by \id
key. This actually returns an array of node ids because an Event with \strum
can fire up more than one node/synth. Also, the \id
value is nil
while the event is not playing. But this indexing method is fairly unnecessary for what you want, because...
You can end the (associated) synth by ending the Event
early with release
, just like for the Synth
itself. What this does is basically gate-out its internal synth. (In your example, this release
call transitions to the release point of the ASR envelope generated by Linen
, by lowering gate
to 0.). And, of course, use a variable to save the "reference" to the synth and/or event, if don't plan to release it right away in a program (which would produce no sound with a gated envelope).
Basically
fork { var x = Synth(\testEvt); 2.wait; x.release }
does the same as
fork { var e = (instrument: \testEvt, sustain: inf).play; 2.wait; e.release }
except there's one layer of indirection in the latter case for the release. The first example is also equivalent to
fork { var x = Synth(\testEvt); 2.wait; x.set(\gate, 0); }
which does the work of release
explicitly. Event
also supports set
and it passes the value to the corresponding Synth
control (if the latter was properly add
ed on the server.)
Now the complicated method you asked about (retrieving node ids for the event and sending them messages) is possible too... although hardly necessary:
fork { var e = (instrument: \testEvt, sustain: inf).play; 2.wait;
e[\id].do({ arg n; s.sendMsg("/n_set", n, "gate", 0); }) }
By the way, you can't use wait
outside of a Routine
, that's why fork
was needed in the above examples. Interactively, in the editor, you can "wait manually", of course, before calling release
on either the Synth
or the Event
.
As a somewhat subtle point of how envelope gating works, it doesn't actually start playing (technically begin transitioning to the endpoint of the first [attack] envelope segment) until you set gate to 1. I.e. you can delay the (envelope) start as in:
fork { x = Synth(\testEvt, [\gate, 0]); 3.wait; x.set(\gate, 1); 2.wait; x.release }
Beware that the default Event.play
doesn't generate this 0 to 1 gate
transition though, i.e. you can't rely on it to fire your synth's envelope if you set the initial gate
value to zero in your SynthDef
.
Also, I'm assuming that by "free" you mean "stop playing" rather than "free their memory on the server". There's no need to manually free those (event) synths in the latter sense since they have doneAction:2
in the envelope, which does that for you once they are released and the final segment of the envelope finishes playing. If you somehow want to kill the synth right away (like Ctrl+. does) instead of triggering its fade-out you can replace the message sent in the inner function of the "complicated" example (above) with s.sendMsg("/n_free", n)
. Or much more simply
fork { var e = (instrument: \testEvt, sustain: inf).play; 2.wait; e.free }
Also, if you wonder about \strum
, an example is:
e = (instrument: \testEvt, sustain: inf, strum: 1, out: #[0, 0]).play
Now e[\id]
is an array of two nodes. Event
is a bit cheeky in that it will only create multiple nodes for arrays passed to actual Synth
controls rather than random fields, so "strumming" \freq
(or its precursors like \degree
etc.) only creates multiple nodes if your SynthDesc
has a freq
control.
Alas the "complicated" method is almost useless when it comes to playing Pbind
s (patterns). This is because Pbind.play
returns and EventStreamPlayer
... that alas makes a private copy of the prototype event being played and plays that private copy, which is inaccessible to the caller context (unless you hack EventStreamPlayer.prNext
). Confusingly EventStreamPlayer
has an accessible event
variable, but that's only the "prototype", not the private copy event being played... So if p
is an instance of an EventStreamPlayer
then p.event[\id]
is always nil (or whatever you set it to beforehand) even while playing. Since one seldom plays Events
individually and much more often patterns...
Simply as a hacking exercise tough, it turns out there is an even more convoluted way to access the ids of nodes that EventStreamPlayer
fires... This relies on overriding the default Event play
which thankfully can be extended outside class inheritance because the default is conveniently saved in a class dictionary...
(p = Pbind(\instrument, \testEvt, \sustain, Pseq([1, 2]), \play, {
arg tempo, srv;
var rv;
"playhack".postln;
rv = Event.parentEvents[\default][\play].value(tempo, srv);
~id.postln;
rv;
}).play)
In general however, patterns are clearly not designed to be used this way, i.e. by hacking "a layer below" to get to the node ids. As "proof", while the above works well enough with Pbind
(which uses the default Event
type \note
) it doesn't work reliably with Pmono
which doesn't set the Event \id
on its first note (Event type \monoNote
) but only on subsequent notes (which generate a different Event type, \monoSet
). Pmono
keeps an internal copy of the node id, but this is completely inaccessible on the first mono note; it only copies it to the Event
s on the subsequent notes for some reason (bug perhaps, but could be "by design"). Also, if you use Pdef
which extends Event
with type \phrase
... the above hack doesn't work all, i.e. \id
is never set by type \phrase
; perhaps you can get to the underlying sub-events generated somehow... I haven't bothered to investigate further.
The SC documentation (in the pattern guide) even says at one point
Remember that streams made from patterns don't expose their internals. That means you can't adjust the parameters of an effect synth directly, because you have no way to find out what its node ID is.
That's not entirely correct given the above hack, but it is true in some contexts.