Gå til indholdet

Klangflade og tekstur

Som vi så tidligere, giver granular syntese adgang til at strække eller transponere lyd på meget fleksible måder. Men teknik er også attraktiv på grund af dens evne til at skabe brede klangflader og abstrakte teksturer. Disse kompositionsmuligheder antyder vi herunder, men det er væsentligt at bemærke, at potentialet i denne teknik bedst kan udforskes gennem egne eksperimenter, da forskelligt lydmateriale vil give anledning til vidt forskellige klange, når det bearbejdes med granular syntese.

I det følgende bruger vi samme kalimba-sample som i de foregående afsnit, indlæst i buffer under variablen ~kalimba.

Teksturdannelse i granular syntese

Ved at læse korte grains fra forskellige positioner i et sample, kan vi skabe en interessant, lydlig tekstur. For at skabe spredning og variation i teksturen kan vi fordele de læste grains i stereofeltet og transponere hvert enkelt grain et tilfældigt antal halvtoner op og ned.

Til dette formål kan vi bruge nogle specielle UGens, der genererer nyt output, hver gang de modtager en trigger. Her kan vi især bruge tilfældighedsgeneratorer som TRand.kr(minimum, maksimum, trigger) eller TIRand.kr(minimum, maksimum, trigger), som henholdsvis genererer tilfældige decimaltal og heltal mellem et givet minimum og maksimum. Der findes også UGens som TExpRand, der i sin funktion minder om vores gode ven Pexprand, og TChoose, der tilsvarende minder om Prand, idet den vælger tilfældigt mellem et antal givne muligheder.

Lad os skabe en lydlig tekstur med GrainBuf og nogle af disse redskaber:

  • Her sætter vi tæthed til 10 for at få en kontinuerlig strøm af lyd.
  • Grainvarigheden sættes til 25 ms.
  • Triggerfrekvensen udregnes automatisk.
  • Vi transponerer det enkelte grain op til to oktaver op eller ned med en kombination af TIRand og .midiratio.
  • Vi læser med TRand det enkelte grain fra et tilfældigt sted i bufferen.
  • Vi placerer med TRand det enkelte grain tilfældigt i hele stereofeltets bredde.
Intens tekstur præget af aleatorik på mikroskala
{
    var density = 10;
    var grainDur = 0.025;
    var trigFreq = (density / grainDur);
    var trigger = Dust.kr(trigFreq);

    GrainBuf.ar(
        numChannels: 2,
        trigger: trigger,
        dur: grainDur,
        sndbuf: ~kalimba,
        rate: TIRand.kr(-24, 24, trigger).midiratio,
        pos: TRand.kr(0, 1, trigger),
        pan: TRand.kr(-1, 1, trigger)
    );
}.play;

Dette må siges at resultere i en intens, abstrakt og tæt tekstur, der ville egne sig til en tæt baggrundslyd eller et lydligt intermezzo. Men som jeg tidligere har bemærket, holder den konstante og totale tilfældighed hurtigt op med at være perceptuelt interessant, og øret mættes hurtigt af den tætte tekstur. Men kombinerer vi disse teknikker med en mere ordinær sample-afspilning, kan vi bevæge os ind på nogle ganske interessante områder.

Klangtekstur med jitter

Vi kan kombinere elementer af den kaotiske tekstur med den teknik, vi bruger til at styre aflæsningspositionen med en pointer. Det gør vi konkret ved at tilføje lidt "støj" til pointeren. Man kan forestille sig en lynhurtig DJ, der dog ryster lidt på hånden og derfor springer lidt frem og tilbage i lyden. Støjen, som man med et engelsk udtryk kan kalde for jitter, kan vi generere med TRand og lignende UGens, som vist ovenfor. Vi indfører dertil et argument jitter, som angiver den maksimale afstand til pointeren, vi læsepositionen på springe til - målt i sekunder1. Vi laver også justerbar spredning i stereofeltet med et argument kaldet spread.

