Created
March 17, 2021 11:37
-
-
Save julianrubisch/7abd957c55ab978514139bfa51c0a669 to your computer and use it in GitHub Desktop.
Supercollider Variable Length Looper
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
( | |
Server.killAll; | |
~gtranspose = 0; | |
~rec_end = 0; | |
~stretch = 1.0; | |
~density = 0.0; | |
~num_channels = 12; | |
~pitch_env_amp = 0.0; | |
~pitch_range = 0.0; | |
~pitch_mod_amp = 0.0; | |
~create_notes = { | |
|variation_count=10| | |
Array.fill(variation_count, {[ | |
Array.fill(~num_channels, {[0, 2, 3, 7, 4, 2].choose}).scramble | |
]}) | |
}; | |
~create_transpositions = { | |
|variation_count=10| | |
Array.fill(variation_count, {[ | |
Array.fill(~num_channels, {[0, 12, -12, 5, 7, -7].wchoose([0.5, 0.15, 0.15, 0.1, 0.5, 0.5])}).scramble | |
]}) | |
}; | |
~create_rests = { | |
|variation_count=10| | |
Array.fill(variation_count, {[ | |
Array.fill(~num_channels, {[1, Rest(0)].choose}).scramble | |
]}) | |
}; | |
~create_rqs = { | |
|variation_count=10| | |
Array.fill(variation_count, Array.exprand(~num_channels, 10.0, 1.0)) | |
}; | |
ServerTree.removeAll; | |
s.newBusAllocators; | |
s.options.memSize = 2.pow(20); // ca. 1GB | |
MIDIClient.init; | |
MIDIIn.connectAll; | |
MIDIFunc.trace(false); | |
// note 68 record | |
MIDIdef.noteOn(\rec_on, { | |
|val, num, chan, src| | |
~rec = Synth(\rec, [\bufnum, b]); | |
}, 68); | |
MIDIdef.noteOff(\rec_off, { | |
|val, num, chan, src| | |
~rec.set(\rec, 0); | |
}, 68); | |
// note 64 launch | |
MIDIdef.noteOn(\pb_launch, { | |
// if(~pb.isPlaying, {~pb.set(\t_stop, 1)}); | |
if(Pbindef(\pattern).isPlaying, { Pbindef(\pattern).stop; }); | |
// ~pb = Synth(\playback, [\bufnum, b, \end, ~rec_end, \rate, ~rate]).register; | |
Pbindef(\pattern, \end, ~rec_end).play; | |
}, 64); | |
// note 65 stop | |
MIDIdef.noteOn(\pb_stop, { | |
// ~pb.set(\t_stop, 1); | |
Pbindef(\pattern).stop; | |
}, 65); | |
// CC 112 transpose | |
MIDIdef.cc(\transpose, { | |
|val, num, chan, src| | |
// ~rate = val.linlin(0, 127, -2.0, 2.0); | |
~gtranspose = val.linlin(0, 127, -24, 24).round; | |
if(Pbindef(\pattern).isPlaying, { Pbindef(\pattern, \gtranspose, ~gtranspose) }); | |
}, 112); | |
// CC 32 "stretch" | |
MIDIdef.cc(\stretch, { | |
|val, num, chan, src| | |
~stretch = val.asFloat.linexp(0.0, 127.0, 1.0, 100.0); | |
if(Pbindef(\pattern).isPlaying, { Pbindef(\pattern, \stretch, ~stretch) }); | |
}, 32); | |
// CC 28 "pitch_range" | |
MIDIdef.cc(\pitch_range, { | |
|val, num, chan, src| | |
~pitch_range = val.asFloat.linexp(0.0, 127.0, 1.0, 2.0) - 1.0; | |
if(Pbindef(\pattern).isPlaying, { Pbindef(\pattern, \pitch_range, ~pitch_range) }); | |
}, 28); | |
// CC 10 "pitch_mod_amp" | |
MIDIdef.cc(\pitch_mod_amp, { | |
|val, num, chan, src| | |
~pitch_mod_amp = val.asFloat.linlin(0.0, 127.0, 0.0, 10.0); | |
if(Pbindef(\pattern).isPlaying, { Pbindef(\pattern, \pitch_mod_amp, ~pitch_mod_amp) }); | |
}, 10); | |
// CC 46 "density" / Fader 1 on Channel 0 | |
MIDIdef.cc(\density, { | |
|val, num, chan, src| | |
~density = val.asFloat.linlin(0.0, 127.0, 0.0, 1.0); | |
if(Pbindef(\pattern).isPlaying, { | |
~density.postln; | |
Pbindef(\pattern, \amp, Pseq([1.0, Pgeom(0.7, 0.9, 5)], inf) * Pif(Pfunc({ ~density.coin }), Prand(~create_rests.(100), inf), Pseq([[Rest(0)!~num_channels]], inf))) | |
}); | |
}, 46, 0); | |
// CC 46 "pitch envelope amplitude" / Fader 2 on Channel 1 | |
MIDIdef.cc(\pitch_env_amp, { | |
|val, num, chan, src| | |
~pitch_env_amp = val.linlin(0, 127, -24, 24).postln; | |
if(Pbindef(\pattern).isPlaying, { Pbindef(\pattern, \pitch_env_amp, ~pitch_env_amp) }); | |
}, 46, 1); | |
s.waitForBoot({ | |
s.freeAll; | |
Buffer.freeAll; | |
s.sync; | |
SynthDef(\rec, { | |
|in=0, bufnum=0, rec=1| | |
var sig = SoundIn.ar(in), | |
stopTrig = (rec <= 0), | |
phase = Phasor.ar(0, 1, 0, BufFrames.kr(bufnum)); | |
// RecordBuf.ar(sig, bufnum, doneAction: 2, loop: 0); replaced with BufWr | |
BufWr.ar(sig, bufnum, phase); | |
SendReply.ar(K2A.ar(stopTrig), '/recEnded', phase.poll); | |
FreeSelf.kr(stopTrig); | |
}).add; | |
// see https://scsynth.org/t/looper-with-a-variable-length/818/6 | |
OSCdef(\ended, { |msg| | |
// msg is ['/recEnded', nodeID, replyID, value0...] | |
// so the data point is msg[3] | |
~rec_end = msg[3]; // save ending frame index | |
if(Pbindef(\pattern).isPlaying, { Pbindef(\pattern, \end, ~rec_end) }) | |
}, '/recEnded', s.addr); | |
SynthDef(\playback, { | |
|out=0, bufnum=0, start=0, end=44100, loop=0, t_stop=0, t_trig=0, rate=1, pan=0, atkcrv=1, relcrv=(-10), gtranspose=0| | |
var sig, phasor, env, t_gate; | |
var dur = \dur.kr / \tempo.kr(1); | |
var atk = \atk.kr(0.002); | |
var sus = \sus.kr(0); | |
var rel = \rel.kr(0.2); | |
end.poll; | |
t_gate = \t_gate.kr(Array.fill(~num_channels, 1)); | |
env = Env.new([0, 1, 1, 0], [atk, sus, rel], [atkcrv, 0, relcrv]).kr(gate: t_gate, timeScale: dur * \env_stretch.kr(1.0), doneAction: 2); | |
phasor = Phasor.ar( | |
trig: t_trig, | |
rate: BufRateScale.kr(bufnum) * (\note.kr(Array.fill(~num_channels, 0)) * \pitch_range.kr(~pitch_range) | |
+ gtranspose | |
+ Env.perc(atk, rel, \pitch_env_amp.kr(0.0), -12).ar(gate: t_gate) | |
+ LFNoise1.ar(6, \pitch_mod_amp.kr(0.0)) | |
).midiratio, | |
start: start, | |
end: end, | |
resetPos: start | |
); | |
sig = BufRd.ar(1, bufnum, phasor, loop); | |
sig = BLowPass.ar(sig, Env([\filter_freq.kr(200), Rand(5000, 3000) + \filter_freq.kr(200), Rand(300, 0) + \filter_freq.kr(200)], [atk, rel], [3, 0]).kr(gate: t_gate, timeScale: dur), rq: \rq.kr(Array.fill(~num_channels, 1))); | |
sig = sig * env * (\amp.kr(Array.fill(~num_channels, 0.5)) * (-3*~num_channels).dbamp); | |
sig = Splay.ar(sig); | |
Out.ar(out, sig); | |
}).add; | |
s.sync; | |
b = Buffer.alloc(s, s.sampleRate * 10.0, 1); | |
s.sync; | |
}) | |
) | |
( | |
Pbindef(\pattern, | |
\instrument, \playback, | |
\bufnum, b, | |
\tempo, Pfunc {thisThread.clock.tempo}, | |
\dur, Pwrand([1, 2, 4, 6, 8, 12], [80, 20, 5, 3, 2, 1].normalizeSum, inf) / 8, // in beats, (default tempo clock) | |
\note, Prand(~create_notes.(100), inf) + Prand(~create_transpositions.(100), inf), | |
\gtranspose, ~gtranspose, | |
\amp, Pseq([1.0, Pgeom(0.7, 0.9, 5)], inf) * Pif(Pfunc({ ~density.coin }), Prand(~create_rests.(100), inf), Pseq([[Rest(0)!~num_channels]], inf)), | |
\start, 0, //Pbrown(0, ~rec_end * 0.75, 1000, inf), | |
\end, ~rec_end, | |
\filter_freq, Pseq([60, 66, 70, 72].collect {|midinote| midinote.midicps}, inf), | |
\rq, Prand(~create_rqs.(100), inf), // multichannel expand | |
\atkcrv, Pseq(Array.interpolation(64, -5, -3), inf), // increasing ramp | |
\atk, Pexprand(0.005, 0.5, inf), | |
\rel, Pexprand(0.2, 0.6, inf), | |
\sus, Pexprand(0.02, 0.15, inf), | |
\env_stretch, ~stretch, | |
\pitch_env_amp, ~pitch_env_amp, | |
\pitch_range, ~pitch_range, | |
\pitch_mod_amp, ~pitch_mod_amp | |
); | |
) | |
MIDIFunc.trace(true); | |
TempoClock.default.tempo_(1.2); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment