User:Godtone

From Xenharmonic Wiki
Jump to navigation Jump to search

I'll be putting basically all my microtonal thoughts, theories and even some coding here. It'll increase in organisation as I add stuff and figure out how to prettify stuff.

Simple ratios and where I think limits should be drawn

This is maybe the obvious place to start. I listen to a variety of dyads in order to judge and try to absorb their qualities and to figure out if and why I like them. My opinions of intervals have changed over time. Anyway, as all positive rationals are ratios of positive naturals (nonzero everyday numbers), I think superparticular intervals are a good place to start. I think the melodic Just Noticeable Difference is important here so that intervals have a reasonable chance at being singable, even if the harmonic JND is significantly lower (partly depending on timbre). For me a reasonable upper limit on the melodic JND is about 11 cents as more than that and I hear something as pretty definitively mistuned, although that doesn't necessarily imply unusabibility as an approximation in a low-complexity system (one with a small amount of average tones per octave). This means that in the series of superparticular intervals (of the form (n+1)/n), the first two that are too close in size to be comfortably distinguished are 14/13 and 13/12, whose difference is 169/168 or about 10.274c. I also think that powers of 2 in the denominator of an interval, broadly/generally speaking, helps the interval feel less disorienting due to a stronger suggestion of the fundamental, so beyond 13/12, for a bit, the superparticulars of the form (2n+1)/(2n) should be prioritised. This concludes at the following superparticular intervals being of particular (no pun intended) importance to a 'general melodic semi-harmonic system':
2/1, 3/2, 4/3, 5/4, 6/5, 7/6, 8/7, 9/8, 10/9, 11/10, 12/11, 13/12, 15/14, 17/16, 19/18.
I stopped at 19/18 because (19/18)/(21/20) = 380/378 = 190/189 which is again under 11 cents, as well as because superparticulars beyond 19/18 aren't really musically/melodically interesting to me. Note that this also corresponds to the 19-odd-limit, a subset of the 19-prime-limit, with 169/168 and 190/189 tempered. From there, we can choose to temper the missing superparticulars (the ones with odd denominators) to any of the adjacent superparticulars. Note that I am considering all of these intervals as intervals to move upwards with; 16/15 is an interval that to me works better for going downwards as it implies 15/8 = (3/2)(5/4) when you measure it relative to an imagined octave below the initial tone, and notice that 15/8 is expressible in terms of simple existing superparticulars (and thus as is 16/15). Also note that this does not mean I think the 19-limit should be where we stop. I think if we apply the idea of prime limit to Just Intonation music, the 23-limit is a far better stopping point due to being just before a record prime gap, and as there are various intervals using the prime 23 and more generally using the 27-odd-limit that I like.

Categories

Categories are about distinctions: the number of distinctions made and how they relate to other distinctions. As such, I will list various distinctions here, from the simplest to the most complex I consider interesting and based on their number and hierarchy, giving my thoughts and opinions along the way:

  1. If we assume octave equivalence (meaning multiplying or dividing all intervals by 2 until they are within the range of 1/1 to 2/1 and so that all intervals that differ only by powers of 2 are considered equivalent), then performing an octave complement creates an interesting and nontrivial relation between intervals, splitting the octave into 2 pieces: from 0c to 600c and from 600c to 1200c.
  2. If we use an xLys MOSS (Moment Of Symmetry Scale) to create interval categories, the most basic interesting MOSS is one with 5 notes in an octave, and the most consonant is the Pythagorean 2L3s MOSS, which extends naturally to a 5L2s MOSS while maintaining reasonable consonance in the Dorian mode, which I consider to be 5L2s Pyth's maximally consonant mode due to all intervals (measured relative to the root) containing no more than 3 factors of 3 in the numerator or denominator. These correspond to unequal 5-tone and 7-tone scales, which can be well approximated in 12 EDO as a good trade-off between accuracy and tone-efficiency. I also consider 5 to 9 notes to be the optimal number of notes for scales or they start to sound too much like chords (4) or start to sound too "chromatic" (10) although this doesn't at all mean scales outside this range aren't useful and arguably 4 and 10 are edge cases. Furthermore, there are some more symmetric 12 EDO scales of great note: the diminished scale (4L4s), an 8-note scale, and the whole tone scale (6 EDO), a 6-note scale. Considering the diminished scale has 4 EDO, a 4-note scale, as its core, this about covers most of the scale sizes, and hence much of 12 EDO music implicitly makes use of combinations of these scales. Note however that scales themselves are defined by their pieces just as much as or even more than their pieces define the scales, AKA that the "mode" of a song is often somewhat of a myth; only songs written in modes truly have a "mode" as opposed to impressions of modes.
  3. If we examine the traditional 7 interval categories in 5L2s diatonic scales, then each interval comes in two basic "types": major and minor. If we merge these types as in 7 EDO, then we get intervals neither major nor minor, thus named "neutral", which can often be approximated using the 11th harmonic. This gives 3 "types" per category. Furthermore, if we push the categories to their extremes, we get supermajor and subminor, which can often be approximated using the 7th harmonic. This gives 5 "types" per category. There is a subtlety about neutral intervals though that I consider important which is they generally sound more "minor" and "sad" than "major", and thus should generally be slightly flat relative to an "exact midpoint" between minor and major, which begs the question, what would be the fifth-complement of a "minor-ish neutral"? A "major-ish neutral"? If we use 11/9 as a standard example, its fifth complement is 27/22, but it'd be useful if there was a simpler - perhaps more consonant - interval we can use in its place, and there is! It differs from 16/13 by (11/9)(16/13)/(3/2) = 352/351 which gives a sharp 3/2, and thus provides a nice link to the 13th harmonic. Meanwhile, if distinguishing two types of neutrals is too precise, we can instead consider (27/22)/(11/9) = (11/9)^2/(3/2) = 243/242 which gives a flat 3/2, which would thus be compatible with meantone.
  4. In the case that we consider 11/9 and 16/13 as approximate fifth-complements, this clearly creates quite a precise distinction, and we thus must add at least 2 more distinctions in the spectrum. There is quite a large gap between subminor and minor or between major and supermajor, so adding some middleground subtly different from 12 EDO minor and major would be fitting, as the most basic minor and major are traditionally 5-limit and are closer together than in 12 EDO. For this purpose we will consider 12 EDO on its much more accurate "19-limit" (and in that sense "novemdecimal") 2.3.17.19 subgroup, thus creating a rather familiar "noveminor" and "novemajor" (short for novemdecimal), which, at least in the case of thirds, can be equated with "Pyth minor" and "Pyth major" due to identification by (9/8)^2/(24/19) = 513/512 (with 24/19 as the fifth-complement of the harmonic minor third of 19/16), leaving two options for the expression of this category depending on which makes more sense for a temperament. Note that using the "nove(m)-" prefix for something that could be either "novemdecimal" or "Pyth" may be confusing and so I propose altering slightly to "novaminor" and "novamajor", using the analogy that novemdecimal and Pyth is exaggerated from classical major and minor and builds from both it and from itself, like a star creating new, stronger elements, and creating a "brighter" (as opposed to "sweet"/"solemn") sound to triads, and just as a star's nova is initially bright and fades over time, this "brightness" of Pyth/novemdecimal major/minor has faded over time due to familiarisation/desensitisation. Next, we will consider - if needed - optional additional/finer categories between novaminor and subminor and between novamajor and supermajor. These "new" and "subtly exaggerated from familiar" categories I think fit with the prefix of "neo" and can be considered represented by 13/11 and 14/11 which again can be equated with a sharp 3/2 through identification by (13/11)(14/11)/(3/2) = 364/363 and again creates an interesting link between the 11th and 13th harmonics. Then even subtler versions of the usual major and minor categories can be added - subtle in the sense of closer to but distinct from neutral - these are supraminor and submajor. Finally, for completeness, even more extreme versions of subminor and supermajor can be added that push into the "neither major nor minor at all" territory; these are ultraminor and ultramajor. The final list looks like this:
    1. ultraminor
    2. subminor
    3. neominor
    4. novaminor
    5. (classic) minor
    6. supraminor
    7. subneutral (or "minor neutral" if you prefer)
    8. superneutral (or "major neutral" if you prefer)
    9. submajor
    10. (classic) major
    11. novamajor
    12. neomajor
    13. supermajor
    14. ultramajor
  5. More specifically though, the order in which "types" are introduced is approximately:
    1. minor, major (2 types)
    2. minor, neutral, major (3 types)
    3. (ultraminor or) subminor, (supra)minor, (sub)major, supermajor (or ultramajor) (4 types)
    4. (ultraminor or) subminor, minor, neutral, major, supermajor (or ultramajor) (5 types)
    5. (ultraminor,) subminor, novaminor/neominor, minor, neutral, major, novamajor/neomajor, supermajor(, ultramajor) (7 (or 9) types)
    6. [same as 7 (or 9) types but with neutral split into subneutral & superneutral] (8 (or 10) types)
    7. [same as 8 (or 10) types but with either neo- & nova- distinguished (+2) or with supraminor & submajor added (+2)] (10 (or 12) types)
    8. [both neo- & nova- and supraminor & submajor distinguished] (12 (or 14) types)
    Note that in the above, ultraminor and ultramajor are not necessarily to be included as they represent the points at which the basic "minor vs major" distinction ceases to make sense and becomes unclear/too dependent on context, so they represent absolute limits. Also note that these categories are mainly centered on thirds and to a lesser extent seconds as I consider thirds and seconds to be the most important intervals in terms of providing musical meaning, however this doesn't mean other interval sizes should be ignored or taken less seriously.
  6. To better understand what these interval types mean, study of a variety of intervals and their qualities is required. Due to the in-depth-ness of this study, this is something that is far from complete, and which I will dedicate its own large section to: the "Intervals" section. This study is also something that is subjective just as 12 EDO music is subjective, but that doesn't mean we can't try to blaze a trail for an approximate framework for the interpretation of microtonal music by trying to stick to some guiding principles.


Colourful EDOs

My above progression of "types"/"colours" can be used as a perhaps-interesting alternative to finding "good" EDOs for music by judging them not based on approximation of rationals of interest but instead based on their "colour palette"; not that these two methods are contradictory, and in fact I believe a combination of both is desirable. However, as a demonstration and a starting point, we will look at EDOs providing progressively more complex colour palettes, starting from a broad equalised 7-note approximation of the 5L2s diatonic scale (AKA 7 EDO) and considering only the 'seconds' and 'thirds' (and thus by octave complement, their inversions), with fourths and fifths not considered except to the extent that they should ideally not be too "out of tune", with "out-of-tune-ness" judged relative to approximating either 4/3 or 11/8 (but not both), as these are the simplest J.I fourths. Furthermore, we want the colour palettes to be generally symmetric about the "neutral" type (whether a system has a neutral type or not), so this excludes a large number of EDOs; this is intentional, as otherwise we would end up listing every EDO, and as it is a symmetry which I think is important or at least an interesting restriction.

  1. 7 EDO is the simplest/"trivial" EDO as it provides only the (at times very approximate) "neutral" colour. Note that its fourths are very out-of-tune; this EDO is mainly included as a trivial case. This corresponds to "1 type".
  2. 12 EDO is the next simplest as it provides (nova)major and (nova)minor seconds and thirds. Also a tone-efficient pure Pyth approximation so very good fourths. This corresponds to "2 types".
  3. 15 EDO, in terms of colours, is similar to 12 EDO except that its minor third is a little sharper and that it now has 3 types of second which are roughly subminor, neutral and supermajor. The "subminor" and "supermajor" designations are used due to symmetry; in actuality the subminor second is closer to a neominor second and the supermajor second is closer to an ultramajor second. The prefixes may be omitted, or more exact colour terms may be used, making it have a "superneutral second". Note this EDO is more of an honourable mention due both to somewhat significant asymmetry and due to very off fifths, and also to explain why I won't include it in the "final" list..
  4. 17 EDO is the first EDO to truly have minor, neutral and major for both seconds and thirds, and is thus quite significant as a potential next step up from 12 EDO. More exactly, these come in the flavours of neominor, neutral and neomajor.
  5. 19 EDO is the next step up, having seconds and thirds of the ultraminor, minor, major and ultramajor varieties. It does this by conflating an ultramajor second as an ultraminor third, creating quite a distinct interval that escapes 5L2s categorisation. Note that 18 EDO does this too, but is less symmetric. Thus 19 (or 18 - if you are so inclined - which represents a sharpening of seconds and thirds) is the next step up as corresponding to "4 types".

Note that 17 and 19 have an interesting symmetry with each-other: while 17's new type is per 5L2s category and represents something between the types of major and minor, 19's new type is between 5L2s categories. Thus 17 respects diatonic interval categories more, which actually makes 19 the more novel system to me, for example I quite like the very distinct sound of the 5L4s "semaphore" scale; dyads and triads alike.
Furthermore, note that after this point I focus on EDOs with 'good enough' fourths.

  1. 22 EDO is next, having subminor, supraminor, submajor and supermajor seconds and thirds, although only approximately, and in this spirit "supraminor and submajor" can be shortened to "minor and major" for brevity. 22 is distinct from 19 in that it does not have any of its types overlap between categories.
  2. 24 EDO is next, being easy to categorise as a colour extension of 12, and thus it has seconds and thirds of the ultraminor, novaminor, neutral, novamajor and ultramajor varieties, with the ultramajor second equal to the ultraminor third.
  3. 26 EDO is next and mirrors 22 in its thirds but has one more type of second, and so the seconds are approximately: subminor, minor, neutral, major, supermajor.
  4. 29 EDO next, (approximately) with seconds of types ("fifth-tone" =) ~ultraminor, neominor, supraminor, submajor, neomajor, ("semifourth" =) ~ultramajor, and with thirds of types ("semifourth" =) ultraminor, neominor, supraminor, submajor, neomajor, ("semisixth" =) ulramajor.
  5. 31 EDO is then quite ideal, as it gives us seconds and thirds of subminor, minor, neutral, major and supermajor types!
  6. 34 EDO is ideal if your main gripe with 31 EDO is the lack of semifourths/semisixths (which is my main gripe with it), although note that instead of a subminor third it has a flat neominor third, but its shrubmajor third (4.2) is roughly the same as 31 EDO's; this corresponds to their opposite tendencies as systems.

