surge-synthesizer / tuning-library

Micro-tuning format parsing and frequency finding as a header-only C+ library
https://surge-synth-team.org/tuning-library/
MIT License
83 stars 15 forks source link

Inconsistency with Scala when formal octave is different from scale size and mapping size #68

Open arseniiv opened 5 months ago

arseniiv commented 5 months ago

Using 12edo and a KBM file of the form

! Size of map:
4
! MIDI range and references
0
127
60
60
261.625565
! Scale degree to consider as formal octave:
7
! Mapping.
0
2
4
5

makes Scala render these frequencies and quick info about the mapping:

Range         : 0.C  .. 127.G 
Middle        : 60.C 
Reference     : 261.625565 Hertz at note 60.C 
Octave degree : 7
Mapping :
 60.C : 0           0:  60.C            1/1
 61.C#: 2           2:  61.C#           200.000
 62.D : 4           4:  62.D            400.000
 63.Eb: 5           5:  63.Eb           500.000
 64.E : 7           7:  64.E            700.000
|
    0: 0.607670      C -1:-10500.000 cents
    1: 0.682086      C#-1:-10300.000 cents
    2: 0.765616      D -1:-10100.000 cents
    3: 0.811142      D#-1:-10000.000 cents
    4: 0.910476      E -1:-9800.000 cents
    5: 1.021975      F -1:-9600.000 cents
    6: 1.147128      F#-1:-9400.000 cents
    7: 1.215340      G -1:-9300.000 cents
    8: 1.364173      G#-1:-9100.000 cents
    9: 1.531232      A -1:-8900.000 cents
   10: 1.718750      A#-1:-8700.000 cents
   11: 1.820952      B -1:-8600.000 cents
   12: 2.043950      C 0:-8400.000 cents
   13: 2.294256      C#0:-8200.000 cents
   14: 2.575215      D 0:-8000.000 cents
   15: 2.728346      D#0:-7900.000 cents
   16: 3.062464      E 0:-7700.000 cents
   17: 3.437500      F 0:-7500.000 cents
   18: 3.858463      F#0:-7300.000 cents
   19: 4.087899      G 0:-7200.000 cents
   20: 4.588512      G#0:-7000.000 cents
   21: 5.150431      A 0:-6800.000 cents
   22: 5.781163      A#0:-6600.000 cents
   23: 6.124929      B 0:-6500.000 cents
   24: 6.875000      C 1:-6300.000 cents
   25: 7.716927      C#1:-6100.000 cents
   26: 8.661957      D 1:-5900.000 cents
   27: 9.177024      D#1:-5800.000 cents
   28: 10.300861     E 1:-5600.000 cents
   29: 11.562326     F 1:-5400.000 cents
   30: 12.978272     F#1:-5200.000 cents
   31: 13.750000     G 1:-5100.000 cents
   32: 15.433853     G#1:-4900.000 cents
   33: 17.323914     A 1:-4700.000 cents
   34: 19.445436     A#1:-4500.000 cents
   35: 20.601722     B 1:-4400.000 cents
   36: 23.124651     C 2:-4200.000 cents
   37: 25.956544     C#2:-4000.000 cents
   38: 29.135235     D 2:-3800.000 cents
   39: 30.867706     D#2:-3700.000 cents
   40: 34.647829     E 2:-3500.000 cents
   41: 38.890873     F 2:-3300.000 cents
   42: 43.653529     F#2:-3100.000 cents
   43: 46.249303     G 2:-3000.000 cents
   44: 51.913087     G#2:-2800.000 cents
   45: 58.270470     A 2:-2600.000 cents
   46: 65.406391     A#2:-2400.000 cents
   47: 69.295658     B 2:-2300.000 cents
   48: 77.781746     C 3:-2100.000 cents
   49: 87.307058     C#3:-1900.000 cents
   50: 97.998859     D 3:-1700.000 cents
   51: 103.826174    D#3:-1600.000 cents
   52: 116.540940    E 3:-1400.000 cents
   53: 130.812783    F 3:-1200.000 cents
   54: 146.832384    F#3:-1000.000 cents
   55: 155.563492    G 3:-900.000 cents
   56: 174.614116    G#3:-700.000 cents
   57: 195.997718    A 3:-500.000 cents
   58: 220.000000    A#3:-300.000 cents
   59: 233.081881    B 3:-200.000 cents
   60: 261.625565    C 4: 0.000 cents
   61: 293.664768    C#4: 200.000 cents
   62: 329.627557    D 4: 400.000 cents
   63: 349.228231    D#4: 500.000 cents
   64: 391.995436    E 4: 700.000 cents
   65: 440.000000    F 4: 900.000 cents
   66: 493.883301    F#4: 1100.000 cents
   67: 523.251131    G 4: 1200.000 cents
   68: 587.329536    G#4: 1400.000 cents
   69: 659.255114    A 4: 1600.000 cents
   70: 739.988845    A#4: 1800.000 cents
   71: 783.990872    B 4: 1900.000 cents
   72: 880.000000    C 5: 2100.000 cents
   73: 987.766603    C#5: 2300.000 cents
   74: 1108.730524   D 5: 2500.000 cents
   75: 1174.659072   D#5: 2600.000 cents
   76: 1318.510228   E 5: 2800.000 cents
   77: 1479.977691   F 5: 3000.000 cents
   78: 1661.218790   F#5: 3200.000 cents
   79: 1760.000000   G 5: 3300.000 cents
   80: 1975.533205   G#5: 3500.000 cents
   81: 2217.461048   A 5: 3700.000 cents
   82: 2489.015870   A#5: 3900.000 cents
   83: 2637.020455   B 5: 4000.000 cents
   84: 2959.955382   C 6: 4200.000 cents
   85: 3322.437581   C#6: 4400.000 cents
   86: 3729.310092   D 6: 4600.000 cents
   87: 3951.066410   D#6: 4700.000 cents
   88: 4434.922096   E 6: 4900.000 cents
   89: 4978.031740   F 6: 5100.000 cents
   90: 5587.651703   F#6: 5300.000 cents
   91: 5919.910763   G 6: 5400.000 cents
   92: 6644.875161   G#6: 5600.000 cents
   93: 7458.620184   A 6: 5800.000 cents
   94: 8372.018090   A#6: 6000.000 cents
   95: 8869.844191   B 6: 6100.000 cents
   96: 9956.063479   C 7: 6300.000 cents
   97: 11175.303406  C#7: 6500.000 cents
   98: 12543.853951  D 7: 6700.000 cents
   99: 13289.750323  D#7: 6800.000 cents
  100: 14917.240369  E 7: 7000.000 cents
  101: 16744.036179  F 7: 7200.000 cents
  102: 18794.545147  F#7: 7400.000 cents
  103: 19912.126958  G 7: 7500.000 cents
  104: 22350.606812  G#7: 7700.000 cents
  105: 25087.707903  A 7: 7900.000 cents
  106: 28160.000000  A#7: 8100.000 cents
  107: 29834.480737  B 7: 8200.000 cents
  108: 33488.072358  C 8: 8400.000 cents
  109: 37589.090293  C#8: 8600.000 cents
  110: 42192.327285  D 8: 8800.000 cents
  111: 44701.213623  D#8: 8900.000 cents
  112: 50175.415806  E 8: 9100.000 cents
  113: 56320.000000  F 8: 9300.000 cents
  114: 63217.062561  F#8: 9500.000 cents
  115: 66976.144717  G 8: 9600.000 cents
  116: 75178.180587  G#8: 9800.000 cents
  117: 84384.654570  A 8: 10000.000 cents
  118: 94718.572214  A#8: 10200.000 cents
  119: 100350.831611 B 8: 10300.000 cents
  120: 112640.000000 C 9: 10500.000 cents
  121: 126434.125122 C#9: 10700.000 cents
  122: 141917.507060 D 9: 10900.000 cents
  123: 150356.361174 D#9: 11000.000 cents
  124: 168769.309139 E 9: 11200.000 cents
  125: 189437.144428 F 9: 11400.000 cents
  126: 212636.005161 F#9: 11600.000 cents
  127: 225280.000000 G 9: 11700.000 cents

