This is how I got started with 31-edo tuning.
Python script to generate array of frequencies, in this case I keep A = 440 Hz
# generate list of frequencies for 31-edo tuning
from decimal import Decimal, getcontext
from collections import OrderedDict
getcontext().prec = 56
freq_a = 440
# devide the range from freq_a to 2 * freq_a in 31 equal devisions according to equal temperament
step_31_edo = Decimal(2) ** (Decimal(1)/Decimal(31))
print(step_31_edo)
frequencies = [Decimal(freq_a)]
for i in range(0,31):
frequencies.append(frequencies[i] * step_31_edo)
frequencies0 = []
frequencies1 = []
frequencies2 = []
frequencies3 = []
frequencies4 = []
frequencies5 = []
frequencies6 = []
frequencies7 = []
frequencies8 = []
for f in frequencies:
frequencies0.append(float(str(round(f/16,3))))
frequencies1.append(float(str(round(f/8,3))))
frequencies2.append(float(str(round(f/4,3))))
frequencies3.append(float(str(round(f/2,3))))
frequencies4.append(float(str(round(f,3))))
frequencies5.append(float(str(round(f*2,3))))
frequencies6.append(float(str(round(f*4,3))))
frequencies7.append(float(str(round(f*8,3))))
frequencies8.append(float(str(round(f*16,3))))
all_frequencies = frequencies0 + frequencies1 + frequencies2 + frequencies3 + frequencies4 + frequencies5 + frequencies6 + frequencies7 + frequencies8
all_frequencies = list(OrderedDict.fromkeys(all_frequencies)) # remove duplicates, keep ordering
print('f0 = ' +str(frequencies0))
print('f1 = ' +str(frequencies1))
print('f2 = ' +str(frequencies2))
print('f3 = ' +str(frequencies3))
print('f4 = ' +str(frequencies4))
print('f5 = ' +str(frequencies5))
print('f6 = ' +str(frequencies6))
print('f7 = ' +str(frequencies7))
print('f8 = ' +str(frequencies8))
print(' ')
print('freqs = ' + str(all_frequencies))
Use the result in Sonic Pi. I’m setting an offset to 119 where the G3 frequency is in the array and defining a :speel function (dutch word for play) where you can pass an index relative to the offset. so speel [31] will play G4, speel [1] plays G+ (G half sharp). Notes go like this G G+ G# Ab Ad A but it’s easier to think in numbers.
Example melody, first line of Buuvei (lullaby) by Urna Chahar-Tugchi with chords by me:
freqs = [27.5, 28.122, 28.758, 29.408, 30.073, 30.753, 31.448, 32.159, 32.887, 33.63, 34.391, 35.168, 35.963, 36.777, 37.608, 38.459, 39.328, 40.217, 41.127, 42.057, 43.008, 43.98, 44.975, 45.991, 47.031, 48.095, 49.182, 50.294, 51.432, 52.595, 53.784, 55.0, 56.244, 57.515, 58.816, 60.146, 61.506, 62.897, 64.319, 65.773, 67.26, 68.781, 70.336, 71.927, 73.553, 75.216, 76.917, 78.656, 80.435, 82.253, 84.113, 86.015, 87.96, 89.949, 91.983, 94.063, 96.19, 98.365, 100.589, 102.863, 105.189, 107.568, 110.0, 112.487, 115.031, 117.632, 120.292, 123.012, 125.793, 128.637, 131.546, 134.521, 137.562, 140.673, 143.853, 147.106, 150.433, 153.834, 157.312, 160.869, 164.507, 168.227, 172.031, 175.92, 179.898, 183.966, 188.126, 192.38, 196.729, 201.178, 205.727, 210.379, 215.135, 220.0, 224.975, 230.062, 235.264, 240.583, 246.023, 251.586, 257.275, 263.092, 269.041, 275.124, 281.345, 287.707, 294.212, 300.865, 307.668, 314.625, 321.739, 329.014, 336.453, 344.061, 351.841, 359.796, 367.932, 376.251, 384.759, 393.459, 402.356, 411.453, 420.757, 430.271, 440.0, 449.949, 460.123, 470.527, 481.166, 492.046, 503.172, 514.55, 526.184, 538.082, 550.249, 562.691, 575.414, 588.425, 601.73, 615.336, 629.25, 643.478, 658.028, 672.907, 688.122, 703.682, 719.593, 735.864, 752.503, 769.518, 786.918, 804.711, 822.907, 841.514, 860.542, 880.0, 899.898, 920.246, 941.054, 962.333, 984.092, 1006.344, 1029.099, 1052.368, 1076.164, 1100.498, 1125.381, 1150.828, 1176.85, 1203.46, 1230.672, 1258.499, 1286.956, 1316.056, 1345.814, 1376.244, 1407.363, 1439.186, 1471.728, 1505.006, 1539.036, 1573.836, 1609.423, 1645.814, 1683.028, 1721.084, 1760.0, 1799.796, 1840.492, 1882.108, 1924.665, 1968.185, 2012.688, 2058.198, 2104.737, 2152.328, 2200.995, 2250.763, 2301.656, 2353.7, 2406.92, 2461.344, 2516.999, 2573.912, 2632.111, 2691.627, 2752.489, 2814.727, 2878.372, 2943.456, 3010.011, 3078.072, 3147.672, 3218.845, 3291.628, 3366.056, 3442.168, 3520.0, 3599.592, 3680.984, 3764.217, 3849.331, 3936.37, 4025.377, 4116.396, 4209.474, 4304.656, 4401.991, 4501.526, 4603.312, 4707.399, 4813.84, 4922.688, 5033.997, 5147.823, 5264.223, 5383.255, 5504.978, 5629.453, 5756.743, 5886.911, 6020.023, 6156.144, 6295.344, 6437.69, 6583.256, 6732.113, 6884.335, 7040.0, 7199.185, 7361.968, 7528.433, 7698.662, 7872.74, 8050.753, 8232.793, 8418.948, 8609.312, 8803.981, 9003.052, 9206.624, 9414.799, 9627.681, 9845.377, 10067.995, 10295.647, 10528.446, 10766.509, 11009.955, 11258.906, 11513.486, 11773.823, 12040.046, 12312.289, 12590.687, 12875.381, 13166.511, 13464.225, 13768.671, 14080.0]
# G3 op 119
set :offset, 119 # we start in the key of G
# testen akkoordenprogressie
define :speel do |notes_to_play, duration|
for n in notes_to_play do
note = hz_to_midi(freqs[get[:offset] + n])
puts n, note
play note, sustain: duration, attack: 0, release: 0, amp: 0.6
end
sleep duration
end
live_loop :chords do
use_synth :sine
speel [-33, -2, 4, 18], 2.328 + 0.579 # Gb Gb Ad D
speel [-31, 0, 7, 18], 2.148 + 0.218 + 0.417 + 0.178 # G G A# D
speel [-23, 0, 8, 18], 2.391 + 0.442 + 0.223 + 0.333 # Bb G Bb D
speel [-12, 0, 8, 21], 3.408 + 0.247 + 0.348 # D+ G Bb Eb
speel [-30, 1, 11, 19], 4 # G+ G+ B+ D+
sleep 2
end
live_loop :melody do
use_synth :saw
sleep 2.328
speel [18], 0.579 # D
speel [0], 2.148 # G
sleep 0.218
speel [19], 0.417 # D+
speel [14], 0.178 # C+
speel [8], 2.391 # Bb
sleep 0.442
speel [16], 0.223 # Db
speel [26], 0.333 # F
speel [21], 3.408 # Eb
speel [38], 0.247 # A#
speel [36], 0.348 # A
speel [32], 4 # G+
sleep 2
end
Side note: it totally pisses me of that Sonic Pi’s send_midi_note_on function only sends integer values, making it totally useless for microtonal music.
Remark on my side note: actually you can use Surge XT with a custom tuning so (integer) midi notes are mapped to micro tones. You are limited to the 0 - 127 range so with 31 notes per octave that are not many octaves so I needed multiple instances of Surge to get the full range. I have a this python script modified to also generate tuning files for Surge but it’s on my other pc.