233 lines
5.8 KiB
Racket
233 lines
5.8 KiB
Racket
#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;
|
|
::
|
|
]
|
|
|
|
|