Godtone (talk | contribs)
add option to disable direct mapping; this changes the order of only the last parameter which i suspect wasnt used by anyone in argument order; also makes the omission of et2 make more sense later on and also makes more sense in general as specifying the et2 suggests using a mapping may not be desirable
Godtone (talk | contribs)
 
(36 intermediate revisions by the same user not shown)
Line 4: Line 4:
== [[User:Godtone/Bird's eye view of temperaments by accuracy|Bird's eye view of temperaments by accuracy]] (W.I.P) ==
== [[User:Godtone/Bird's eye view of temperaments by accuracy|Bird's eye view of temperaments by accuracy]] (W.I.P) ==
If you want to contribute make sure the temperaments make sure to follow the format, such as specifying the exact note count needed for a set of odds separately from the smallest MOS scale that has at least or more than that number of notes. The focus is on practically-useful temperaments of general interest. Many temperaments have not yet been documented there. It's supposed to be a compilation of "cream of the crop" basically, sorted by accuracy so that you can determine your own accuracy-to-simplicity tradeoff that is best for you, but note many higher-accuracy temperaments there are simple ''despite'' being accurate, so I recommend looking at the note counts and set of targetted odds for higher-accuracy stuff first.
If you want to contribute make sure the temperaments make sure to follow the format, such as specifying the exact note count needed for a set of odds separately from the smallest MOS scale that has at least or more than that number of notes. The focus is on practically-useful temperaments of general interest. Many temperaments have not yet been documented there. It's supposed to be a compilation of "cream of the crop" basically, sorted by accuracy so that you can determine your own accuracy-to-simplicity tradeoff that is best for you, but note many higher-accuracy temperaments there are simple ''despite'' being accurate, so I recommend looking at the note counts and set of targetted odds for higher-accuracy stuff first.
== zeta and optimal_edo_sequence ==
On a separate subpage I've shown the best scoring equal temperaments according to the zeta function, using a small modification to the code kindly provided at [[User:Sintel/Zeta plot python]]:
* [[User:Godtone/zeta]]
Whose significance is backed up by the more psychoacoustically-informed tuning metrics I've designed (<code>optimal_edo_sequence</code>s from [[#My Python 3 code]]):
* [[User:Godtone/optimal edo sequences]] (and [[User:Godtone/strict_optimal_edo_sequences]])
...as well as by my own knowledge of tuning theory.


