Understanding Streams, Patterns and Events - Part 7

Practical Considerations

Using your own ~instrument

(

SynthDef(\help_SPE7_BerlinB, { arg i_out=0, freq = 80, amp = 0.2, pan=0;

var out, a, b;

amp = Decay2.kr(Impulse.kr(0), 0.05, 8, amp);

out = RLPF.ar(

LFPulse.ar(freq, 0, SinOsc.kr(0.12,[0,0.5pi],0.48,0.5), amp),

freq * SinOsc.kr(0.21,0,4,8),

0.07

);

#a, b = out;

DetectSilence.ar(a, 0.0001, doneAction: 2);

Out.ar(i_out, Mix.ar(PanAz.ar(4, [a, b], [pan, pan+1])));

}).memStore;

SynthDef(\help_SPE7_CFString1, { arg i_out, freq = 360, gate = 1, pan, amp=0.1;

var out, eg, fc, osc, a, b, w;

fc = LinExp.kr(LFNoise1.kr(Rand(0.25,0.4)), -1,1,500,2000);

osc = Mix.fill(8, { LFSaw.ar(freq * [Rand(0.99,1.01),Rand(0.99,1.01)], 0, amp) }).distort * 0.2;

eg = EnvGen.kr(Env.asr(1,1,1), gate, doneAction:2);

out = eg * RLPF.ar(osc, fc, 0.1);

#a, b = out;

Out.ar(i_out, Mix.ar(PanAz.ar(4, [a, b], [pan, pan+0.3])));

}).memStore;

)

Pattern-play creates an EventStreamPlayer for you and also supplies a default protoEvent. If you were using your own event model you would just pass in your own protoEvent to the play method.

(

Pbind(

\instrument, Prand([\help_SPE7_BerlinB, \help_SPE7_CFString1],inf),

\degree, Pseq([0,1,2,4,6,3,4,8],inf),

\dur, 0.8,

\octave, 3,

\amp, 0.03

).play; // this returns an EventStreamPlayer

)

Defining your own message bindings

The default event prototype uses a msgFunc to determine which bindings to pass to the server. Synthdefs that have been stored in a SynthDescLib ("synth description library") construct the msgFunc automatically. The default event looks up the instrument name in a SynthDescLib of your choosing (using the \synthLib key). Normally only the global SynthDescLib is used; if \synthLib is empty, the global library is the default.

You should not send or load synthdefs that you plan to use with patterns. Instead, store them in a SynthDescLib.

// saves .scsyndef file on disk (like .load), and adds description to the global library

SynthDef(...).store;

// adds description to the global library; no file is saved (like .send)

SynthDef(...).memStore;

If you don't do this, nondefault bindings will be ignored. In that case, you can provide a custom msgFunc manually. Here's an example:

(

SynthDef(\help_SPE4_CFString2, { arg i_out, freq = 360, gate = 1, pan, amp=0.1, dorkarg=1;

var out, eg, fc, osc, a, b, w;

fc = LinExp.kr(LFNoise1.kr(Rand(0.25,0.4)), -1,1,500,2000);

osc = Mix.fill(8, { LFSaw.ar(freq * [Rand(0.99,1.01),Rand(0.99,1.01)], 0, amp * dorkarg ) }).distort * 0.2;

eg = EnvGen.kr(Env.asr(1,1,1), gate, doneAction:2);

out = eg * RLPF.ar(osc, fc, 0.1);

#a, b = out;

Out.ar(i_out, Mix.ar(PanAz.ar(4, [a, b], [pan, pan+0.3])));

}).send(s); // change .send(s) to .memStore

)

As you can see I have added dorkarg to the arglist of the SynthDef from earlier.

(

Pbind(

\instrument, \help_SPE4_CFString2,

\degree, Pseq([0,1,2,4,6,3,4,8],inf),

\dur, 0.4,

\octave, 3,

\amp, 0.03,

\dorkarg, Pseq([1,0,1],inf) // silence every second note - doesn't work

).play;

)

'dorkarg' is ignored because the SynthDef was not properly .memStore'd and consequently, the event prototype doesn't know that dorkarg is important.

You could also supply a \msgFunc that includes dorkarg:

(

Pbind(

\instrument, \help_SPE4_CFString2,

\degree, Pseq([0,1,2,4,6,3,4,8],inf),

\dur, 0.4,

\octave, 3,

\amp, 0.03,

\dorkarg, Pseq([1,0,1],inf), // silence every second note - now works

\msgFunc, { arg out = 0, freq = 440, amp = 0.1, pan = 0, vol = 1,

dorkarg = 1;

[\out, out, \freq, freq, \amp, amp, \pan, pan, \vol, vol,

\dorkarg, dorkarg];

}

).play;

)

But this is quite clumsy. It is strongly recommended to get into the habit of using .store or .memStore for all SynthDefs intended for use with Patterns.

The other option you have if you will be using unspecified bindings, is of course to define an event with the appropriate msgFunc as default. Have a look at Event's source, it's easy, and it's cleaner than passing in the msgFunc every time.

Manipulating an EventStreamPlayer in Realtime

(

p = Pbind(

\degree, Pwhite(0,12),

\dur, 0.2,

\instrument, \help_SPE4_CFString2

);

// e is an EventStreamPlayer

e = p.play;

)

(

// you can change the stream at any point in time

e.stream = Pbind(

\degree, Pseq([0,1,2,4,6,3,4,8],inf),

\dur, Prand([0.2,0.4,0.8],inf),

\amp, 0.05,

\octave, 5,

\instrument, \help_SPE7_BerlinB, // you can also use a symbol

\ctranspose, 0

).asStream;

)

(

e.stream = Pbind(

[\degree, \dur], Pseq(

[

Pseq([[0,0.1],[2,0.1],[3,0.1],[4,0.1],[5,0.8]],2),

Ptuple([Pxrand([6,7,8,9],4), 0.4]),

Ptuple([Pseq([9,8,7,6,5,4,3,2]), 0.2])

], inf

),

\amp, 0.05,

\octave, 5,

\instrument, \Help_SPE7_CFString1

).asStream;

)

The following methods are possible because an EventStreamPlayer is a PauseStream:

e.mute; // keeps playing, but replaces notes with rests

e.unmute;

e.reset; // reset the stream.

e.pause; // will resume where paused.

e.resume;

e.stop; // will reset before resume.

e.resume;