I’ve been trying to train myself to hear in just intonation recently. SuperCollider has been quite helpful for this and I thought I might try my hand at making a GUI so that my friends, who don’t use SC, could also try it out. I am not 100% sure but I think the basic design seems ok, but there are two major problems and one very minor problem.
Here’s the code:
// GUI
(
var numButtons = 4;
var numSliders = 4;
var numNumberBoxes = 4;
var numLabels0 = 4;
var numLabels1 = 4;
var buttons, sliders, numberBoxes, labels0, labels1, sliderLayout, buttonLayout, numberBoxesLayout, labelsLayout0, labelsLayout1, layout;
w = Window.new;
// objects
buttons = numButtons.collect{
Button.new
};
sliders = numSliders.collect{
Slider.new.orientation_(\horizontal)
};
numberBoxes = numNumberBoxes.collect{
NumberBox.new
};
labels0 = numLabels0.collect{
StaticText.new
};
labels1 = numLabels1.collect{
StaticText.new
};
// layout
buttonLayout = HLayout.new(*buttons);
sliderLayout = VLayout.new(*sliders);
numberBoxesLayout = HLayout.new(*numberBoxes);
labelsLayout0 = VLayout.new(*labels0);
labelsLayout1 = HLayout.new(*labels1);
//layout = VLayout(sliderLayout, buttonLayout, numberBoxesLayout);
layout = VLayout(HLayout(labelsLayout0, sliderLayout), buttonLayout, VLayout(numberBoxesLayout, labelsLayout1));
// buttons
// Start pattern
buttons[0].states_([["Start Pattern", Color.black, Color.gray]]).action_({|obj|
Pdef(\pattern).play
});
// Stop pattern
buttons[1].states_([["Stop Pattern", Color.black, Color.gray]]).action_({|obj|
Pdef(\pattern).stop;
});
// Start bass note
buttons[2].states_([["Start Bass Note", Color.black, Color.gray]]).action_({|obj|
~bassNote
});
// Stop bass note
buttons[3].states_([["Stop Bass Note", Color.black, Color.gray]]).action_({|obj|
~bassNote.free;
});
// sliders
// Slider 0 - change dur
~s0 = sliders[0].action_({|obj|
var val = obj.value;
// Scale duration, make sure it is not zero
val = val.linlin(0.0, 1.0, 0.05, 1.0);
Pdefn(\pDur, val)
});
// Slider 1 - change atk
~s1 = sliders[1].action_({|obj|
var val = obj.value;
val = val.linlin(0.0, 1.0, 0.01, 0.3);
Pdefn(\pAtk, val)
});
// Slider 2 - change rel
~s2 = sliders[2].action_({|obj|
var val = obj.value;
val = val.linlin(0.0, 1.0, 0.5, 1);
Pdefn(\pRel, val)
});
// Slider 3 - change amp
~s3 = sliders[3].action_({|obj|
var val = obj.value;
val = val.linlin(0.0, 1.0, 0.0, 0.75);
Pdefn(\pAmp, val)
});
// numberBoxes
~bassFreq = numberBoxes[0].value_(55)
.clipLo_(30)
.clipHi_(100);
~bassAmp = numberBoxes[1].value_(0.1)
.clipLo_(0)
.clipHi_(0.75);
~startPartial = numberBoxes[2].value_(10)
.step_(1)
.clipLo_(1)
.clipHi_(25);
~numNotes = numberBoxes[3].value_(5)
.step_(1)
.clipLo_(2)
.clipHi_(30);
// labels - fund, bass amp, start partial, num notes
labels0[0].string_("Speed");
labels0[1].string_("Attack");
labels0[2].string_("Release");
labels0[3].string_("Volume");
labels1[0].string_("Fundamental");
labels1[1].string_("Bass Volume");
labels1[2].string_("Start Partial");
labels1[3].string_("Scale Length");
// tidying up
w.layout = layout;
w.front;
)
// SynthDefs
(
SynthDef(\blip, {
arg baseFreq=55, ratio=10, amp=0.25, pan=0, atk=0.02, rel=0.1, out=0;
var sig, env;
env = Env.perc(atk, rel);
env = EnvGen.ar(env, doneAction: 2);
sig = SinOsc.ar(baseFreq * ratio);
sig = sig * env * amp;
sig = Pan2.ar(sig, pan);
sig = FreeVerb.ar(sig);
Out.ar(out, sig);
}).add;
)
(
SynthDef(\bass, {
arg freq=55, amp=0.25, pan=0, out=0, lfo=0.1, lpf=10000;
var sig, moog;
moog = LFNoise1.ar(lfo).exprange(freq*4, freq*120);
sig = LFSaw.ar(freq);
sig = MoogFF.ar(sig, moog);
sig = LPF.ar(sig, lpf);
sig = sig * amp;
sig = Pan2.ar(sig, pan);
sig = FreeVerb.ar(sig);
Out.ar(out, sig);
}).add;
)
~bassNote = Synth(\bass, [\freq, ~bassFreq.value, \amp, ~bassAmp.value]);
Synth(\bass, [\lpf, 1000])
// basic pattern to control
(
Pdef(\pattern,
Pbind(
\instrument, \blip,
\dur, Pdefn(\pDur, 1),
//\ratio, 5, // this works
\ratio, Pdefn(\pRatio, Pseries(~startPartial, 1, ~numNotes)), // this doesn't
\amp, Pdefn(\pAmp, 0.25),
\atk, Pdefn(\pAtk, 0.01),
\rel, Pdefn(\pRel, 0.8),
\pan, 0
)
);
)
I’m guessing there are problems with how I’m calling the Bass synth, but I’m mostly frustrated by the following error message regarding the Pattern:
ERROR: Primitive '_QObject_GetProperty' failed.
Failed.
RECEIVER:
Instance of NumberBox { (0x5618580c82d8, gc=E0, fmt=00, flg=00, set=06)
instance variables [46]
qObject : RawPointer 0x561852c8d500
finalizer : instance of Finalizer (0x5618580f7488, size=2, set=1)
virtualSlots : nil
wasRemoved : false
font : nil
resize : Integer 1
alpha : Float 1.000000 00000000 3FF00000
decorator : nil
layout : nil
userCanClose : true
deleteOnClose : true
action : nil
mouseDownAction : nil
mouseUpAction : nil
mouseEnterAction : nil
mouseLeaveAction : nil
mouseMoveAction : nil
mouseOverAction : nil
mouseWheelAction : nil
keyDownAction : nil
keyUpAction : nil
keyModifiersChangedAction : nil
keyTyped : nil
focusGainedAction : nil
focusLostAction : nil
dragLabel : nil
beginDragAction : nil
canReceiveDragHandler : nil
receiveDragHandler : nil
toFrontAction : nil
endFrontAction : nil
onClose : nil
onResize : nil
onMove : nil
step : Integer 1
shift_scale : Float 100.000000 00000000 40590000
ctrl_scale : Float 10.000000 00000000 40240000
alt_scale : Float 0.100000 9999999A 3FB99999
scroll : true
scroll_step : Integer 1
align : nil
buttonsVisible : false
normalColor : instance of Color (0x56185760dfa8, size=4, set=2)
typingColor : instance of Color (0x561857681018, size=4, set=2)
object : nil
setBoth : true
}
CALL STACK:
MethodError:reportError
arg this = <instance of PrimitiveFailedError>
Nil:handleError
arg this = nil
arg error = <instance of PrimitiveFailedError>
Thread:handleError
arg this = <instance of Routine>
arg error = <instance of PrimitiveFailedError>
Thread:handleError
arg this = <instance of Routine>
arg error = <instance of PrimitiveFailedError>
Thread:handleError
arg this = <instance of Routine>
arg error = <instance of PrimitiveFailedError>
Thread:handleError
arg this = <instance of Routine>
arg error = <instance of PrimitiveFailedError>
Object:throw
arg this = <instance of PrimitiveFailedError>
Object:primitiveFailed
arg this = <instance of NumberBox>
NumberBox:value
arg this = <instance of NumberBox>
var type = nil
var val = nil
Pseries:embedInStream
arg this = <instance of Pseries>
arg inval = <instance of Event>
var outval = nil
var counter = 0
var cur = nil
var len = nil
var stepStr = nil
var stepVal = nil
Routine:prStart
arg this = <instance of Routine>
arg inval = <instance of Event>
^^ The preceding error dump is for ERROR: Primitive '_QObject_GetProperty' failed.
Failed.
RECEIVER: a NumberBox
Any feedback would be super helpful. Oh, and the last question: is there a simple way to make the labels at the bottom of the GUI smaller?
Thanks!
Jordan