~shaky = {
    arg transpose = 0, moveRate = 1,
    jitter = 0.01, spread = 0.1;

    var buf = ~kalimba;
    var numFrames = BufFrames.kr(buf);
    var pointer = Phasor.ar(
        rate: moveRate,
        start: 0,
        end: numFrames
    ) / numFrames;

    var trigger = Dust.kr(200);

    var jit = TRand.kr(jitter.neg, jitter, trigger) / BufDur.kr(buf);

    var pan = TRand.kr(spread.neg, spread, trigger);

    GrainBuf.ar(
        numChannels: 2,
        trigger: trigger,
        dur: 0.1,
        sndbuf: buf,
        rate: transpose.midiratio,
        pos: pointer + jit,
        pan: pan
    ) * 0.1;
}.play;

Når ovenstående er startet, kan vi justere på indstillingerne og introducere jitter og stereo-spredning med .set-method'en. Vi kan også justere transponering og pointerens fart.

Variationsmuligheder med stereo-spredning, jitter mm.
// Fordeling af grains i stereofelt (høres bedst i hovedtelefoner)
~shaky.set(\spread, 0.3)
~shaky.set(\spread, 1)

// Jitter
~shaky.set(\jitter, 0.1)
~shaky.set(\jitter, 0.5)

// Langsom eller stillestående pointer
~shaky.set(\moveRate, 0.5)
~shaky.set(\moveRate, 0)

// Tilfældig transponering
~shaky.set(\transpose, rrand(-12, 12))

Ønsker man at gå videre ud ad denne sti, kan man starte med at tilføje en tilsvarende form for jitter til transponering. Dette overlades til den nysgerrige læser at implementere på egen hånd.

Klangflader

Hidtil har vi anvendt Phasor som pointer, hvilket resulterer i en lineær bevægelse frem eller tilbage. Men vi er på ingen måde tvunget til at tænke statisk eller lineært på denne eller andre parametre. Ved at modulere parametrene med envelopes og LFO'er kan vi fremmane andre lydlige forløb og måske endda abstrakte rytmer, som skaber klanglige mønstre over tid. Her handler det procesmæssigt om at vælge og indstille modulatorerne omhyggeligt, lytte til resultatet, og tune modulatorerne, lytte igen og så fremdeles.

En atmosfærisk rejse baglæns i kalimba-samplet

Når vi komponerer på dette abstraktionsniveau, dvs. uden brug af patterns som formgivende element, kan vi i stedet bruge envelopes. Som eksempel kan vi tage grain-tætheden som en central kompositorisk parameter. Til at styre denne opretter jeg en ny envelope med en varighed på i alt 30 sekunder, som kan ses på et plot herunder.

En envelope til at styre grain-tætheden over tid
~densityEnv = Env.new(
    levels: [0.1, 15, 75, 15],
    times: [5, 10, 15],
    curve: [4, -5, 3]
);
~densityEnv.plot;

En envelope til at styre grain-tætheden over tid
En envelope til at styre grain-tætheden over tid

Sammen med envelopen til styring af tæthed anvendes der en Env.linen til den overordnede volumen i kompositionen, og en simpel Line styrer sammen med en smule jitter læsepositionen i bufferen. Grains med en varighed på 200 ms kan måske knap nok kaldes grains, men det er en mindre detalje.

Atmosfærisk klangflade
{
    var density = EnvGen.kr(~densityEnv);
    var grainDur = 0.200;
    var trigFreq = density / grainDur;
    var trigger = Dust.ar(trigFreq);
    var env = Env.linen(sustainTime: 30, releaseTime: 3, curve: \sin).kr(2);
    GrainBuf.ar(
        numChannels: 2,
        trigger: trigger,
        dur: grainDur,
        sndbuf: ~kalimba,
        rate: TRand.ar(-0.1, 0.1, trigger).midiratio,
        pos: Line.kr(0.95, 0.05, 30) + TRand.ar(-0.01, 0.01, trigger),
        pan: TRand.ar(-1, 1, trigger),
    ) * 0.1 * env;
}.play;

Bemærk, at der ikke er tilføjet rumklang til dette lydeksempel. Fornemmelsen af rumlighed stammer fra dels stereospredningen, dels fra en chorus-lignende effekt af de mange samtidigt klingende grains.

