Konstruktion af SynthDefs
Hidtil har vores lyddesign været enkle og midlertidige - med {}.play
kan vi hurtigt teste UGens og ideer. Men vi kan gøre vores lyddesign meget mere fleksibelt og anvendeligt til længere patternbaserede kompositionsforløb ved at samle vores UGens i en såkaldt SynthDef
.
Hvad er en Synth?
Bemærk hvad der bliver vist i SuperColliders post window, når vi kører nedenstående linje.
Vi får at vide, at vi har startet en såkaldt Synth
. Teknisk set er en Synth en klynge af sammenhængende UGens, som defineres af en UGen-funktion og kører på lydserveren som en "node". Hver gang vi producerer lyd på lydserveren, har vi altså gang i en eller flere Synths. Når vi fx afspiller toner med en Pbind
, genererer vi en ny Synth for hver tone, der spiller. Kør fx nedenstående blok og se listen over Synths i Node Tree-vinduet.
Vi kan også starte Synths direkte ved at bruge Synth.new
.
Synth.new(\default);
Synth(\default);
// Samme resultat: .new er implicit
Men hov, hvad er mon \default
? Jo, det er navnet på en bestemt SynthDef
.
Hvad er en SynthDef?
Ordet SynthDef
står, ikke overraskende, for Synth-definition. Med SynthDef
s kan vi lave lyddesign-opskrifter og generere Synth
s på meget fleksibel vis.
En SynthDef adskiller sig fra den mere primitive form {}.play
på følgende måder:
- En ny SynthDef (
SynthDef.new
) skal registreres på lydserveren med.add;
1. - En SynthDef skal have et navn, så vi kan henvise til den senere, fx
\minSynthDef
. - Vores UGen-funktion angives lige efter navnet, som argument nr. 2 til
SynthDef.new()
. - For at høre lyd-outputtet skal vi inde i UGen-funktionen route det ønskede signal ud med den særlige UGen
Out
.
(
// Vi skriver først SynthDef'en og registrerer den på lydserveren
SynthDef(\minSynthDef, {
Out.ar(0, SinOsc.ar(440) * 0.1);
}).add;
)
// Start en Synth baseret på SynthDef'en
Synth(\minSynthDef);
Interfacet mellem Synth, SynthDef og Pbind
Sammenhængen mellem Synth
, SynthDef
og Pbind
er helt central i SuperCollider.
SynthDef
kan forstås som et instrument, der bestemmer mulighederne for tonernes klangdannelse over tid (spektromorfologi).Synth
s kan forstås som de konkrete lyde, man skaber med instrumentet.Pbind
udgør således kompositionen eller partituret.
Hvis man i denne analogi synes, at der mangler en musiker til at udføre kompositionen, kan man tænke på den EventStreamPlayer
, som opstår, når vi bruger Pbind().play
- det er dette objekt, som faktisk udfører partituret og starter/stopper Synths på lydserveren ud fra de instrukser, vi har angivet med Pbind
. Vi bruger sjældent denne klasse direkte, bortset fra når vi gemmer resultatet af Pbind.play();
under en variabel og efterfølgende interagerer med denne variabel.
SynthDef og Synth
Lad os se på hvordan samspillet mellem Synth
og SynthDef
fungerer i praksis.
SynthDef'en \minSynthDef
ovenfor fungerer, men er ikke særligt fleksibel. Vi kan fx kun spille én tone med den, og vi er nødt til at stoppe den manuelt med Ctrl/Cmd-Punktum. For at kunne spille forskellige toner kan vi indføre et argument. Herunder vælger jeg at indføre argumentet freq
ved hjælp af nøgleordet arg
, og jeg angiver også en standardværdi på 440
.
SynthDef(\tone, {
arg freq = 440; // <--- her erklærer vi argumentet og angiver standardværdien
Out.ar(0, SinOsc.ar(freq) * 0.1); // <--- her bruger vi argumentet, ligesom en variabel
}).add;
Når vi har tilføjet vores SynthDef til lydserveren, kan vi starte nye Synths baseret på den.
Synth(\tone);
Synth(\tone, [\freq, 220]);
Synth(\tone, [\freq, 1000]);
// Vi kan også justere frekvensen efterfølgende ved at gemme Synth'en under en variabel og bruge .set
~minLyd = Synth(\tone, [\freq, 330]);
~minLyd.set(\freq, 220);
~minLyd.set(\freq, rrand(100, 800));
I mange SynthDefs er det nyttigt at bruge en envelope til at styre volumen over tid. Med doneAction
angiver vi, at Synth'en skal fjernes, når tonen er klinget ud (læs nærmere herom i afsnittet om automatisk oprydning med doneAction).
SynthDef og Pbind
Vi behøver imidlertid ikke starte vores Synths manuelt. Når SynthDef er indlæst korrekt på lydserveren, kan vi bruge Pbind
til at generere sekvenser af Synths helt automatisk. Vi fortæller Pbind, hvilken SynthDef, der skal anvendes, ved at bruge nøglen \instrument
. Det betyder, at Pbind(\instrument, \simpel)
vil starte Synths baseret på en SynthDef med navnet \simpel
. Hvis ikke en sådan SynthDef er registreret på lydserveren, vil vi få en fejlmeddelelse.
// Først registrerer vi SynthDef'en på lydserveren
SynthDef(\simpel, {
arg freq = 440;
Out.ar(0, SinOsc.ar(freq) * 0.1
* EnvGen.kr(Env.perc, doneAction: Done.freeSelf)
);
}).add;
Nu kan vi bruge Pbind til at "spille på" vores SynthDef, som om den er et instrument.
Pbind(
// SynthDef-navnet angives under nøglen \instrument
\instrument, \simpel,
\degree, Pwhite(-7, 7).trace,
).play;
// -> 6, 0, -4, 6, 1, 1, -7, -4, -5, 4, 1, -7, -7 , 5, 4, 7
SynthDefs bliver i øvrigt meget lettere at læse, hvis vi bruger lokale variabler. Koden herunder fungerer præcis som ovenfor, men er markant mere læsbar, da vi kan følge signelvejen gennem de lokale variabler (hvis ellers de variabelnavne vi har valgt er tilstrækkeligt deskriptive).
SynthDef(\simpel, {
arg freq = 440;
// 'sig' er variabelnavnet for vores hovedsignal
var sig = SinOsc.ar(freq);
// 'env' er variabelnavnet for vores envelope
var env = EnvGen.kr(Env.perc, doneAction: Done.freeSelf);
sig = sig * env * 0.1;
Out.ar(0, sig);
}).add;
Vi kan indføre lige så mange argumenter i en SynthDef
, som vi har lyst. Når vi vil bruge argumenterne i en Pbind
, skal nøglerne blot svare til argumenterne.
Med et par ekstra argumenter og en Pan2
-UGen kan vi styre parametre som lydstyrke, release-tid og stereo-panorering. De argumenter, vi angiver i begyndelsen af en SynthDef, kan vi nemlig anvende som nøgler i Pbind
. Dette interface mellem SynthDef
/Synth
og Pbind
, danner grundlag for de utroligt righoldige, generative muligheder i SuperCollider.
SynthDef(\fleksibel, {
arg freq = 440, pan = 0, amp = 0.1, release = 1;
var sig = SinOsc.ar(freq);
var env = EnvGen.kr(Env.perc(releaseTime: release), Done.freeSelf);
sig = sig * env;
sig = Pan2.ar(sig, pan, amp);
Out.ar(0, sig);
}).add;
Nu kan vi bruge vores viden om patterns til at skabe en algoritmisk komposition, hvor også klanglige forhold styres af patterns.
Pbind(
\instrument, \fleksibel,
\degree, Pwhite(-7, 7).stutter(4),
\pan, Pbrown(-1.0, 1.0, 0.2),
\amp, Pexprand(0.1, 0.3),
\dur, Pseries(0.100, 0.010, 35),
\release, Prand([0.100, 0.300, 2], inf)
).play;
Hvis ovenstående pattern-komposition er vanskelig at følge, bør du genopfriske din viden om pattern-baseret komposition fra tidligere kapitler på grundlæggende og lidt mere avanceret niveau.
Legato og staccato med vedvarende envelopes
Pbind
kan også afslutte vedvarende envelopes for os. Vi kan dermed få adgang til at komponere med legato- og staccatofrasering. Det kræver dog, at SynthDef
'en indrettes på følgende måde:
- Vi indfører et argument kaldet
gate
med standardværdi 1. - Vi bruger en vedvarende envelope, fx
Env.asr
. - Vi angiver
gate
som argument nr. 2 tilEnvGen
.
SynthDef(\vedvarende, {
arg freq = 440, pan = 0, amp = 0.1, gate = 1;
var sig = SinOsc.ar(freq);
var env = EnvGen.kr(Env.asr, gate, doneAction: Done.freeSelf);
sig = sig * env;
sig = Pan2.ar(sig, pan, amp);
Out.ar(0, sig);
}).add;
Med ovenstående SynthDef indlæst på lydserveren kan vi nu også spille legato og staccato. Det gør vi ved hjælp af nøglerne legato
og \sustain
i Pbind
. Til \sustain
-nøglen kan vi angive en absolut nodeværdi, dvs. hvor længe tonerne skal holdes. Dette er et alternativ til \legato
, som automatisk udregner \sustain
relativt til \dur
-nøglen.
( // Legato-frasering
Pbind(
\instrument, \vedvarende,
\degree, Pwhite(-7, 7, 5),
\sustain, 3,
).play;
)
( // Staccato-frasering
Pbind(
\instrument, \vedvarende,
\degree, Pwhite(-7, 7, 5),
\sustain, 0.1,
).play;
)
Vedvarende Synth med Pmono og PmonoArtic
Som vi har set, er Pbind god til sekvenser, der starter mange ynths. Men til gengæld kan Pbind ikke ændre på indstillingerne for Synths, fx tonehøjde eller panorering, når de er startet. Det kan vi gøre manuelt ved hjælp af .set
-metoden.
~minLyd = Synth(\default);
~minLyd.set(\freq, 1000);
~minLyd.set(\freq, 500);
Hvis vi med vores egne SynthDefs vil skabe glissandi frem for spring i tonehøjde, kan vi bruge method'en .lag
1 på frekvens-argumentet, hvilket vil interpolere mellem værdier frem for at springe imellem dem. Dette er ganske nyttigt, hvis man vil blødgøre overgangen mellem skiftende værdier. Vi kan med et argument til .lag
styre, hvor lang tid det tager at glide hen til den nye værdi.
SynthDef(\glissando, {
arg freq = 440, pan = 0, amp = 0.1, gate = 1;
// .lag giver en glidende overgang mellem skiftende værdier (her glissando)
var sig = SinOsc.ar(freq.lag(0.01));
var env = EnvGen.kr(Env.asr, gate, doneAction: Done.freeSelf);
sig = sig * env;
sig = Pan2.ar(sig, pan, amp);
Out.ar(0, sig);
}).add;
~minLyd = Synth(\glissando);
~minLyd.set(\freq, exprand(200, 2000));
Hvis vi vil skabe en komposition med patterns, hvor vi bruger denne glissando-egenskab ved vores SynthDef, kan vi i stedet for Pbind bruge Pmono
eller PmonoArtic
. Begge disse kusiner til Pbind starter blot én Synth ad gangen og justerer efterfølgende parametrene ved hjælp af de sædvanlige koblinger af nøgler og patterns, som vi kender fra Pbind. SynthDef-navnet angives som første argument, dvs. uden \instrument
-nøglen.
Pmono(\glissando,
\degree, Pseq([0, 2, 4, 6], inf),
\mtranspose, Pwhite(0, 7).stutter(4),
\dur, 0.15,
).play;
// vis Synths på serveren - Pmono starter kun én Synth
s.plotTree;
Hvis vi har brug for at lave pauser i lyden mellem de strømme af værdier, der bliver skiftet imellem ved hjælp af Pmono
, kan vi bruge PmonoArtic
.
På pattern-siden styres artikulationen med \sustain
- eller \legato
-nøglen; når \sustain
er mindre end \dur
, afsluttes Synth'en, og der startes en ny ved næste event. Den mest praktiske tilgang er at bruge nøglen legato
, hvor vi angiver en værdi, som er mindre end 1, når vi ønsker at lave ophold, og en værdi der er højere end eller lig med 1, når vi ønsker en sammenhængende tone.
PmonoArtic(\glissando,
\degree, Pseq([0, 2, 4, 6], inf),
\mtranspose, Pwhite(0, 7).stutter(4),
\dur, 0.4,
\legato, Pseq([1, 1, 1, 0.1], inf),
).play;
Argumentnavne i SynthDef til kombination med Pbind
Argumenterne i vores SynthDefs kan i princippet have de navne vi gerne vil give dem (dog skal de starte med små bogstaver, ligesom variabler). Argumentnavne kunne fx være kaffe
, the
, mario
, luke
eller leia
. Men sædvanligvis kan det være en god ide at give argumenterne nogle deskriptive navne som fx cutoffFreq
, release
, drive
, delayTime
eller lignende. På den måde kan man regne ud hvad de betyder, når man vender tilbage til koden efter noget tid.
Når man vælger argumentnavne til sin SynthDef, findes der dog nogle få specielle navne, som er værd at kende og rette sig efter, hvis man gerne vil bruge sin SynthDef sammen med Pbind og patterns/events. Derudover findes der nogle bredt anvendte, konventionelle argumentnavne, som det er nyttigt at kende til, samt nogle bestemte argumentnavne, som det er fornuftigt at undgå.
Vigtige argumentnavne
Disse argumentnavne er nødvendige for at fungere med centrale dele af SuperCollider såsam patterns, events, live coding-klasser mm.:
freq
-
Anvendes til at angive tonefrekvens i SynthDefs, hvor det giver mening at forstå frekvensen som en tonehøjde, fx et højt c eller et lavt gis. Dette giver os mulighed for at bruge nøgler som
\degree
,\midinote
,\octave
,\scale
osv. og automatisk få disse informationer omregnet til oscillatorfrekvens, når vi arbejder medPbind
. Standardværdien er ofte 440 (kammertonen). gate
-
Anvendes typisk til at afslutte vedvarende envelopes og arbejde med legato og staccato-frasering, som vist ovenfor. Standardværdien er typisk 1.
amp
-
Bruges til at angive lydstyrke. Giver mulighed for at omregne automatisk fra
\db
, når vi arbejder medPbind
. Standardværdien er ofte 0.1, som svarer til -20 dB. out
-
Anvendes til at route et signal fra en Synth til en bestemt
Bus
. Dette er fx relevant, hvis man fx skal arbejde med mange højttalere på én gang i en lydinstallation, eller hvis man udforsker live coding med JITLib. Standardværdien er ofte 0, som er den første hardware-outputkanal.
Anbefalede argumentnavne
Disse argumentnavne er ikke så vidt vides strengt nødvendige. Men der er tale om meget udbredte konventioner, som gør kildekoden sammenlignelig og kompatibel med andres kildekode.
pan
-
Bruges til at angive panorering, ofte i et stereofelt mellem -1 og 1. Standardværdien er ofte 0, i midten af et stereofelt.
buf
-
Anvendes til at angive en
Buffer
på lydserveren. Dette er fx relevant, når der arbejdes med samples, wavetables og lignende.
Argumentnavne, som næsten altid bør undgås
dur
,scale
,sustain
,stretch
,midinote
,detune
med flere-
Disse navne bruges til automatiske omregninger, når vi komponerer med Pbind. Hvis man bruger dem som SynthDef-argumentnavne uden at være klar over dette, kan der hurtigt opstå mærkelige konsekvenser, som kan være svære at gennemskue. Man kan se en samlet liste over termer, der bruges til automatisk udredning af tonehøjde, timing og amplitude i patterns og events i James Harkins' oversigt (2009)2.
Min anbefaling er derfor:
- Brug navnene
freq
,amp
oggate
til SynthDef-argumentnavne, som vist ovenfor. - Brug
\scale
,\degree
,\mtranspose
,\sustain
,\legato
,\db
osv. som nøgler i forbindelse med patterns og events, og ikke som argumentnavne i SynthDefs.
- Brug navnene
-
Teknisk set kan SynthDefs også gemmes i et særligt filformat, hvilket dog kun er nødvendigt i særlige tilfælde. Læs nærmere herom i dokumentationen for SynthDefs. ↩↩
-
Harkins, H. J. (2009). Pattern Guide 07: Value Conversions (A Practical Guide to Patterns). In SuperCollider 3.3 Documentation. http://doc.sccode.org/Tutorials/A-Practical-Guide/PG\_07\_Value\_Conversions.html ↩