Beatslicing med patterns
Beatslicing er en teknik, som kan udvide det kreative potentiale i loop-biblioteker. Her bruger vi altså ikke oneshot-samples, som vi gjorde ovenfor, men samlede beats/loops. Teknikken indebærer at skære et beat eller en lydsekvens op i mindre dele, som derefter kan arrangeres på nye måder. Ved at omarrangere slices, kan loops få et helt unikt udtryk og hjælpe med at skille din musik ud fra mængden.
Beatslicing kan implementeres på forskellige måder, blandt andet ved hjælp af såkaldt "onset detection", som er en form for maskinlytning, der kan detektere anslag i et lydsignal ved at lede efter pludselig skift fra stilhed til væsentlige udsving i amplitude. Dette er dog lidt mere avanceret og gennemgås ikke her. I stedet fremstiller vi her en SynthDef, som ganske enkelt deler en buffer op i et givet antal lige store "slices" og afspiller et af disse slices, baseret på SynthDef-argumenter. Her kan vi så bruger vi patterns til at afspille disse slices på forskellig vis. Lydeksemplerne i dette afsnit er efterbehandlet med rumklang, da det giver en bedre fornemmelse af de ellers noget tørre trommelyde.
Kildemateriale og slice-varighed
Med beatslicing er det afgørende, hvordan kildematerialet er organiseret. I denne artikel bruger vi som eksempel et sample, der indeholder et metrisk organiseret trommebeat af præcis én takts varighed. Der findes ganske mange af disse samples på nettet under kategorier som "drum loops", "instrumental beats", etc.
Til eksemplerne herunder bruger jeg et udsnit af lydfilen funky drum loops.84 bpm.mp31 af freesound.org-brugeren 'ajubamusic' (2015)3. Filen består af en række vellydende, akustisk indspillede trommebeats. Jeg har valgt netop dette udsnit af filen, fordi det indeholder et mindre "fill" og dermed en række forskellige trommelyde, som vi kan klippe ud og sætte sammen på ny.
Trommebeatet er underdelt i sekstendedele, og det giver således mening at dele det op i 16 lige lange slices. Når vi afspiller et slice, kan vi derfor beregne en startposition i bufferen ud fra hvilket nummer det ønskede slice har (her fra 0 til 15). Når vi kender samplets samlede varighed, kan vi også fremstille en envelope med en fikseret varighed på en sekstendedel heraf. Vi kan indrette envelopen således, at den sidste halvdel af envelopens release-segment ganske kort overlapper med begyndelsen af det efterfølgende slice.
Med denne algoritme kan vi således afspille det første slice i en buffer med i alt 16 slices på følgende vis.
{
var buf = ~beat, slice = 0, numSlices = 16,
attack = 0.002, release 0.010;
// Beregn startposition for det ønskede slice
var startPos = (slice % numSlices) / numSlices;
// Beregn varigheden af et slice og envelopens sustain-tid
var duration = BufDur.kr(buf) / numSlices;
var sustainTime = duration - attack - (release * 0.5);
var env = EnvGen.kr(
Env.linen(attack, sustainTime, release),
doneAction: Done.freeSelf
);
PlayBuf.ar(
numChannels: 2,
bufnum: buf,
rate: BufRateScale.kr(buf),
startPos: BufFrames.kr(buf) * startPos
) * env;
}.play;
En SynthDef til beatslicing
Ovenstående kan omskrives til en SynthDef, hvor de første variabler laves om til argumenter, så vi kan styre dem med patterns. Vi kan også tilføje transponering og afspilningsretning, klanglig manipulation med drive og lavpasfilter samt panorering, amplitude og output-routing med tilhørende argumenter. Således opnår vi nedenstående SynthDef.
SynthDef(\slice, {
arg buf, slice = 0, numSlices = 16,
attack = 0.002, release = 0.010,
transpose = 0, direction = 1,
drive = 0, cutoff = 20000, rq = 1,
amp = 0.1, out = 0, pan = 0;
var startPos = (slice % numSlices) / numSlices;
var duration = BufDur.kr(buf) / numSlices;
var sustainTime = duration - attack - (release * 0.5);
var env = EnvGen.kr(
Env.linen(attack, sustainTime, release),
doneAction: Done.freeSelf
);
var sig = PlayBuf.ar(
numChannels: 2,
bufnum: buf,
rate: BufRateScale.kr(buf) * transpose.midiratio * direction.sign,
startPos: BufFrames.kr(buf) * startPos
);
sig = (sig * drive.linexp(0, 1, 1, 100)).tanh;
sig = sig * drive.lincurve(0, 1, 1, 0.1, -2);
sig = RLPF.ar(sig, cutoff.clip(20, 20000), rq.clip(0.0001, 1));
sig = Compander.ar(sig, sig, 0.1, 1.0, 0.25, 0.01, 0.01) * 10.dbamp;
sig = Balance2.ar(sig[0], sig[1], pan, amp) * env;
Out.ar(out, sig);
}).add;
Vi kan teste SynthDef'en ved at afspille tilfældigt valgte slices.
Algoritmisk sammensætning af slices
Med ovenstående SynthDef kan vi fleksibelt sammensætte slices ved hjælp af patterns.
En enkelt overvejelse vi skal have med angår forholdet mellem to tempi: Originalsamplets tempo versus tempoet i vores nye beat. Der kan nemlig opstå "huller" i strømmen af slices, hvis vores nye tempo er langsommere end tempoet i det oprindelige, da ét slice nødvendigvis vil vare kortere end den tid, det skal udfylde. Vil man omgå dette, må man modificere SynthDef'en, så envelopens varighed udregnes på anden vis.
TempoClock.tempo = 45 / 60;
TempoClock.tempo = 84 / 60;
Pbind(
\instrument, \slice,
\buf, ~beat,
\dur, 1/16 * 4,
\numSlices, 16,
\slice, Pseries(length: 16),
).play;
Da mange af vores Pbinds vil tage udgangspunkt i nogle af de samme data, kan vi oprette en Pbind som basis for andre Pbinds samt angive et tempo, som anvendes i resten af eksemplerne herunder, medmindre andet er angivet.
TempoClock.tempo = 95 / 60;
~base = Pbind(
\instrument, \slice,
\buf, ~beat,
\dur, 1/16 * 4,
\numSlices, 16
);
Semi-tilfældig rækkefølge af slices
Vi kan selvsagt sammensætte slices på kryds og tværs. Vi behøver ikke bruge alle slices, og det er kun fantasien (og SuperColliders bibliotek af patterns), der sætter grænser. Her er eksempelvis en sekvens, hvor nogle slices er faste og andre vælges tilfældigt blandt et udvalg af muligheder. I kompositionsprocessen er denne balance mellem det helt tilfældige og den genkendelige struktur ofte væsentlig (men også til tider vanskelig) at finde.
Pbindf(~base,
\db, Pgauss(-18, 0.5),
\slice, Pseq([
2,
Prand([1, 3, 7, 9], 1),
15,
Prand([5, 4, 14, Rest()], 1)
], 8).stutter(2),
).play;
Til beatslicing kunne det være relevant at generere en tilfældig rækkefølge bestående af et vilkårligt antal elementer ud fra en liste med mulige valg. På den måde kan vi fx i forhold til beatslicing skabe korte sekvenser ud af de 16 mulige slices, hvor måske kun 4 slices bliver valgt og udgør en repeterende sekvens. Dette kan ikke lade sig gøre i ét pattern med de indbyggede patterns i SuperCollider. Men heldigvis kan man selv udvikle nye pattern-klasser, og med netop dette formål har jeg designet et pattern kaldet Pshunc
. Man kan let installere det2 som en udvidelse til SuperCollider med Quark'en alea (Eskildsen, 2022)4. Pshunc
har tre argumenter: Pshunc(list, length, repeats)
. list
er den liste af muligheder, som vi kan vælge imellem. Den bliver sat i tilfældig rækkefølge ligesom ved Pshuf
, men medtager kun det antal elementer, vi angiver med length
. repeats
fungerer som ved Pseq
og Pshuf
, idet den angiver hvor mange gange den genererede sekvens skal gentages.
TempoClock.tempo = 95 / 60;
Pbindf(~base,
\slice, Pshunc((0..15), 4, 4)
).play;
Klangligt manipuleret beatslicing
Vi kan selvfølgelig også variere andet end slice-rækkefølge. Her følger en Pbind, som transponerer, skifter retning til baglæns en gang imellem, varierer filter-indstillinger og panorering samt drive, lydstyrke og mikrotiming.
TempoClock.tempo = 130 / 60;
Pbindf(~base,
\slice, Pseq([
0,
Prand([
Pwhite(1, 3, 3),
Pbrown(0, 15, 1, 3),
Prand((0..15)).stutter(3),
Pseq(Rest().dup(3))
]),
], inf),
// Transponering og retning
\transpose, Prand([-12, -7, 0], inf) + Pwhite(-5, 5).stutter(32),
\direction, Pwrand([1, -1], [0.8, 0.2], inf),
// Klanglig manipulation
\drive, Pgauss(0.7, 0.05),
\cutoff, Pexprand(3500, 8000),
\rq, Phprand(0.1, 0.8),
// Panorering og volumen
\db, Pgauss(-15, 1),
\lag, Pgauss(0, 0.005),
\pan, Pgauss(0, 0.2),
).play;
-
funky drum loops.84 bpm.mp3 er udgivet under Creative Commons-licensen CC BY 3.0 og kan findes via platformen freesound. Det fremgår af dette kapitel, hvordan samplet er anvendt og behandlet i denne bog. ↩
-
For at installere udvidelser til SuperCollider (såkaldte Quarks), skal man først installere programmet git på sin computer. Det bruger SuperCollider til at downloade og installere udvidelsen. Når git er installeret, kan man køre denne linje i SuperCollider for at installere alea:
Quarks.install("https://github.com/aeskildsen/alea")
. ↩ -
ajubamusic. (2015). Funky drum loops.84 bpm.Mp3. In Freesound. https://freesound.org/people/ajubamusic/sounds/320803/ ↩
-
Eskildsen, A. (2022). Alea. https://github.com/aeskildsen/alea ↩