Beyond this point, it really becomes about narrowing down EDOs based on approximative or other considerations than just 'types'/'colours'. Having said this, there are still yet a few EDOs to be considered, but we will require hereon a fifth strictly between that of 31edo and that of 34edo (AKA that of 17edo).

  1. 41 EDO is ideal if your main gripe with 31 EDO is impurity of the fifth and lack of novamajor/novaminor as well as the unideal flatness of the supermajor third, although personally that is not my main gripe with 31 EDO.
  2. 43 EDO is ideal if your main gripe with 31 EDO is lack of semifourths/semisixths and submajor/supraminor if you don't mind having a gothminor third (2.8) instead of a subminor one (2.7) and a neomajor third (4.1) instead of a shrubmajor one (4.2 or often we consider it less accurately a supermajor third (4.3) in 31 EDO for symmetry with its subminor third (2.7)).
  3. 53 EDO is ideal if you want something that fixes the gripes stated in 34 EDO, 41 EDO and 43 EDO; that is: lack of semifourths/semisixths, lack of novamajor/novaminor & impurity of the fifth & lack of a proper supermajor third, and lack of (semifourths/semisixths &) submajor/supraminor / superneutral/subneutral, respectively, making it unusually complete & comprehensive as a system, corresponding to garibaldi+nestoria+catakleismic.

Beyond 53 EDO, what EDOs are notable as colour palettes becomes too subjective and dependent on various hyper-specific criteria that may have only very niche applicability to anyone, so beyond 53 EDO you are better off looking at the systems from a tuning perspective first and a colour palette perspective second rather than a colour palette perspective first and a tuning perspective second; 53 EDO is sort of unique as the middleground between these two ways of thinking, representing both a fairly complete palette and an excellent tuning system in the no-17's 19-limit (with optional higher primes added depending on context/what you wanna achieve). It should perhaps be noted, however, that 58edo is a colour palette with similar elegance to 53 but with less tuning accuracy.

Favourite EDOs

12, 13, 16, 17, 19, 20, 22, 24, 26, 31, 32, 34, 36, 50, 53, 58, 68, 72, 80, 87, 270, 311.
EDOs < 12 not included as usually better conceptualised in a superset of that EDO and because otherwise I'd list too many consecutive EDOs.
Favourite EDOs best to worst, not listed = even worse, my opinion obviously, also my opinions are still in development about many of these:

  • 12: Pythagorean Meantone: the musical language. From a circle-of-nths relative-consistency point of view, it is very strong in the 2.3.5.19(.17) subgroup. Not to be underestimated. Has hints of the 7-limit through the inaccuracy of its 5. Has been called "the EDO chosen by God" by some - I'm definitely inclined to agree in the context of casual non-xen Western music.
  • 13: Distorted 12. As such, almost xenharmonic by definition, due to maximising opportunities for alienness. The next good EDO after 12. Dreamy scales that I like a lot but I'm not sure about if that alone means they're good to use. I hope it does as 13 has huge potential if so.
  • 16: The first interesting superset of 4 other than 12. Also a mavila tuning, not that I like Mavila too much.
  • 17: Notable as the first step up from 12 in colour palette. Good fifths that are slightly worse than in 12 but in the sharp direction. Kinda a bright feel.
  • 19: Flattish/solemn meantone tuning. The semifourth in semaphore has a very neat sound but I wouldn't say it approximates the 7-limit. If anything, 19 is 2.3.5.37 with it representing a circle of 37/32's, thus also being the first good approximation of the 2.37 subgroup, and thus of 37/32, which represents probably my favourite interval of 19.
  • 20: The first EDO to have both the 5L5s and 4L4s symmetrical scales, and significant for that reason alone. Can sound quite atonal, however:
    • Its 10 EDO subset has a very strong circle of 16/13's and 15/14's.
    • Its 5 EDO subset has a strong (and remarkably small) circle of 23/20's.
    • Its 4 EDO subset has a strong (and remarkably small) circle of 19/16's.
    • 10/9 is approximated well by 3\20 and 14/11 is approximated well by 7\20. Has a flattish approximation of 7/4 and some higher (octave-reduced) harmonics but I don't think I'd use it to approximate those higher harmonics.
This gives it the (additional) remarkable property that all its flavours of seconds are arguably consonant other than 1\20, which is arguably an augmented unison anyway.
  • 22: The first EDO that melodically approximates the 11-limit, and very tone efficient for that purpose. Sounds harmonically complex. Superpyth + Orwell tuning. Not a fan of porcupine to be honest.
  • 24: I think neutral intervals and semifourths are kinda cool and unexpected root movement is cool, so acts as a nice stepping stone into microtonality with a strong base of familiarity to build off of. Alternate tuning for semaphore. I also include it because I like highly composite EDOs, and this is very clearly one. Represents the 2.3.11.19.37 subgroup particularly well.
  • 26: Neat for having very good 8/7's and 10/9's, both flavours of major second that I very much appreciate (while 9/8 can get pretty bland). Basically the only tuning of flattone that I'd consider using as its about as big as a flattone system should be. Note that while 19 EDO is technically also flattone, it represents the border between sharper meantones and flattone, so I do not consider it a proper (in the sense of typical/representative) example of such. Furthermore, in this tuning of flattone the minor seconds are 13/12's, thus representing a near-equal diatonic such that the minor seconds are subneutral seconds. Has the benefit of extending 13 EDO into a larger and more complete colour set and conceptual framework, creating some truly xenharmonic and xenmelodic opportunities with flattone acting as a rough roadmap back to the more familiar things.
  • 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.
  • 34: The first good approximation of the 5-prime-limit due to being the first reasonably accurate tuning of Hanson AKA kleismic. 19 is also a tuning for kleismic but feels like it doesn't do justice to the accuracy and pristineness of kleismic to me. Has the sharp 3/2's of 17 EDO, and as 17 EDO is a good colour system, 34 EDO is a natural extension.
  • 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.
  • 50: The last meantone EDO that should ever be considered because it is the last EDO to consistently map 9/8 and 10/9 to the same step and because 81/80 is a rather large comma to temper at this scale and thus costs you a lot of accuracy. It is surprisingly consistent in the higher limits, and that it is quite composite is appealing to me, especially given that it is a superset of 10 EDO.
  • 53: Catakleismic Pythagorean Orwell. If that description doesn't sound epic I don't really know what will. Very colourful EDO. Near-perfect 5-limit JI with good 7-limit, passable 11-limit through Orwell and good no-17's 19-limit. Normally I wouldn't like large prime EDOs but this is a rare exception as in this case it's a practically perfect representation of the 2.3 subgroup.
  • 58: Weirdly consistent tuning with a nice selection of colours. Record in Pepper ambiguity in the 13- and 15-odd-limit. The first EDO to be consistent in the 17-odd-limit. I haven't looked at this EDO very closely but suspect it may have some surprisingly accurate/good approximations hiding under its slightly meh prime error profile.
  • 68: Superset of 34 that enables the 7-prime-limit. Not too remarkable for that reason alone, however my interest in this EDO was increased when I deduced that it has a step size that is close to half the size of 49/48 meaning a 7/6, an 8/7 and a semifourth can all be distinguished with accuracy. For that reason, this EDO is important as an EDO around which other EDOs have the potential for a good selection of colours which approximate these 3 intervals of interest.
  • 72: (catakleismic) miracle octoid hemiennealimmal temperament, and thus a very nice 11-limit (and to a lesser extent (due to inaccuracy) 13-limit) temperament with the added convenience of being a superset of 12 EDO and a very composite EDO. The first true EDO to represent ennealimmal, and better yet, it extends it into the 11-limit well. Note it can also be described as keenanismic hemiennealimmal, and the page for the keenanisma, 385/384, has some explanation for why this is a theoretically interesting comma for extending the 7-limit to the 11-limit.
  • 80: My favourite EDO. In the past, my favourite was 53 EDO. 80 EDO may be a surprising choice for favourite at first but there are a lot of reasons feeding into it which also make it unlikely to become my second favourite any time soon. I will write in depth about it and about my theories for microtonal music based on 80 EDO in the future. Tunes 17-limit Tolermic, a strange temperament which tempers many commas I'm interested in tempering.
  • 87: A good approximation of the 13-limit, with the 5-limit also good, and an alternative Tolermic tuning, so it's closely related to 80 EDO but with better fifths and harmonic sevenths. Compared to 80 EDO, 7/4 is still the worst prime but lower in both absolute and relative error, and it is tuned flatly instead of sharply. The final and most colourful EDO, but 80 EDO is more than enough colours for me. Has an interesting conceptualisation as 29 EDO representing an approximate 2.3 subgroup with 5, 7, 11 and 13 all being 1\87 flat of the 29 EDO circle, providing an elegant model of navigation. 29 EDO is itself not bad as something that sounds like a brighter 12 EDO, but it feels more elegantly and interestingly conceptualised in this superset.

Beyond 99 EDO (which is interesting in its own right, especially the 99 ED4 subset) I don't see much point for using an EDO as opposed to JI, with the exception of 2 truly exceptional EDOs which may be used for simplified models of JI itself.

  • 270: At the moment I don't really have anything to add which isn't already on the page for 270 EDO. Ridiculously strong approximation of the 11 and 13 prime limits and with the nice property of being very composite. However, I don't take too much interest in it as, at this scale, I prefer higher prime limits than 13.
  • 311: If you asked God what his favourite EDO was, he would say 311 EDO. It is almost unsettling how much of the harmonic series this EDO approximates well considering its comparatively small size. Very recommendable alternative to cents for low-complexity (in the sense of integer- or odd-limited) JI, as this EDO is not only consistent in the full 41-odd-limit, but many (mainly non-prime) odd harmonics greater than 41 can be added to the set without causing inconsistencies between them and other odd harmonics. I wonder if a precise JI harmonic series singer would implicitly target notes of 311 EDO in both singing and in their conceptualisation of JI. I find describing the prime subgroup interpretation of this EDO rather amusing, so here it is: 2.3.5.7.11.13.17.19.23.29.31.37.41.73.89.109.113. Note that as 89, 109 and 113 aren't as accurate as 73, so they could arguably be omitted because of their combination of complexity and inaccuracy. Fun fact: in Group Theory (a subfield of Abstract Algebra), excepting 37, all the primes up to and including 41 appear in the prime factorisation of the order of the Monster Group. The largest prime to appear in its factorisation is 71, the prime just before 73, which is the first prime after 41 that 311 EDO approximates well.

Philosophy

Firstly, this will obviously be heavily influenced by my opinions, so I may state subjective things or theories in a rather matter-of-fact way, but feel free to disagree with me/provide criticisms on my user page. Having said that... I think while measuring intervals relative to 12 EDO is useful initially, this should not be the final way of measuring them. Instead, I believe different intervals should be considered like "colours" or "flavours", of which 12 EDO's intervals are (approximately) one type (corresponding generally to novaminor and novamajor), and that these new terms (or whatever terms you prefer) should eventually be more natural a musical language than comparison to 12 EDO, which often causes a variety of intervals to be used similarly rather than distinguishing them as unique categories not subservient to (but related to) other categories. This is primarily due to a stark lack of theory in the musical and especially emotional meanings of these new intervals. This also makes me more open to the idea of using larger EDOs - which provide more distinctions - as frameworks in which many colours are possible, however, I have relatively high standards for large EDOs, as a large number of tones is something that needs to be quite seriously justified. For example, I generally dislike prime or not-very-composite EDOs unless they have exceedingly pure intervals and/or are very remarkable for other reasons. Sub-EDOs or extremely pure intervals in an EDO generally help to give it a feeling of griddedness and thus orientation in. I think the most important takeaway from 12 EDO is the effectiveness of simplicity and that you shouldn't underestimate how much can be built and how much nuance can be achieved by carefully combining simple things, and that the most objective possible interpretations of music come from the aggregation and combination of many patterns. This also leads me to believe scales are interpreted in terms of their pieces more than vice versa, as aforementioned.
Secondly, I think in new and unfamiliar territory, it will be helpful to use symbolism and analogies in order to get a rough initial understanding for potential emotional/symbolic meanings as the usual music theory isn't enough.

Number symbolism

