Recent livecoding in SuperCollider

Over the winter break I’ve been spending some time working on my livecoding/algorave setup in SuperCollider. Here’s a quick practice run, this is how things are going at the moment.

The most recent idea here is the \warp synth, a granulator slowly reading through a choice of soundfiles. In this particular run, I think the .choose threw up a fragment of a Stokowski Bach transcription https://archive.org/details/J.S.BACH-OrchestralTranscriptions-NEWTRANSFER and perhaps a bit of the theme tune from The IT Crowd as well. A nice background wash of sound behind the rhythmic stuff. For the latter, the samples in the first half of the video are various from here http://machines.hyperreal.org/manufacturers/ and in the second half of the run, after I exectue ~changesamples, from here http://theremin.music.uiowa.edu/MIS.html and here http://www.philharmonia.co.uk/explore/sound_samples.

The synths I’m using and my initialisation file is up on GitHub at https://github.com/tedthetrumpet/supercollider.

Rave the Space

Last night I gave a performance called ‘Rave the Space’ at Stereo in Glasgow, part of a series of events called INTER run by Iain Findlay-Walsh ‘creating a focused, public listening context for deep experiments in / with sound’.

My proposal was to ‘perform the soundscape of the venue through the medium of livecoding’. What I did was to visit the venue the day before, at a quiet time, and make some recordings – a fairly typical basement club/rock venue, so I was able to wander onstage, through dressing rooms, behind the bar, into the toilets etc, all the while recording both the ambience and, in some cases, tapping or hitting objects of particular interest – there was a group of CO2 cylinders that were particularly nice.

On the morning of the event, I roughly levelled these recordings, discarded uninteresting ones, cut out handling noise, mobile phone interference, and initial and terminal clicks from the recorder. This left me with nine recordings, each about a minute long.

I decided to challenge myself by doing as little rehearsal for the performance as possible. I had one synth precoded, a slicing sampler. All this does is to take an audio file and play back one of n slices: in the case of these roughly 60 second long files, I used n=64. On previous occasions when I’ve done livecoding/algorave with found sounds, I’ve gone through the source audio files carefully in Audacity, looking for particular short sounds that I can then isolate and shape into something resembling a drum hit, then performed with those sounds in place of drum sounds.

The slicing approach used here is deliberately less controlled: it’s a matter of luck what sound falls where as the point at which the file is sliced is quite arbitrary, perhaps falling just on ambience, or half-way through a percussive noise.

The performance was only to be ten minutes: which is not a lot on my timescale of livecoding in SuperCollider! I decided to start with a blank screen: in retrospect, I could have got to better musical gestures faster if I’d had maybe ten or a dozen lines precoded. Nevertheless, in this sit-down and concentrate atmosphere, the blank-page start was quite intruiging for the audience, I think.

The performance mostly went well, although there was one of those moments where I had what looked like a correctly typed line that evaluated correctly, that did not seem to be doing anything! I still can’t figure out what I was doing wrong.

I’m pleased with this idea and intend to repeat it, particuarly the site-specific approach to gathering sounds.

Here’s the code, not much to see here:

(//setup
s.waitForBoot{};
SynthDef(\sl, { |out, gate=1, buf, sig, slices=16, slice=0, freq = 261.6255653006, amp=0.1|
var myenv, env, start, len, basefreq = 60.midicps, rate;
rate = freq / basefreq;
len = BufFrames.kr(buf);
start = (len / slices * slice);
myenv = Env.asr(attackTime: 0.01, sustainLevel: 1, releaseTime: 0.1);
sig = PlayBuf.ar(2, buf, BufRateScale.kr(buf) * rate, startPos: start);
env = EnvGen.kr(myenv, gate, doneAction: 2);
Out.ar(out, sig * env * amp)
}).add;
t = TempoClock(140/60).permanent_(true);
u = TempoClock(140/60 * 2/3).permanent_(true);
Pbindef.defaultQuant_(4);
Pdefn.defaultQuant_(4);
)
(
~paths = [
"/Users/jsimon/Music/SuperCollider Recordings/stereoglasgow/bar.aiff", // 0
"/Users/jsimon/Music/SuperCollider Recordings/stereoglasgow/c02ambience.aiff", // 1
"/Users/jsimon/Music/SuperCollider Recordings/stereoglasgow/cafe.aiff", // 2
"/Users/jsimon/Music/SuperCollider Recordings/stereoglasgow/co2.aiff", // 3
"/Users/jsimon/Music/SuperCollider Recordings/stereoglasgow/corner.aiff", // 4
"/Users/jsimon/Music/SuperCollider Recordings/stereoglasgow/lane.aiff", // 5
"/Users/jsimon/Music/SuperCollider Recordings/stereoglasgow/seatingbank.aiff", // 5
"/Users/jsimon/Music/SuperCollider Recordings/stereoglasgow/space.aiff", // 6
"/Users/jsimon/Music/SuperCollider Recordings/stereoglasgow/stage.aiff", // 7
"/Users/jsimon/Music/SuperCollider Recordings/stereoglasgow/stairs1.aiff", // 8
"/Users/jsimon/Music/SuperCollider Recordings/stereoglasgow/stairs2.aiff" // 9
]
)
~thebuf = Buffer.read(s, ~paths[7]);
~thebuf.play
//
Pbindef(\x, \instrument, \sl, \buf, ~thebuf, \slices, 64)
Pbindef(\x).play(t)
Pbindef(\x, \slice, 0)
Pbindef(\x, \slice, 64.rand)
Pbindef(\x, \slice, Pwhite(0,63,inf))
Pbindef(\x, \legato, 1/4)
Pbindef(\x, \dur, 1/4)
Pbindef(\x, \note, Pwhite(0,12,inf))
//
Pbindef(\y, \instrument, \sl, \buf, ~thebuf, \slices, 64)
Pbindef(\y).play(u)
Pbindef(\y, \slice, 0)
Pbindef(\y, \slice, 64.rand)
Pbindef(\y, \legato, 4)
Pbindef(\y, \dur, 1/2)
Pbindef(\y, \note, Pwhite(-12,12,inf))
t.sched(t.timeToNextBeat(4), {u.sync(120/60, 10)});