but Surge XT 1.3.0.20a8e16 seems to deviate from those.

Very nice that there’s HTML export, I attached it here: surge_info.zip. Indeed I see degrees of notes in the mapping go ... 7 2 4 5 7 2 4 5 0 2 4 5 7 2 4 5 7 ... which is quite peculiar. In Scala output, degrees (mod 12) are ... .3. 5 7 8 .10. 0 2 3 .5. 7 9 10 .0. 2 4 5 .7. 9 11 0 .2. 4 6 7 .9. ... where periods of the mapping are marked with dots.

I have a theory about what exactly does Scala do, which so far is consistent with what I tested. I’ll add it in another post later.

arseniiv commented 5 months ago

So what Scala seems to do when mapping size is not 0 (because that’s a case which (a) works perfectly and doesn’t use neither mapping pattern nor formal octave and (b) doesn’t mathematically fit with all the other cases):

Let S be the scale size from SCL, M be mapping size from KBM and P be formal octave from KBM.

First for KBM taken alone, it takes KBM’s pattern placed to start on the scale start note, thus mapping M = 4 notes:

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
x  x  x  x  x  x  x  x  0  2  4  5  x  x  x  x  x  x  x  x

Then it repeats the pattern up and down, each time adding or subtracting P = 7:

 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
-14-12-10 -9 -7 -5 -3 -2 0  2  4  5  7  9  11 12 14 16 18 19
 \_________/ \_________/ \_________/ \_________/ \_________/
     -14         -7           0          +7          +14

And only now it finally consults SCL data about which degree is what interval (incidentally using S for that, which didn’t get used before), and also being able to calculate exact frequencies because now it can know the interval for the reference note, so it can calculate the frequency for degree 0 of the scale (which of course may even not appear here in the mapping).

P. S. This should also be applicable to cases when P is larger than S or is negative or zero, really, so probably KBM with P > S might now get accepted!

baconpaul commented 5 months ago

Thank you - this is exactly the detail I needed

arseniiv commented 1 month ago

For our book-keeping (or mine; I’m taking another look at the code and maybe I’ll be brave enough to mess with it!), what in the library code corresponds to which of my variables here.

I also have my own Python implementation but it’s quite noisy because I intended for it to also be universal, plus there’s code to convert (SCL, KBM) pairs into “simplified” (SCL, KBM) pairs where SCL bears all the interval logic and KBM is only used to set up frequencies and nothing more. If you feel interested, I’ll upload it as a Gist though I better just write relevant pseudocode with nothing else, if I won’t end up touching this code here myself.

Addition 2024-07-12: also seeing Tuning.allowTuningCenterOnUnmapped; — my algorithm doesn’t work if the tuning center is unmapped (at least if there’s no filling the blanks but that should depend on having frequencies already which makes a cyclical dependency).

Also I wonder why (-)256 is left as a magic constant when there’s N. Though I don’t immediately have a good suggestion for the name: something like M is too cryptic; N0 means something else, START or BIAS are both too vague and probably already too long…