Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save madskjeldgaard/e8460ee78a57f6411caa7222650d89bc to your computer and use it in GitHub Desktop.
Save madskjeldgaard/e8460ee78a57f6411caa7222650d89bc to your computer and use it in GitHub Desktop.
Notes for talk about making Composition Instruments at the Notam SuperCollider meetup May 2024
/*
Examples used in talk about composition-instruments at the Notam SuperCollider meetup May 22nd 2024.
*/
/**************************************************
Part 1: Using vanilla SuperCollider classes
**************************************************/
// Make a base pattern
// This example uses Pbindef to gradually build up the pattern for demonstration purposes, but normally I would just use Pdef
(
Pbindef(\myinstrument,
\degree, Pseq([0, 2, 4, 5, 7], inf),
\dur, 0.125
);
Pdef(\myinstrument).play;
)
// Add a parameter: Degree offset
(
Pdefn(\degreeOffset, 0);
Pbindef(\myinstrument, \degree, Pseq([0, 2, 4, 5, 7], inf) + Pdefn(\degreeOffset));
)
// Change parameter
Pdefn(\degreeOffset).source = 5;
Pdefn(\degreeOffset).source = -5;
Pdefn(\degreeOffset).source = 0;
// You can also use patterns as values here
Pdefn(\degreeOffset).source = Pwrand([0, 5], [0.75, 0.25], inf);
// And reset
Pdefn(\degreeOffset).source = 0;
// Changes can be quantized in time (mega useful for instruments!!!)
Pdefn(\degreeOffset).quant = 1; // In beats
// Now if we change it, the changes will only happen on the beat
// This works really well when changing a Pdefn with a controller for example
Pdefn(\degreeOffset).source = rrand(-5, 5);
/**************************************************
Part 2: Using Spec to design parameters
**************************************************/
// Specs can take normalized values in the range of 0.0 to 1.0 and convert them to whatever target range
// this is significant for making things easy to control using GUIs, controllers or sensors since these often output normalized values in this range, and they do not have to know about the specifics of the parameter they are controlling.
~myDegreeSpec = ControlSpec.new(minval: -10, maxval: 10, warp: \lin, step: 2);
// Random input values in the range of 0.0 to 1.0 to see the effect
~myDegreeSpec.map(rrand(0.0,1.0)).postln;
// Use it with Pdefn
Pdefn(\degreeOffset).source = ~myDegreeSpec.map(rrand(0.0,1.0));
// Let's make a small silly gui with one slider to exemplify this
(
var win = Window.new("degreez");
var slider = Slider
.new(win)
.action_({|obj|
var sliderval = obj.value;
var mapped = ~myDegreeSpec.map(sliderval);
"Slider setting value from % using spec: %".format(sliderval, mapped).postln;
Pdefn(\degreeOffset).source = mapped;
});
var layout = VLayout(slider);
win.layout = layout;
win.front();
)
/**************************************************
Part 3: Using Monolithic classes
**************************************************/
// Install dependencies
Quarks.install("https://github.com/madskjeldgaard/Monolithic");
// A parameter: The love child of Pdefn and Spec
d = Pparam.new(0, ~myDegreeSpec);
// .map takes values in 0.0-1.0 range
d.map(0.9)
// See the resulting state:
d.value.postln;
// Introducing Pctrldef
(
Pctrldef('thebestinstrument', {|ctrl|
var offset = ctrl['degreeOffset'];
Pbind(
\degree, Pseq([0, 2, 4, 5, 7], inf) + offset.trace,
\dur, 0.125
)
});
// Parameters
Pctrldef('thebestinstrument').addParam('degreeOffset', 0, ~myDegreeSpec);
Pctrldef('thebestinstrument').play;
)
// Set value using internal spec
Pctrldef('thebestinstrument').map('degreeOffset', rrand(0.0,1.0));
// Quantize sets the quantization of all parameters and the main event pattern itself
Pctrldef('thebestinstrument').quant_(1);
// Set new val with quantization
Pctrldef('thebestinstrument').map('degreeOffset', rrand(0.0,1.0));
// A bonus feature: You can use a list of values as params
(
Pctrldef('thebestinstrument', {|ctrl|
var offset = ctrl['degreeOffset'];
var scale = ctrl['scale'];
Pbind(
\scale, scale.trace,
\degree, Pseq([0, 2, 4, 5, 7], inf) + offset,
\dur, 0.125
)
});
// degreeOffset uses a spec
Pctrldef('thebestinstrument').addParam('degreeOffset', 0, ~myDegreeSpec);
// scale just uses an array of some scales
Pctrldef('thebestinstrument').addParam(
// Name
'scale',
// Default value
Scale.major,
// Possible choices
[Scale.major, Scale.minor, Scale.whole, Scale.majorPentatonic]
);
Pctrldef('thebestinstrument').play;
)
// Select a scale using values between 0.0 to 1.0:
Pctrldef('thebestinstrument').map('scale', rrand(0.0,1.0));
/**************************************************
Final: Make a composition machine
**************************************************/
(
Pctrldef('compmachine2000', {|ctrl|
var scale = ctrl['scale'];
var degree = ctrl['degree'];
var dur = ctrl['dur'];
var harmony = ctrl['harmony'];
Pbind(
\scale, scale,
\degree, degree,
\dur, dur,
\mtranspose, harmony
)
});
// degreeOffset uses a spec
Pctrldef('compmachine2000').addParam('degreeOffset', 0, ~myDegreeSpec);
// Scale
Pctrldef('compmachine2000').addParam(
// Name
'scale',
// Default value
Scale.major,
// Possible choices
[Scale.major, Scale.minor, Scale.whole, Scale.majorPentatonic]
);
// Degree
Pctrldef('compmachine2000').addParam(
// Name
'degree',
// Default value
0,
// Possible choices
[Pseq([0,2,4,5,7], inf), Pwhite(-7,7,inf), 0, 2, 4, 5]
);
// Dur
Pctrldef('compmachine2000').addParam(
// Name
'dur',
// Default value
1,
// Possible choices
[1.0, 0.25, 0.125, Pseq([0.25,Rest(0.25)], inf), Pexprand(0.1,1.0,inf)]
);
// Harmony: Used as offsets for the note in the degree
Pctrldef('compmachine2000').addParam(
// Name
'harmony',
// Default value
0,
// Possible choices
[
0,
[0,4],
[0,2,4],
[0,2,4,5],
]
);
Pctrldef('compmachine2000').quant_(1);
Pctrldef('compmachine2000').play;
)
// Randomly change degree
Pctrldef('compmachine2000').map('degree', rrand(0.0,1.0));
// Create a GUI to control these parameters
(
var win = Window.new("CompMachine2000");
// Create sliders from all aparameters
var sliders = Pctrldef('compmachine2000').params.collect{|param, name|
var slider = Slider
.new(win)
.orientation_(\horizontal)
.action_({|obj|
var sliderval = obj.value;
var mapped = param.value;
"Setting param % using normalized value %: %".format(name, sliderval, mapped).postln;
param.map(sliderval)
});
var label = StaticText.new(win).string_(name);
VLayout(label, slider)
}.asArray;
var layout = VLayout(*sliders);
win.layout = layout;
win.front();
)
// You can even copy a Pctrldef with all of it's internals to a new one
Pctrldef('compmachine2000').copy('compmachine2001');
Pctrldef('compmachine2001').play;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment