Rational Quark update

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?

  1. 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.

  2. 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.

  3. Floating-point arithmetic can lead to subtle bugs, inaccuracy, and the infamous problem of not being able to precisely represent certain decimal fractions.

  4. 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;

)
5 Likes