1 : Unity, simplicity & wholeness (obviously). The ultimate source/root. The Formless. The first Form.
2 : Duality (obviously), contrast, distinction; the first prime number, and thus the first pure essence (assuming we don't count 1 as a pure essence). The source of the most basic/primitive/trivial kinds of order. As such, it is a fundamental organising order from which more complex orders can be judged through (imperfect and careful) assumptions of octave equivalence, and to a lesser extent, octave complements, both of which specifically make sense in the context of approximating simple ratios in EDOs (or vice versa). 2 notes is a dyad and specifies an interval.
3 : Trinity (obviously) and stability - due to each element helping define and stabilise the other two in relation to each-other; think of how the triangle is the strongest shape for scaffolding. Thus represents a place of completeness and strength, and thus sometimes also of rest due to its stability. (It is a pure essence due to being prime.) 3 notes is a triad - the most primitive kind of chord - with 3 inversions that maximise closeness of notes all having at least subtly different but generally similar meanings. (See the subsection on inversions.)
4 : The first composite number. The tetrahedron (AKA 3-simplex) with its 6 edges is an important 3D and general mathematical shape (a musically notable instance is its relation to the structure of tetrads AKA 4-note chords). Thus represents the 3 spatial dimensions or the 3 dimensions of space paired with 1 dimension of time. The tetrahedron is a strong shape due to being made of 4 triangles which all share a unique 3 point subset of a set of 4 points, thus 4 similarly represents a place of completeness, strength and sometimes rest, but can be sharp/hazardous partly due to containing repeated duality (this applies (at least to small extent) to all numbers with powers of 2 in their prime factorisations) and thus pitfalls/negations of the ideal (as duality creates negation). Thus represents the Earth/its likeness (AKA the material world) and thus also represents the limitations of the Earth. Interestingly, melody seems to only be registered by the human mind (or at least mine) in (approximately) 2 octaves of pitch space, as beyond that, melody doesn't have as much meaning and the timbre starts to take over. Because of its limiting nature (being the primitive order of the Earth), it can also be thought of as a number of death.
5 : The number of sides of a square-based pyramid, which has 4 triangular (but not necessarily regular/fully symmetric) faces/sides and thus similar stability but with far more orientedness, both because of the shape and because it is a pure essence due to being prime, and can strongly suggest a harmonic fundamental to the point of encouraging a sense of transparency and orientation. Also the number of vertices/corners of a square-based pyramid. 6 square based pyramids form a cube which tiles 3D space, and where squares themselves tile 2D space. 2 square-based pyramids form an octahedron - the dual of a cube. Additionally, pentagons are the last 2D regular convex polygon for which you can join 3 mutually to each-other and get something 3D, and thus 5 is also related to the dodecahedron, and thus the number 12. Also the 4 points of a tetrahedron with an added/unifying centre (which could be interpreted to be at a distance in a 4th dimension thus forming a 4-simplex, in which case it is projected like a shadow into the Earth), thus representing Earthly dualities that have had their pitfalls tempered through unification through an ideal (which is perhaps not fully of the Earth), thus representing construction upwards to something better, using what is available. Thus represents the pure essence of The Transcendent's grace, benevolence and revitalisation on the Earth, AKA the first/simplest form of transcendence from/beyond the Earth (as it is 4+1), although not a complete/sufficient one for transcendence on its own. Also of course strongly related to pentads (AKA 5-note chords), and due to the combinatorics therein (which relate to the 4-simplex), to the number 10.
6 : As 6 is 2*3, it represents a mixture of their meanings. Furthermore, as it is the number of sides a cube has or the number of corners an octahedron has, it represents the outside shell of an object with no core, and thus represents purposelessness and indifference as well as constant conflict caused by the stability of widespread self-opposing duality, and thus represents the metaphysical engines of war. This is further evidenced by it being 4+2, thus representing duality (2) on earth (4) or the combination of death (4) and duality (2). Furthermore, it also represents the potential for growth due to the lack of a core.
7 : The last "small prime". Represents purity (partly due to being the last "small prime" and thus the last obvious pure essence), wateriness (due to being at the limit of what is immediately intuitively comprehendible to the human mind in terms of identifying numbers of objects as well as making numbers of distinctions on a scale or in general from a symbolic perspective; due to being expressible as many sums; due to many other mathematical properties it has) and perfection (due to many cultural associations). It derives much of its meaning through being expressible as 6+1, 5+2 and 4+3. 6+1 means that it is unity applied to the duality of earth, thus providing a core, while simultaneously transcending the symbolic evils associated with the number 6. 5+2 means that it is duality applied to grace, offering multiplicity/variety in grace and thus representing many forms of grace unified by their purity of form. 4+3 means it is the stabilisation and completion of the earth and of death, hence can be seen as purifying, also because of meaning derived from being 6+1.
8 : Represents power. 2^3 means stability achieved through duality. 2*4 means duality of the earth - and thus the division of it into the abstract patterns of power and its negation; this also induces a sort of death (4) due to the imbalance. 2+6 means duality of the shell; the dual nature of conflict comes from power imbalance.
9 : As it is 3^2, it represents duality achieved through stability and repeated completeness, which is in a sense dual to the meaning of 8. Note that this number has a very different meaning to 8 as duality achieved in this way is a well-formed duality absent of conflict, and so represents a sort of finality of existential balance. Being 5+4, it is also grace on earth, and being 8+1 it is also the transcension of and progression above power vs powerlessness.
10 : As it is 5*2, and as much of human organisation revolves around this number, this is the number of man and human governments. Fittingly, it contains both grace (5) and duality (2), thus representing a corrupted grace and thus implicitly containing the sin of man, also due to being 4+6 and 3+7 (thus being a sort of pure and heartless metastability without regard for anything else). This number is important in connection to the numbers 11 and 12 whose meanings are related.
11 : The simplest "complex" prime essence, and the end of the prime gap starting at 7, this number symbolises divine judgement and chaos. This is partly because it is between 10, the number of the order of man, and 12, discussed next. It is also because it is:

  • The purification (7) of the order of the earth (4), being 7+4.
  • Where power (8) and stability (3) mix, being 8+3, thus representing that appropriate judgement is needed for the stabilisation of power.
  • The duality (2) of finality (9), given that judgement requires an axis along which to judge.
  • The mix of 5 and 6, where 5 represents a stable foundation and 6 represents corelessness, and where 5 represents grace and harmony and 6 represents sin and disharmony. Thus, it takes on a "neutral" nature imbetween the two, necessary for judgement, which is fitting considering that the lowest complexity intervals involving this harmonic are neutral. Even 11/8 is a mixture of an antitonic and a serviant, thus representing an ambiguous region which is nonetheless rooted (contrasting it with 4/3).

12 : The number of divine, perfect order, as it is 7+5 and 4*3. Means pure grace/graceful purity stabilised on the earth and is the stabilisation of death such that it becomes under control for good. This is also partly because it has a large number of factors, thus integrating many simpler orders into a perfect (7) and graceful (5) combined one.
13 : Represents rebellion against the first divine order (12+1), but also represents the attempted progression upwards/further and thus also represents new beginnings. Thus represents the smokeless flame in its raw and partially sinful (in the sense of imperfect) desire to blaze new trails and fly higher. It is 7+6 and thus can also be a number representing pure evil, although using it for this meaning only would be shallow, as mistakes (sin; 6) give further opportunity for enhanced perfection (7). It is 8+5 and thus can be the graceful side of power, again representing a sinful counterpart to divine order, hence the potential association with evil. It is 9+4 and thus is supreme/complete stability applied to the laws of earth and applied to death without necessarily giving sufficient concern to other principles/values, hence again fitting all the themes mentioned prior. Finally, it is 10+3, representing the stabilisation of the order of man, and thus the attempted solidification of imperfection into an essence. Thus is associated with fire, as misguided transcendence may have steep consequences and as fire can be used for good for burning off imperfection by first drawing strong attention to it by illuminating it. Note that 13/8 is a rooted rational approximation of phi (the golden ratio) which is mathematically in some sense "the most irrational number" due to having a continued fraction consisting only of 1's, thus 13/8 takes on some of this mysterious quality while being perfectly just; a sort of ambiguous smoothness emerges in at least some of the low complexity intervals involving this prime essence, analogous to the beauty and smoothness of a smokeless flame.

Diatonic functions

TODO: Expand & revise this section. Topics to expand on:

  • Defining mediant and contramediant in different cases following on from analysis of the cases.
  • Defining emergent ambiguous areas between the 10 basic functions.
  • Defining generalised functions that apply across all MOSSes following on from the analysis of the cases; the lead and contralead are a simple example of this as they are mostly the same in all reasonable cases.
  • (Maybe?) How to create natural progressions - both in terms of melody and chords, following on from all previous points, as well as from the perspective of harmony and an analysis of its meaning. This implies an analysis of various #Intervals and JI #Scales is needed in conjunction with this step. This is the goal.

This section is largely inspired by Aura's Ideas on Functional Harmony combined with my own observations, intuitions and simplifications, as I admittedly do not understand nearly as much as Aura on this subject, but I do intuitively understand my musical sense, which I wish to approximately translate here. This will also focus on generalisation from the diatonic MOSS to other MOSSes.

We begin with a rooted generator, meaning an interval of the form a/2^n where 2^(n+1) > a > 2^n. This is taken as the most important interval, other than the period, which we will assume and define to be an octave as I believe this is the most natural period for a MOSS, especially in the case where the generator or its octave-complement is rooted. We also begin with a tonic, meaning a 'centre' of the scale from which the scale is constructed by stacking periods and generators up or down. The position of the tonic depends on the mode of the MOSS used. Note that we will start with the Pythagorean tuning of the diatonic MOSS and generalise from there. Now we are ready to start defining the most fundamental functions:

  • The tonic, notated 0. The number 0 is meant to represent an origin/reference point. This is always 1/1 - and due to assuming octave-equivalence throughout this analysis of function - is also always equivalent to 2/1, 4/1, etc.
  • The dominant, notated 6. The number 6 looks visually somewhat like a reversed delta/d symbol, for "dominant". The 6th harmonic up to octave-equivalence is 3/2, the perfect fifth. The dominant should ideally always be a(n approximation of a) rooted generator.
  • The serviant, notated 4. The number 4 should remind you that the perfect fourth is 4/3. The serviant is always the octave-complement (AKA period-complement) of the dominant, with the only distinguishing feature between the two being which is a (up to octave-equivalence) a (rooted) harmonic and which is a subharmonic. The serviant should (ideally) always be a subharmonic of the tonic.
  • The supertonic, notated 2, is a stack of two dominants upwards (and octave-reduced if needed). The 2 should remind you that it is two dominants. It should also be clear from 6 * 2 = 12, as 10 is the tonic and so 12 is a supertonic above the tonic, hence "super"-tonic.
  • The subtonic, notated 8, is the octave-complement of the supertonic, and is thus a stack of two serviants upwards (and octave-reduced if needed). The 8 should remind you that it is two (2) serviants (4) as 2 * 4 = 8.
  • The antitonic, notated 5, is approximately half of an octave, being important due to both being a less strong consonance or a dissonance and due to being the mirror line when performing octave-complementing.

Note that in the system, there is always the rule that the octave-complement (AKA period-complement) of a function's number is 10 minus that number. This is because in the final system, 10 functions are derived for diatonic. These functions' numbers are picked in the order they appear in diatonic, as this notation is intended for diatonic. For other MOSSes, the names of the functions remain the same but some of the notation may change. However, the numbers for the functions 0, 1, 5, 9 are generalisable to all octave-period MOSSes. This leads us to our next pair of functions: 1 and 9.

  • The lead, notated 9, is a small step (s) or chroma (L-s) below the tonic. The number 9 represents being 1 step (chroma or small step) below the tonic (10 - 1 = 9). The name is derived from the term "leading tone". Diatonically, this is a semitone.
  • The contralead, notated 1, is a small step (s) or chroma (L-s) above the tonic. The name is derived from contra+lead. The number 1 represents being 1 step (chroma or small step) above the tonic.

Finally, there are a pair of functions which act as 'gap fillers' in the MOSS, which can be assigned to the result of stacking 3 to 5 generators (whichever amounts avoid the regions corresponding to other functions and fit the restrictions). These are the mediant and the contramediant. Before defining the mediant, an analysis of the order of the functions so far is required. The basic order is:

tonic < contralead < antitonic < lead < tonic (or in numeric notation: 0 < 1 < 5 < 9 < 10)

Then the position of the dominant, serviant, supertonic and subtonic depend on whether the dominant is above or below the antitonic. If it is above the antitonic, then it is also below the lead. This means that two dominants exceed an octave and therefore that the supertonic is always strictly between the contralead and the lead. Thus the subtonic is also strictly between the contralead and the lead. We will examine this first as there are more low-complexity rooted generators in this case, namely 3/2, 7/4, 13/8 and 15/8, with 5/4 and 11/8 being the main exceptions, and with 9/8 contained in the MOSS for 3/2 and 5/4 reachable indirectly through meantone or schismic. It is 3/2, 5/4 and [[7/4] that are of most interest though, as in those cases the generator is simple enough that stacking it twice still constitutes a reasonable consonance, while 11/8 requires sometihng as complex as 121/64. Finally, 15/8 stacked twice is 225/128 (up to octave-equivalence) which can be equated with 7/4 if you temper 225/224. The result is:

tonic < contralead < serviant < antitonic < dominant < lead < tonic AND contralead < supertonic/subtonic < lead

Can we deduce more than this? We can answer this question by considering the extreme cases. The lowest dominant above the antitonic would result in a supertonic just above the contralead (such as 5L 2s in 12edo) and therefore below the dominant. The highest dominant above the antitonic would result in a supertonic just below the contralead, but also below the dominant, as by stacking the dominant twice, the distance from the tonic is doubled. Therefore, the supertonic must be below the dominant and the subtonic must be above the serviant. Therefore:

contralead < supertonic < dominant < lead AND contralead < serviant < subtonic < lead

This justifies the following numeric notation for when dominant > antitonic:

0 (tonic) < 1 (contralead) < 2 (supertonic) ? 4 (serviant) < 5 (antitonic) < 6 (dominant) ? 8 (subtonic) < 9 (lead) < (1)0 (tonic)

Note however that the supertonic may in some cases be above the antitonic (and correspondingly the subtonic may in some cases be below the antitonic). This leaves 3 (the mediant) and 7 (the contramediant) as gap-fillers for below and above 5 (the antitonic) respectively. This does however point to the next place to investigate: when is the supertonic above the antitonic? This would imply that twice the dominant is above 1.5 octaves, or that the dominant is above 900 cents while being below the contralead, thus implying that an EDO approximation must be 9 or above to support such a MOSS (as there must be space for the contralead to be distinct).

Another interesting observation is that this system suggests an at-least-5-note-per-period MOSS because in order for all of the functions 0, 1, 2, 4, 6, 8, 9 to be present, there must be at least 5 notes. That way, there exists at least one mode where the dominant, serviant, supertonic and subtonic are all present relative to the tonic, with the (contra)lead being - at minimum - a reality of the (chromatic) extension of the MOSS, which is always important as it acts as melodic background. Note that the digits whose functions are potentially absent from the minimum MOSS are 1, 3, 5, 7, 9 which is to say all the odd digits, and where 3 and 7 are context-dependent gap-filler functions while 5 is a function unrelated to the generator and instead related to the period-complement symmetry of the functions suggesting a midpoint between adjacent instances of the tonic. Also note that for 5-note MOSSes, the mediant and contramediant necessarily cannot exist in the same mode where the dominant, serviant, supertonic and subtonic are simultaneously present. Yet more interestingly, the desire to include the functions 1, 3, 7, 9 suggests using the child MOSS as the basis for analysing a 5-note and sometimes 6-note (and even 7-note) MOSS in cases where the small step is too large to serve as a true (contra)lead. Meanwhile, if the small step is small enough to serve as a (contra)lead, it will necessarily be confused with either a supertonic or a subtonic, implying an interesting musical reality for such MOSSes. This system thus also suggests that modes which have at least two generators from the tonic in each direction have a unique musical capability and thus modes that do not satisfy this restriction have emergent musical properties based on their very lack of some of those functions.

Examples of functions

  • If 5/4 is taken as the generator and dominant, then 25/16 is a supertonic. Thus 8/5 is a serviant and 32/25 is a subtonic. if 32/25 is equated with 9/7 by tempering 225/224, then 9/7 is a subtonic and 14/9 is a supertonic. This forms a sephiroid (3L7s) scale.
  • if 13/8 is taken as the generator and dominant, then 169/128 is a supertonic, which seems rather complex, but this can neatly be equated with 21/16 by tempering 169/168, which is convenient as we want to preserve the rootedness of the generator when stacking it. Thus 16/13 is a serviant and 32/21 is a subtonic. This also forms a sephiroid (3L7s) scale, but the generator is now above the antitonic rather than below.
  • If 3/2 is taken as the generator and dominant, then 9/8 is a supertonic. Thus 4/3 is a serviant and 16/9 is a subtonic. This is a uniquely very low complexity instance. This forms a diatonic (5L2s) scale.
  • If 7/4 is taken as the generator and dominant, then 49/32 is a supertonic. Thus 8/7 is a serviant and 64/49 is a subtonic. This forms a machinoid (5L1s) scale which extends to a 5L 6s chromatic/extended scale.
  • If 11/8 is taken as the generator and dominant, then 121/64 is a supertonic. Thus 16/11 is a serviant and 128/121 is a subtonic. This is a peculiar case where the supertonic can reasonably be equated with the lead (and correspondingly the subtonic can reasonably be equated with the contralead). Given this, it doesn't seem necessary to introduce any temperings, as leads and contraleads are a more dissonant type of function. This forms an antidiatonic scale which extends to a 2L 7s and 2L 9s chromatic/extended scale, or 11L 2s for very accurately tuned 11/8's.
  • If 15/8 is taken as the generator and dominant, then 225/128 is a supertonic. Thus 16/15 is a serviant and 256/225 is a subtonic. This is a peculiar case where the dominant can reasonably be equated with the lead (and correspondingly the serviant can reasonably be equated with the contralead). Furthermore, if we temper 225/224 then the supertonic is 7/4 and the subtonic is 8/7, meaning the supertonic and subtonic are noticeably more consonant and stable both harmonically and functionally than the dominant and serviant. Due to the close proximity of the generator to the tonic, the MOSSes formed by this get quite big before the generator changes from being the small step to being the large step, with 1L 9s being the last MOSS where 16/15 is the small step, and the MOSS after that (assuming near-just tuning of 16/15) is 10L 1s. However, in the case of 10L 1s, the large and small steps are so close in size that you may want to use 11edo as a simplified tuning. This does come at the cost of tempering 225/224 being suboptimal however.

Intervals

For now, I will leave tables for the interval/colour/type namings of the seconds and thirds of my two favourite highly-chromatic EDOs, both of which related to 17-limit Tolermic due to this family tempering many commas I am interested in tempering, and due to the resulting intervals providing a good framework for thinking about interval colours:

Generalised colours of supertonics, subtonics, leads and contraleads

inframinor (AKA 'ultraminor') second aka one quarter-tone: (many things)
subminor second: 30/29 or 29/28 or 28/27 or 27/26 or 26/25
neominor second: 25/24 or 24/23 or 23/22 or 22/21
novaminor (AKA pythminor) second: 21/20 or 20/19 or 19/18
minor second: 18/17 or 17/16 or 16/15
supraminor second: 15/14 or 14/13
subneutral second: 13/12
neutral second AKA three quarter-tones: 12/11
superneutral second: 11/10
submajor second: 10/9 or 21/19
major second: 19/17 or 28/25
novamajor (AKA pythmajor) second: 9/8
neomajor second: 17/15 or flat 25/22
supermajor second: 25/22 or flat-to-just 8/7
ultramajor second: sharp 8/7 or 23/20
semifourth AKA five quarter-tones: 15/13

80 EDO interval colours/types (seconds, thirds, fourths and antitonics/tritones)

 45c ultraminor (AKA ~quarter-tone)
 60c subminor (AKA ~third-tone)
 75c neominor
 90c novaminor
105c minor
120c supraminor
135c subneutral (AKA minor neutral)
150c superneutral (AKA major neutral)
165c submajor
180c major
195c novamajor
210c neomajor
225c supermajor
240c ultramajor
255c ultraminor (AKA ~semifourth)
270c subminor
285c neominor
300c novaminor
315c minor
330c supraminor
345c subneutral
360c superneutral
375c submajor
390c major
405c novamajor
420c neomajor
435c supermajor
450c ultramajor (AKA ~semisixth)
465c oneiro
480c pentatonic
495c perfect
510c flattone/semiquartal
525c acute/hendrix/mavila
540c ?
555c ultraminor
570c neominor
585c minor
600c neutral
615c major
630c neomajor
645c ultramajor
660c ?
675c grave/hendrix/mavila
690c flattone/semiquartal
705c perfect
720c pentatonic
735c oneiro
...
80 EDO MOSS gens table:

(1\10: 120c)
(~1\9: 135c (~~8L1s))
(1\8: 150c)
7L1s: 165c
6L1s: 180c (7L6s) (note 195c (6L7s) is extreme with L/s=6.5)
5L1s: 210c (6L5s), 225c (5L6s)
(1\5: 240c)
5L4s: 255c
4L5s: 270c, 285c
(1\4: 300c)
4L7s: 315c
(~~1\11: 330c (~7L4s))
7L3s: 345c
3L4s: 360c
3L7s: 375c, 390c
3L8s: 405c, 420c, 435c (all also ofc 3L5s; biased to 435c)
(3\8: 450c)
5L3s: 465c
(2\5: 480c)
5L2s: 495c, 510c
7L2s: 525c
2L7s: 540c, 555c, 570c, 585c (all also ofc 2L5s; biased to 540c)

RINGER 80

80 EDO is a great no-limit system for conceptualising and internalising harmonic series interval categories/structures through RINGER 80 which contains the entirety of the no-127's no-135's no-141's 145-odd-limit and in which ~84.44% of all intervals present are mapped consistently. This RINGER 80 uses the best-performing val for 125-odd-limit consistency by various metrics (squared error, sum of error, number of inconsistencies, number of inconsistencies if we require <25% error, etc.). To achieve the CS (constant structure) property, the primes 31, 47, 53, 61, 67, 73, 79, 107, 109 are sharpened by 1 step compared to their flat patent val mapping (AKA are mapped to their second-best mapping); all other primes are patent val. This scale has a few remarkable properties. Firstly, all the intervals that are not inconsistent are mapped - at worst - to their second-best mapping, meaning you will never have a categorical/interval mapping exceeding 15 cents (which seems like a very reasonable result for a RINGER scale). Secondly, all of the "filler harmonics" beyond the 125-odd-limit fit in an obvious way; note how there are no warts beyond the 113-prime-limit (which the 125-odd-limit corresponds to due to a record prime gap from 113 to 127) meaning all composite harmonics were either already part of the 113-prime-limit or if prime the primes were patent val and sharp-tending. "In an obvious way" also means that every superparticular (n+1)/n in the 125-odd-limit that was mapped to 2 steps is split into (2n+2)/(2n+1) and (2n+1)/(2n), retaining the lowest possible complexity. Finally, note that while composite odd harmonics start going missing after the 125th harmonic, that prime harmonics are very much not lacking. This scale exists inside the no-127's no-151's no-163's 179-prime-limit, meaning that all primes up to and including 179 are present excepting those three, making it full of prime flavour (on top of its high compositeness due to the 125-odd-limit corresponding to a record-prime-gap). My only personal qualm with this scale is that prime 73 is not patent val when I'd like it to be, but keeping it warted allows the introduction of the 145th harmonic which adds a lot of low-complexity and consistent intervals, and not warting it means no longer using the best-performing mapping and using the "147.2th" harmonic instead of the much-preferable "145th". Specifically, all intervals made in ratio with the 145th harmonic that simplify are mapped consistently. Furthermore, warting prime 73 means that 73/63 is mapped correctly as an extremely accurate approximation of the 255c semifourth (80 EDO is a circle of 73/63's). Finally, its worth noting that I used #My_Python_3_code to find and hone this scale.

mode 63 of the harmonic series (corresponding to 125-odd-limit) with added odds from mode 63*2=126 in square brackets:
63:64:[129]:65:[131]:66:[133]:67:68:[137]:69:[139]:70:71:[143]:72:[145]:73:74:[149]
75:76:[153]:77:78:[157]:79:80:[161]:81:82:83:[167]:84:85:86:[173]:87:88:89
[179]:90:91:92:[185]:93:94:95:96:97:[195]:98:99:100:101:102:103:104:[209]:105
106:107:108:109:110:111:112:113:114:115:116:117:118:119:120:121:122:123:124:125(:126)
(the above is split into 20 harmonics per line AKA ~300c worth of harmonic content)

in lowest terms as a /105 scale (corresponding to a primodal /107, /109 or /113 first-octave scale or to a primodal /53 or /59 second-octave scale or even to a primodal /29 third-octave scale or /7 fifth-octave scale):
105:106:107:108:109:110:111:112:113:114:115:116:117:118:119:120:121:122:123:124:125:126:128:129:130:131:132:133:134:136:137:138:139:140:142:143:144:145:146:148:149:150:152:153:154:156:157:158:160:161:162:164:166:167:168:170:172:173:174:176:178:179:180:182:184:185:186:188:190:192:194:195:196:198:200:202:204:206:208:209:210

87 EDO interval colours/types (seconds and thirds)

 41.4c fifth-tone
 55.2c ultraminor (AKA quarter-tone)
 69.0c subminor (AKA third-tone)
 82.8c neominor
 96.6c novaminor
110.3c minor
124.1c supraminor
137.9c subneutral (AKA minor neutral)
151.7c superneutral (AKA major neutral)
165.5c submajor
179.3c major
193.1c novamajor
206.9c neomajor
220.7c supermajor
234.5c ultramajor
248.3c semifourth
262.1c ultraminor
275.9c subminor
289.7c neominor
303.4c novaminor
317.2c minor
331.0c supraminor
344.8c subneutral
358.6c superneutral
372.4c submajor
386.2c major
400.0c novamajor
413.8c neomajor
427.6c supermajor
441.4c ultramajor
455.2c semisixth

MOSS names i think are pretty

Note: I am only considering octave-period MOSSes here. Table is an edited version of the standardised version at TAMNAMS#Mos_pattern_names. I made a few small changes of personal preference:

  • 4L 1s I call "pentoid" after the 11-limit 4&5 exotemperament for which practically the entire tuning range of the MOSS is valid due to its impractically low accuracy/high damage (this is evidenced by it also being supported by patent val in EDOs 4+5=9 (the basic tuning) and 4+5+4=14 (the hard tuning), and by the 13b val (the soft tuning) where you take the (only barely) second-best fifth).
  • 1L 6s I find significant as a 7-note scale underlying all nL1s scales for n>=7. I have named it "onyx", which has a variety of aesthetic reasonings for it: "1Ln-ic's" and "nL1-ic's (like, the -ic suffix applied to MOSS names, collectivised for 1Lns and nL1s) sounds like "one-el-en-ics" or "en-el-one-ics" which abbreviated sort of sounds like "one-ics" => "onyx". Then "onyx" sounds sort of like "one-six". Furthermore the onyx mineral comes in many colours and types, which seems fitting given this is the parent scale for a wide variety of MOSSes; specifically of interest being 7L 1s (pine), 8L 1s (subneutralic) and 9L 1s (sinatonic). Finally, the name "onyx" is also supposed to be vaguely reminiscent of "anti-archaeotonic" as "chi" (the greek letter) is written like an "x" (this is related to why "christmas" is abbreviated sometimes as "X-mas") and other than that, the letters "o" and "n" and their sounds are also present in "archaeotonic", and "x" is vaguely reminiscent of negation and multiplication. There is also something like a "y" sound in "archaeotonic" in the "aeo" part (depending partially on your pronounciation).
  • 4L 3s I noticed is unique in that out of 1L 6s, 2L 5s, 3L 4s, 4L 3s and 5L 2s it is the only MOSS pattern that doesn't have both of its child MOSSes named and included. Note that it makes sense to make an exception for 6L 1s as the large number of large steps and small number of small steps makes the range of valid tunings for the generator(s) the most strict of all the 7-note MOSSes. Anyway, as generators close to 6/5 corresponding to kleismic temperament seem to be of especial importance for this MOSS, I consider the 4L 7s pattern important enough to be named, and it had a proposed name of "kleistonic", a name I think is fitting. As a result, 7L 4s could be named "anti-kleistonic", but I think its actually sort of the more natural extension of the 4L 3s scale pattern which has sharper minor thirds in less complex tunings, hence I've opted for naming it "suprasmitonic" instead after its (and to a lesser extent smitonic's) supraminor third generator AKA "sharp minor third" - the origin of the name "smitonic". Note that another reason I think 4L 7s is worth distinguishing and naming is because kleismic temperaments tend to need both more generators and very hard tunings for 4L 3s, as evidenced by the basic tuning of 11edo being way too sharp for kleismic and the hard tuning of 15edo being the sharpest kleismic that could ever make some sense (as it requires, for instance, accepting 720c as an approximation of 3/2 and 400c as an approximation of 5/4). The interval prefixes/abbreviations for 7L 4s are "ssmi" to show preference in comparing it to the underlying 4L 3s scale and as a shortenage of "sharp supraminor third" and "suprasmitonic" correspondingly.
  • I gave names for the interval prefixes and interval name abbreviations for 5L 7s (p-chromatic) and 7L 5s (m-chromatic). For 7L 5s I picked "chrome" as it is strongly befitting of meantone as meantone does the 7-limit efficiently, elegantly and colourfully, and because its a contraction of "chromatic" and the "me" in "meantone". Pronounced like the word. For 5L 7s, in contrast with 5L 7s which is mellow and harmonic, 5L 7s is sharp and active, perhaps like fire, hence I picked "pyr-" from "pyro" and from "[py]thago[r]ean", with the letters "pyr" of course also being for "[p]a[r]a[py]th", "su[p]e[rpy]th" and "ult[r]a[py]yth".
  • Similarly to how I made an exception for the 1Lns scale form of 1L 6s, I'm also making an exception to include the multiMOSS of 6L 2s as echidna (from which the MOSS derives its name) is a strong and important temperament to me with a period of half an octave and where one generator is both 11/10 and 9/7 depending on from which direction you view it, as 11/10 * 9/7 is extremely close to half an octave, the period for this MOSS scale. ("MultiMOSS" stands for "multi-period Moment Of Symmetry Scale".) However, this would not be included if I were to have this as "the semi-official TAMNAMS minimal MOSS name list", because this is a true exception to a lot of the design principles behind choosing this set of MOSSes to name and recognise as important.
TAMNAMS mos names
5-note mosses
Pattern Name Interval prefix[1] Abbreviation[2] Notes
2L 3s pentic pent- pent Shortening of pentatonic.
3L 2s antipentic apent- apent Sister MOSS of pentic.
4L 1s pentoid man- man The 11-lim[4&5] exotemperament "pentoid" is practically equivalent.
6-note mosses
Pattern Name Interval prefix[1] Abbreviation[2] Notes
5L 1s machinoid mech- mech Named after the 2.9.7.11 5&6 temperament machine.
7-note mosses
Pattern Name Interval prefix[1] Abbreviation[2] Notes
1L 6s onyx on- on My name for this scale. It is important as the parent of 7L1s, 8L1s, 9L1s, etc.
2L 5s antidiatonic pel- pel Established name. pel comes from pelog.
3L 4s mosh mosh- mosh Graham Breed's name, from mohajira-ish.
4L 3s smitonic smi- smi From sharp minor third.
5L 2s diatonic none none
6L 1s arch(a)eotonic arch(a)eo- arch A name originally given to 13edo's 6L 1s.
8-note mosses
Pattern Name Interval prefix[1] Abbreviation[2] Notes
3L 5s sensoid sen- sen From sensi temperament.
5L 3s oneirotonic oneiro- on A name originally given to 13edo's 5L 3s.
6L 2s echinoid ech- ech From hedgehog and echidna temperaments.
7L 1s pine pine- pine Named after the 11-limit 7&8 temperament porcupine.
9-note mosses
Pattern Name Interval prefix[1] Abbreviation[2] Notes
2L 7s joanatonic jo- jo From joan temperament.
4L 5s orwelloid or- or From orwell temperament.
5L 4s semiquartal sequar- seq From half-fourth.
7L 2s superdiatonic arm- arm Established name. arm- comes from armodue theory.
8L 1s subneutralic blu- blu From subneutral 2nd generator. blu comes from bleu temperament.
10-note mosses
Pattern Name Interval prefix[1] Abbreviation[2] Notes
3L 7s sephiroid sephi- seph Named after the 2.5.11.13.17 3&10 temperament sephiroth.
7L 3s dicotonic dico- dico Named after the 11-limit 7&10 temperament dichotic.
9L 1s sinatonic sina- si Named after the sinaic that generates the pattern, which in turn is named after Ibn Sina.
11-note mosses
Pattern Name Interval prefix[1] Abbreviation[2] Notes
4L 7s kleistonic klei- klei- Named after kleismic and its extensions.
7L 4s suprasmitonic ssmi- ssmi- Generated by (sharp) supraminor thirds.
12-note mosses
Pattern Name Interval prefix[1] Abbreviation[2] Notes
5L 7s p-chromatic pyr- pyr- p- is for "pure or sharp (para-/super-)pyth(agorean).
7L 5s m-chromatic chrome- chrome- m- is for "(maybe-mellow) meantone chromatic".
  1. 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 used in interval names, e.g. perfect 3-oneirostep
  2. 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 used in abbreviations of interval names, e.g. P3ons

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:

>>> sg = [2, 3, 7, 11, 13, 17, 19]
>>> [unfact(x,sg) for x in inconsistent_ivs_by_val( [fact(x,sg) for x in odd_lim(21,[5,15],complements=False)], val(sg,ed(104)) )]
[(11, 7), (19, 14), (21, 16), (19, 17), (13, 7), (21, 13), (21, 19), (17, 9), (17, 12), (7, 6), (19, 12), (19, 18), (11, 8), (21, 11), (13, 11), (21, 17), (13, 8), (7, 4), (17, 16), (17, 13), (11, 9), (19, 16), (11, 6), (19, 13), (13, 9), (13, 12), (17, 11), (9, 7), (17, 14), (19, 11)]

This code is licensed under the AGPLv3 (https://www.gnu.org/licenses/agpl-3.0.html), a version of the AGPL corresponding to the GPLv3.

# this code is licensed under the AGPLv3 <https://www.gnu.org/licenses/agpl-3.0.html>

import math
import functools

# prod() computes the product of a list of things.
# It can be thought of as the multiplicative counterpart to sum().
# EG: `prod([3,2,4])` evaluates to (3*2)*4 = 24.
prod = functools.partial(functools.reduce, lambda x,y: x*y)

# IMPORTANT NOTE ABOUT USING SUBGROUPS:
# if you're using a subgroup in the form of a list of integers > 1,
# define it as a short temporary variable and pass it in to every function
# that asks for a subgroup parameter "p"; the "p" in the gen_prime(p) function below
# is sort of an exception because it expects a full-prime-limit primes-only subgroup.
# ALSO note that the only type of subgroup currently supported is, as aforementioned,
# a list of integers > 1, as otherwise factorisation gets complicated.

# p is a complete list of (at least 2) primes up to and including some prime.
# gen_prime(p) appends the next prime to p and returns that prime.
def gen_prime(p):
	c = p[-1] + 2
	while True:
		i = 0
		found_divisor = False
		while c >= p[i]*p[i]:
			if c % p[i] == 0:
				found_divisor = True
				break
			i+=1
		if not found_divisor: # aka if c is prime
			p.append(c)
			return c
		c += 2
primes = [2,3]

# Cannot give index error (unless you run out of memory) and
# allows implicit convenient access to global variable `primes`
def prime_idx(i):
	global primes
	while i >= len(primes):
		gen_prime(primes)
	return primes[i]
# Returns a list of primes up to and (if applicable) including the limit,
# representing the corresponding prime limit.
def prime_lim(limit): # includes limit (if applicable)
	l = []
	i = 0
	while prime_idx(i) <= limit:
		l.append(prime_idx(i))
		i += 1
	return l
lim = prime_lim
prime_limit = prime_lim
# Can be thought of as the conceptual opposite of strip_list_of_right().
def right_justify_list(l, n, justify_with=0):
	while len(l) < n:
		l.append(justify_with)
	return l
# Can be thought of as the conceptual opposite of right_justify_list().
# Note that what_to_strip is a list of elements eligible for stripping (removal).
def strip_list_of_right(l, what_to_strip=[0]):
	while len(l)>0 and (l[-1] in what_to_strip):
		l.pop()
	return l

# If p is None (default):
#    Factorises a positive integer into a list of the exponents/powers
#    of corresponding primes starting with the exponent/power of 2.
#    Note that any non-positive integer will give an empty list/factorisation
#    and that this is the same factorisation that the number 1 gets.
# Otherwise:
#    Attempts factorisation based on a list of positive integers p
#    which can be used to try to divide n down to 1.
#    If n cannot be divided down to 1, an empty list is returned.
#    Note that two versions of each function dealing with factorisations exist;
#    one of which specifying such a list of positive integers p.
#    Also note that the positive integers are tried in the order they are listed.
def fact_int(n, p=None):
	if p==None:
		f = []
		while n > 1:
			f.append(0)
			while n % prime_idx(len(f)-1) == 0:
				f[-1] += 1
				n //= prime_idx(len(f)-1)
		return f
	else:
		f = [0]*len(p)
		i = 0
		while n > 1 and i < len(p):
			while n % p[i] == 0:
				n //= p[i]
				f[i] += 1
			i += 1
		return f if n==1 else []
# Note that this works for negative exponents of primes too but produces
# a floating point value rather than an exact rational representation.
# Also note that this failing when p!=None and f==[] is intentional
# because [] represents a failed subgroup factorisation in the subgroup case.
def unfact_int(f, p=None):
	return (
		1 if f==[] and p==None
		else prod([ prime_idx(i)**f[i] for i in range(len(f)) ]) if p==None
		else prod([ p[i]**f[i] for i in range(len(f)) ])
		)

# Note: "fact" stands for "factorise(d form (of))/factorisation".
# Note: when combining two factorisations in some way,
#       both must either be normal or subgroup-based.
def mul_fact(*fs):
	max_len = max([ len(f) for f in fs ]) # to convert list to tuple next line
	fs = (*[ right_justify_list(f.copy(),max_len) for f in fs ],) # conversion
	result = [ sum([ fs[n][i] for n in range(len(fs)) ]) for i in range(max_len)]
	return result
def mul_iv(*rs):
	return iv( prod([ r[0] for r in rs ]), prod([ r[1] for r in rs ]) )
def recip_fact(f, p=None): # useless parameter p used for niceness
	return [-i for i in f]
def recip_iv(r):
	return (r[1], r[0])
def div_fact(f1, f2):
	return mul_fact(f1, recip_fact(f2))
def div_iv(r1, r2):
	return mul_iv(r1, recip_iv(r2))
def add_iv(r1, r2):
	return iv( r1[0]*r2[1] + r2[0]*r1[1], r1[1]*r2[1] )
def sub_iv(r1, r2):
	return iv( r1[0]*r2[1] - r2[0]*r1[1], r1[1]*r2[1] )
def as_float(x, p=None):
	if type(x)==list: # monzo (factored)
		uf = unfact(x, p)
		return uf[0] / uf[1]
	elif type(x)==tuple: # ratio (unfactored)
		return x[0] / x[1]
	else: # assumed to already be a float (or integer)
		return x
def S(k):
	return (k*k, k*k-1)
s = S
def convert(x, totype, p=None):
	if totype==float:
		return as_float(x,p)
	if totype==int:
		if type(x)==int:
			return x
		interval = 0
		if type(x)==tuple:
			interval = iv(x[0],x[1])
		if type(x)==list:
			interval = unfact(x,p)
		if len(fact_int(interval[1]))==1: # if harmonic (denominator in 2-lim)
			return interval[0]
		if len(fact_int(interval[0]))==1: # if subharmonic (numerator in 2-lim)
			return interval[1]
		else:
			raise TypeError("interval is not harmonic (/2^n) or subharmonic (2^n/)")
	if totype==tuple:
		if type(x)==int:
			return (x,1)
		if type(x)==tuple:
			return x
		if type(x)==list:
			return unfact(x,p)
	if totype==list:
		if type(x)==int:
			return fact_int(x,p)
		if type(x)==tuple:
			return fact(x,p)
		if type(x)==list:
			return x
def fact(r, p=None):
	if p==None:
		return strip_list_of_right(div_fact( fact_int(r[0]), fact_int(r[1]) ))
	else:
		numer = fact_int(r[0],p)
		denom = fact_int(r[1],p)
		if len(numer)>0 and len(denom)>0 and r[0]!=r[1]:
			return div_fact(numer,denom)
		else:
			return []
def unfact(f, p=None):
	return unfact_int([max(n,0) for n in f],p), unfact_int([-min(d,0) for d in f],p)
# to simplify an existing rational "x" expressed as a (pythonic) 2-tuple, use: "iv(*x)"
def iv(n, d, p=None): # provides automatic reduction to simplest form
	if n==0:
		return (0, 1)
	r = unfact(fact( (abs(n),abs(d)),p ),p)
	return (sgn(n*d)*r[0], r[1])
def striv(n, d=None): # accepted formats: [(n, d, s)], [(n, d), s], [n, d], [n=h]
	if d==None and type(n)==tuple and type(n[0])==int and type(n[1])==int:
		if len(n)==2:
			return str(n[0])+'/'+str(n[1])
		elif len(n)==3 and type(n[2])==int: # (future expansion)
			return str(n[0])+'/'+str(n[1])+': '+str(n[2])
	if type(d)==int and type(n)==tuple and type(n[0])==int and type(n[1])==int:
		return str(n[0])+'/'+str(n[1])+': '+str(d) # (future expansion)
	if type(n)==int:
		if d==None:
			return str(n)+'/1'
		elif type(d)==int:
			return str(n)+'/'+str(d)
	return '[not interval]'
# use str.ljust(width,space) and str.rjust(width,space) for left- and right-padding and
# use str.center(width,space) if you dont want to centre around a specific character (e.g '/')
def pad(what,width,centre='/',space=' '):
	if len(what) >= width:
		return what
	lspaces = (width+1)//2
	rspaces = width - lspaces
	c = what.find(centre)
	if c != -1: # if centre present in what
		c += (len(centre)+1)//2
	else:
		c = (len(what)+1)//2
	lspaces -= c
	rspaces -= len(what)-c
	if lspaces >= 0 and rspaces >= 0:
		return lspaces*space + what + rspaces*space
	if rspaces < 0 and lspaces >= 0:
		return (lspaces+rspaces)*space + what
	if lspaces < 0 and rspaces >= 0:
		return what + (lspaces+rspaces)*space
	return what
# Converts a prime factorisation into a prettier and potentially
# more readable (simple) string representation. (Not for subgroup-factorisations.)
# redundancy is either 0, 1 or 2 and has the following effects:
# redundancy=0 (default):
#    Only includes primes with nonzero exponent and omits the exponent if
#    if the exponent is 1 (AKA if the prime would be followed by '^1').
# redundancy=1:
#    Same as redundancy=0 except exponents are always explicit ('^1' is included).
# redundancy=2:
#    Shows all primes up to the largest prime with nonzero exponent.
def fact_to_str(f, redundancy=0):
	sl = []
	for i in range(len(f)):
		if redundancy==2 or f[i]!=0:
			sl.append( str(prime_idx(i))
				+( '^'+str(f[i]) if redundancy>0 or f[i]!=1 else '' )
				)
	if len(sl) == 0:
		return '1'
	return ' * '.join(sl)

# The closest approximation of r in (d)ED(n) is `round(in_ed(r,d,n))`, or
# more commonly, the closest approximation of r in n-EDO is `round(in_ed(r,n))`.
def steps(r, et2):
	return math.log(convert(r,float),2) / et2
# Note that error is measured as deviation from the exact value of r in
# (divisions)ED(octave_equivalent). The unsigned AKA absolute error of the
# closest approximation of r in n-EDO would be written `abs(err_in_ed(r,n))`.
def step_err(r, et2):
	exact = steps(r, et2)
	return round(exact) - exact
# For use with steps(), step_err(), etc. as the et2 argument
def ed(equal_divisions, of_n=2):
	return math.log(of_n,2)/equal_divisions
def pval(f, et2, p=None):
	f = convert(f,list)
	if p==None:
		p = prime_lim(prime_idx( len(f)-1 ))
	return sum([ f[i]*round(steps(p[i],et2)) for i in range(len(f)) ])
def map_iv(val_map, x): # assumes same harmonic subgroup
	x = convert(x,list)
	return sum([ x[i]*val_map[i] for i in range(len(x)) ])
# returns the signed error of an interval according to a val map
def iv_map_err(x, m, equave=2): # assumes m[0] = period/equave
	return map_iv(m,x) - steps(x,ed(m[0],equave))
def sgn(x):
	if x > 0:
		return 1
	elif x < 0:
		return -1
	else:
		return 0
def idx_alpha(c):
	n = ord(c)
	if n>=ord('a') and n<=ord('z'):
		return n-ord('a')
	elif n>=ord('A') and n<=ord('Z'):
		return n-ord('A')+26
	elif c=='#': # the hash/octothorpe character is
		return 52 # the beginning of a record prime gap
	else: # prime_idx(53) = 251 is the highest prime reachable
		return 53 # with extended warts
def alphawarts(alphabet, gens = prime_lim(256)):
	return [gens[idx_alpha(c)] for c in alphabet]
# the wart_tendency determines if a wart's behaviour is:
# 0: to change to second-best mapping (default)
# 1: to change to one-step-sharper mapping always
# -1: to change to one-step-flatter mapping always
# currently only one wart per gen is supported;
# if you want more its easier to specify the val yourself plus note that
# combined with wart_tendency you can sort of specify up to 2 warts per gen
def make_val(gens, et2, warts = [], wart_tendency = 0):
	val = []
	if type(warts)==str:
		warts = alphawarts(warts,gens)
	for g in gens:
		gen_steps = steps(g,et2)
		pval_steps = round(gen_steps)
		if g in warts:
			if wart_tendency != 0:
				val.append(pval_steps + wart_tendency)
			else:
				val.append(pval_steps - sgn(pval_steps - gen_steps))
		else:
			val.append(pval_steps)
	return val
val = make_val
def odd_lim(lim, remove=set(), add=set(), complements=True):
	odds = set([i for i in range(1,lim+1,2) if i not in remove])
	odds |= set(add)
	ivs = set()
	for numer in odds:
		for denom in odds:
			if numer > denom:
				oct_denom = denom
				while oct_denom*2 < numer:
					oct_denom *= 2
				ivs|={iv(numer,oct_denom)}
	if complements:
		ivs |= set([ iv(i[1]*2,i[0]) for i in ivs ])
	return ivs

def subgroup(ivs):
	sg = []
	for i in ivs:
		f = fact(i)
		for j in range(len(f)):
			if f[j] and prime_idx(j) not in sg:
				sg.append(prime_idx(j))
	return sorted(sg)

# if x is a monzo (which is assumed if type(x) not in [int,tuple]), assumed to be full prime limit (or check would be redundant)
def in_subgroup(x, sg):
	if type(x)==int:
		x = (x, 1) # ensure (x, y) = x/y tuple form if not a monzo
	if type(x)==tuple and x[0]==x[1]:
		return True
	return len( fact(x,sg) if type(x)==tuple else fact(unfact(x),sg) )==len(sg)
def powerset(s):
	pset = set()
	for i in range(1 << len(s)):
		pset |= {frozenset([ s[j] for j in range(len(s)) if (i & (1<<j)) ])}
	return pset
def inconsistent_ivs_by_val(ivs, val, threshold=1/2):
	return [ i for i in ivs if abs(iv_map_err(i,val))>=threshold ]
def deduce_subgroup(r):
	if type(r)==tuple:
		r = fact(r)
	elif type(r)==int:
		r = fact_int(r)
	# else assumed to be monzo
	return [prime_idx(i) for i in range(len(r)) if r[i]!=0]
def get_first_nonpositive(val,lim,start=1):
	if lim % 2 == 1:
		lim += 1
	for i in range(start,lim):
		f = fact( (i+1,i) )
		if not (map_iv(val,f) > 0):
			return (i,f)
	return (lim,[])
def firstnp(val,odd_lim,start=1):
	r = get_first_nonpositive(val,odd_lim,start)
	return (r[0],r[1]) if r[0]%2==0 else (r[0]-1,r[1])
def firstnp_edowarts(edo, warts, tendency = 0, pl = []):
	if pl == []:
		pl = prime_lim(2*edo-1)
	return firstnp( make_val(pl,ed(edo),warts,tendency), 2*edo-1 )
# NOTE: this* condition is a small optimisation and restriction that says if you want prime p fixed then assume full p-odd-limit must be achieved.
def base_ringer(edo, tendency = 0, fixed_subgroup = [2], initial_warts = None):
	if 2 not in fixed_subgroup:
		fixed_subgroup = [2] + fixed_subgroup
	pl = prime_lim(2*edo-1)
	best_warts = initial_warts
	if initial_warts==None:
		best_warts = [ [] ]
	while True:
		last_size = len(best_warts)
		for warts in best_warts:
			(barrier,f) = firstnp_edowarts(edo,warts,tendency,pl)
			wartable = [p for p in deduce_subgroup(f) if p not in fixed_subgroup+warts]
			for new_warts in powerset(wartable):
				if( firstnp_edowarts(edo,warts+list(new_warts),tendency,pl)[0] > max(fixed_subgroup)+1 # *SEE NOTE ABOVE
				and warts+list(new_warts) not in best_warts ):
					best_warts.append(warts+list(new_warts))
		if last_size==len(best_warts): # if no new vals found this time
			best_warts.sort(key=lambda warts: -firstnp_edowarts(edo,warts,tendency,pl)[0])
			best_lim = firstnp_edowarts(edo,best_warts[0],tendency,pl)[0]
			result = (best_lim,[])
			initial_warts = [ [] ]
			i = 0
			while (i<len(best_warts) and
				firstnp_edowarts(edo,best_warts[i],tendency,pl)[0]==best_lim):
				result[1].append(best_warts[i])
				i += 1
			return result
# if the val given splits a 2-step superparticular interval into two 1-step superparticular intervals without modification
# then it will show that, otherwise it will just show what the superparticular interval is mapped to by the val.
# used for completing building Ringer scales. 
def showRinger(edo, warts = None, tendency = 0):
  if warts==None:
    warts = []
  v = val(lim(edo*4),ed(edo),warts,tendency)
  for h in range(v[0],v[0]*2):
    x = (h+1,h)
    edosteps = map_iv(v,x)
    if edosteps==2 and map_iv(v,(2*h+1,2*h))==map_iv(v,(2*h+2,2*h+1))==1:
      print(striv( (2*h+1,2*h  ),1 ))
      print(striv( (2*h+2,2*h+1),1 ))
    else:
      print(striv(x,edosteps))
def rwartable(edo,warts,tendency=0):
	l = firstnp_edowarts(edo,warts,tendency)[0]
	results = []
	for p in lim(l):
		w = []
		if p in warts:
			w = [i for i in warts if i!=p]
		else:
			w = warts+[p]
		if firstnp_edowarts(edo,w,tendency)[0] >= l:
			results.append(p)
		return results

def maxringer(edo):
	return max(base_ringer(edo)[0],base_ringer(edo,1)[0],base_ringer(edo,-1)[0])

# this function is experimental and has a higher chance of bugs/unexpected behaviour
def genRinger(oddlim,edo,warts,tendency=0):
	if oddlim%2==1:
		oddlim+=1
	h=[k for k in range(oddlim//2,oddlim+1)]
	i = 0
	basev=val(lim(oddlim*4),ed(edo),warts,tendency)
	wadd=[]
	while i+1 < len(h):
		x=iv(h[i+1],h[i])
		m = map_iv(basev,x)
		if m==1:
			i+=1 # no filler needed
			continue
		elif m>1:
			y=iv(h[i]*2+1,h[i]*2)
			if in_subgroup(y,lim(oddlim)):
				if map_iv(basev,y) < m:
					h = h[:i+1] + [h[i]*2+1] + h[i+1:]
					i+=2
					continue
				else:
					print(striv(x)+' is mapped to 2 steps but '+striv(y)+' is mapped to '+str(map_iv(basev,y))+' steps!')
					i+=1
					continue
			else:
				f=fact_int(h[i]*2+1)
				wp=prime_idx(len(f)-1)
				if map_iv(basev,y)==1:
					h = h[:i+1] + [h[i]*2+1] + h[i+1:]
					i+=2
					continue
				else:
					m2=map_iv( val(lim(oddlim*4),ed(edo),warts+wadd+[wp],tendency), y )
					if m2 < m:
						h = h[:i+1] + [h[i]*2+1] + h[i+1:]
						wadd.append(wp)
						i+=2
						continue
					else:
						print(striv(y)+'\'s mapping could not trivially be fixed!')
						i+=1
						continue
		else:
			print(striv(x)+' is mapped to '+str(m)+' steps! (bad val)')
			i+=1
			continue
	return ':'.join([ str(k)+('' if k<=oddlim else '/2') for k in h ])

def toneji(strneji,minimalmode=False):
	strhs = strneji.split(':')
	denoms = [ int(h.split('/')[1]) for h in strhs if '/' in h ]
	multiplier = math.lcm(*denoms) # note: math.lcm not present in some older versions of Python 3
	neji = [ int(h.split('/')[0]) * multiplier // int(h.split('/')[1])
		if '/' in h else int(h) * multiplier for h in strhs ]
	if minimalmode:
		neji = neji[:-1] # remove octave-duplication of lowest harmonic
		while neji[-1]%2==0:
			neji = [neji[-1]//2] + neji[:-1]
		neji.append(neji[0]*2) # add back octave-duplication of lowest harmonic
	return neji

def worstneji(neji,n=1,**val_or_warts):
	if type(neji)==str:
		neji = toneji(neji)
	v = val_or_warts.get('val') or val_or_warts.get('v')
	# in the below or expressions, the rightmost value is the default
	warts = val_or_warts.get('warts') or val_or_warts.get('warted') or val_or_warts.get('w') or []
	tends = val_or_warts.get('tendency') or val_or_warts.get('tends') or val_or_warts.get('tend') or val_or_warts.get('t') or 0
	if warts and type(v)==list:
		print('WARNING: can\'t give both a val and warts at the same time! using val...')
	if not v or type(v)==int: # if an explicit full val map (list of ints) was not specified, deduce the val
		v = val( lim(neji[-1]), ed(len(neji)-1) if not v else ed(v), warts if warts else [], tends if tends else 0 )
	ineji = [( iv(x,y), iv_map_err((x,y),v) ) for x in neji for y in neji]
	ineji.sort(key=lambda xerr: -xerr[1]) # (no duplicates; use positive errors)
	for x,err in ineji[:n]:
		print(striv(x)+':',err)
	if not val_or_warts.get('suppress') and not all([ map_iv( v, (neji[i+1],neji[i]) )==1 for i in range(len(neji)-1) ]):
		print('bad val (scale is potentially not CS); see below:')
		for i in range(len(neji)-1):
			x = iv(neji[i+1],neji[i])
			print(striv( x, map_iv(v,x) ))
# reduce with harmonic integer equave "ave" into range [1, ave)
def reduce(x,ave=2):
	x = convert(x,tuple)
	while x[0] >= x[1] * ave:
		if x[0] % ave == 0:
			x = (x[0]//ave, x[1])
		else:
			x = (x[0], x[1]*ave)
	while x[0] < x[1]:
		if x[1] % ave == 0:
			x = (x[0], x[1]//ave)
		else:
			x = (x[0]*ave, x[1])
	return iv(x[0], x[1])
def avg(vs,weights=None):
	if not weights:
		weights=[ 1.0 for v in vs ]
	return [ sum([ vs[vi][i]*weights[vi] for vi in range(len(vs)) ])/sum(weights) for i in range(len(vs[0])) ]
def length(v):
	return sum([ c**2 for c in v ])**(1/2)
def turns(v): # assumes v is 2D vector; turns expressed as unit up being 0 turns and unit left being -1/2 turns
	return math.atan2(v[0],v[1])
# [0,1] (unit up) is exactly in-tune with et2; note the below:
# mean = turns(result)/math.tau, variance = 1 - length(result), badness = length(result)
def circular_mean(et2,harmonics,weights=None):
	if weights==True: # interpret as primelike subgroup; use commonness in harmonic series
		weights = [ 1/math.log(h,2) for h in harmonics ]
	herr=[ (h,step_err(h,et2)) for h in harmonics ]
	return avg([ [math.sin(math.tau*e[1]),math.cos(math.tau*e[1])] for e in herr ],weights)
def circular_normalise(e,m): # into range -1/2, +1/2
	return (e-m)%1 if (e-m)%1 < 0.5 else (e-m)%1 - 1
def circular_step_err(x,et2,m):
	return circular_normalise(step_err(x,et2),m)

def orderedapproximator(et2,startingharmonics,maximalharmonics,n=None,weights=None,noisy=True):
	currentharmonics=startingharmonics.copy()
	oldl = 1
	if n==None:
		n = -1
	widest = max([ len(str(reduce(h)[0])) for h in maximalharmonics ])
	while set(currentharmonics)!=set(maximalharmonics) and (
		n!=0 if type(n)==int else not set(n)<set(currentharmonics)
		):
		if type(n)==int:
			n -= 1
		oldl = length(circular_mean(et2,currentharmonics,weights))
		addable = [ h for h in maximalharmonics if h not in currentharmonics ]
		addable.sort(key=lambda i:length( circular_mean(et2,currentharmonics+[i],weights) ))
		currentharmonics.append(addable[-1])
		if noisy:
			cm = circular_mean(et2,currentharmonics,weights)
			print( str.rjust( str(reduce(addable[-1])[0]), widest ), '+',
				str( 100*(oldl-length( cm )) )[:6] + ',',
				str( math.atan2(cm[0],cm[1])/math.tau*360 )[:6],
				'('+str( 100 - length(cm)*100 )[:5]+'% out)'
				)
	return currentharmonics


# below is not well tested! consider yourself warned.
def scalar_mul_fact( s, f ):
	return [ s*entry for entry in f ]

def temp_coord_iv( tmp, x ):
	monzo = convert(x,list)
	return mul_fact(*[ scalar_mul_fact(monzo[i],tmp[i]) for i in range(len(monzo)) ])

def temp_gen_iv( tmp, gens, x ):
	return sum([ temp_coord_iv(tmp,x)[i]*gens[i] for i in range(len(gens)) ])

def temp_max_err( tmp, gens, ol ):
	worstiv = (1,1)
	maxerr = 0.0
	for x in ol:
		e = temp_gen_iv( tmp, gens, x ) - steps(as_float(x),ed(1200))
		if abs(e) > abs(maxerr):
			maxerr = e
			worstiv = x
	print(striv(worstiv),str(maxerr)+'c')

def temp_err( tmp, gens, ol ):
	worstupperiv = (1,1)
	maxerr = 0.0
	worstloweriv = (1,1)
	minerr = 0.0
	for x in ol:
		e = temp_gen_iv( tmp, gens, x ) - steps(as_float(x),ed(1200))
		if e > maxerr:
			maxerr = e
			worstupperiv = x
		if e < minerr:
			minerr = e
			worstloweriv = x
	print( striv(worstupperiv), str(maxerr)+'c' )
	print( striv(worstloweriv), '-'+str(-minerr)+'c' )


def bright_gen(nL, ns): # credit to inthar for this function
	nL, ns = nL//math.gcd(nL, ns), ns//math.gcd(nL, ns)
	path = []
	while nL > 1 or ns > 1:
		path.append((nL, ns))
		nL, ns = min(nL, ns), abs(nL - ns)
	result = [1, 0]
	for j in range(len(path)-1, -1, -1):
		child_nL, child_ns = path[j][0], path[j][1]
		if child_nL > child_ns:
			result = [child_nL - (result[0] + result[1]), child_ns - result[0]]
		else:
			result = [result[0], result[0] + result[1]]
	return result

def bright_gens(nL, ns):
	r = bright_gen(nL, ns)
	return [iv(r[0], nL), iv(r[0]+r[1], nL+ns)]
def least_gens(nL, ns):
	d = math.gcd(nL,ns)
	r = bright_gens(nL//d, ns//d)
	if as_float(r[0])>=1/2:
		r[0] = sub_iv((1,1), r[0])
	if as_float(r[1])>=1/2:
		r[1] = sub_iv((1,1), r[1])
	if as_float(r[0]) > as_float(r[1]):
		r = [r[1], r[0]]
	return [mul_iv( r[0], (1,d) ), mul_iv( r[1], (1,d) )]
def strmoss(nL, ns, period=1200):
	r = least_gens(nL, ns)
	return( str(nL)+'L '+str(ns)+'s has range '
		+str(r[0][0])+'\\'+str(r[0][1])+' to '+str(r[1][0])+'\\'+str(r[1][1])+' AKA '
		+str( as_float(r[0])*period )[:6]+' - '+str( as_float(r[1])*period )[:6]
		+(' (period='+str(period)+')' if period!=1200 else '') )

def mediant(r1, r2):
	return (r1[0] + r2[0], r1[1] + r2[1])
def mediant_iv(r1, r2): # for convenience; provides simplified form
	return iv(*mediant(r1,r2))
def mulmediant(r, k):
	return (r[0]*k, r[1]*k)

def gs(gs,n=0,ave=2):
	if n==0:
		n = len(gs)*2 + 1
	scale = [(1,1)]
	gi = 0
	while len(scale) < n:
		new_iv = reduce(mul_iv( scale[-1], gs[gi] ),ave)
		gi = (gi+1)%len(gs)
		while new_iv in scale:
			new_iv = reduce(mul_iv( new_iv, gs[gi] ),ave)
			gi = (gi+1)%len(gs)
		scale.append(new_iv)
	scale.sort(key=lambda x: as_float(x))
	scale.append((ave,1))
	return scale[1:]
ags = gs

# returns [] if cs or details of earliest counterexample if not;
# "earliest" is defined as "search all 1-steps from beginning to end, then all 2-steps, etc."
def cs(scale_in):
	stepmap = dict()
	scale = scale_in.copy()
	n = len(scale)
	scale = scale + [ mul_iv(x,scale[-1]) for x in scale ]
	for j in range(1,(n+1)//2):
		for i in range(n):
			interval = div_iv( scale[i], scale[i+j] )
			if interval in stepmap:
				if stepmap[interval] != j:
					return [interval, (i+1)%n, j]
			else:
				stepmap[interval] = j
	return []

def rotate(scale,n,p=(2,1)):
	if n==0:
		return scale.copy()
	rs = scale + [ mul_iv(x,p) for x in scale ]
	return [ div_iv( x, rs[n-1] ) for x in rs[n:len(scale)+n] ]
def equiv_scales(x,y,p=(2,1)):
	if len(x)!=len(y):
		return 0
	x = [ iv(i[0],i[1]) for i in x.copy() ]
	y = [ iv(i[0],i[1]) for i in y.copy() ]
	if x==y:
		return len(x)
	for k in range(len(x)):
		if rotate(x,k,p) == y:
			return k
	return 0

# generates a detempering of any EDO into a minimal-complexity set of octave-reduced prime harmonics;
# only uses patent val primes as else itd not only be nontrivial but lead to more questionable scales
def primer(edo, prime_octave=False):
	primerscale = [placeholder for placeholder in range(0,edo+1)]
	k = 0
	while not all([ type(x)==tuple for x in primerscale ]):
		prime_iv = reduce( (prime_idx(k),1) )
		stepsofedo = round(steps( as_float(prime_iv), ed(edo) ))
		if type(primerscale[stepsofedo])==int:
			primerscale[stepsofedo] = prime_iv
		k += 1
	if prime_octave:
		return primerscale[1:]
	return primerscale[1:-1] + [(2,1)] # replace the prime at the octave with 2/1

# note: it's easy for any pythonista to write the equivalent print statement out manually,
# but i've included it here for brevity/convenience as it's common to want to do to get
# smth compatible with EG ScaleWorkshop or other tuning formats for ease of copypasting
def print_scale(scale):
	print('\n'.join([ striv(x) for x in scale ]))

def interpret_edo(edo,ol=17,sg=[],no=[],add=[],styler=lambda x,relerr: striv(x) if abs(relerr) < 1/2 else '*'+striv(x)+'*',dec='*'):
	considered_bad = styler if type(styler)==int or type(styler)==float else 1/2
	if considered_bad != 1/2 or dec != '*': # if a custom bound was provided instead of a styler or a decoration was provided
		styler=lambda x,relerr: striv(x) if abs(relerr) < considered_bad else dec+striv(x)+dec[::-1]
	# else use default styler if no custom decoration was provided and the styler is not a numeric type
	print(edo,'EDO interpretation')
	if not sg:
		sg = lim(ol)
	odds = [odd for odd in range(1,ol+1,2) if in_subgroup(odd,sg) and odd not in no] + add
	sedo = 0
	for x in sorted( odd_lim(1,[],odds), key=lambda x: steps(x,ed(edo)) ):
		sedoofx = pval(x,ed(edo))
		relerr = steps(x,ed(edo)) - sedoofx
		if sedoofx!=sedo:
			sedo = sedoofx
			print('\n'+str(sedo)+'\\'+str(edo)+': '+styler(x,relerr),end='')
		else:
			print(', '+styler(x,relerr),end='')
	print()

Important 23-limit EDOs

Produced with the help of my above code and corrected with love. Below are theoretically important 23-limit EDOs in that they do an increasingly better job at representing the 27-odd-limit minus zero or one of composite odds 25 and 27. The exception is that if an EDO (such as 94) achieves less than 2 inconsistencies in the 25-odd-limit or no-25's 27-odd-limit then we don't consider a record to be set to give a chance to larger EDOs that are often interesting for having better approximations (of the intervals mentioned or of other intervals in higher limits). The logical stopping point is 311edo due to its extreme efficiency and elegance as a 23-limit and 41-limit temperament (plus some optional large primes). Please note that there may be one or two important 23-limit EDOs missing here, because I've only checked patent val and occasionally a non-patent-val will perform better for consistency, however, one can argue that a patent val representation should be a requirement for any serious 23-limit EDO.

It should hopefully go without saying that by "serious 23-limit EDO" I mean in terms of archetypal and structural importance; to me it's obvious, with the exception of maybe EDOs 46 and 53 (corresponding to Eros (rank 3) and maybe also Amity (rank 2)), that any 23-limit EDO must have at least 77 notes, to allow for the fitting of a submajor third (21/17 plus various higher-complexity submajor thirds), a superneutral third (16/13), a subneutral third (11/9) and a supraminor third (most notably 17/14 and 23/19; equated in any "small" system) between 5/4 and 6/5, corresponding to splitting 25/24 into 5 equal parts. If you do this with one part equated to 81/80 you get absurdity, explaining its non-absurdity and, to the contrary, intuitiveness, as a 29-limit temperament (given that prime 29 is essentially free for multiples of 7 EDO). If you instead choose to exaggerate 81/80 to 2 parts you get an extension of 5-limit artoneutral (I specify 5-limit as 80edo, one logical tuning for it, uses a different mapping for 7).

10edo gets 13 wrong in the no-25's 27-odd-limit (and 14 wrong in the 25-odd-limit (noticed the code produced a mistake here))

12edo gets 12 wrong in the no-25's 27-odd-limit (and 13 wrong in the 25-odd-limit (noticed the code produced a mistake here))

15edo gets 9 wrong in the 25-odd-limit

16edo gets 8 wrong in the 25-odd-limit

24edo gets 10 wrong in the no-25's 27-odd-limit

29edo gets 9 wrong in the no-25's 27-odd-limit

41edo gets 8 wrong in the no-25's 27-odd-limit

43edo gets 8 wrong in the 25-odd-limit

46edo gets 5 wrong in the no-25's 27-odd-limit and 8 wrong in the 25-odd-limit (noticed the code produced a mistake here)

50edo gets 5 wrong in the 25-odd-limit

53edo gets 5 wrong in the 27-odd-limit

68edo gets 5 wrong in the 25-odd-limit

77edo gets 5 wrong in the no-25's 27-odd-limit

80edo gets 4 wrong in the 25-odd-limit

94edo gets 0 wrong in the no-25's 27-odd-limit

111edo gets 2 wrong in the no-25's 27-odd-limit

113edo gets 2 wrong in the no-25's 27-odd-limit

130edo gets 3 wrong in the no-25's 27-odd-limit

149edo gets 4 wrong in the 25-odd-limit

159edo gets 1 wrong in the 27-odd-limit

183edo gets 3 wrong in the no-25's 27-odd-limit

193edo gets 1 wrong in the 25-odd-limit & no-25's 27-odd-limit (union of these is not the same as 27-odd-limit!)

217edo gets 2 wrong in the 27-odd-limit

282edo gets 0 wrong in the 27-odd-limit

296edo gets 2 wrong in the no-25's 27-odd-limit

311edo gets 0 wrong in the 27-odd-limit

Novation Launchpad Pro MK3 isomorphic keyboard code

This code is licensed under the AGPLv3 (https://www.gnu.org/licenses/agpl-3.0.html), a version of the AGPL corresponding to the GPLv3.

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.)

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.

# this code is licensed under the AGPLv3 <https://www.gnu.org/licenses/agpl-3.0.html>
import math
import mido # pip install mido
# requires loopMIDI on Windows (using the default name should work; the code searches for a name starting with "loop")

def lpselect(of):
	return 'aunchpad' in of or 'aunchPad' in of or 'AUNCHPAD' in of or 'LP' in of
# IMPORTANT: requires the launchpad to be plugged in with updated firmware and be in programmer mode!
def basic_iso(vel=100): # y=10, x=1
	midi_inputs = mido.get_input_names()
	print('midi inputs detected:')
	for mi in midi_inputs:
		print(mi)
	print('selecting:')
	launchpad = [mi for mi in midi_inputs if lpselect(mi)][0]
	print(launchpad)
	# transform the launchpad pro mk3's midi input (mi) into custom midi output (mo)
	with mido.open_input(launchpad) as mi, mido.open_output() as mo:
		for msg in mi: # process midi msg from launchpad
			to_send = msg.copy()
			if 'control' in str(msg): # buttons not pressure-sensitive so use set velocity (i choose 100)
				to_send = mido.Message( 'note_on', channel=0,
					note=msg.control, velocity=vel if msg.value else 0
					)
			if not 'clock' in str(to_send):
				print(to_send)
			mo.send(to_send)
# rb for rainbow
rb12 = [5, 9, 13, 17, 21, 25, 77, 37, 45, 81, 53, 57]
rb13 = [5, 9, 13, 17, 21, 25, 77, 37, 41, 45, 81, 53, 57] # add 41 to rb12
cm16 = [4, 5, 6, 7, 12, 13, 14, 15, 24, 25, 26, 27, 44, 45, 46, 47]
rb16 = [120, 60, 9, 109, 13, 98, 122, 25, 29, 37, 41, 45, 49, 81, 53, 57]
rb17 = [120, 60, 84, 96, 109, 13, 98, 122, 25, 29, 37, 41, 45, 49, 81, 53, 57]
rb18 = [120, 60, 84, 96, 109, 13, 98, 122, 25, 29, 37, 41, 45, 49, 81, 94, 82, 57]
rb29old = [120, 5, 60, 84, 96, 61, 62, 97, 13, 74, 98, 75, 122, 21, 25, 29, 33, 37, 41, 79, 67, 80, 49, 81, 94, 53, 95, 57, 58]
rb30old = rb29old[:6] + [126] + rb29old[6:] # insert 126 between 61 and 62
rb31old = rb30old[:4] + [9] + rb30old[4:] # insert 9 between 84 and 96

rb36ry = [120, 5, 60, 84, 96, 61, 126, 62, 99, 97] # len-1=9; 61 & then 126 to be removed for distinctness
rb36yg = [97, 13, 74, 98, 85, 75, 17, 122, 21] # len-1=8; 98 & 75 to be removed for distinctness
rb36gb = [21, 25, 29, 33, 37, 78, 41, 79, 45] # len-1=8; lacking in colours so do not remove any if making rb36 more even
rb36br = [45, 80, 69, 49, 81, 94, 53, 82, 95, 57, 58, 120] # len-1=11 53 or 82 to be removed for smoothness; 80 to be removed for distinctness
rb36 = rb36ry[:-1] + rb36yg[:-1] + rb36gb[:-1] + rb36br[:-1] # maximal, but uneven, rainbow
rb34 = [c for c in rb36 if c not in [61,62]]
ry5 = [120, 60, 84, 126, 99]
yg5 = [97, 13, 74, 98, 75]
gc5 = [17, 122, 21, 25, 29]
cb5 = [33, 37, 78, 41, 79]
bp5 = [45, 80, 49, 81, 94]
pr5 = [53, 82, 95, 57, 58]
rb30 = ry5 + yg5 + gc5 + cb5 + bp5 + pr5
rb31 = [120, 5] + rb30[1:]
rb29 = [c for c in rb30 if c!=75]
# 29 colours is a fairly ideal rainbow; we use it for indexing into, so as to derive a 17-colour palette.
# note that i chose the +0.4 experimentally based on the colour palette that seemed most distinct to me.
def cols(cs, n, offset=0.4):
	return [ cs[round( i/n*len(cs)+offset )%len(cs)] for i in range(n) ]
rb17v2 = cols(rb29,17)
rb19 = cols(rb29,19)
rb28 = [c for c in rb29 if c!=95]
rb27 = [c for c in rb28 if c!=99] # or 97?
rb26 = [c for c in rb27 if c!=37]
rb25 = [c for c in rb26 if c!=74]
colmaps = { 12: rb12, 13: rb13, 16: cm16, 17: rb17v2, 18: rb18, 19: rb19, 25: rb25, 26: rb26, 27: rb27, 28: rb28, 29: rb29, 30: rb30, 31: rb31, 34: rb34, 36: rb36 }
# read as "i want [notes] notes of [of] (EDO/other background scale) and the generator is [gen] scalesteps of [of]".
# if you want to use the (0-indexed) [tonic]th degree as the tonic specify it as the 4th argument [tonic] > 0.
# if you want to generate downwards by -tonic gens from the tonic, specify it as the 4th argument [tonic] < 0.
def genindices(notes,of,gen=7,tonic=0):
	notesubset = [0]
	while len(notesubset) < notes:
		notesubset.append( (notesubset[-1] + gen) % of )
	tonic_scalesteps = 0
	if tonic < 0:#, then instead of going up by gen\of notes times, we add tonic to the amount
		tonic_scalesteps = notesubset[-tonic] # of times we go up so that we go down -tonic times
	notesubset.sort() # it's important that this line is exactly here
	if tonic_scalesteps:
		tonic = notesubset.index(tonic_scalesteps)
	if tonic > 0: # use degree [tonic] as the new tonic
		tonic = tonic % notes # [notes]th degree = 0th degree = tonic
		notesubset += [i+of for i in notesubset]
		notesubset = [i-notesubset[tonic] for i in notesubset[tonic:tonic+notes]]
	return notesubset[1:] + [of] # exclude 1/1, include period
# TODO: add more colmaps
import platform # to check whether we're running Windows, in which case we want to use loopMIDI
mo = mido.open_output() if platform.system()!='Windows' else mido.open_output([ port for port in mido.get_output_names() if 'loop' in port ][0])
# mo = mido.open_output() # use the same output for entire session
def iso(x=5, y=1, colmap_in=[34], highlight=[], **args):
	global mo
	colmap = colmap_in # make default value immutable
	# select colmap
	global colmaps
	if type(colmap)==int:
		if colmaps.get(colmap):
			colmap = colmaps[colmap]
		elif colmap < 31 or colmap%2==0 and colmap//2 < 31:
			if colmap > 31 and colmap%2==0:
				colmap //= 2
			colmaps[colmap] = [ rb30[round( k/colmap*30+0.4 )%30] for k in range(colmap) ]
			colmap = colmaps[colmap]
		else:
			print('no colmap in colmaps for',colmap,'notes; using default colmap')
	if highlight:
		highlight = sorted(list(set(highlight)))
		mul = math.lcm(len(colmap),highlight[-1])
		colmap = [( colmap*mul )[i%len(colmap)] if i%highlight[-1] in [0]+highlight else 3 for i in range(mul)]
	# get arguments with default values
	bottomleftnote = args.get('bottomleftnote') or args.get('blnote') or args.get('bl') or args.get('b') or min(x,y)
	vel = args.get('velocity') or args.get('vel') or args.get('v') or 102 # velocity used when lacking sensitivity
	min_vel = ( args.get('minimum_velocity') or args.get('minimum_vel') or args.get('minimum_v')
		or args.get('min_velocity') or args.get('min_vel') or args.get('min_v')
		or args.get('minvelocity') or args.get('minvel') or args.get('minv')
		or 1 ) # minimum velocity required (else ignored)
	half = args.get('half') or args.get('h') or 40 # how many midi notes to go up from bottomleft before the velocity is halved
	# for the launchpad pro MK3 specifically:
	distinct_half_buttons = ( args.get('distinct_half_buttons') or args.get('distinct_halves')
		or args.get('distinct_buttons') or args.get('distinct') or args.get('d') # (d for double)
		or False ) # whether to interpret the half-buttons as being two buttons for the same note or a single button
	
	# find launchpad output (what we send colour (col) data to)
	midi_outputs = mido.get_output_names()
	print('midi outputs detected:')
	for i in midi_outputs:
		print(i)
	print('selecting:')
	launchpad_col = [i for i in midi_outputs if lpselect(i)][0]
	print(launchpad_col)
	# find launchpad input (what we get midi input (mi) data from)
	midi_inputs = mido.get_input_names()
	print('midi inputs detected:')
	for i in midi_inputs:
		print(i)
	print('selecting:')
	launchpad_mi = [i for i in midi_inputs if lpselect(i)][0]
	print(launchpad_mi)
	
	# define mapping from midi note given by launchpad to midi note in isomorphic layout
	def transform(midi_note):
		if midi_note < 100:
			return midi_note%10 * x + midi_note//10 * y + bottomleftnote
		else:
			return (midi_note - 100) * x + bottomleftnote
	
	key_codes = range(1,109) # includes a few key codes not corresponding to anything on launchpads but shouldn't be an issue.
	note_state = dict() # for keeping track of how many equivalent notes are being held at a given time
	for k in key_codes:
		note_state[transform(k)] = 0
	
	with mido.open_input(launchpad_mi) as mi, mido.open_output(launchpad_col) as col:
		print('colouring...')		
		for k in key_codes:
			col.send(mido.Message( 'note_on', channel=0,
				note=k, velocity=colmap[ transform(k) % len(colmap) ]
				))
		print('starting midi event processing...')
		# transform the launchpad's midi input (mi) into custom midi output (mo)
		for msg in mi: # process midi msgs from launchpad
			to_send = msg.copy()
			k = 0 # what key is being pressed
			note_vel = 0
			is_half_button = False
			if 'control' in str(msg):
				k = msg.control
				note_vel = vel if msg.value else 0
				is_half_button = msg.control > 100 or msg.control < 10
			elif 'note' in str(msg):
				k = msg.note
				note_vel = msg.velocity
			else:
				continue
			note_vel = note_vel if note_vel >= min_vel else 0
			midi_note = transform(k)
			if note_vel: # if we're pressing a key
				note_vel = round( note_vel * 2**( -(midi_note-bottomleftnote)/half ) )
				if note_state[midi_note]: # if one or more equivalent keys are already held
					to_send = mido.Message('note_on', channel=0, note=midi_note, velocity=0)
					if not is_half_button or distinct_half_buttons:
						print(to_send)
						mo.send(to_send) # turn off equivalent midi note before repressing
				else: # turn off all equivalent keys to indicate being pressed
					for k in key_codes:
						if transform(k)==midi_note:
							col.send(mido.Message('note_on', channel=0, note=k, velocity=0))
				to_send = mido.Message('note_on', channel=0, note=midi_note, velocity=note_vel)
				if not is_half_button or note_state[midi_note]==0 or distinct_half_buttons:
					print(to_send)
					mo.send(to_send) # turn on equivalent midi note
					note_state[midi_note] += 1
			elif note_state[midi_note]: # if we're releasing a key and there are equivalent keys held
				note_state[midi_note] -= 1
				if note_state[midi_note]==0: # if we turn off all keys as a result... turn off
					to_send = mido.Message('note_on', channel=0, note=midi_note, velocity=0)
					print('col='+str(colmap[ midi_note % len(colmap) ]),to_send)
					mo.send(to_send) # turn off equivalent midi note
					for k in key_codes:
						if transform(k)==midi_note: # if equivalent... set key to unpressed col
							col.send(mido.Message('note_on', channel=0,
								note=k, velocity=colmap[ transform(k) % len(colmap) ]
								))