#lang scribble/manual
@(require (for-label racket))

@title{Understanding Streams, Patterns and Events - Part 7}
 Practical Considerations@section{related}
  Tutorials/Streams-Patterns-Events1, Tutorials/Streams-Patterns-Events2, Tutorials/Streams-Patterns-Events3, Tutorials/Streams-Patterns-Events4, Tutorials/Streams-Patterns-Events5, Tutorials/Streams-Patterns-Events6
@section{categories}
  Tutorials>Streams-Patterns-Events

@section{section}
 Practical Considerations

@section{subsection}
 Using your own ~instrument


@racketblock[
(
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: Done.freeSelf);
	Out.ar(i_out, Mix.ar(PanAz.ar(4, [a, b], [pan, pan+1])));
}).add;

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: Done.freeSelf);
	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])));
}).add;
)
::

link::Classes/Pattern#-play:: creates an link::Classes/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.

]

@racketblock[
(
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
)
::

]
@section{subsection}
 Defining your own message bindings

The default event prototype uses a 
@racketblock[msgFunc:: to determine which bindings to pass to the server. Synthdefs that have been stored in a link::Classes/SynthDescLib:: ("synth description library") construct the ]

@racketblock[msgFunc:: automatically. The default event looks up the instrument name in a SynthDescLib of your choosing (using the ]

@racketblock[\synthLib:: key). Normally only the global SynthDescLib is used; if ]

@racketblock[\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.

]

@racketblock[
// 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(...).add;
::

If you don't do this, nondefault bindings will be ignored. In that case, you can provide a custom ]

@racketblock[msgFunc:: manually. Here's an example:

]

@racketblock[
(
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: Done.freeSelf);
	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 .add
)
::

As you can see I have added ]

@racketblock[dorkarg:: to the arglist of the SynthDef from earlier.

]

@racketblock[
(
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;
)
::

]

@racketblock[dorkarg:: is ignored because the SynthDef was not properly ]

@racketblock[.add::'d and consequently, the event prototype doesn't know that ]

@racketblock[dorkarg:: is important.

You could also supply a ]

@racketblock[\msgFunc:: that includes ]

@racketblock[dorkarg:: :

]

@racketblock[
(
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 ]

@racketblock[.add:: 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 ]

@racketblock[msgFunc:: as default. Have a look at Event's source, it's easy, and it's cleaner than passing in the ]

@racketblock[msgFunc:: every time.

]
@section{subsection}
 Manipulating an EventStreamPlayer in Realtime


@racketblock[
(
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 link::Classes/EventStreamPlayer:: is a link::Classes/PauseStream:: :

]

@racketblock[
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;
::
]