Layering visuals with SuperCollider

When I was at emfcamp last week, I saw a couple of instances of people layering up visuals with their code. Claudius Maximus had that going with his clive system, SonicPi (and Gibber??) can do it out of the box, and Shelly Knotts  had some sort of setup for (I think?) doing it completely within SuperCollider, with the cool idea of a webcam pointing down at her hands on the keyboard.

After a bit of thought, I’ve come up with this, just a still for now:

sclayervisuals.png

How this works: I used a $10 utility called ScreenCaptureSyphon that can amongst other things grab an application window and send it into Syphon. Then, Resolume Arena runs as a Syphon client, which lets me do almost anything including, as in the shot below, pull in the webcam and colorize. Not tried it yet, but Arena exposes its interface to OSC, so should in theory be possible to script visual changes from the SuperCollider IDE.

A reasonably concinnitous hack, if I say so myself. (MInd you, it’s the first thing I’ve ever done with my MacBook Air that turns the fan on full blast the whole time!)

Getting my fingers burned

Sooner or later, I’m going to have to get back to the ‘ol soldering iron:

 

JS_Synth-ttt01

I have a plan: to reconstruct the PE Minisonic you can see above, that I made while I was in school. I still have some of the boards…

Here’s my screen

Show us your screens! Ok, well at last maybe I’m ready. Here’s five minutes or so of me improvising in SuperCollider that’s not as embarrassing as some of my other attempts:

The code is on GitHub if anyone is madly interested.

First public livecode

Last night I stumbled into my first public outing of some livecoding I’ve been working on in SuperCollider. The context was an improvisation night called In Tandem run by Bruce Wallace at the Academy of Music and Sound in Glasgow. I hadn’t intended to play, as I really don’t feel I’m ready yet, but I had my laptop and cables with me, they had a projector, so…!

I was jamming along with three other people, on bass, guitar and analog synth. It all went by in a blur, but everyone there seemed to think what I was doing was ok – mostly making grooves out of a random collection of drum samples, but running some algorithmically chosen chords as well.

The code is below: this is my screen exactly as I left it at the end of the night, mistakes and all. Although Toplap say ‘show us your screens’, they don’t say ‘show us your code’, but… it seems the right thing to do.

// the end!
// they still going
// if you're curious, this is SuperCollider
// musci programming language
// writing code live is called, er, livecoding
// i'm just starting out
"/Users/jsimon/Music/SuperCollider Recordings/hitzamples/".openOS;

(
s.waitForBoot{
Pdef.all.clear; // clear things out
~hitzpath="/Users/jsimon/Music/SuperCollider Recordings/hitzamples/"; // a folder of samples
~hbufs = (~hitzpath ++ "*.aiff").pathMatch.collect({ |i| Buffer.read(s, i)}); // samples into an array of buffers
t = TempoClock(140/60).permanent_(true); // tempo 140 bpm
u = TempoClock(140/60 * 2/3).permanent_(true); // tempo 140 bpm * 2/3
SynthDef(\bf, {|out=0 buf=0 amp=0.1 freq=261.6255653006|
var sig = PlayBuf.ar(2, buf, BufRateScale.kr(buf) * freq/60.midicps, doneAction:2);
Out.ar(out, sig * amp)
}).add; // this whole chunk defines a synth patch that plays samples
};

// Pdef.all.clear;
//"/Users/jsimon/Music/SuperCollider Recordings/".openOS;
// t.sync(140/60, 16);
)

(instrument: \bf, \buf: ~hbufs.choose).play; // play an event using the synth called \bf
// pick a randoms sample from the array
(instrument: \bf, \buf: ~z).play;
~z = ~hbufs.choose;

t.sync(140/60, 32); // gradual tempo changes possible
u.sync(140/60 * 2/3, 16);
v.sync(140/60 * 5/3, 16);

Pbindef(\x, \instrument, \bf, \buf, ~hbufs.choose).play(t).quant_(4);
Pbindef(\y, \instrument, \bf, \buf, ~hbufs.choose).play(u).quant_(4);
Pbindef(\z, \instrument, \bf, \buf, ~hbufs.choose).play(v).quant_(4);
Pbindef(\z, \instrument, \bf, \buf, ~hbufs.choose).play(v).quant_(4);
~g1 = {~hbufs.choose}!16; // choose sixteen samples at random = one bar full
~g2 = {~hbufs.choose}!16;
Pbindef(\x, \buf, Pseq(~g1, inf)); // play those sixteen samples chosen
Pbindef(\x, \buf, Pseq(~g2, inf)); // different sixteen, so, a variation.
Pbindef(\x, \dur, 0.5);
~d1 = {2.rand/10}!16;
~d2 = {2.0.rand/10}!16;
Pbindef(\x, \amp, Pseq(~d1, inf));
Pbindef(\x, \amp, 0.2);
Pbindef(\x, \note, Prand((-36..0), inf));
Pbindef(\x, \note, Pseq({(-24..0).choose}!16, inf)); // pitch each sample down by random amount
Pbindef(\x, \note, nil);
Pbindef(\x).resume;
Pbindef(\x).pause;
Pbindef(\z).pause;
Pbindef(\y).resume;

// hmm. blx diminished, that's just C major!
// was using \degree instead of \note, better sounds a bit more like messiaen now :)
~c = {var x = Scale.diminished2.degrees.scramble.keep(4).sort; x.insert(1,(x.removeAt(1)-12))};
// hexMajor thing also works beautifully now!
~c = {var x = Scale.hexMajor6.degrees.scramble.keep(4).sort; x.insert(1,(x.removeAt(1)-12))};

// next question might be changing \note, \dur and \root in a coordinated way
(
Pbindef(\k, \note, Pstutter(Prand([5,7,9,11,13]*2, inf), Pfunc(~c)),
\dur, 0.5,
\root, 3, // best option for feeling of key change
\amp, Prand((2..5)/70,inf)
).play(t);
)
Pbindef(\k).pause;
Pbindef(\k).pause;

Recursive synthesis

recursivesynth.jpg

I’ve been working for a while with an improvising setup that uses what is sometimes jokingly called ‘recursive synthesis’ – that is, plugging an effect unit back in to itself and experimenting with the no-input feedback sounds.

Today I’ve had some success with the next step in developing this system. I’ve written a SuperCollider patch that allows me to gate and pitchshift the feedback sounds, so that I can begin to find a way to play them musically using a keyboard. Here’s the very first run at playing this system: careful, some rather loud and uncontrolled noises here!

diverses.mp3

In the picture, you can see the work-in-progress setup. There’s a cheapo DigiTech RP55 guitar pedal feeding back through a small mixing desk. I’m using a swell pedal to contral some of the parameters of the various fx from the DigiTech, particularly sweeping the pitch of the ‘whammy’ and ‘pitch shift’ functions, set up in various presets. The mixing desk is not entirely necessary, but the tone controls are useful to have in the feedback loop.

Below is the code for the SuperCollider patch. As always, my thanks to the developers of this software, and all the help received from the community on the mailing list.
(
fork{
~velbus = Bus.control.set(1); // not using yet
s.sync;
SynthDef(\pitchin, { | midinote = 60, gate = 1, amp = 0.1 |
var in, sig, env, ratio, trans, shift, sel;
trans = midinote - 60;
in = SoundIn.ar([0,1]);
ratio = trans.midiratio;
shift = PitchShift.ar(in, pitchRatio: ratio, timeDispersion: 0.1);
sel = trans.abs > 0;
sig = Select.ar(sel, [in, shift]);
env = EnvGen.kr(Env.adsr, gate, doneAction: 2);
Out.ar(0, sig * env * amp * 37) // compensate for quiet;
}).add;
};
MIDIClient.init;
MIDIIn.connectAll;
~on.free;
~off.free;
~cc1.free;
~notes = Array.newClear(128); // array one slot per MIDI note
~on = MIDIFunc.noteOn({ |veloc, num, chan, src|
~notes[num] = Synth(\pitchin, [\midinote, num, \amp, veloc * 0.2/127]);
});
~off = MIDIFunc.noteOff({ |veloc, num, chan, src|
~notes[num].release;
});
~cc1 = MIDIFunc.cc({ |val|
val.postln;
~velbus.set(val/127*4);
}, 1, 0 ); // cc1 on channel 0 = midi channel 1, not using yet
)