== Simple ratios and where I think limits should be drawn ==
== Simple ratios and where I think limits should be drawn ==
Line 98: Line 105:
* 31: The next EDO that melodically approximates the 11-limit, and considerably better. Extremely nice arrangement of intervals that feels weirdly intuitive and ideal. Colourful EDO. Basically ideal meantone tuning as more notes than this is overkill for meantone if you don't specifically want meantone.
* 31: The next EDO that melodically approximates the 11-limit, and considerably better. Extremely nice arrangement of intervals that feels weirdly intuitive and ideal. Colourful EDO. Basically ideal meantone tuning as more notes than this is overkill for meantone if you don't specifically want meantone.
* 32: 16 EDO with a sharp fifth. I like it primarily because of it being a power of 2. Exploration into this EDO could be interesting. 80 EDO offers a reasonably good approximation of it through a 16L16s MOSS.
* 32: 16 EDO with a sharp fifth. I like it primarily because of it being a power of 2. Exploration into this EDO could be interesting. 80 EDO offers a reasonably good approximation of it through a 16L16s MOSS.
* 34: The first good approximation of the 5-prime-limit due to being the first reasonably accurate tuning of [[Kleismic family|kleismic]] and [[srutal archagall]] which are IMO the best 5-limit temperaments that observe the [[81/80|syntonic comma]]. 19 is also a tuning for kleismic but feels like it doesn't do justice to the accuracy and pristineness of kleismic to me, plus its harmonic interpretation is pretty lacking. And 22 is very obviously to Has the sharp 3/2's of 17 EDO, and as 17 EDO is a good colour system, 34 EDO is a natural extension. Also is a very logical "completion" of 17 due to giving a very logical 2.3.5.13.17(.23)-subgroup interpretation of the sqrt(2).sqrt(3) subgroup with some really intriguing possibilities. If you're lacking in inspiration and its wide array of supported MOSSes aren't inspiration enough, try taking a look at the diaschismic-tetracot continuum ([[2048/2025]])<sup>n</sup> / ([[20000/19683]]).
* 34: The first good approximation of the 5-prime-limit due to being the first reasonably accurate tuning of [[Kleismic family|kleismic]] and [[srutal archagall]] which are IMO the best 5-limit temperaments that observe the [[81/80|syntonic comma]]. 19 is also a tuning for kleismic but feels like it doesn't do justice to the accuracy and pristineness of kleismic to me, plus its harmonic interpretation is pretty lacking. And 22 is very obviously too high-damage for srutal archagall. Has the sharp 3/2's of 17 EDO, and as 17 EDO is a good colour system, 34 EDO is a natural extension. Also is a very logical "completion" of 17 due to giving a very logical 2.3.5.13.17(.23)-subgroup interpretation of the sqrt(2).sqrt(3) subgroup with some really intriguing possibilities. If you're lacking in inspiration and its wide array of supported MOSSes aren't inspiration enough, try taking a look at the diaschismic-tetracot continuum ([[2048/2025]])<sup>n</sup> / ([[20000/19683]]).
* 35: A subset of 140 EDO, a ridiculously strong generalist system, which endows it with magical tuning qualities.
* 35: A subset of 140 EDO, a ridiculously strong generalist system, which endows it with magical tuning qualities.
* 36: Because of being a superset of 12, quite overlooked. It is actually a very good subgroup temperament! A natural extension of 12 EDO's colour palette, preferring to avoid the neutral and semi- intervals of 24 EDO. I should note though that while both 24 and 36 are reasonably good systems, I do not think they should be used together, as there are preferable EDOs in the high end range, such as 80 EDO.
* 36: Because of being a superset of 12, quite overlooked. It is actually a very good subgroup temperament! A natural extension of 12 EDO's colour palette, preferring to avoid the neutral and semi- intervals of 24 EDO. I should note though that while both 24 and 36 are reasonably good systems, I do not think they should be used together, as there are preferable EDOs in the high end range, such as 80 EDO.
Line 422: Line 429:
| [[2L 7s]] || joanatonic || jo- || jo || From [[joan]] temperament.
| [[2L 7s]] || joanatonic || jo- || jo || From [[joan]] temperament.
|-
|-
| [[4L 5s]] || orwelloid || or- || or || From [[orwell]] temperament.
| [[4L&nbsp;5s]] || gramitonic || gram- || gm || From "grave minor third". Formerly "orwelloid".
|-
|-
| [[5L 4s]] || semiquartal || sequar- || seq || From ''half-fourth''.
| [[5L 4s]] || semiquartal || sequar- || seq || From ''half-fourth''.
Line 459: Line 466:


