I’d like to announce that I’ve made some performance optimizations and updates to the Rational Quark.
I found it fitting to revisit and adapt a classic example by JMC to showcase the Quark.
Before you dive into the example, please ensure you’ve updated your Quark to the latest version. This is important because I’ve introduced a new method in the example code below.
The code is robust and regularly tested. I’d love to hear your thoughts and feedback.
A quick note on the purpose of it: Why a Rational type in a programming language?
-
Rational numbers maintain precision, unlike floating-point representations, which can introduce rounding errors due to their binary nature. This exact representation is crucial in fields like music, which works with metric rhythms and tunning systems.
-
Operations on rational numbers (addition, subtraction, multiplication, division) can be done without loss of precision. This is especially important in algebraic computations where accuracy cannot be compromised.
-
Floating-point arithmetic can lead to subtle bugs, inaccuracy, and the infamous problem of not being able to precisely represent certain decimal fractions.
-
Rational types can interact with other numeric types; the class is well-integrated with the standard library and SC OOP model, being a subclass of Number
// Quarks.install("https://github.com/smoge/Rational")
// Quarks.update("https://github.com/smoge/Rational")
s.boot;
(
var w, buttons;
var series, series2;
SynthDef("tone", { arg freq = 440, amp=0.1, gate =1;
var tone, env;
env = Linen.kr(gate, 0.01, amp, 0.2, 2);
tone = OnePole.ar(Saw.ar(freq + {Rand(-0.05,0.05)}.dup, env), 0.95);
Out.ar(0, tone);
}).add;
series = (8..15);
series.remove(13);
series2 = series ++ (2 * series) ++ (4 * (series ++ 16));
series = 4 * (series ++ 16);
w = Window("diamond", Rect(200,200,460,560));
w.view.decorator = FlowLayout(w.view.bounds);
series2.reverse.do { |denom|
series.do { |numer|
var b, label, ratio, id, freq;
var color, rational;
rational = numer %/ denom;
rational.postln;
freq = 50.midicps * rational;
label = rational.asString;
b = Button(w, 50 @ 20);
color = if (numer.isPowerOfTwo && denom.isPowerOfTwo) { Color.red(1, 0.4) } {
if (numer.isPowerOfTwo || denom.isPowerOfTwo) { Color.yellow(1, 0.4) } { Color.clear }
};
b.states = [[label, Color.black, color], [label, Color.white, Color.black]];
b.action = {
if (b.value == 1) {
s.sendMsg(\s_new, \tone, id = s.nextNodeID, 0, 1, \freq, freq.asFloat);
[\on, rational].postln;
} {
s.sendMsg(\n_set, id, \gate, 0);
[\off, rational].postln;
};
};
};
w.view.decorator.nextLine;
};
w.front;
)