euwbah / musescore-xen-tuner

full xenharmonic & microtonal support for musescore! just intonation, regular temperaments, chromatic staves, bohlen pierce, anything at all
GNU General Public License v3.0
35 stars 2 forks source link

Feature Request: SCL parsing #10

Closed tank-trax closed 10 months ago

tank-trax commented 11 months ago

Firstly, I would like to thank you for this tuning plugin for MuseScore. I like it a lot but it is very complex and I have a bit of work to do to figure out how to create just intonation and other non equal scales. I would ideally like to be able to create user scales with the help of an additional tool if possible.

My idea, I was wondering if it would be possible to create a script or feature that could parse SCL files?

If that is not possible, something that could create the .txt or .json files with a sequence of notes using either ratios or cents.

the following example is a scale using ratios:

25/24 9/8 6/5 5/4 4/3 45/32 3/2 8/5 5/3 9/5 15/8 2/1

or using something like this for the same scale but with deviations from equal:

0.00 -29.33 3.91 15.64 -13.69 -1.96 -9.78 1.96 13.69 -15.64 17.6 -11.73

euwbah commented 10 months ago

Hi! I completely understand where you're coming from, it's quite confusing at the moment.

Regarding SCL files, I'm afraid it wouldn't be possible as it won't know what accidentals to automatically use or the reference point of the tunings (since this plugin basically assumes none of the conventional 'music theory' facts: 7 nominals, octave equivalence, enharmonic equivalences, etc...)

Once I have enough time, I'm planning a GUI/web interface for making tuning config files. Unfortunately I haven't had a lot of free time since I made this, still half-finished, plugin.

From what I understand, you want to have a standard 12 edo notation system using the standard sharps and flats and assign the 12 semitones' spellings to the respective ratios? Assuming A4 is the reference tuning note at 440 Hz, the tuning config would look something like this:

// 12-note JI tuning. 1/1 = A4 = 440 Hz.
//
// 1/1 25/24 9/8 6/5 5/4 4/3 45/32 3/2 8/5 5/3 9/5 15/8

A4: 440

// the nominal & accidental chain tunings don't matter because all of them
// will be overriden by the tuning override

// When generating tuning overrides, make sure the first nominal declared
// matches the reference note of the tuning config!
0 0 0 0 0 0 0 2/1

bb b (0) # x
// empty 2nd accidental chain below, because python script assumes 2nd acc chain of ups and downs, 
// but 12edo doesn't need them
(0)

displaysteps(12, below)

aux(0)
aux(0,1)

override()
0 -2 0 9/5/(2)
0 -1 0 15/8/(2)
0 0 0 1/1
0 1 0 25/24
0 2 0 9/8
1 -2 0 1/1
1 -1 0 25/24
1 0 0 9/8
1 1 0 6/5
1 2 0 5/4
2 -2 0 25/24
2 -1 0 9/8
2 0 0 6/5
2 1 0 5/4
2 2 0 4/3
3 -2 0 6/5
3 -1 0 5/4
3 0 0 4/3
3 1 0 45/32
3 2 0 3/2
4 -2 0 4/3
4 -1 0 45/32
4 0 0 3/2
4 1 0 8/5
4 2 0 5/3
5 -2 0 45/32
5 -1 0 3/2
5 0 0 8/5
5 1 0 5/3
5 2 0 9/5
6 -2 0 8/5
6 -1 0 5/3
6 0 0 9/5
6 1 0 15/8
6 2 0 1/1*(2)

Because the #/b accidentals will affect the tuning differently based on which nominal/'note alphabet' it is being attached to, so you will need to approach it the same way as notating a NEJI, using the override() declaration, which unfortunately, I haven't had time to document them yet.

The way it works is you specify the usual 12edo tuning config, and make use of tuning overrides which are currently autogenerated with the generate-overrides.py python script, which specifies a tuning for every unique pitch spelling.

For the above tuning config, I've modified the python script to look like this:

import math

# MODIFY THIS:
NUM_NOTES = 12

# MODIFY THIS: (can use javascript expression to stretch cents)
# If neji_tunings are specified in cents, specify this in cents
# as well.
#
# e.g. '1200c', instead of '2/1'
EQUAVE_INTERVAL = '2' # 2/1 equave

# MODIFY THIS:
# Tunings must all be of the same type (all ratios or all cents).

neji_tunings = [
 '1/1', '25/24', '9/8', '6/5', '5/4', '4/3', '45/32', '3/2', '8/5', '5/3', '9/5', '15/8'
]

assert(len(neji_tunings) == NUM_NOTES)

apotome = 1 # MODIFY THIS no. of edosteps for 2187/2048 (sharp/apotome)
limma = 1 # MODIFY THIS no. of edosteps for 256/243 (diatonic semitone)

tone = apotome + limma

# sharps/flats chain degrees
acc_chain1 = range(-2,3) # -2 to 2 inclusive, double flats to double sharps.
acc_chain1_step_size = apotome # apotome size 4

# ups/downs chain degrees
acc_chain2 = range(0, 1) # Only 0. No need for ups or downs in 12edo.
acc_chain2_step_size = 0 # up/downs are 1 step

# MODIFY THIS
# As per the tuning config, the reference note is A4.
# If the reference note is changed, the 'mode' of the nominals
# must also change accordingly.
# tone = Diatonic "whole step"
# limma = Diatonic "half step"
nominals_steps = [0, tone, tone+limma, 2*tone+limma, 3*tone+limma, 3*tone+2*limma, 4*tone+2*limma]

print('override()')
for nom_idx, nom_steps in enumerate(nominals_steps):
    for acc1 in acc_chain1:
        for acc2 in acc_chain2:
            acc1_steps = acc1 * acc_chain1_step_size
            acc2_steps = acc2 * acc_chain2_step_size
            neji_steps = acc1_steps + acc2_steps + nominals_steps[nom_idx]
            mod_steps = neji_steps % NUM_NOTES
            equave_offset = math.floor(neji_steps / NUM_NOTES)
            equave_offset_str = ''
            if equave_offset > 0:
                equave_offset_str = f'*({EQUAVE_INTERVAL})' * equave_offset
            elif equave_offset < 0:
                equave_offset_str = f'/({EQUAVE_INTERVAL})' * -equave_offset

            print(f'{nom_idx} {acc1} {acc2} {neji_tunings[mod_steps]}{equave_offset_str}')