Last active
May 27, 2024 15:56
-
-
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
This file contains 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
/* | |
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