Sigma

Sigma environment with four modules loaded

A very small, free environment for easily improvising computer music.

Includes sample convolution, granular sample playback, pitched sample playback, and FM oscillators. Each module is a small GUI wrapper around a simple built-in UGen, and any number of modules may be spawned to evolve the sound texture over time.

Written in SuperCollider in 2023, loosely inspired by ppooll.

Used in reverb hunting (2023, Final Tapes), a collaborative recording with SuperCollider, portable guitar amp, and trumpet in spaces which feature interesting natural reverbs around Austin, TX.

Getting Started

Download the file above, or paste the following into SuperCollider, select all, then type Cmd-Enter to run.

Be sure to change the path in bufferEntries to point to a folder of samples!

(
// sigma
// by: Colin Drake

var w, bufferEntries, buffers, names, cnvNum = 0, grnNum = 0, oscNum = 0, plyNum = 0, fbNum = 0;

// !!!!
// !!!!
// !!!!

// Change path to folder of samples here...
bufferEntries = PathName("/Users/colin/Music/Samples/sigma").entries;

// !!!!
// !!!!
// !!!!

names = bufferEntries.collect({ |p| p.fileName });
buffers = bufferEntries.collect({ |p| Buffer.readChannel(s, p.asAbsolutePath, channels: [0]) });

// Setup main GUI...
Window.closeAll;
w = Window("sigma", Rect(500, 500, 450, 70), false);

//// CONVOLUTION ////

Button(w, Rect(10, 10, 100, 20)).states_([["cnv", Color.black, Color.white]]).action_({
	var cnv, w, samples1, samples2, amp, ampLabel;

	cnvNum = cnvNum + 1;

	cnv = { |inputBuf, kernelBuf, inputRate = 1, kernelRate = 1, amp = 0|
		var inputPlayer = PlayBuf.ar(1, inputBuf, inputRate, loop: 1);
		var kernelPlayer = PlayBuf.ar(1, kernelBuf, kernelRate, loop: 1);
		Limiter.ar(Convolution.ar(inputPlayer, kernelPlayer, 1024) * amp.lagud * 0.2 ! 2);
	}.play;

	w = Window("cnv" + cnvNum, Rect(500, 500, 270, 140), false).onClose_({
		cnv.stop;
		cnv.free;
	});

	samples1 = ListView(w, Rect(10, 10, 120, 100))
	    .items_(names)
	    .action_({ |samp| cnv.set(\inputBuf, buffers[samples1.value]); })
	    .value_(0);

	samples2 = ListView(w, Rect(140, 10, 120, 100))
	    .items_(names)
	    .action_({ |samp| cnv.set(\kernelBuf, buffers[samples2.value]); })
	    .value_(0);

	amp = Slider(w, Rect(70, 115, 190, 20))
	    .action_({ cnv.set(\amp, amp.value); })
	    .value_(0);
	ampLabel = StaticText(w, Rect(10, 115, 60, 20));
	ampLabel.string = "amp";

	w.front;
});

//// GRANULAR ////

Button(w, Rect(120, 10, 100, 20)).states_([["grn", Color.black, Color.white]]).action_({
	var grn, w, samples, rate, rateLabel, duration, durationLabel, amp, ampLabel, pitch, pitchLabel, seek, seekLabel;

	grnNum = grnNum + 1;

	grn = { |buf = 0, rate = 4, duration = 1, pitch = 1, seek = 0, amp = 0|
		GrainBuf.ar(2, Dust.kr(rate), duration, buf, pitch, seek, pan: LFNoise0.kr(1) * 0.5) * amp.lagud * 0.2;
	}.play;

	w = Window("grn " + grnNum, Rect(500, 500, 360, 120), false).onClose_({
		grn.stop;
		grn.free;
	});

	samples = ListView(w, Rect(10, 10, 120, 100))
	    .items_(names)
	    .action_({ |samp| grn.set(\buf, buffers[samples.value]); })
	    .value_(0);

	seek = Slider(w, Rect(200, 10, 150, 20))
	    .action_({ grn.set(\seek, seek.value); })
	    .value_(0);
	seekLabel = StaticText(w, Rect(140, 10, 60, 20));
	seekLabel.string = "seek";

	rate = Slider(w, Rect(200, 30, 150, 20))
	    .action_({ grn.set(\rate, rate.value.linlin(0, 1, 0.1, 200)); })
	    .value_(0.1);
	rateLabel = StaticText(w, Rect(140, 30, 60, 20));
	rateLabel.string = "trig rate";

	duration = Slider(w, Rect(200, 50, 150, 20))
	    .action_({ grn.set(\duration, duration.value.linlin(0, 1, 0.05, 2)); })
	    .value_(1);
	durationLabel = StaticText(w, Rect(140, 50, 60, 20));
	durationLabel.string = "duration";

	pitch = Slider(w, Rect(200, 70, 150, 20))
	    .action_({ grn.set(\pitch, pitch.value.linlin(0, 1, 0.01, 4)); })
	    .value_(0);
	pitchLabel = StaticText(w, Rect(140, 70, 60, 20));
	pitchLabel.string = "pitch";

	amp = Slider(w, Rect(200, 90, 150, 20))
	    .action_({ grn.set(\amp, amp.value); })
	    .value_(0);
	ampLabel = StaticText(w, Rect(140, 90, 60, 20));
	ampLabel.string = "amp";

	w.front;
});

//// OSCILLATOR ////

Button(w, Rect(230, 10, 100, 20)).states_([["osc", Color.black, Color.white]]).action_({
	var osc, w, freq, freqLabel, modRate, modRateLabel, modAmnt, modAmntLabel, amp, ampLabel;

	oscNum = oscNum + 1;

	osc = { |freq = 440, modRate = 0.3, modAmnt = 0, amp = 0|
		SinOsc.ar(freq.lagud + (modAmnt * SinOsc.ar(modRate))) * 0.1 * amp.lagud ! 2
	}.play;

	w = Window("osc" + oscNum, Rect(500, 500, 270, 100), false).onClose_({
		osc.stop;
		osc.free;
	});

	freq = Slider(w, Rect(70, 10, 190, 20))
	    .action_({ osc.set(\freq, freq.value.linlin(0, 1, 1, 1000)); })
	    .value_(0.5);
	freqLabel = StaticText(w, Rect(10, 10, 60, 20));
	freqLabel.string = "freq";

	modRate = Slider(w, Rect(70, 30, 190, 20))
	    .action_({ osc.set(\modRate, modRate.value.linlin(0, 1, 0.1, 200)); })
	    .value_(0);
	modRateLabel = StaticText(w, Rect(10, 30, 60, 20));
	modRateLabel.string = "mod rate";

	modAmnt= Slider(w, Rect(70, 50, 190, 20))
	    .action_({ osc.set(\modAmnt, modAmnt.value.linlin(0, 1, 0, 200)); })
	    .value_(0);
	modAmntLabel = StaticText(w, Rect(10, 50, 60, 20));
	modAmntLabel.string = "mod amt";

	amp = Slider(w, Rect(70, 70, 190, 20))
	    .action_({ osc.set(\amp, amp.value); })
	    .value_(0);
	ampLabel = StaticText(w, Rect(10, 70, 60, 20));
	ampLabel.string = "amp";

	w.front;
});

//// PLAYER ////

Button(w, Rect(340, 10, 100, 20)).states_([["ply", Color.black, Color.white]]).action_({
	var ply, w, samples, pitch, pitchLabel, amp, ampLabel;

	plyNum = plyNum + 1;

	ply = { |buf, pitch = 1, amp = 0|
		PlayBuf.ar(1, buf, pitch, loop: 1) * 0.5 * amp.lagud ! 2
	}.play;

	w = Window("ply" + plyNum, Rect(500, 500, 400, 120), false).onClose_({
		ply.stop;
		ply.free;
	});

	samples = ListView(w, Rect(10, 10, 120, 100))
	    .items_(names)
	    .action_({ |samp| ply.set(\buf, buffers[samples.value]); })
	    .value_(0);

	pitch = Slider(w, Rect(200, 10, 190, 20))
	    .action_({ ply.set(\pitch, pitch.value.linlin(0, 1, -2, 2)); })
	    .value_(0.75);
	pitchLabel = StaticText(w, Rect(140, 10, 60, 20));
	pitchLabel.string = "pitch";

	amp = Slider(w, Rect(200, 30, 190, 20))
	    .action_({ ply.set(\amp, amp.value); })
	    .value_(0);
	ampLabel = StaticText(w, Rect(140, 30, 60, 20));
	ampLabel.string = "amp";

	w.front;
});

//// SCOPE ////

Button(w, Rect(10, 40, 200, 20)).states_([["scope", Color.black, Color.white]]).action_({
	s.scope;
});

w.front;
)