TuningOffset : EqualTemperament


TuningOffset allows you to specify deviations from equal temperament in terms of fractions of note indices.


*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 offset from equal temperament. 0.01 = one cent above equal temperament (where cent = 1/100 of one note index, with stepsPerOctave * 100 cents per octave).


calibrate(freq, noteindex)


Behaves as in EqualTemperament.


cps(noteindex)

value(noteindex)


Compute frequency in Hz for the given note index. If you use a 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.


t = TuningOffset(12, 60.midicps, 60, ([0, 0.1] ! 6).flat);

t.cps(0).cpsmidi // note 0 has no offset

0


t.cps(1).cpsmidi // note 1 has offset 0.1 (1.1 = 1.0 + 0.1)

1.1


t.cps(0.5).cpsmidi // note 0.5 has offset 0.05 (0.55 = 0.5 + 0.05)

0.55


t.cps(0.75).cpsmidi

0.82499999999999 // or 0.825 = 0.75 + 0.075



t.cps((0.0, 0.02 .. 4.0)).plot; // observe that the plot is just off of a straight line



// Example: Kirnberger III (?)

// cents values from http://tonalsoft.com/enc/k/kirnberger.aspx


c = [0, 90.225, 193.157, 294.135, 386.314, 498.045, 590.224, 696.578, 792.18, 889.735, 996.09, 1088.27];


// convert to offsets from intervals of 100 cents, then divide by 100 for tuning values

c = (c - (0, 100 .. 1100)) * 0.01;


t = TuningOffset(12, 60.midicps, 60, c);


// 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 Kirnberger 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.copy.put(\freq, #{ ~tuning.cps(~midinote.value + ~ctranspose) * ~harmonic }));

)


p.stop;