Tuning, Scala, Aleph waves module

I read a compelling interview between two cool smart people who make electronic music and instruments

On the surface it’s a highbrow advertisement for the Korg Monologue, below it’s much more dense. I got some good keywords and went down a rabbit hole on tunings.

Anyone work with Scala (not the programming language) to generate tunings? The Aleph waves module expresses pitch using values which my intuition says is capable of moving between semitones. Perhaps a general purpose synth with arbitrary tuning like the Korg Monologue could be built?

1 Like

A recent update of CV Toolkit software supports Scala, if that’s of interest.

1 Like

Telex TXo expander also supports scala. Tun is another scale file format. Here’s a list of synths that support one or the other, in no particular order:

  • Pianoteq
  • Garritan Aria
  • Zebra
  • Diva
  • ACE
  • Alchemy
  • Omnisphere
  • Aalto
  • Logic synths
  • FM8

Looks like I missed a few:


http://www.microtonal-synthesis.com/micro_af.html
http://xenharmonic.wikispaces.com/List+of+Microtonal+Software+Plugins

1 Like

Peter Blasser is very interested in tuning.

A few thoughts here, but he has many others as well:

This was my introduction to Just Intonation:

3 Likes

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!

2 Likes

This is fantastic, thank you. I’m constantly amazed by the plans that went into this special purpose computer.

1 Like

I have never used the Scala resource, but this piqued my interest.
I wonder if this was already discussed and I missed it: would there be a way to implement this within the Teletype framework?

should be & think this issue is fixed on monome/dev ! (I’ll measure it)

My understanding was 8 bits per semitone (i.e. 256 tones per semitone). Had just blindly assumed the scale stays pretty close to log ‘in-between’ semitones. Playing the HARRY op patch quadrangularis, I always thought something might be not quite right, but wasn’t sure enough what all those ratios were supposed to sound like. I plan to build a polyphonic (non-oversampling) FM module at some point - it’d be nice to play around with tonality diamond construct for building chords as well as melodies…

Now, assuming the scale does stay true to log between semitones, feel like it’d be worth adding a JI operator which outputs a ‘Hz-scale’ equal-temperament log value based on a numerator & denominator. Then you could build arbitrary just scales using pairs of LIST8 without rebooting the device to load a new temperament.

theres definitely only 1024 entries in the bees scaler table for notes

a way to implement this within the Teletype

seems easy enough to parse scala data files, a matter of implementing it
http://www.huygens-fokker.org/scala/scl_format.html

how to glue to TT filesystem another story…

1 Like

OK so now I’m finally getting it! Sat with jaaa (realtime fft software) & aleph. Seems that Hz params are not doing any linear interpolation to get the full 256 microtones-per-semitone the 16 bit BEES param could potentially support.

But I had a brainwave - the ‘tune’ param + LINLIN gives you 4 octives of JI for free! (doesn’t it!?) Think HARRY should be modified to output tune params, not Hz & there’s no need for JI, it’s just LINLIN

yes exactly forgot to finish the thought – no interpolation between BEES scaler table entries. (the real osc rez is pretty fine)

i think i decided this based on some really weird purist thought - that it was too not good, tuning wise, to have linear interpolation between points of logarithmic interpolation, better to keep things more predictable… this is probably insane

the GH issue i found though is that even after lookup values were not honored - i assumed because of MADD accumulator depth (40b), or something. maybe this is what you fixed

so in other words that issue is about the result of ‘hz’ and ‘tune’ params effectively quantizes the values of ‘hz’ or something like that

yes let me get back to collecting the values to really check it worked. BTW I think there might be a radix error in the tune param! 440Hz/1.00000 on BEES screen reports as 220Hz in jaaa

oh that’s probably true, i can’t hear the difference between octaves sometimes

well… unless waves is out of CPU and downsampling :smiley:

bahahaha you never know! crazy little box!

made me remember a random thought though - do you know a way to measure the time before & after running the DSP on bfin? cos we already have working param reads - so mean, modal, & median cpu times could get bolted on as params during module dev… then you could read them back to play screen during module testing to get a sense of CPU load

i’ve only done that by flipping a LED pin, which works fine

there is a cycle counter which you can get at through ASM (i don’t think theres an intrinsic but worth searching the toolchain). my attempts to make use of it as a readable parameter over SPI didn’t really pan out for some reason.

(recent misadventure with CV driver illustrates my reluctance to mess around too much with bfin core programming, blerrg)

here’s a thread about enabling cycles count
https://ez.analog.com/docs/DOC-15311

measurements of waves’ output frequency vs tune param:

#BEES(16 bit), frequency(Hz)
8192,219.3
8211 219.8
8230 220.3
8248 220.8

therefore I calculate resolution in this frequency range is 0.2% or 1/25 semitone - that’s very much accurate enough for really good JT sounds, no!?

But I mean really the tune/Hz scheme should constitute massive overkill, not ‘just good enough’. Just looking at the function freq_to_phase I think this part could use some norm_fr1x32 magic…

1 Like

Specific scale lookup tables for the CV N?
Or a way to populate the PATTERN with note values from selected scale?
…?

You can change TXo’s scales using Scala files and a utility that generates code for the module’s firmware.

1 Like

yup I think it’s nothing to worry about - linear interpolation between 1/8 semitone steps seems to be closer than 1 part in 10^4 - I compute this would therefore require 8810 steps per semitone before the linear interpolation would lead to a pitch error!

Just implemented the linear interpolation for param scaler & improved frequency-phase calculation accuracy. This should now enable extremely fine microtonal control of the digital oscillators in either:

  • equal-temperament (aka log) paradigm… 256 increments per semitone
  • just intonation paradigm using LINLIN to convert rational numbers into fract - resolution of this guy can now generate like 30 second beat frequencies between 2 330Hz waves!
2 Likes

Ah, I thought I saw the discussion of that somewhere. Is there more detail somewhere on the process of converting and uploading the files?
Does not sound terribly easy, but perhaps not prohibitive?
@bpcmusic is there a step-by-step somewhere?