Een SynthDef maakt het mogelijk om een aantal UGens te combineren en daarmee Synths te maken die terwijl ze draaien bestuurd kunnen worden. Je kunt er OSC-berichten naartoe sturen die ze 'live' gebruiken om bepaalde parameters aan te passen.
Een voorbeeld van een SynthDef die een sinus genereert
SynthDef("mySine", { arg freq=400; Out.ar(0, SinOsc.ar(freq,0,0.2)) }).add;
Dit beschrijft een geluid maar maakt uit zichzelf nog geen geluid. Met deze SynthDef maken we een Synth:
a=Synth("mySine");
of zo:
a=Synth("mySine",[\freq, 440]);
Vervolgens kunnen we de frequentie aanpassen terwijl de Synth draait:
a.set(\freq, 800); a.set(\freq, 400);
Voor het aansturen van één sinustoon is een SynthDef misschien niet zo nuttig, maar voor complexere synthesizers is het heel handig om dat in een SynthDef te verpakken. Hieronder een voorbeeld van een complexere SynthDef, daarna zullen we daar een aantal onderdelen nader bekijken.
(
SynthDef("klank",{|freq=500,ffreq=500,rate=1,amp=0.5|
var env,sig,mod;
mod = SinOsc.ar(0.5,0,50,freq);
env = EnvGen.ar(Env.linen(0.01,0.5,1.0,amp),Dust.ar(rate));
sig = Ringz.ar(Dust.ar(rate*2,0.3),mod,0.6,env);
sig = BLowPass.ar(sig,ffreq,0.5);
sig = FreeVerb.ar(sig);
Out.ar([0,1],sig);
}).add;
)
~klank= Synth("klank",[\rate,10]);
~klank.set(\ffreq,500,\freq,1000,\rate,1000);
SynthDef("klank",{|freq=500,ffreq=500,rate=1,amp=0.5|
In de eerste regel wordt de SynthDef gedefinieerd. De naam van deze
SynthDef staat tussen dubbele aanhalingstekens. ""
Daarna volgt de
functie waarin de synth wordt beschreven. Deze functie staat, net als een
losse functie, tussen accolades {}
. Het eerste onderdeel van deze functie
is een lijst met argumenten die van buitenaf aangepast kunnen worden, met
daarbij ook een defaultwaarde voor elke parameter. Deze lijst met
argumenten begint en eindigt met een pipe: |
.
var env,sig,mod;
In de tweede regel worden de lokale variabelen gedefinieerd. In tegenstelling tot globale variabelen die met een tilde beginnen worden lokale variabelen met het woordje var ervoor gedefinieerd. Deze variabelen zijn alleen binnen deze SynthDef bekend en kan je niet van buitenaf veranderen. In deze variabelen worden delen van de synthesizer opgeslagen. Na het aanmaken van de variabelen kan de definitie van de synthesizer opgebouwd worden.
mod = SinOsc.ar(0.5,0,50,freq);
In deze regel wordt de modulator voor de frequentie gemaakt. Het is een sinus die met een frequentie van 0.5 beweegt tussen -50 en +50 rond de frequentie die bovenin is gedefinieerd als freq.
env = EnvGen.ar(Env.linen(0.01,0.5,1.0,amp),Dust.ar(rate));
EnvGen is een envelope-generator. Om een envelope te genereren zijn er
minimaal twee argumenten nodig. Het eerste argument is het type envelope.
Dit kan een percussieve zijn (Env.perc)
, een lineaire (Env.linen)
of
een adsr (Env.adsr)
. In de beschrijving van Env
is de complete lijst met
envelopes te vinden. Elke envelope heeft verschillende argumenten nodig.
Meestal een attack- en decaytijd, amplitude en curve. Om het verloop van de
envelope te kunnen zien is er de plotfunctie:
Env.perc(0.1,0.1,0.5,-4).plot
Het tweede argument van EnvGen
geeft aan hoe de envelope getriggerd moet
worden. In het geval hierboven gebeurt dat met een Dust-generator. Als in
plaats van deze Dust-generator het woordje gate als argument wordt gebruikt
kan de envelope handmatig worden gestart van buiten de SynthDef. Zie
onderaan de pagina voor een voorbeeld. In datzelfde voorbeeld wordt ook een
derde argument toegevoegd, een doneAction: Dit geeft aan wat er moet
gebeuren als de envelope klaar is met afspelen (action when done). De meest gebruikte waarde bij een doneAction is 2, hiermee wordt de synth gestopt en verwijderd als de envelope klaar is.
sig = Ringz.ar(Dust.ar(rate*2,0.3),mod,0.6,env); sig = BLowPass.ar(sig,ffreq,0.5); sig = FreeVerb.ar(sig);
In deze drie regels wordt de daadwerkelijke klank-generatie gedaan: een resonant filter met een Dust-generator als input, met als filterfrequentie de hierboven genoemde modulerende sinus. Een q van 0.6 en als mul de envelope. Daarna gaat het signaal door een lowpass filter en daarna door een reverb. Door bij elke UGen van deze code de help-file te openen krijg je te zien hoe ze precies werken.
De notatie waarbij je op elke volgende regel opnieuw sig gebruikt heeft als voordeel dat daarmee duidelijk is dat dit het signaal is dat je in een aantal stappen aan het bewerken bent. Je kunt natuurlijk voor elke stap een nieuwe variabele maken, maar dan zie je die flow niet zo mooi.
Out.ar([0,1],sig);
Het laatste onderdeel van de SynthDef is de output waarnaar het signaal moet worden gestuurd. Out.ar
heeft twee argumenten: de bussen waar het signaal naar moet worden gestuurd. Dit kan ook een array zijn met meerdere bussen, om het signaal naar meerdere outputs te sturen. Het tweede argument is het signaal dat wordt verstuurd, in dit geval opgeslagen in de variabele sig.
}).add
De functie en de SynthDef worden afgesloten. met .add wordt de functie naar de server gestuurd en daar toegevoegd aan het lijstje met beschikbare SynthDefs om aan te roepen.
Voorbeeld van een van buitenaf getriggerde envelope:
(
SynthDef("envelope",{|freq=300,attack=0.1,decay=0.4,gate|
var env, sig;
env = EnvGen.ar(Env.perc(attack,decay,0.5),gate,doneAction:2);
sig = Saw.ar(freq,env);
sig = BLowPass.ar(sig,freq*2);
Out.ar([0,1],sig);
}).add;
)
~env = Synth("envelope",[\gate,1]);