Hello,
While testing the following code:
The link in the hidden text now contains an updated code. See the code block below which contains the previous code to understand the question:
(
s.options.numWireBufs = 128; // 256;
~numSliders = 64; // Tested at up to 120
~mul = 0.1;
~freq = 200; // 393.44
s.waitForBoot {
var numSliders, indexThumbSize, indexWidth, centre, upperHeight, lowerHeight, width, win;
var freqBox, multiSliderFreq, multiSliderPhase, phases, phaseButtons, ampNum, ampSlider, freqScope;
var lable, lables, waveformView, waveform;
var lablesHeight, interactiveLableFreq, interactiveLablePhase, interactiveLableUpdate, antiAliasing;
numSliders = ~numSliders;
indexThumbSize = 16;
indexWidth = indexThumbSize + 1;
centre = [Window.screenBounds.width, Window.screenBounds.height] / 2;
width = if (numSliders <= 36) {
36 * indexWidth + 6 + 255;
} {
numSliders * indexWidth + 6
};
win = Window("overtone explorer", Rect(centre[0] - (width / 2), centre[1] - 325, width, 650));
win.front.onClose_{
x.free;
freqScope.kill;
}
.acceptsMouseOver_(true);
lowerHeight = 250;
upperHeight = win.bounds.height - lowerHeight ;
lable = { |parent, left, top, width, height, align, fontSize, string, colour, background|
StaticText(parent, Rect(left, top, width, height))
.align_(align)
.font_(Font("Arial", fontSize))
.stringColor_(colour)
.string_(string)
.background_(background)
};
lables = numSliders.collect { |i|
var string, left;
string = (i + 1).asString.padLeft(3, "0");
left = i * indexWidth + 7;
[
lable.(win, left, 2, 10, 10, \center, 11, string[0], Color.black, Color.clear),
lable.(win, left, 14, 10, 10, \center, 11, string[1], Color.black, Color.clear),
lable.(win, left, 26, 10, 10, \center, 11, string[2], Color.black, Color.clear)
]
};
lablesHeight = 36;
antiAliasing = { |freqBase|
numSliders.do { |index|
var thisFreq;
thisFreq = index + 1 * freqBase;
interactiveLableFreq.string_("");
if (thisFreq < (s.sampleRate / 2)) {
lables[index].size.do { |i|
lables[index][i].stringColor_(Color.black)
}
}
{
multiSliderFreq.value_(multiSliderFreq.value.put(index, 0));
lables[index].size.do { |i|
lables[index][i].stringColor_(Color.grey(0.6))
}
}
}
};
multiSliderFreq = if (numSliders < 36) {
MultiSliderView(win, Rect(1, lablesHeight, numSliders * indexWidth + 6, upperHeight - 50 / 2));
} {
MultiSliderView(win, Rect(1, lablesHeight, width, upperHeight - 50 / 2));
};
multiSliderFreq.value_((0 ! numSliders))
.showIndex_(true)
.indexThumbSize_(indexThumbSize)
.action_{ |sliders|
var numNotZero = ~numSliders - sliders.value.occurrencesOf(0);
x.set(
\numNotZero, numNotZero,
\overtoneLast, ~numSliders,
\amplitudes, sliders.value
)
}
.mouseOverAction_{ |view, x, y|
var mouseOverIndex;
mouseOverIndex = (x / indexWidth).round;
interactiveLableUpdate.(x, y, multiSliderFreq, interactiveLableFreq, "Hz;\n", 100, "%",
"\n0 % to prevent aliasing");
interactiveLableFreq.stringColor_(if(mouseOverIndex * freqBox.value < (s.sampleRate / 2)) {
Color.blue(1, 0.5)
} {
Color.grey(0.5)
});
}
.mouseMoveAction_{ |view, x, y|
var mouseMoveIndex;
mouseMoveIndex = (x / indexWidth).round;
interactiveLableUpdate.(x, y, multiSliderFreq, interactiveLableFreq, "Hz;\n", 100, "%",
"\n0 % to prevent aliasing");
interactiveLableFreq.stringColor_(if(mouseMoveIndex * freqBox.value < (s.sampleRate / 2)) {
Color.blue(1, 0.5)
} {
Color.grey(0.5)
});
}
.mouseUpAction_{ |view, x, y|
antiAliasing.(freqBox.value);
interactiveLableUpdate.(x, y, multiSliderFreq, interactiveLableFreq, "Hz;\n", 100, "%",
"\n0 % to prevent aliasing")
}
.mouseLeaveAction_{
interactiveLableFreq.string_("Amplitudes")
.bounds_(Rect(
multiSliderFreq.bounds.extent.x - 120 / 2,
multiSliderFreq.bounds.extent.y - 20 / 2,
120,
25));
multiSliderFreq.showIndex_(false)
}
.mouseEnterAction_{
multiSliderFreq.showIndex_(true)
}
.background_(Color.grey(0.9, 0.1))
.colors_(Color.grey(0.3, 0.5), Color.grey(0.3, 0.5));
interactiveLableFreq = lable.(multiSliderFreq,
multiSliderFreq.bounds.extent.x - 120 / 2,
multiSliderFreq.bounds.extent.y - 20 / 2,
120,
25,
\left, 24, "Amplitudes", Color.blue(1, 0.5), Color.grey(0.9, 0.7));
phases = 0 ! ~numSliders;
phaseButtons = ~numSliders.collect { |i|
Button(win, Rect(
indexThumbSize + 1 * i + 4, upperHeight / 2 - 15 + 26, indexThumbSize, indexThumbSize
))
.states_([["O", Color.black], ["Ø", Color.green(0.6)]])
.action = { |view|
var phase = if(view.value == 0) {
0
} {
pi
};
if (phaseButtons[i].states.size == 1) {
phaseButtons[i]
.states_([["O", Color.black], ["Ø", Color.green(0.6)]])
};
multiSliderPhase.value = multiSliderPhase.value.put(i, phase.linlin(0, 2pi, 0, 1));
phases.put(i, phase);
x.set(\phases, phases)
}
};
multiSliderPhase = if (numSliders < 36) {
MultiSliderView(win, Rect(
1, upperHeight - 50 / 2 + lablesHeight + indexThumbSize, numSliders * indexWidth + 6, upperHeight - 50 / 2
));
} {
MultiSliderView(win, Rect(
1, upperHeight - 50 / 2 + lablesHeight + indexThumbSize, width, upperHeight - 50 / 2
));
};
multiSliderPhase.value_(((0 ! numSliders)))
.showIndex_(true)
.indexThumbSize_(indexThumbSize)
.action_{ |sliders|
var phases = sliders.value.linlin(0, 1, 0, 2pi);
if(phases[sliders.index] == 0.0 || (phases[sliders.index] == 180.0)) {
phaseButtons[sliders.index]
.states_([["O", Color.black], ["Ø", Color.green(0.6)]]);
phaseButtons[sliders.index].value = (phases[sliders.index] % 180)
} {
phaseButtons[sliders.index]
.states_([["↓", Color.blue(1, 0.5)]])
};
x.set(\phases, phases)
}
.mouseOverAction_{ |view, x, y|
var mouseOverIndex;
mouseOverIndex = (x / indexWidth).round;
interactiveLableUpdate.(x, y, multiSliderPhase, interactiveLablePhase, "Hz;\n", 360, "°", "");
//multiSliderPhase.stringColor_(if(mouseOverIndex * freqBox.value < (s.sampleRate / 2)) { Color.blue(1, 0.5) } { Color.grey(0.5) });
}
.mouseMoveAction_{ |view, x, y|
var mouseMoveIndex;
mouseMoveIndex = (x / indexWidth).round;
interactiveLableUpdate.(x, y, multiSliderPhase, interactiveLablePhase, "Hz;\n", 360, "°", "");
}
.mouseUpAction_{ |view, x, y|
interactiveLableUpdate.(x, y, multiSliderPhase, interactiveLablePhase, "Hz;\n", 360, "°", "")
}
.mouseLeaveAction_{
interactiveLablePhase.string_("Phases" +
"(If the mouse is moved too quickly, some phase buttons may not be updated.)")
.bounds_(Rect(
multiSliderPhase.bounds.extent.x - 920 / 2,
multiSliderPhase.bounds.extent.y - 20 / 2,
920,
25));
multiSliderPhase.showIndex_(false)
}
.mouseEnterAction_{
multiSliderPhase.showIndex_(true)
}
.background_(Color.grey(0.9, 0.1))
.colors_(Color.grey(0.3, 0.5), Color.grey(0.3, 0.5));
interactiveLablePhase = lable.(multiSliderPhase,
multiSliderPhase.bounds.extent.x - 920 / 2,
multiSliderPhase.bounds.extent.y - 20 / 2,
920,
25,
\left, 24, "Phases" +
"(If the mouse is moved too quickly, some phase buttons may not be updated.)", Color.blue(1, 0.5), Color.grey(0.9, 0.7));
interactiveLableUpdate = { |x, y, where, which, hzORphase, how, unit, antialiasing|
var index, indexLable, indexLableSize, value, cps;
index =(x / indexWidth).ceil.clip(1, ~numSliders);
indexLable = index.asInteger.asString;
indexLableSize = indexLable.size;
value = y.linlin(0, where.bounds.extent.y - 1, how, 0).asInteger;
cps = index * freqBox.value;
which.string_(
indexLable ++ ":" + cps ++ hzORphase + (" " ! (indexLableSize + 2)).join + value ++ unit +
(if(s.sampleRate / 2 <= cps) { antialiasing } { "" })
);
which.bounds = which.bounds.size_(which.sizeHint);
which.bounds_(
x = if(x + which.bounds.width < where.bounds.extent.x) {
x
} {
x - which.bounds.width
};
y = where.bounds.extent.y - which.bounds.height / 2;
Rect(x, y, which.bounds.width, which.bounds.height);
)
};
lable.(win, 10, upperHeight + 3, 300, 20, \left, 12,
"Base frequency (Hz): (test with 393.44)",
Color.black, Color.clear);
freqBox = NumberBox(win, Rect(130, upperHeight + 3, 50, 20))
.font_(Font("Arial", 12));
freqBox.value_(~freq)
.action_{ |numb|
var freqBase = numb.value;
x.set(\freq, numb.value);
waveform.cycle_(s.sampleRate / numb.value);
antiAliasing.(freqBase)
};
freqScope = FreqScopeView(win, Rect(2, upperHeight + 3 + 22, 511, lowerHeight - 28));
freqScope.active_(true);
ListView(win, Rect(2 + 511 - 30, upperHeight + 3 + 22, 30, 34))
.items_(["lin", "log"])
.action_{ |menu|
freqScope.freqMode_(menu.value);
};
ampNum = NumberBox(win, Rect(3 + 511, upperHeight + 3, 29, 20))
.font_(Font("Arial", 12))
.value_(0.1)
.action_{ |numb|
ampSlider.value_(numb.value);
x.set(\mul, numb.value);
};
ampSlider = Slider(win, Rect(2 + 511, upperHeight + 2 + 22, 31, lowerHeight - 26))
.value_(~mul)
.action_{
x.set(\mul, ampSlider.value);
ampNum.value_(ampSlider.value);
};
ServerMeterView(s, win, (2 + 511 + 30) @ (upperHeight + 18), 0, 2);
waveformView = View(win, Rect(
2 + 511 + 30 + ServerMeterView.getWidth(0, 1, s),
upperHeight + 1,
255,
255
));
waveform = Stethoscope(s, 1, view: waveformView);
// waveform = Plotter(parent: waveformView)
// value =
SynthDef(\overtone_additive, { |overtoneFirst = 1, overtoneLast = 1, numNotZero = nil|
var freq, phases, mul, numOvertones, maxLayers, amplitudes, temp, signalArray, sum;
mul = \mul.kr(~mul);
freq = \freq.kr(~freq);
phases = \phases.kr(0 ! ~numSliders);
maxLayers = ~numSliders;
amplitudes = \amplitudes.kr(0 ! maxLayers);
numOvertones = if(numNotZero == nil) {
overtoneLast - overtoneFirst + 1
} {
numNotZero - overtoneFirst + 1
}.clip(1, ~numSliders);
signalArray = maxLayers.collect { |i|
var nthOvertone, minDetect, maxDetect, aliasingDetect, switch;
nthOvertone = i + 1 * freq;
minDetect = overtoneFirst <= (i + 1);
maxDetect = overtoneLast >= (i + 1);
aliasingDetect = nthOvertone < (SampleRate.ir / 2);
switch = (minDetect * maxDetect * aliasingDetect).lag(0.2);
SinOsc.ar(nthOvertone, phases[i].lag(0.2)) * switch * numOvertones.reciprocal.sqrt.lag(0.2)
};
signalArray = signalArray * amplitudes.lag(0.2);
sum = signalArray.sum * mul.lag(0.2);
Out.ar(0, sum ! 2);
}).add;
s.sync;
waveform.cycle_(s.sampleRate / ~freq);
x = Synth(\overtone_additive);
}
)
I found that very fast mouse movements were not fully reflected in the button value. When you move the mouse pointer in MultiSliderView, the value of the corresponding slider button should change. However, some of them do not change. Here is a video recording of this:
link to video
Is this due to the wrong code structure or due to the GUI response limit?
The relevant part of the code is as follows:
(
s.options.numWireBufs = 128; // 256;
~numSliders = 64; // Tested at up to 120
~mul = 0.1;
~freq = 200; // 393.44
s.waitForBoot {
var numSliders, indexThumbSize, indexWidth, centre, upperHeight, lowerHeight, width, win;
var freqBox, multiSliderFreq, multiSliderPhase, phases, phaseButtons, ampNum, ampSlider, freqScope;
var lable, lables, waveformView, waveform;
var lablesHeight, interactiveLableFreq, interactiveLablePhase, interactiveLableUpdate, antiAliasing;
numSliders = ~numSliders;
indexThumbSize = 16;
indexWidth = indexThumbSize + 1;
centre = [Window.screenBounds.width, Window.screenBounds.height] / 2;
width = if (numSliders <= 36) {
36 * indexWidth + 6 + 255;
} {
numSliders * indexWidth + 6
};
win = Window("overtone explorer", Rect(centre[0] - (width / 2), centre[1] - 325, width, 650));
win.front.onClose_{
x.free;
freqScope.kill;
}
.acceptsMouseOver_(true);
lowerHeight = 250;
upperHeight = win.bounds.height - lowerHeight ;
lable = { |parent, left, top, width, height, align, fontSize, string, colour, background|
StaticText(parent, Rect(left, top, width, height))
.align_(align)
.font_(Font("Arial", fontSize))
.stringColor_(colour)
.string_(string)
.background_(background)
};
lablesHeight = 36;
phases = 0 ! ~numSliders;
phaseButtons = ~numSliders.collect { |i|
Button(win, Rect(
indexThumbSize + 1 * i + 4, upperHeight / 2 - 15 + 26, indexThumbSize, indexThumbSize
))
.states_([["O", Color.black], ["Ø", Color.green(0.6)]])
.action = { |view|
var phase = if(view.value == 0) {
0
} {
pi
};
if (phaseButtons[i].states.size == 1) {
phaseButtons[i]
.states_([["O", Color.black], ["Ø", Color.green(0.6)]])
};
multiSliderPhase.value = multiSliderPhase.value.put(i, phase.linlin(0, 2pi, 0, 1));
phases.put(i, phase);
//x.set(\phases, phases)
}
};
multiSliderPhase = if (numSliders < 36) {
MultiSliderView(win, Rect(
1, upperHeight - 50 / 2 + lablesHeight + indexThumbSize, numSliders * indexWidth + 6, upperHeight - 50 / 2
));
} {
MultiSliderView(win, Rect(
1, upperHeight - 50 / 2 + lablesHeight + indexThumbSize, width, upperHeight - 50 / 2
));
};
multiSliderPhase.value_(((0 ! numSliders)))
.showIndex_(true)
.indexThumbSize_(indexThumbSize)
.action_{ |sliders|
var phases = sliders.value.linlin(0, 1, 0, 2pi);
if(phases[sliders.index] == 0.0 || (phases[sliders.index] == 180.0)) {
phaseButtons[sliders.index]
.states_([["O", Color.black], ["Ø", Color.green(0.6)]]);
phaseButtons[sliders.index].value = (phases[sliders.index] % 180)
} {
phaseButtons[sliders.index]
.states_([["↓", Color.blue(1, 0.5)]])
};
// x.set(\phases, phases)
}
.mouseOverAction_{ |view, x, y|
var mouseOverIndex;
mouseOverIndex = (x / indexWidth).round;
interactiveLableUpdate.(x, y, multiSliderPhase, interactiveLablePhase, "Hz;\n", 360, "°", "");
}
.mouseMoveAction_{ |view, x, y|
var mouseMoveIndex;
mouseMoveIndex = (x / indexWidth).round;
interactiveLableUpdate.(x, y, multiSliderPhase, interactiveLablePhase, "Hz;\n", 360, "°", "");
}
.mouseUpAction_{ |view, x, y|
interactiveLableUpdate.(x, y, multiSliderPhase, interactiveLablePhase, "Hz;\n", 360, "°", "")
}
.mouseLeaveAction_{
interactiveLablePhase.string_("Phases" +
"(If the mouse is moved too quickly, some phase buttons may not be updated.)")
.bounds_(Rect(
multiSliderPhase.bounds.extent.x - 920 / 2,
multiSliderPhase.bounds.extent.y - 20 / 2,
920,
25));
multiSliderPhase.showIndex_(false)
}
.mouseEnterAction_{
multiSliderPhase.showIndex_(true)
}
.background_(Color.grey(0.9, 0.1))
.colors_(Color.grey(0.3, 0.5), Color.grey(0.3, 0.5));
interactiveLablePhase = lable.(multiSliderPhase,
multiSliderPhase.bounds.extent.x - 920 / 2,
multiSliderPhase.bounds.extent.y - 20 / 2,
920,
25,
\left, 24, "Phases" +
"(If the mouse is moved too quickly, some phase buttons may not be updated.)", Color.blue(1, 0.5), Color.grey(0.9, 0.7));
interactiveLableUpdate = { |x, y, where, which, hzORphase, how, unit, antialiasing|
var index, indexLable, indexLableSize, value, cps;
index =(x / indexWidth).ceil.clip(1, ~numSliders);
indexLable = index.asInteger.asString;
indexLableSize = indexLable.size;
value = y.linlin(0, where.bounds.extent.y - 1, how, 0).asInteger;
/*cps = index * freqBox.value;
which.string_(
indexLable ++ ":" + cps ++ hzORphase + (" " ! (indexLableSize + 2)).join + value ++ unit +
(if(s.sampleRate / 2 <= cps) { antialiasing } { "" })
);*/
which.bounds = which.bounds.size_(which.sizeHint);
which.bounds_(
x = if(x + which.bounds.width < where.bounds.extent.x) {
x
} {
x - which.bounds.width
};
y = where.bounds.extent.y - which.bounds.height / 2;
Rect(x, y, which.bounds.width, which.bounds.height);
)
};
}
)
Thank you in advance!