for reference, frequency parameters in aleph BEES use a 10-bit table of frequencies. by default this covers 128 semitones in 12tet, with 8 steps per semitone - so 7 additional “microtones” per semitone. the microtones are also equally spaced on logarithmic frequency axis.
parameter scaling data is deliberately kept in a separate flash memory section from code. performing a “clean boot” (holding down SW1) re-copies the parameter scaling data from sdcard to internal flash.
so you can pretty easily replace the default tables with your own JI flavor or something
here’s the supercollider code that generates the default tuning tables (also in the aleph repo under utils/param_scaling)
// note scaling table
// 12-tone equal temperament, with microtunes
// 10-bit linear input gives 7 microtunes, costs 4k
n = 2**10;
// 12-bit linear input gives 31 microtunes, costs 16k
// n = 2**12;
i = Array.series(n);
// let's use 128 semitones.
// so the full range is 10 octaves + 8 semitones.
// number of semitones
~nsemi = 128;
// microtuned steps per semitone
~nmicro = n / ~nsemi;
// semitone tempered ratio
~root_semi = 2 ** (1/12);
// microtune tempered ratio
~root_micro = ~root_semi ** (1/~nmicro);
// semitone ratios of fundamental
~fsemi = ~nsemi.collect({
|j| (2 ** ( j / 12).floor)
* (~root_semi ** (j % 12))
});
// fill in semitones on main table
// float ratios
f = i.collect({ |j|
if( (j % ~nmicro) == 0, {
~fsemi[ (j / ~nmicro).floor ]
}, {
nil
});
});
// fill in microtunings
{
var fSemId = 0;
// 32nd root of ratio between semitones
~nsemi.do({ arg iSem;
~nmicro.do({
arg iMic;
if (iMic != 0, {
f[fSemId + iMic] =
f[fSemId] * (~root_micro ** iMic)
});
});
fSemId = fSemId + ~nmicro;
});
}.value;
// base frequency: concert middle c, minus 5 octaves (~8hz !)
~base = 60.midicps / 32;
~base.postln;
// multiply float ratio table
f = f * ~base;
//f.plot;
f.do({ |x, j|
(" " ++ j ++ " : " ++ x ++ " hz : " ++ x.cpsmidi).post;
if( (x.cpsmidi - (x.cpsmidi.floor )) < 0.0001, {
" <<<<---------------------------"
}, { ""
}).postln;
});
// convert to 16.16.
// fortunately, it doesn't cost much to use the 16.16 representation directly in the DSP.
// so i think we can get away with only one table.
v = f.collect({ |x, i|
var xint, xfr, y;
xint = x.floor;
xfr = x - xint;
xint = xint.asInteger;
y = (xint << 16) | (0xffff & ((xfr * 0x10000).floor.asInteger));
});
v.do({ arg y; y.asHexString.postln; });
////// output to files
/// binary, big endian
// value
~vf = File.open(File.getcwd ++ "/scaler_note_12tet_val.dat", "wb");
// write size
~vf.putInt32(n);
// write data
v.do({ |x| ~vf.putInt32(x) });
~vf.close;
// use the same 16.16 values for SVF corner frequency representation.
// new table for corresponding coefficients.
// 2x oversampled
~sr = 96000.0;
~cf = f.collect({ |fc|
2.0 * sin(pi * min(0.25, fc / ~sr));
});
//~cf.do({ |x| x.postln; });
//~cf.plot;
~cfv = ~cf.collect({ |x| (x * 0x7fffffff).asInteger });
~cfv.do({ |x| x.postln; });
// value
~cfvf = File.open(File.getcwd ++ "/scaler_svf_fc_val.dat", "wb");
// write size
~cfvf.putInt32(n);
// write data
~cfv.do({ |x| ~cfvf.putInt32(x) });
~cfvf.close;
i haven’t ever personally used scala, never having found a compelling need for it. i do love my JI though.
doesn’t seem too hard to generate aleph param scaling data from a scala file if you want to do that.
because any poast about tuning requires sharing a tuning (i do declare) here’s a 5-limit tuning i like for chromatic music. inverts nicely
[
1.0,
16/15,
9/8,
4/3 * 4/3 * 4/3 / 2,
5/4,
4/3,
7/5,
3/2,
4/3 * 4/3 * 4/3 / 3 * 2,
5/3,
4/3 * 4/3,
15/8
];
course the whole thing about JI is that you really kinda need to pick ratio set according to musical intention.
[edit] a little clarifcation and a caveat. this parameter scaling / scale stuff on aleph is all on the control side. the modules just take 16.16 parameter values, totally arbitrary.
the caveat is that on waves, there is an outstanding issue where (i think) some bits are truncated in the MACC when “base pitch” and “ratio” are multiplied.
[ https://github.com/monome/aleph/issues/269 ]
someone should investigate whether the “new” fract intrinsics are helping!