== My Python 3 code ==
== My Python 3 code ==
IMPORTANT NOTE: there seems to be a bug for subgroup mappings at the moment, pending investigation, but ideally usage of subgroups should be made far easier too:
IMPORTANT NOTE: there seems to be bugs for subgroup mappings at the moment, pending investigation, but ideally usage of subgroups should be made far easier too:
<syntaxhighlight lang="python">
<syntaxhighlight lang="python">
>>> sg = [2, 3, 7, 11, 13, 17, 19]
>>> sg = [2, 3, 7, 11, 13, 17, 19]
Line 599: Line 606:
def sub_iv(r1, r2):
def sub_iv(r1, r2):
return iv( r1[0]*r2[1] - r2[0]*r1[1], r1[1]*r2[1] )
return iv( r1[0]*r2[1] - r2[0]*r1[1], r1[1]*r2[1] )
# IMPORTANT: assume x and y are nonnegative!
def iv_greater_than(x, y):
result = div_iv(x,y)
return result[0] > result[1]
def iv_greater_or_equal(x, y):
result = div_iv(x,y)
return result[0] >= result[1]
def iv_less_than(x, y):
return not iv_greater_or_equal(x, y)
def iv_less_or_equal(x, y):
return not iv_greater_than(x, y)
def as_float(x, p=None):
def as_float(x, p=None):
if type(x)==list: # monzo (factored)
if type(x)==list: # monzo (factored)
Line 1,225: Line 1,245:
print(edo,'EDO interpretation')
print(edo,'EDO interpretation')
if not sg:
if not sg:
sg = lim(ol)
sg = lim(max(ol,max(add)))
odds = [odd for odd in range(1,ol+1,2) if in_subgroup(odd,sg) and odd not in no] + add
odds = [odd for odd in range(1,ol+1,2) if in_subgroup(odd,sg) and odd not in no] + add
sedo = 0
sedo = 0
Line 1,326: Line 1,346:
# and not underweighting simpler intervals and accounting for that more complex intervals will tend to be used
# and not underweighting simpler intervals and accounting for that more complex intervals will tend to be used
# in a harmonic/chordal context which justifies their otherwise-higher effective felt error through templating
# in a harmonic/chordal context which justifies their otherwise-higher effective felt error through templating
if weighting==1 or weighting in ['unweighted','trivial','basic']:
if type(weighting)==str:
weighting = lambda x: 1
if weighting in [None,'none','unweighted','trivial','basic','constant']:
elif weighting=='sensitive':
weighting = 0
weighting = lambda x: iv_complexity(x)**2
elif weighting in ['','default','proportional']:
weighting = 1
elif weighting in ['sensitive','dyadic']:
weighting = 2
elif weighting=='simple':
weighting = -0.5 # as we may square after when doing a MSE, we use the square root
if type(weighting) in [int,float]:
weighting = lambda x,w=weighting: iv_complexity(x)**w
global ivs_cache
global ivs_cache
global ivs_int_cache
global ivs_int_cache
Line 1,373: Line 1,400:
# alternatively, feed the real number of divisions of the octave that generates your val for each val you want to compare.   
# alternatively, feed the real number of divisions of the octave that generates your val for each val you want to compare.   
global patent_vals
global patent_vals
if type(v)==int:
if type(v)==int and v:
if v in patent_vals:
if v in patent_vals:
v = patent_vals[v]
v = patent_vals[v]
Line 1,381: Line 1,408:
elif type(v)==float:
elif type(v)==float:
v = val(lim(255),ed(v))
v = val(lim(255),ed(v))
elif not mapping and not v:
pass # not using a val
elif mapping and not v:
raise(Exception('mapping=True but no val v is given'))
# deduce the number of arguments in the badness function to allow only using the first n arguments as needed
# deduce the number of arguments in the badness function to allow only using the first n arguments as needed
num_args = badness.__code__.co_argcount
num_args = badness.__code__.co_argcount
Line 1,404: Line 1,435:
# IMPORTANT: on Jan 9 i corrected rel_err**2 * et2 to rel_err**2 * et2**2 in et_badness which optimal_edo_sequence depends on;
# IMPORTANT: on Jan 9 i corrected rel_err**2 * et2 to rel_err**2 * et2**2 in et_badness which optimal_edo_sequence depends on;
#            strict_optimal_edo_sequence is unaffected however.
#            strict_optimal_edo_sequence is unaffected however.
def optimal_edo_sequence(ivs_or_edo_badness,edo_set=range(2,311+1),weighting=lambda x: iv_complexity(x),combine='avg',mapping=True):
def optimal_edo_sequence(ivs_or_edo_badness,edo_set=range(2,311+1),weighting=lambda x: iv_complexity(x),combine='avg',mapping=True,times_better=1+2**-29):
et_badness_judger = ivs_or_edo_badness
et_badness_judger = ivs_or_edo_badness
if type(ivs_or_edo_badness) in [int,set,list]: # user gave intervals (default et_badness)
if type(ivs_or_edo_badness) in [int,set,list]: # user gave intervals (default et_badness)
Line 1,414: Line 1,445:
for edo in edo_set:
for edo in edo_set:
current = et_badness_judger(edo)
current = et_badness_judger(edo)
if current < best_edo:
if current < best_edo * times_better:
best_edo = current
best_edo = current
best_edos.append(edo)
best_edos.append(edo)
Line 1,425: Line 1,456:
return optimal_edo_sequence(lambda edo: et_badness(ivs,edo,lambda rel_err: rel_err**2,weighting,combine,mapping),edo_set)
return optimal_edo_sequence(lambda edo: et_badness(ivs,edo,lambda rel_err: rel_err**2,weighting,combine,mapping),edo_set)


# returns a set of odds in the subgroup that are sorted by their octave-reduced size
# which map to every distinct number of steps/degrees of the val given, therefore,
# dwarf(lim(p),N) is the set of odds for the dwarf scale of N EDO in the p-limit
def dwarf(sg,v):
if type(v)==int: # to be able to specify N for the patent val for N EDO
v = val( lim(max(sg)), ed(v) )
result = [0] * v[0] # implies you can have a tritave dwarf if sg and v agree on the subgroup
odd = 1
while 0 in result:
if in_subgroup(odd,sg) and result[ map_iv(v,(odd,1)) % v[0] ]==0:
result[ map_iv(v,(odd,1)) % v[0] ] = odd
odd += 2
return result
def scalestr(liststr):
commastrs = []
if ',' in liststr:
commastrs = [comma.strip() for comma in liststr.split(',')]
else:
commastrs = liststr.split()
return [iv( int(comma.split('/')[0]), int(comma.split('/')[1]) ) for comma in commastrs]
# find positive (possibly contorted) comma for a^n ~ b and by default print the result
def continuumpt(a,b,n,printcomma=1): # assumes n >= 0
comma = div_iv( (a[0]**n[0], a[1]**n[0]), (b[0]**n[1], b[1]**n[1]) )
if comma[0] < comma[1]:
comma = recip_iv(comma)
if printcomma==1:
return print(striv(comma))
elif printcomma>=2:
print('['+' '.join([ str(k) for k in fact(comma) ])+']',end='')
if printcomma==2:
return print()
elif printcomma==3:
return print(' = '+striv(comma))
else:
return comma
# note: the default can be used to get n octaves of an interval set (ivs) contained in the 1/1 to 2/1 range
def interleave(n,ivs,offset=(2,1)): # https://en.xen.wiki/w/Interleaving
totalivs = []
for i in range(n):
totalivs = totalivs + [mul_iv( *([x] + [offset]*i) ) for x in ivs if x not in totalivs]
totalivs.sort(key=lambda x: as_float(x))
return totalivs
def mediant_path(x):
x = convert(x,tuple)
x = iv(x[0], x[1])
bottom, middle, top = (0, 1), (1, 1), (1, 0)
result = ''
while x != middle:
if iv_less_than(x, middle):
result += 'D'
top = middle
else:
result += 'U'
bottom = middle
middle = (bottom[0] + top[0], bottom[1] + top[1])
return result
# the length of the mediant path of whichever octave-revoicing gives the interval x the least length
def mediant_complexity(x,revoicing_octs=7):
mincomplexity = 2**30
for octs in range(revoicing_octs+1):
mincomplexity = min(mincomplexity,len(mediant_path( div_iv(x,(2**octs,1)) )))
mincomplexity = min(mincomplexity,len(mediant_path( mul_iv(x,(2**octs,1)) )))
return mincomplexity
# showmode: minimum number of equated interval pairs (incl. distinct octave-complements).
# if minimum isnt reached, only the spacing interval is shown. 0 means dont print anything.
# for odd-limits, to look for "multiple indistinction commas", use 3 for showmode.
def spaces_in_set(ivs,max_cents = 100,showmode = 1):
spaces = dict()
if type(max_cents) in [tuple,float,int]:
max_cents = steps(max_cents,ed(1200))
for lower in ivs:
for upper in ivs:
diff = div_iv(upper,lower)
if 0 < steps(diff,ed(1200)) <= max_cents:
if diff not in spaces:
spaces[diff] = [(upper,lower)]
else:
spaces[diff].append((upper,lower))
commas = [comma for comma in spaces]
commas.sort(key=lambda x: steps(x,1))
if showmode:
for comma in commas:
print(striv(comma)+':',', '.join([
'('+striv(ivpair[0])+')/('+striv(ivpair[1])+')' for ivpair in spaces[comma]
if len(spaces[comma])>=showmode
]))
print()
return spaces
# there is only finitely many EDOs which provide some simplification of a set of intervals as contrasted to all-distinct
def efficient_edos( n, inconsistencies=0, min_simplifications=1, edos=range(1,1000) ):
if type(n)==int:
n = odd_lim(n)
elif type(n)==list and type(n[0])==int:
n = odd_lim(1,[],n)
results = []
for edo in edos:
v = edo
if type(v)==int:
v = val( lim(max([ prime_idx(len(fact(x))-1) for x in n ])), ed(edo) )
# else v is assumed to be a mapping
m = dict()
for x in n: # collect mappings of intervals
sedo = map_iv(v,x)
if sedo in m:
m[sedo].append(x)
else:
m[sedo] = [x]
if len(inconsistent_ivs_by_val(n,v)) <= inconsistencies:
if len(n) - len([ sedo for sedo in m ]) >= min_simplifications:
results.append(edo)
return results
</syntaxhighlight>
</syntaxhighlight>


Line 1,487: Line 1,637:
The code and usage/debugging instructions are the same as for the Novation Launchpad Pro MK3, so see [[#Novation Launchpad Pro MK3 isomorphic keyboard code]].
The code and usage/debugging instructions are the same as for the Novation Launchpad Pro MK3, so see [[#Novation Launchpad Pro MK3 isomorphic keyboard code]].


Note that in theory, any Launchpad with programmer mode should work, so any MK3 model such as the Launchpad X and the cheaper (but not velocity-sensitive) mini version of that should work.
Note that any Launchpad with programmer mode should work, so any MK3 model such as the Launchpad X and the cheaper (but not velocity-sensitive) mini version of that should work. The Launchpad X has successfully been used as an isomorphic keyboard with this code, but currently requires manually specifying the MIDI ports; how to do this is explained below.


A launchpad X is potentially recommendable as having more comfortable/similar side/control buttons so that you get what feels more like a proper 9x9 grid (also due to being less costly), as the Pro MK3 has hard side-buttons that require significantly more pressure, though in both cases note that these buttons do not have pressure sensitivity so depend on the default velocity outputted by the program.
A launchpad X is potentially recommendable as having more comfortable/similar side/control buttons so that you get what feels more like a proper 9x9 grid (also due to being less costly), as the Pro MK3 has hard side-buttons that require significantly more pressure, though in both cases note that these buttons do not have pressure sensitivity so depend on the default velocity outputted by the program.
Line 1,496: Line 1,646:
This code is licensed under the AGPLv3 (https://www.gnu.org/licenses/agpl-3.0.html), a version of the [[wikipedia:Affero General Public License|AGPL]] corresponding to the [[wikipedia:GNU General Public License#Version 3|GPLv3]].
This code is licensed under the AGPLv3 (https://www.gnu.org/licenses/agpl-3.0.html), a version of the [[wikipedia:Affero General Public License|AGPL]] corresponding to the [[wikipedia:GNU General Public License#Version 3|GPLv3]].


'''It should be compatible with any Launchpad that has programmer mode; it has been confirmed to work for the Launchpad X so will likely also work for a Launchpad Mini MK3.'''
'''It should be compatible with any Launchpad that has [[#Putting the Launchpad in programmer mode|programmer mode]]; it has been confirmed to work for the Launchpad X so will likely also work for a Launchpad Mini MK3.'''


NOTE: I discovered it was possible to use custom colour palettes after I'd spent a lot of time making these rainbows using the rather limited and uneven set of factory colours. I haven't tested using this yet, but for those who want to take that route to achieve a more even rainbow, here is the link: https://fw.mat1jaczyyy.com/
ALSO: This should go without saying but take care of your launchpad! If you wipe it don't expect it to be properly responsive until fully dry and even then wiping it can have risks if the liquid goes inside. Similarly, do not bang it and probably don't leave it upside down (especially if for long periods of time) in case it causes the velocity sensitivity to go weird. Treat it as an instrument. If you do all this, it should be fine to use it for its intended purpose: just don't press ''too'' hard; try not press with more velocity than the buttons actually require, if possible. (Because of my silliness (not heeding such obvious procedures) there is a possibility that I may have weared my launchpad slightly in that I think I now require more pressure for the pressure-sensitive pads. I thought it responsible to add this note about taking care of your launchpad, as we are technically using it in a nonstandard way by treating the buttons as though they were just insensitive pads that require extra velocity/pressure.)


ALSO: This should go without saying but take care of your launchpad! If you wipe it don't expect it to be properly responsive until fully dry and even then wiping it can have risks if the liquid goes inside. Similarly, do not bang it and probably don't leave it upside down (especially if for long periods of time) in case it causes the velocity sensitivity to go weird. Treat it as an instrument. If you do all this, it should be fine to use it for its intended purpose: just don't press ''too'' hard; try not press with more velocity than the buttons actually require, if possible. (Because of my silliness (not heeding such obvious procedures) there is a possibility that I may have weared my launchpad slightly in that I think I now require more pressure for the pressure-sensitive pads. I thought it responsible to add this note about taking care of your launchpad, as we are technically using it in a nonstandard way by treating the buttons as though they were just insensitive pads that require extra velocity/pressure.)
IMPORTANT: for the Python 3 code to work, it requires the launchpad to be plugged in with updated firmware and be in [[#Putting the Launchpad in programmer mode|programmer mode]]! Also requires [https://www.tobias-erichsen.de/software/loopmidi.html loopMIDI] on Windows.
 
=== Installation issues/troubleshooting strategies/recommendations ===
The most potentially difficult part is that depending on what OS you are using, how you have Python installed, what version, etc. there can be complications so that running <code>pip install rtmidi</code> and <code>pip install mido</code> in a terminal does not work, or only appears to work so that there's issues when the code tries to use mido. I list potential issues below, so please read carefully for something applicable to your case if it isn't going smoothly.


IMPORTANT: for the Python 3 code to work, it requires the launchpad to be plugged in with updated firmware and be in programmer mode! Also requires loopMIDI on Windows.
'''IMPORTANT: A strange error has been found: Python 3.14 and later versions have removed a functionality used by mido internally, so downgrading to Python 3.12 is required if nothing here works.'''


=== Installation issues/troubleshooting strategies/recommendations: ===
'''IMPORTANT:''' Before deeming an installation of mido/rtmidi to "not be working", make sure that the issue is not just one of what MIDI ports are being autoselected; see (6.) onwards for details.
The most potentially difficult part is that depending on what OS you are using, how you have Python installed, what version, etc. there can be complications so that running <code>pip install rtmidi</code> in a terminal does not work. I list potential issues below, so please read carefully for something applicable to your case if it isn't going smoothly.


0. Technically not an issue with rtmidi, but an easy one to solve: you forgot to update the firmware of your launchpad to be up-to-date via the official site: https://components.novationmusic.com/
0. Technically not an issue with rtmidi, but an easy one to solve: you forgot to update the firmware of your launchpad to be up-to-date via the official site: https://components.novationmusic.com/
Line 1,513: Line 1,665:
2. If you installed Python 3 but it doesn't work on command line, maybe you haven't used an official installer, as that usually shouldn't be an issue, or if you did use an official installer, you may not have marked the checkbox that makes it change PATH environment variables for you automatically, in which case adding the location of the Python 3 (and potentially pip) executables should fix it.
2. If you installed Python 3 but it doesn't work on command line, maybe you haven't used an official installer, as that usually shouldn't be an issue, or if you did use an official installer, you may not have marked the checkbox that makes it change PATH environment variables for you automatically, in which case adding the location of the Python 3 (and potentially pip) executables should fix it.


3. <code>rtmidi</code> seems to install but there's issues when trying to use it; installing <code>python-rtmidi</code> may help in such a case. That's why I have "pip install mido && pip install python-rtmidi" with a note about the loopMIDI requirement on Windows as the default/simplest installation recommendation present at the beginning of the code where I <code>import mido</code>.
3. <code>rtmidi</code> doesn't seem to exist when trying to install it; try installing <code>python-rtmidi</code> instead.
 
3.1. <code>rtmidi</code> seems to install but there's issues when trying to use it; installing <code>python-rtmidi</code> may help in such a case; if that still doesn't help, try uninstalling rtmidi (<code>pip uninstall rtmidi</code>) and following the instructions of (7.1.). {{nowrap| (As a note of my experience: }} in the code, as a comment, I have <code>pip install mido && pip install python-rtmidi</code> (with a note about the loopMIDI requirement on Windows) as the default/simplest installation goal/recommendation, present at the beginning of the code where I <code>import mido</code>, because lately it seems that's the most reliable, however some systems may need <code>rtmidi</code> instead of <code>python-rtmidi</code> or may need both; I'm not sure what the most common case is.)


4. If you are getting a long error where towards the end there is <code>error: Microsoft Visual C++ 14.0 or greater is required.</code> (which might happen if you are using an older version of Windows) then try <code>pip install --only-binary :all: rtmidi</code> which will save you a huge amount of trouble if it works. Versions apparently should be available for almost every relevant version of Python 3, so if it isn't, try a different version of Python 3 if you know how. If your system doesn't use Python 3 and you installed it, it should definitely be safe to uninstall the current version.
4. If you are getting a long error where towards the end there is <code>error: Microsoft Visual C++ 14.0 or greater is required.</code> (which might happen if you are using an older version of Windows) then try <code>pip install --only-binary :all: rtmidi</code> which will save you a huge amount of trouble if it works. Versions apparently should be available for almost every relevant version of Python 3, so if it isn't, try a different version of Python 3 if you know how. If your system doesn't use Python 3 and you installed it, it should definitely be safe to uninstall the current version.
Line 1,519: Line 1,673:
4.1. I haven't seen this issue, but possibly if <code>pip install --only-binary :all: rtmidi</code> seems to work but then brings an error when trying to use the code, try <code>pip install --only-binary :all: python-rtmidi</code> in case it helps.
4.1. I haven't seen this issue, but possibly if <code>pip install --only-binary :all: rtmidi</code> seems to work but then brings an error when trying to use the code, try <code>pip install --only-binary :all: python-rtmidi</code> in case it helps.


5. If you are on Windows, make sure you have [https://www.tobias-erichsen.de/software/loopmidi.html loopMIDI] installed and running with the default port name! (The default name should work; the code searches for a name starting with "loop".)
5. If you are on Windows, make sure you have [https://www.tobias-erichsen.de/software/loopmidi.html loopMIDI] installed and running with the default port name! (The default name should work; the code searches for the first name starting with "loop".) Though it hasn't been observed, if loopMIDI itself doesn't seem to be working, check that it's not a firewall issue.
 
6. '''If after all these remedies it doesn't work,''' then it's likely because the attempt at autoselecting the name of the MIDI input and MIDI output failed. To fix this:
 
6.0. A trivial but easy to forget fix: make sure that the USB you're using isn't faulty by trying a different USB that you can confirm works/isn't faulty.
 
6.1. First check that the Launchpad, ''after being set into programmer mode'' (see [[#Putting the Launchpad in programmer mode]]), is responsive, by using it as a MIDI input on [[Scale Workshop]] and checking if one of the Launchpad-related options makes Scale Workshop clearly press/release notes based on what you are pressing/releasing (so not as a mess of vaguely correlated noisy data, but as obviously directly responding to you touching the Launchpad). If this doesn't work (no [[#Success indicators]]), see (step 0.) and try restarting your computer (which is much more likely to be a relevant fix if you are using Windows).
 
6.2. Look at the output of running <code>iso()</code>; specifically, it'll tell you <code>midi outputs detected:</code> followed by a list of the ''text strings'' that correspond to valid MIDI outputs detected (here meaning valid devices/targets which the code can ''output''/''send'' MIDI data to); so you want to find the one that corresponds to telling the Launchpad how to light up, EG {{nowrap| <code>iso(midi_out_col{{=}}'MIDIOUT2 (LPX MIDI) 2')</code> }} if you saw {{nowrap| <code>MIDIOUT2 (LPX MIDI) 2</code> }} in the list.  If this is the only fix that was needed, you should see all [[#Success indicators]].
 
6.3. '''If this did not fix it,''' you should get a new error with new information to help you; you should see <code>midi_inputs_detected:</code> followed by a list of the ''text strings'' that correspond to valid MIDI inputs (here meaning valid devices/targets that the code can ''input''/''read'' MIDI data from); so you want to find the one that corresponds to receiving the data about what pads and buttons are pressed on the Launchpad, EG {{nowrap| <code>iso(midi_out_col{{=}}'MIDIOUT2 (LPX MIDI) 2',midi_input{{=}}'MIDIIN2 (LPX MIDI) 1')</code> }} if you saw {{nowrap| <code>MIDIIN2 (LPX MIDI) 1</code> }} in the list, where note we need to keep the {{nowrap| <code>midi_out_col=...</code> }} part so that we don't go back to failing at the first error. Again, if this was the last fix that was needed, you should see all [[#Success indicators]]. If you ''still'' can't get it working, see the final remedies below:
 
7. '''Final remedies''' (mainly for Windows being janky):
 
7.1. Try uninstalling and reinstalling <code>mido</code> and <code>python-rtmidi</code> with <code>pip</code> (or if on your system that corresponds to Python 2 rather than Python 3, then with <code>pip3</code> instead):
<pre>
pip uninstall python-rtmidi
pip uninstall mido
pip install mido
pip install python-rtmidi
</pre>
 
7.2. Restart the system (this is especially likely to fix things if you use Windows). (Possibly followed by uninstalling and reinstalling if that didn't fix it or fix something at least.)


'''Mix and match these solutions as you find them potentially relevant to your situation, and hopefully you should be able to get it working.'''
7.3. Try installing (or if you did that, uninstalling and reinstalling) <code>rtmidi</code> in case it helps (note the distinctness from <code>python-rtmidi</code>). I vaguely recall this being a valid installation target in some cases. However, if installing <code>rtmidi</code> is valid (it installs) but it doesn't help, uninstall it and then uninstall and reinstall <code>python-rtmidi</code> and <code>mido</code> as shown in (7.1) above.
 
'''Mix and match these solutions as you find them potentially relevant to your situation, and hopefully you should be able to get it working.''' Also, remember, the order of doing these can matter; for example, if the installation is subtly bugged for whatever incomprehensible reason, then that'll invalidate all the other steps you took so you'll have to do them again with a non-bugged installation.


Feel free to send me a friend request to ask me for help on Discord (<code>@osmiumic</code>).
Feel free to send me a friend request to ask me for help on Discord (<code>@osmiumic</code>).
==== Putting the Launchpad in programmer mode ====
'''Launchpad X''': Once plugged in by USB and on, "hold Session, then press bottom Scene Launch button" (which I think should be orange?); the pads may flash to display a running text saying "Program" for confirmation. Once successfully put in programmer mode, the Launchpad should become unlit and appear unresponsive. '''This means it worked.''' To get it out of programmer mode and into the default mode, simply unplug it and plug it in again.
'''Launchpad Pro MK3:''' Once plugged in by USB and on, hold the small "Setup" button (bottom left) and press and release the "Print to Clip" button (bottom right). The Launchpad should become unlit and appear unresponsive. '''This means it worked.''' To get it out of programmer mode and into the default mode, simply unplug it and plug it in again.
==== Success indicators ====
If your fixes worked, then assuming you didn't specify a colouring so that you didn't specify anything other than running either <code>iso()</code>, <code>iso(midi_out_col='...')</code> or <code>iso(midi_out_col='...',midi_input='...')</code>, the launchpad should turn fully cyan. Then, assuming the call to <code>iso(...)</code> is currently running in your terminal/console, you should be able to see MIDI data being printed to the terminal/console whenever you press and release a pad or button on your Launchpad. As a result, and assuming that if on Windows you set up loopMIDI correctly and that it's running, you should be able to select the appropriate instance of RtMidi or loopMIDI as a MIDI input in Scale Workshop and play whatever scale you have loaded via the Launchpad. ''Remember to allow the website MIDI permissions first before looking at its list of MIDI inputs, as there can be a small (few seconds) latency for it to update the list of detected MIDI inputs, and that list may not update unless you close the browser and open it again to hard-refresh the page.''


=== Isomorphic keyboard code for launchpads with programmer mode ===
=== Isomorphic keyboard code for launchpads with programmer mode ===
Line 1,808: Line 1,994:
if j!=0 or i!=0: # equiv. if (i,j) != (0,0) (i think)
if j!=0 or i!=0: # equiv. if (i,j) != (0,0) (i think)
steps = i*x - i//every_x*x_reducer + j*y - j//every_y*y_reducer
steps = i*x - i//every_x*x_reducer + j*y - j//every_y*y_reducer
print( str(steps)+'\\'+str(edo) if args.get('backslash') or args.get('backslashes') else int(.5 + 10000 * steps/edo*1200)/10000 )
print( str(steps)+'\\'+str(edo) if args.get('backslash') or args.get('backslashes') else int(.5 + 1000**2 * steps/edo*1200)/1000**2 )
print()
print()
# for the launchpad pro MK3 specifically:
# for the launchpad pro MK3 specifically: