An Even Tempered Pico

I was having a bit of a discussion about tuning and temperament in music with Midi_in_out on Twitter, after they posted the latest update to their MIDISID project and we got on to talking about polyphony.  I know the general theory behind natural harmonics vs the compromise that is the even tempered scale we use in Western music, but had not really looked into it in detail.

It occurred to me that my previous experiments with setting frequencies would allow me to experiment a little, so I pulled out my Pi Pico PIO Poly Tone MIDI Keyboard and had a go.

Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

These are the key tutorials for the main concepts used in this project:

If you are new to microcontrollers, see the Getting Started pages.

Parts list

The Circuit

I’m not going to go over the hardware side here, as I’m using my Pi Pico PIO Poly Tone MIDI Keyboard which is fully explained elsewhere.  I’ve used my two DIY “packs” as follows, but you could use the breadboard equivalents or an alternative:

I am using the additional IO on the board to connection GP6 and GP7 to two buttons, linked to GND to choose the “mode” to use for playing.

IMG_6241

Equal vs Just Temperament

The idea is to explore, musically, the differences between equal temperament, as used on any keyboard instrument today, and “just” temperament which is more aligned to the natural, physical harmonics of sound.

For full details, see the wikipedia pages linked above, but computationally, what we need to know here is that equal temperament splits each octave into equally spaced parts using the ration 1:”12th root of two”.

This means that the frequency for any specific note can be calculated by the following code:

frequency = lowestfreq * 2**(n/12))

where n is the number of the note from the lowest frequency note defined.  Note that in python ** means “raised to the power of”.

Just temperament is defined not by equal parts, but by parts “tuned” to ratios that are ratios of natural harmonics.  The ratios used are listed on the wikipedia “Music and Mathematics” page as:

Just Intonation Ratios

So to calculate the frequency for any note using just intonation, I use the following:

justratios = [1, 16/15, 9/8, 6/5,5/4, 4/3, 45/32, 3/2, 8/5, 5/3, 9/5, 15/8]
base = lowfreq * 2 ** (int (n/12))
frequency = base * justratios[n % 12]

Where n is the note number above the lowest frequency defined note as before.  You can use any note as the base, but this is where the compromise comes in – a single note, middle C say, will have a different frequency depending on the base note used to calculate it.

I’m using frequency scales based on A to A, which means that for both systems all As are the same frequencies: 55Hz, 110Hz, 220Hz, 440Hz, 880Hz, 1760Hz, etc.

I wanted to get a feel for how different they were so I put versions of the above into a spreadsheet and graphed out the differences between the two sets of frequencies.  Here are the results for MIDI notes 45 to 69 (A2 to A4, where A4 = 440Hz).

Temperaments

The octaves are coloured green; fifths are red; and the major third in orange.  You can instantly see that the third is sharper and the fifth slightly flatter in equal compared to “just”.  But the real compromises come at the top end.  The second octave has bigger differences as the frequencies are all twice as big of course.  Percentage wise, I’d guess they are the same.

The Code

I’ve used the code from the Pi Pico PIO Poly Tone MIDI Keyboard but have created two tables of frequencies: midi2tet[] and midi2jst[] using the calculations described above.

for n in range(0, numnotes):
    midi2tet.append(osc[0].calc_pitch(lowfreq*2**(n/12)))
    base = lowfreq * 2 ** (int (n/12))
    midi2jst.append(osc[0].calc_pitch(base * justratios[n % 12]))

Which is used is determined by a “mode” variable which is set by two switches.

if (mode):
    freq = midi2jst[x-lownote]
else:
    freq = midi2tet[x-lownote]
osc[o].note_on(freq)

I’ve used GP6 to set equal temperament mode and GP7 to select just temperament.  I could have just used one switch as a toggle, but might include more modes in the future, and quite liked the idea of being able to use a “toggle” switch at some point in the future too.

btn1 = button1.value()
if (btn1val == True) and (btn1 == False):
    print ("Even Temperament")
    mode = 0
btn1val = btn1

btn2 = button2.value()
if (btn2val == True) and (btn2 == False):
    print ("Just Temperament")
    mode = 1
btn2val = btn2

Everything else is pretty much as it was in the previous project, but I did take the opportunity to refactor out the SimpleMIDIDecoder code and use my module of the same name.

Find it on GitHub here.

Closing Thoughts

This has been an interesting diversion, but I’m still musically chewing over the differences.  I don’t know if it really comes out in the video, but an A major triad in just has a lot less “beating” than in equal, so sounds a lot more “hollow”.  But F and C in “just” definitely sound out of tune.

It is a fun thing to play with.  I know some synthesizers supporting microtuning which allowed experimentation with different temperaments and modes, but this is a fairly cheap way to do something if all you have is a “normal” MIDI keyboard, plus it means you really have to think about the maths to program it into a Pico!

But I must admit that I’m starting to lost my ability to decide which sounds more “in tune”.  There is probably a “bad tempered player” joke in there somewhere.

Fundamentally it makes you realise both how much music and maths are tied together via natural physics, but also quite what an invention equal temperament must have been at the time.

I’d like to experiment with some more scales, but I think if I was to go much further I’d want to switch over to some kind of sine-wave generation (e.g. a wave table of other wise) as it is much easier on the ears!

Kevin

Leave a comment