En transponeringssekvens, som går i stå og bevæger sig på samme tid

Hvis vi vil arbejde med lidt en lidt mere tonalt dynamisk komposition, kan vi bruge en envelope til at modulere GrainBufs rate-argument. Herunder anvender jeg en Env.circle med en række transponeringstrin, der fungerer lidt ligesom en LFO og omregnes til afspilningshastighed med .midiratio. Til envelopens curve-argument angives værdien \step, hvilket får envelopen til at bevæge sig i trin frem for i gradvise kurver.

Derudover kan vi under en lokal variabel xline definere en XLine, som bevæger sig i en eksponentiel bane fra 1 til 3 over 30 sekunder. Denne UGen spiller en central rolle, da den modulerer flere andre parametre:

  • Den føromtalte envelope til modulation af tonehøjde bliver gradvist strakt, da segmenternes varigheder skaleres med xline. Tempoet i sekvensen bliver derfor langsommere og langsommere.
  • Skiftene mellem modulationstrinnene sker pludseligt, men med .lag glider vi kort mellem værdierne. xline modulerer her hvor hurtigt dette slide sker.
  • GrainBuf producerer i modsætning til tidligere eksempler ikke to kanaler men én. Til gengæld bliver den fordoblet på grund af multichannel expansion i triggeren Dust samt den LFTri, der bestemmer aflæsningspositionen. I begyndelsen læser vi fra den samme position i bufferen (0.1), men over tid vokser LFTris rækkevidde med xline, og vi hører som resultat større og større forskel på venstre og højre kanal.
Granular syntese med en XLine som central modulator
{
    var density = 25;
    var grainDur = 0.100;
    var trigFreq = density / grainDur;
    var trigger = Dust.ar(trigFreq.dup(2));
    var xline = XLine.kr(1, 3, 30);
    var rate = EnvGen.kr(
        Env.circle(
            levels: [-12, -8, -5, -14],
            times: [1, 1, 2, 1],
            curve: \step
    ), timeScale: xline).lag(0.05 * xline.pow(2)).midiratio;
    var env = Env.linen(sustainTime: 30, releaseTime: 5, curve: \sin).kr(2);
    GrainBuf.ar(
        numChannels: 1,
        trigger: trigger,
        dur: grainDur,
        sndbuf: ~kalimba,
        rate: rate,
        pos: LFTri.kr([10, 10.1]).range(0.1, 0.1 * xline),
    ) * 0.1 * env;
}.play;

Videre perspektiver med granular syntese

Det er helt forståeligt, hvis eksempler som det foregående er vanskelige at gennemskue ved første øjekast. Men hvis man læser grundigt op på de grundlæggende emner som envelopes, skalering og multichannel expansion, er det bestemt muligt at forstå og lade sig inspirere af kildekoden. Rent teknisk er granular syntese et komplekst emne med mange muligheder og parametre, og i dette kapitel er kun et par grundlæggende teknikker og kompositionsmuligheder antydet. For videregående emner såsom granulering af et live-lydsignal henvises nysgerrige læsere til Eli Fieldsteels SuperCollider Tutorial: 26. Granular Synthesis, Part I/II (Fieldsteel, 2020a, 2020b)2 3.

Da vi grundet de tekniske kompleksiteter ikke her har implementeret granular syntese i en SynthDef-form, er dette naturligvis en oplagt øvelse for læseren. Her gælder det blot om at læse godt op på SynthDef-konstruktion og så ellers eksperimentere løs på egen hånd. God fornøjelse med det!


  1. Bemærk her, at vi både kan springe frem og tilbage. Det effektive "vindue" vi læser i bufferen fra er derfor i praksis dobbelt så langt som jitter-argumentets værdi. 

  2. Fieldsteel, E. (2020a). SuperCollider Tutorial: 25. Granular Synthesis, Part I. https://www.youtube.com/watch?v=WBqAM\_94TW4 

  3. Fieldsteel, E. (2020b). SuperCollider Tutorial: 26. Granular Synthesis, Part II. https://www.youtube.com/watch?v=MnD8stNB5tE