#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; :: ]