TuningRatios : EqualTemperament

TuningRatios allows you to specify a ratio above the base for each note in an octave. Useful for simplified just intonation schemes.

*new(stepsPerOctave, calibratefreq, calibratenote, tunings)

stepsPerOctave: How many notes per octave.

calibratefreq: What frequency to calibrate to.

calibratenote: What note number should match the calibratefreq.

tunings: An array with stepsPerOctave numbers containing the ratio above the base. The base is the frequency at note 0. The array should begin with 1.0 (the ratio for the base is always 1.0), and all ratios should be less than 2.0.

calibrate(freq, noteindex)

Behaves as in EqualTemperament.

cps(noteindex)

value(noteindex)

Compute frequency in Hz for the given note index. If you use fractional indices, the offset from the array will be interpolated using blendAt. That is, if note 0 has an offset of 0 and note 1 has an offset of 0.1, note 0.5 will have an offset of 0.05.

// build a Pythagorean C tuning, no correction for the comma

(

r = Array.newClear(12);

x = 1.0;

12.do({ |i|

r.wrapPut(i * 7, x);

x = x * 1.5;

if(x >= 2.0) { x = x * 0.5 }; // keep all ratios within the octave

});

r.postln; // see the ratios as floating point

r.asRational; // see the ratios as integer fractions

)

t = TuningRatios(12, 440, 69, r);

// show deviations from 12ET

// note 9 corresponds to 69 used for calibration -- and (correctly) has no deviation

t.cps((0..11)).cpsmidi;

[ -0.058650025961612, 1.0782000346155, 1.9804499913461, 3.1173000519233, 4.0195500086539, 5.156400069231, 6.0586500259616, 6.9608999826923, 8.0977500432694, 9, 10.136850060577, 11.039100017308 ]

// play white-key music using 12ET

// also include debugging output to show scale degree --> Hz mapping

SynthDescLib.global.read;

(

p = Pbind(

\degree, Pwhite(0, 11, inf),

\delta, Pwrand(#[0.125, 0.25, 0.5], #[1, 4, 2].normalizeSum, inf),

\sustain, Pkey(\delta) * Pwhite(0.8, 1.8, inf),

\instrument, \default

).collect({ |ev| [ev.degree, ev.use({ ~freq.value })].postln; ev }).play;

)

p.stop;

// play the same using the above Pythagorean tuning

// I rewrite the protoevent to use a tuning object rather than midicps

(

p = Pbind(

\degree, Pwhite(0, 11, inf),

\delta, Pwrand(#[0.125, 0.25, 0.5], #[1, 4, 2].normalizeSum, inf),

\sustain, Pkey(\delta) * Pwhite(0.8, 1.8, inf),

\instrument, \default,

\tuning, t

).collect({ |ev| [ev.degree, ev.use({ ~freq.value })].postln; ev })

.play(protoEvent: Event.default.put(\freq, #{ ~tuning.cps(~midinote.value + ~ctranspose) * ~harmonic }));

)

p.stop;