Jennifer Nicolay
Welcome to my wild network map. Each station leads to a part of my artistic, pedagogical, curatorial, and digital work. The paths are grouped into social presence, pedagogy, art, and curatorial practice.
Topic Lines / Legend
Art Core
Music & Performance
Pedagogy
Social Line
Curatorial Practice
Special Projects
Human Robotica – Live Coding
Click to view SuperCollider code, video, sample, and repository
+
Human Robotica – Live Coding
Click to view SuperCollider code, video, sample, and repository
SuperCollider Code
Here is my Code - work in progress br> (
// --------------------------------------------------
// HUMAN ROBOTICA
// Sample- oder Live-Input-Version
// --------------------------------------------------
s.waitForBoot({
// -----------------------------
// GLOBALS
// -----------------------------
~buf = nil;
~samplePath = nil;
~liveBuf = nil;
~recSynth = nil;
~density = 0.4;
~erosion = 0.2;
~brightness = 0.5;
~fragDur = 0.25;
~repetitionProb = 0.4;
~masterAmp = 0.8;
~section = \A;
// Live-Controller für B
~densCtl = 0.3;
~erosCtl = 0.2;
~brightCtl = 0.4;
~fragCtl = 0.3;
~freezeCtl = 0.2;
// Lautstärken der Sektionen
~ampA = 1.0;
~ampB = 1.0;
~ampC = 1.0;
~ampD = 1.0;
~ampBusA = Bus.control(s, 1);
~ampBusB = Bus.control(s, 1);
~ampBusC = Bus.control(s, 1);
~ampBusD = Bus.control(s, 1);
~ampBusA.set(~ampA);
~ampBusB.set(~ampB);
~ampBusC.set(~ampC);
~ampBusD.set(~ampD);
// Input-Kanal für Mikro
~inputChan = 0;
// Regionen fürs fragmentierte Lesen
~regions = [0.05, 0.18, 0.31, 0.47, 0.61, 0.74];
~lastRegion = 0.05;
// -----------------------------
// SYNTHDEFS
// -----------------------------
SynthDef(\plainPlayer, {
arg out=0, buf=0, amp=0.4, ampBus=0, rate=1, startPos=0;
var sig, env, ctl;
ctl = In.kr(ampBus, 1);
sig = PlayBuf.ar(2, buf, BufRateScale.kr(buf) * rate, startPos: startPos, doneAction: 0);
env = EnvGen.kr(Env.linen(0.01, BufDur.kr(buf) / max(rate, 0.001), 0.2), doneAction: 2);
Out.ar(out, sig * amp * ctl * env);
}).add;
// spektraler Schatten auf dem Original für erste maschinelle Spur:
SynthDef(\shadowTrace, {
arg out=0, buf=0, amp=0.25, ampBus=0, rate=1, bright=0.7;
var sig, mono, filt1, filt2, delayed, rev, env, ctl, shadow;
ctl = In.kr(ampBus, 1);
sig = PlayBuf.ar(2, buf, BufRateScale.kr(buf) * rate, doneAction: 0);
mono = Mix(sig) * 0.5;
// zwei spektrale Lesarten
filt1 = BPF.ar(mono, LinExp.kr(bright, 0, 1, 500, 2400), 0.12);
filt2 = BPF.ar(mono, LinExp.kr(bright, 0, 1, 1800, 5200), 0.08);
shadow = (filt1 * 0.8) + (filt2 * 0.5);
// leichte zeitliche Verschiebung
delayed = DelayC.ar(shadow, 0.4, 0.08);
// räumlicher / körperloser
rev = FreeVerb.ar(delayed, 0.55, 0.92, 0.45);
env = EnvGen.kr(
Env.linen(0.01, BufDur.kr(buf) / max(rate, 0.001), 0.35),
doneAction: 2
);
Out.ar(out, (rev ! 2) * amp * ctl * env);
}).add;
SynthDef(\erodedFrag, {
arg out=0, buf=0, amp=0.2, ampBus=0, start=0, dur=0.2, erosion=0.3, bright=0.5;
var sig, mono, env, filt, crushed, ctl;
ctl = In.kr(ampBus, 1);
sig = PlayBuf.ar(2, buf, BufRateScale.kr(buf), startPos: start, doneAction: 0);
mono = Mix(sig) * 0.5;
env = EnvGen.kr(Env.perc(0.01, dur), doneAction: 2);
mono = mono * env;
filt = BPF.ar(mono, LinExp.kr(bright, 0, 1, 250, 4500), 0.18);
crushed = Latch.ar(filt, Impulse.ar((150 + (erosion * 2500)).clip(40, 3000)));
Out.ar(out, Pan2.ar(XFade2.ar(filt, crushed, erosion.linlin(0,1,-0.5,0.7)) * amp * ctl));
}).add;
SynthDef(\freezeLoop, {
arg out=0, buf=0, amp=0.18, ampBus=0, start=0, dur=0.15, hold=1.5, rate=1;
var phs, sig, env, ctl;
ctl = In.kr(ampBus, 1);
phs = Phasor.ar(
0,
BufRateScale.kr(buf) * rate,
start,
start + (dur * BufSampleRate.kr(buf))
);
sig = BufRd.ar(2, buf, phs, loop: 1);
sig = Mix(sig) * 0.5;
sig = LPF.ar(sig, 3500);
env = EnvGen.kr(Env.linen(0.02, hold, 0.25), doneAction: 2);
Out.ar(out, Pan2.ar(sig * amp * ctl * env));
}).add;
SynthDef(\reconstructVoice, {
arg out=0, buf=0, amp=0.3, ampBus=0, start=0, dur=0.25, rate=1, bright=0.5;
var sig, mono, env, pitchShifted, filt, ctl;
ctl = In.kr(ampBus, 1);
sig = PlayBuf.ar(2, buf, BufRateScale.kr(buf) * rate, startPos: start, doneAction: 0);
mono = Mix(sig) * 0.5;
env = EnvGen.kr(Env.perc(0.01, dur), doneAction: 2);
mono = mono * env;
pitchShifted = PitchShift.ar(mono, 0.08, [0.5, 1, 1.5, 2].choose, 0.01, 0.01);
filt = BPF.ar(pitchShifted, LinExp.kr(bright, 0, 1, 350, 7000), 0.22);
Out.ar(out, Pan2.ar(filt * amp * ctl, LFNoise1.kr(0.25)));
}).add;
SynthDef(\memoryResidue, {
arg out=0, buf=0, amp=0.15, ampBus=0, start=0, dur=0.4, bright=0.4;
var sig, mono, env, filt, rev, ctl;
ctl = In.kr(ampBus, 1);
sig = PlayBuf.ar(2, buf, BufRateScale.kr(buf), startPos: start, doneAction: 0);
mono = Mix(sig) * 0.5;
env = EnvGen.kr(Env.perc(0.02, dur), doneAction: 2);
mono = mono * env;
filt = BPF.ar(mono, LinExp.kr(bright, 0, 1, 150, 3500), 0.3);
rev = FreeVerb.ar(filt, 0.5, 0.9, 0.5);
Out.ar(out, (rev ! 2) * amp * ctl);
}).add;
// Live-Aufnahme
SynthDef(\recordInput, {
arg buf=0, inChan=0, recLevel=1.0, preLevel=0.0, monitor=0.0, out=0;
var sig;
sig = SoundIn.ar(inChan); // mono mic input
sig = [sig, sig]; // auf stereo duplizieren
RecordBuf.ar(sig, buf, recLevel: recLevel, preLevel: preLevel, loop: 0, doneAction: 0);
Out.ar(out, sig * monitor);
}).add;
s.sync;
// -----------------------------
// HILFSFUNKTIONEN
// -----------------------------
~bufReady = {
~buf.notNil and: { ~buf.numFrames.notNil } and: { ~buf.numFrames > 0 }
};
~chooseRegion = {
var choices;
choices = ~regions.reject { |r| r == ~lastRegion };
~lastRegion = choices.choose;
(~buf.numFrames * ~lastRegion).asInteger;
};
// -----------------------------
// SAMPLE LADEN
// -----------------------------
~loadSample = { |path|
~samplePath = path;
if(File.exists(~samplePath).not, {
"DATEI NICHT GEFUNDEN".postln;
}, {
if(~buf.notNil, { ~buf.free; });
"Lade Sample ...".postln;
~buf = Buffer.read(s, ~samplePath, action: { |buf|
"BUFFER GELADEN".postln;
buf.query;
});
});
};
~reloadSample = {
if(~samplePath.notNil, {
~loadSample.(~samplePath);
}, {
"Kein samplePath gesetzt.".postln;
});
};
// -----------------------------
// LIVE BUFFER
// -----------------------------
~prepareLiveBuffer = {
if(~liveBuf.notNil, { ~liveBuf.free; });
~liveBuf = Buffer.alloc(s, s.sampleRate * 20, 2);
"Live buffer bereit (stereo).".postln;
};
~startRecording = {
if(~liveBuf.isNil, { ~prepareLiveBuffer.(); });
~recSynth = Synth(\recordInput, [
\buf, ~liveBuf.bufnum,
\inChan, ~inputChan,
\monitor, 0.0
]);
"Recording gestartet.".postln;
};
~stopRecording = {
if(~recSynth.notNil, { ~recSynth.free; });
~buf = ~liveBuf;
"Recording gestoppt. Live-Material jetzt in ~buf.".postln;
~buf.query;
};
// -----------------------------
// TASKS
// -----------------------------
~taskB = Task({
var waitTime, durVal, erosVal, brightVal, freezeProb, ampVal, mode;
loop {
if((~section != \B) or: { ~bufReady.().not }) {
0.5.wait;
} {
waitTime = (~densCtl.linlin(0, 1, 1.6, 0.3)).clip(0.2, 2.0);
durVal = (~fragCtl.linlin(0, 1, 0.12, 0.45)).clip(0.08, 0.6);
erosVal = (~erosCtl.linlin(0, 1, 0.0, 0.45)).clip(0, 0.6);
brightVal = (~brightCtl.linlin(0, 1, 0.25, 0.85)).clip(0, 1);
freezeProb = (~freezeCtl.linlin(0, 1, 0.0, 0.18)).clip(0, 0.25);
ampVal = 0.18 * ~masterAmp;
mode = [\frag, \frag, \shadow, \freeze].wchoose([0.45, 0.25, 0.2, 0.1]);
if(mode == \freeze) {
Synth(\freezeLoop, [
\buf, ~buf.bufnum,
\ampBus, ~ampBusB.index,
\start, ~chooseRegion.(),
\dur, durVal * 0.8,
\hold, (~freezeCtl.linlin(0, 1, 0.4, 1.8)).clip(0.3, 2.0),
\amp, ampVal * 0.7
]);
};
if(mode == \frag) {
Synth(\erodedFrag, [
\buf, ~buf.bufnum,
\ampBus, ~ampBusB.index,
\start, ~chooseRegion.(),
\dur, durVal,
\erosion, erosVal,
\bright, brightVal,
\amp, ampVal
]);
};
if(mode == \shadow) {
Synth(\erodedFrag, [
\buf, ~buf.bufnum,
\ampBus, ~ampBusB.index,
\start, ~chooseRegion.(),
\dur, durVal * 1.2,
\erosion, 0.05,
\bright, brightVal * 0.8,
\amp, ampVal * 0.6
]);
};
(waitTime * [0.8, 1.0, 1.4].choose).wait;
}
}
});
~taskC = Task({
var rates = [0.5, 0.75, 1.0, 1.25, 1.5, 2.0];
loop {
if((~section != \C) or: { ~bufReady.().not }) {
0.5.wait;
} {
Synth(\reconstructVoice, [
\buf, ~buf.bufnum,
\ampBus, ~ampBusC.index,
\start, ~chooseRegion.(),
\dur, rrand(0.18, 0.55),
\rate, rates.choose,
\bright, (~brightness * 0.9 + 0.1).clip(0,1),
\amp, (0.32 + (~density * 0.22)) * ~masterAmp
]);
rrand(0.12, (0.7 - (~density * 0.3)).max(0.08)).wait;
}
}
});
~taskD = Task({
loop {
if((~section != \D) or: { ~bufReady.().not }) {
0.5.wait;
} {
Synth(\memoryResidue, [
\buf, ~buf.bufnum,
\ampBus, ~ampBusD.index,
\start, ~chooseRegion.(),
\dur, rrand(0.3, 0.8),
\bright, ~brightness,
\amp, (0.12 + ((1 - ~density) * 0.08)) * ~masterAmp
]);
rrand(0.5, 1.5).wait;
}
}
});
// -----------------------------
// PLAY
// -----------------------------
~playA = {
if(~bufReady.().not) {
"Kein Material in ~buf.".postln;
} {
~section = \A;
"Section A".postln;
Synth(\plainPlayer, [
\buf, ~buf.bufnum,
\ampBus, ~ampBusA.index,
// DRY weniger durch z.B. 0.35
\amp, 0.20 * ~masterAmp
]);
SystemClock.sched(0.2, {
Synth(\shadowTrace, [
\buf, ~buf.bufnum,
\ampBus, ~ampBusA.index,
// WET mehr durch z.B. 0.24
\amp, 0.50 * ~masterAmp,
\bright, ~brightness
]);
nil
});
}
};
~playB = {
if(~bufReady.().not) {
"Kein Material in ~buf.".postln;
} {
~section = \B;
"Section B".postln;
~taskB.play;
}
};
~playC = {
if(~bufReady.().not) {
"Kein Material in ~buf.".postln;
} {
~section = \C;
"Section C".postln;
~taskC.play;
}
};
~playD = {
if(~bufReady.().not) {
"Kein Material in ~buf.".postln;
} {
~section = \D;
"Section D".postln;
~taskD.play;
}
};
// -----------------------------
// STOP / CONTROL
// -----------------------------
~stopAll = {
~taskB.stop;
~taskC.stop;
~taskD.stop;
if(~recSynth.notNil, { ~recSynth.free; });
s.freeAll;
"All stopped".postln;
};
~setAmpA = { |x| ~ampA = x.clip(0,1); ~ampBusA.set(~ampA); ("ampA: " ++ ~ampA).postln; };
~setAmpB = { |x| ~ampB = x.clip(0,1); ~ampBusB.set(~ampB); ("ampB: " ++ ~ampB).postln; };
~setAmpC = { |x| ~ampC = x.clip(0,1); ~ampBusC.set(~ampC); ("ampC: " ++ ~ampC).postln; };
~setAmpD = { |x| ~ampD = x.clip(0,1); ~ampBusD.set(~ampD); ("ampD: " ++ ~ampD).postln; };
~setDens = { |x| ~densCtl = x.clip(0,1); ("densCtl: " ++ ~densCtl).postln; };
~setEros = { |x| ~erosCtl = x.clip(0,1); ("erosCtl: " ++ ~erosCtl).postln; };
~setBright = { |x| ~brightCtl = x.clip(0,1); ("brightCtl: " ++ ~brightCtl).postln; };
~setFrag = { |x| ~fragCtl = x.clip(0,1); ("fragCtl: " ++ ~fragCtl).postln; };
~setFreeze = { |x| ~freezeCtl = x.clip(0,1); ("freezeCtl: " ++ ~freezeCtl).postln; };
"Human Robotica loaded.".postln;
"Start: s.boot;".postln;
"Sample: ~loadSample.(\"/voller/pfad.wav\")".postln;
"Live: ~prepareLiveBuffer.(); ~startRecording.(); ... ~stopRecording.();".postln;
"Dann: ~playA.() / ~playB.() / ~playC.() / ~playD.()".postln;
});
)
// Ende Startblock
//-----------------------------------------------//
// Workflow für Ablauf nach Partitur und ad lib. //
//-----------------------------------------------//
s.boot;
// -->> gesamten Startblock laden:
// markieren von Z.1 bis "Ende Startblock" -->> Cmd + Enter
//-----------------------//
// Live-Einspiel-Option: //
~prepareLiveBuffer.();
~startRecording.();
// Posaune spielt
~stopRecording.();
// direkt weiter bei playB.();
//----------------------//
// Sample Option //
~loadSample.("/Users/junicorn/Documents/Komposition/Samples/instrument/trombone/choral_trombone.wav");
~playA.();
// weiter mit z.B. playB.();
//---------------------------//
// Steuerung Live-Elektronik //
//---------------------------//
~setAmpB.(1.0);
~playB.();
~setDens.(0.15);
~setEros.(0.1);
~setBright.(0.3);
~setFrag.(0.2);
~setFreeze.(0.05);
~setAmpC.(0.0);
~playC.();
~setAmpB.(0.6);
~setAmpC.(0.4);
~setAmpB.(0.3);
~setAmpC.(0.8);
~setAmpB.(0.0);
~setAmpC.(1.0);
~stopAll.();
s.options.outDevice = "Scarlett 8i6 USB";
s.quit;
Media & Repository
Social Line
About / CV
LinkedIn
Instagram
Pedagogy
Music & Code for Youth
Azubi Digital Akademie
Sounding Images
Things Should Sound
Musiktheater Digital
Shift
Art
Highschool Teaching
Code
Compositions
Music & Performance
Concerts
Performances
Human Robotica
Machine Learning for Arts
Digital Tools for Artists
Curatorial Practice
Erasmus+ Curating Concerts
Naxos Hallenkonzerte
Hopes & Losses