1
( 

SynthDef(\testEvt,{
    arg out, gate = 1;
    var sint = Blip.ar(440) * Linen.kr(gate,doneAction:2,releaseTime:0.8);
Out.ar(out, Pan2.ar(sint, 0));
}).add();

Synth(\testEvt) 
(instrument: \testEvt, freq:220, sustain: inf).play;
(instrument: \testEvt,freq:220).play;

)

Executing the first and the second line after the SynthDef would create a synth which playes forever, whereas the third line's synth plays for 0.8 seconds as per default value the generated event.

The problem is that I don't use 'sustain' anywhere in my SynthDef and it uses automatically just because there is a Linen.

The same this doesn't happen for freq: both the events play at 440 and not at 220, and that's just because the SynthDef doesn't use 'freq' as an argument. So why sustain doesn't follow the same rule ?

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.

MrBloom
  • 91
  • 6

2 Answers2

2
(instrument: \testEvt, freq:220, sustain: inf).play;

and

(instrument: \testEvt,freq:220).play;

are events. Events handle a lot of things for you. One thing they do is calculate when to set a gate to 0. (Remember that gate is one of the arguments in your SynthDef.) In the first example, because you sustain for infinite duration, the gate never goes to zero. In the second example, it uses the default duration and sets the gate to zero after that duration has passed. You can find out what key words are used in event environment variables by looking at the Event.sc source file. If you search for sustain, you'll find out the other keywords it uses for timing. One of these is dur. Try this:

(instrument: \testEvt, dur:3).play

Freq is also a keyword for events, but since you have no freq argument, it can't effect your synthDef. If you want to set the freq, you'll need to make a change:

SynthDef(\testEvt,{
    arg out, gate = 1, freq = 440;
    var sint = Blip.ar(freq) * Linen.kr(gate,doneAction:2,releaseTime:0.8);
    Out.ar(out, Pan2.ar(sint, 0));
}).add();

For contrast between events and controlling a synth directly, try: a = Synth.new(\testEvt, [\out, 0, \gate, 1]) You can add in any other arguments you want, like freq or sustain, but they have no effect because those aren't arguments to your synthdef. And, unlike Event, synth doesn't do any calculations on your behalf. When you want the note to end, set the gate to 0 yourself: a.set(\gate, 0)

It's good to be aware of event environment variables because they're also used by Pbinds and by using them, you can effect other things. If you had a synthdef that used sustain as an argument for something else, you could be surprised by it changing your durations.

les_h
  • 347
  • 1
  • 8
0

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 added 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 Pbinds (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 Events 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.

Fizz
  • 4,782
  • 1
  • 24
  • 51