Repository: librosa/librosa
Pull request #1739 (2023-08-15 19:52:04)

Multiple accidentals
<!--
Thanks for contributing a pull request! Please ensure you have taken a look at
the contribution guidelines: https://github.com/librosa/librosa/blob/main/CONTRIBUTING.md#how-to-contribute
-->
#### Reference Issue
Multiple accidentals in tonics, expanded support for modes in note spelling #1724


#### What does this implement/fix? Explain your changes.

I made a first pass at support for multiple accidentals in  `key_to_degrees` and `key_to_notes`.

The `key_to_degrees` method required very little modification.

The basic idea for the approach for `key_to_notes` was: for 'Cbb:min', add flats to each note `key_to_notes('Cb:min')` and then rearrange the scale so that the enharmonic equivalent of C is first.


#### Any other comments?

Some decisions still need to be made regarding whether more than two accidentals are allowed, and the appropriate regular expressions need to be modified.
I have modified one of the existing regular expressions, and added one of my own.

I've also added a few helper methods, namely `note_to_degree` and `simplify_note`.

I've made some comments about places where the code could be made a bit more efficient.





bmcfee: Thanks again for this!  Just leaving a note for now that I'm a bit swamped with life outside of github right now, but hope to find time to properly dig into this soon.  (Don't want to leave you hanging.)
erabinov: Thanks for keeping me posted!
bmcfee: :wave: hi @erabinov , I'm looking into this now, sorry for the delay.

I've taken the liberty of fixing the merge conflict in the AUTHORS.md file so that our CI action can run, but I've noticed that you haven't added or modified any tests yet in this PR.  I tried it out locally, and it seems like your regex doesn't quite work as expected:

```python
In [8]: librosa.key_to_notes(key='B𝄫:maj')
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[8], line 1
----> 1 librosa.key_to_notes(key='B𝄫:maj')

File <decorator-gen-4>:2, in key_to_notes(key, unicode)

File ~/miniconda3/envs/py311/lib/python3.11/site-packages/joblib/memory.py:655, in MemorizedFunc.__call__(self, *args, **kwargs)
    654 def __call__(self, *args, **kwargs):
--> 655     return self._cached_call(args, kwargs)[0]

File ~/miniconda3/envs/py311/lib/python3.11/site-packages/joblib/memory.py:598, in MemorizedFunc._cached_call(self, args, kwargs, shelving)
    595     must_call = True
    597 if must_call:
--> 598     out, metadata = self.call(*args, **kwargs)
    599     if self.mmap_mode is not None:
    600         # Memmap the output at the first call to be consistent with
    601         # later calls
    602         if self._verbose:

File ~/miniconda3/envs/py311/lib/python3.11/site-packages/joblib/memory.py:856, in MemorizedFunc.call(self, *args, **kwargs)
    854 if self._verbose > 0:
    855     print(format_call(self.func, args, kwargs))
--> 856 output = self.func(*args, **kwargs)
    857 self.store_backend.dump_item(
    858     [func_id, args_id], output, verbose=self._verbose)
    860 duration = time.time() - start_time

File ~/git/librosa/librosa/core/notation.py:631, in key_to_notes(key, unicode)
    629 notes = deque([simplify_note(note+additional_acc) for note in intermediate_notes])
    630 #Cycle until the equivalent of 'C' is in the first position. This may be a bit inefficient; if additional_acc == +1, then we need to cycle all the way through.
--> 631 while(note_to_degree(notes[0])!=0):
    632     notes.appendleft(notes.pop())
    634 notes = list(notes)

File ~/git/librosa/librosa/core/notation.py:495, in note_to_degree(key)
    492 if not match:
    493     raise ParameterError(f"Improper key format: {key:s}")
--> 495 letter = match.group('tonic').upper()
    496 accidental = match.group('accidental')
    497 pitch_map = {"C": 0, "D": 2, "E": 4, "F": 5, "G": 7, "A": 9, "B": 11}

IndexError: no such group
```

So before I dig into a proper code review here, would it be possible to add a couple of test cases that verify the new functionality is behaving properly?

I'm also curious why a new regex (`UNICODE_NOTE_RE`) was needed for the note parser.  It already supported both unicode and ascii spellings, and it seems like the logic is already there to accumulate multiple accidentals correctly.  The comment you added there indicates that there might be a problem with the previous regexes, but I'd like to know more about what the problem is/was so we can fix it rather than proliferate new parsers.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/librosa/librosa/pull/1739?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=librosa) Report
Patch coverage is **`29.54%`** of modified lines.

| [Files Changed](https://app.codecov.io/gh/librosa/librosa/pull/1739?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=librosa) | Coverage |
|---|---|
| [librosa/core/notation.py](https://app.codecov.io/gh/librosa/librosa/pull/1739?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=librosa#diff-bGlicm9zYS9jb3JlL25vdGF0aW9uLnB5) | `29.54%` |


:loudspeaker: Thoughts on this report? [Let us know!](https://about.codecov.io/pull-request-comment-report/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=librosa).

erabinov: Hi @bmcfee,

Thanks for the feedback! I'll get working on it tomorrow. 

With the regexes, there are two different changes I made.

1. I changed the `KEY_RE` regex to include support for up to two sharps or two flats in the key signature name. If you want more accidentals (if, e.g., you want to continue down the rabbit hole and have support for modes with multiple accidentals), then this would have to change. (Also, this regex as I have it allows `C#b:maj`, which we probably want to exclude.) 
**So it would be helpful to know for sure whether you have in mind to allow, as input to `key_to_notes` function, more than two sharps or flats, and/or sharps and flats in the same key name** (e.g. `C###:min` or `C#b:maj`)

2. The `UNICODE_NOTE_RE`  regex is almost certainly not necessary and I just used it in the helper functions to make my other additions to the code shorter. I'll get rid of it when I work on the code tomorrow.

As for the tests, I added some to the docstring, but I didn't realize I should add them to `tests.py`. I'll work on that tomorrow too.
bmcfee: Thanks @erabinov for adding the tests - this is looking good so far!  I'll try to find some more time later on to see if I can patch this together with my (very broken) prototype for handling modes to see if it correctly handles the cases I was having trouble with (eg `Cb:phrygian`).

I've added some comments on the code now, mainly having to do with some implementation details and consistency with the rest of the package.  I'm not yet sure if the vectorization part is/will be necessary, so I'm curious for your thoughts there.  
erabinov: Ok, @bmcfee, I implemented the discussed changes, leaving open the question of whether the helper methods should be private or public. I may need to make some more tests, but at first attempt in a notebook, the methods were working as I expected.
bmcfee: Thanks @erabinov - looks pretty good!  Ignore the CI failure due to codecov, but we do still have some linting problems.

Meanwhile, I'm (offline) revising my mode->key converter to see how it plays with your implementation.  If that all works smoothly, I think we'll be good to merge.  But I'll still give it one more thorough review before then.
bmcfee: Alright!  Hacked together a mode converter and tested it with the new and improved multi-accidental key code.  Looks to me like everything is working properly!

```python
major_dict = {
    'ion': {'C': 'C', 'D': 'D', 'E': 'E', 'F': 'F', 'G': 'G', 'A': 'A', 'B': 'B'},
    'dor': {'C': 'B♭', 'D': 'C', 'E': 'D', 'F': 'E♭', 'G': 'F', 'A': 'G', 'B': 'A'},
    'phr': {'C': 'A♭', 'D': 'B♭', 'E': 'C', 'F': 'D♭', 'G': 'E♭', 'A': 'F', 'B': 'G'},
    'lyd': {'C': 'G', 'D': 'A', 'E': 'B', 'F': 'C', 'G': 'D', 'A': 'E', 'B': 'F♯'},
    'mix': {'C': 'F', 'D': 'G', 'E': 'A', 'F': 'B♭', 'G': 'C', 'A': 'D', 'B': 'E'},
    'aeo': {'C': 'E♭', 'D': 'F', 'E': 'G', 'F': 'A♭', 'G': 'B♭', 'A': 'C', 'B': 'D'},
    'loc': {'C': 'D♭', 'D': 'E♭', 'E': 'F', 'F': 'G♭', 'G': 'A♭', 'A': 'B♭', 'B': 'C'}
}

def mode_to_key(signature: str) -> str:
    # Translate a mode (eg D:dorian) into its equivalent major key
    
    match = re.match(
        r"^(?P<tonic>[A-Ga-g])"
        r"(?P<accidental>[#♯𝄪b!♭𝄫]*)"
        r":((?P<scale>(maj|min)(or)?)|(?P<mode>(ion|dor|phr(yg)?|lyd|mix(olyd)?|aeol?|locr?)(ian)?))$",
        signature,
    )
    
    if not match:
        raise ParameterError("Improper format: {:s}".format(signature))

    if match.group('scale') or not match.group("mode"):
        # We're already fine here
        return signature
        
    # We have a mode, time to translate
    mode = match.group("mode").lower()[:3]

    # Get the relative major
    tonic = major_dict[mode][match.group("tonic").upper()]

    return f"{tonic}{match.group('accidental')}:maj"
```

The following test code will try all combinations of tonics, single accidentals, and modes.  It will fail if the selected diatonic note names are not unique (ie use all seven names).
```python
for k in 'CDEFGAB':
    for acc in ['b', '', '#']:
        root = k + acc
        for mode in ['ion', 'dor', 'phr', 'lyd', 'mix', 'aeol', 'loc']:
            query = f"{root}:{mode}"
            equiv = mode_to_key(query)

            degs = librosa.key_to_degrees(equiv)
            notes = librosa.key_to_notes(equiv)
            
            names = [notes[_] for _ in degs]
            print(f"{query:10s}\t->\t{notes}\t->\t{names}", end='')
            if len(set([n[0] for n in names])) != 7:
                print('\t->\tFAIL')
            else:
                print()
        print('-' * 15)
```
As demonstrated below, all notes are correctly generated now.  (Woo!)

The only remaining issue here is that they are out of order because we've lost track of what mode we came from.  This is because I've implemented everything outside the key_to_notes function - it first converts mode to key, then gets the degrees from the key.  Properly spelling notes for a mode will require us to rotate the original tonic back into the root position.  Probably that's out of scope for this PR, but worth considering.

<details>
<summary> Output </summary>

```
Cb:ion    	->	['C', 'D♭', 'D', 'E♭', 'F♭', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'C♭']	->	['C♭', 'D♭', 'E♭', 'F♭', 'G♭', 'A♭', 'B♭']
Cb:dor    	->	['D𝄫', 'D♭', 'E𝄫', 'E♭', 'F♭', 'G𝄫', 'G♭', 'A𝄫', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['B𝄫', 'C♭', 'D♭', 'E𝄫', 'F♭', 'G♭', 'A♭']
Cb:phr    	->	['D𝄫', 'D♭', 'E𝄫', 'E♭', 'F♭', 'G𝄫', 'G♭', 'A𝄫', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['A𝄫', 'B𝄫', 'C♭', 'D𝄫', 'E𝄫', 'F♭', 'G♭']
Cb:lyd    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'C♭']	->	['G♭', 'A♭', 'B♭', 'C♭', 'D♭', 'E♭', 'F']
Cb:mix    	->	['C', 'D♭', 'D', 'E♭', 'F♭', 'F', 'G♭', 'G', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['F♭', 'G♭', 'A♭', 'B𝄫', 'C♭', 'D♭', 'E♭']
Cb:aeol   	->	['D𝄫', 'D♭', 'E𝄫', 'E♭', 'F♭', 'G𝄫', 'G♭', 'A𝄫', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['E𝄫', 'F♭', 'G♭', 'A𝄫', 'B𝄫', 'C♭', 'D♭']
Cb:loc    	->	['D𝄫', 'D♭', 'E𝄫', 'E♭', 'F♭', 'G𝄫', 'G♭', 'A𝄫', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['D𝄫', 'E𝄫', 'F♭', 'G𝄫', 'A𝄫', 'B𝄫', 'C♭']
---------------
C:ion     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['C', 'D', 'E', 'F', 'G', 'A', 'B']
C:dor     	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['B♭', 'C', 'D', 'E♭', 'F', 'G', 'A']
C:phr     	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['A♭', 'B♭', 'C', 'D♭', 'E♭', 'F', 'G']
C:lyd     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['G', 'A', 'B', 'C', 'D', 'E', 'F♯']
C:mix     	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['F', 'G', 'A', 'B♭', 'C', 'D', 'E']
C:aeol    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['E♭', 'F', 'G', 'A♭', 'B♭', 'C', 'D']
C:loc     	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['D♭', 'E♭', 'F', 'G♭', 'A♭', 'B♭', 'C']
---------------
C#:ion    	->	['B♯', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['C♯', 'D♯', 'E♯', 'F♯', 'G♯', 'A♯', 'B♯']
C#:dor    	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['B', 'C♯', 'D♯', 'E', 'F♯', 'G♯', 'A♯']
C#:phr    	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['A', 'B', 'C♯', 'D', 'E', 'F♯', 'G♯']
C#:lyd    	->	['B♯', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'F𝄪', 'G♯', 'A', 'A♯', 'B']	->	['G♯', 'A♯', 'B♯', 'C♯', 'D♯', 'E♯', 'F𝄪']
C#:mix    	->	['C', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['F♯', 'G♯', 'A♯', 'B', 'C♯', 'D♯', 'E♯']
C#:aeol   	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['E', 'F♯', 'G♯', 'A', 'B', 'C♯', 'D♯']
C#:loc    	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['D', 'E', 'F♯', 'G', 'A', 'B', 'C♯']
---------------
Db:ion    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['D♭', 'E♭', 'F', 'G♭', 'A♭', 'B♭', 'C']
Db:dor    	->	['C', 'D♭', 'D', 'E♭', 'F♭', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'C♭']	->	['C♭', 'D♭', 'E♭', 'F♭', 'G♭', 'A♭', 'B♭']
Db:phr    	->	['D𝄫', 'D♭', 'E𝄫', 'E♭', 'F♭', 'G𝄫', 'G♭', 'A𝄫', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['B𝄫', 'C♭', 'D♭', 'E𝄫', 'F♭', 'G♭', 'A♭']
Db:lyd    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['A♭', 'B♭', 'C', 'D♭', 'E♭', 'F', 'G']
Db:mix    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'C♭']	->	['G♭', 'A♭', 'B♭', 'C♭', 'D♭', 'E♭', 'F']
Db:aeol   	->	['C', 'D♭', 'D', 'E♭', 'F♭', 'F', 'G♭', 'G', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['F♭', 'G♭', 'A♭', 'B𝄫', 'C♭', 'D♭', 'E♭']
Db:loc    	->	['D𝄫', 'D♭', 'E𝄫', 'E♭', 'F♭', 'G𝄫', 'G♭', 'A𝄫', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['E𝄫', 'F♭', 'G♭', 'A𝄫', 'B𝄫', 'C♭', 'D♭']
---------------
D:ion     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['D', 'E', 'F♯', 'G', 'A', 'B', 'C♯']
D:dor     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['C', 'D', 'E', 'F', 'G', 'A', 'B']
D:phr     	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['B♭', 'C', 'D', 'E♭', 'F', 'G', 'A']
D:lyd     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['A', 'B', 'C♯', 'D', 'E', 'F♯', 'G♯']
D:mix     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['G', 'A', 'B', 'C', 'D', 'E', 'F♯']
D:aeol    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['F', 'G', 'A', 'B♭', 'C', 'D', 'E']
D:loc     	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['E♭', 'F', 'G', 'A♭', 'B♭', 'C', 'D']
---------------
D#:ion    	->	['B♯', 'C♯', 'C𝄪', 'D♯', 'E', 'E♯', 'F♯', 'F𝄪', 'G♯', 'A', 'A♯', 'B']	->	['D♯', 'E♯', 'F𝄪', 'G♯', 'A♯', 'B♯', 'C𝄪']
D#:dor    	->	['B♯', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['C♯', 'D♯', 'E♯', 'F♯', 'G♯', 'A♯', 'B♯']
D#:phr    	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['B', 'C♯', 'D♯', 'E', 'F♯', 'G♯', 'A♯']
D#:lyd    	->	['B♯', 'C♯', 'C𝄪', 'D♯', 'E', 'E♯', 'F♯', 'F𝄪', 'G♯', 'G𝄪', 'A♯', 'B']	->	['A♯', 'B♯', 'C𝄪', 'D♯', 'E♯', 'F𝄪', 'G𝄪']
D#:mix    	->	['B♯', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'F𝄪', 'G♯', 'A', 'A♯', 'B']	->	['G♯', 'A♯', 'B♯', 'C♯', 'D♯', 'E♯', 'F𝄪']
D#:aeol   	->	['C', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['F♯', 'G♯', 'A♯', 'B', 'C♯', 'D♯', 'E♯']
D#:loc    	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['E', 'F♯', 'G♯', 'A', 'B', 'C♯', 'D♯']
---------------
Eb:ion    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['E♭', 'F', 'G', 'A♭', 'B♭', 'C', 'D']
Eb:dor    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['D♭', 'E♭', 'F', 'G♭', 'A♭', 'B♭', 'C']
Eb:phr    	->	['C', 'D♭', 'D', 'E♭', 'F♭', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'C♭']	->	['C♭', 'D♭', 'E♭', 'F♭', 'G♭', 'A♭', 'B♭']
Eb:lyd    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['B♭', 'C', 'D', 'E♭', 'F', 'G', 'A']
Eb:mix    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['A♭', 'B♭', 'C', 'D♭', 'E♭', 'F', 'G']
Eb:aeol   	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'C♭']	->	['G♭', 'A♭', 'B♭', 'C♭', 'D♭', 'E♭', 'F']
Eb:loc    	->	['C', 'D♭', 'D', 'E♭', 'F♭', 'F', 'G♭', 'G', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['F♭', 'G♭', 'A♭', 'B𝄫', 'C♭', 'D♭', 'E♭']
---------------
E:ion     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['E', 'F♯', 'G♯', 'A', 'B', 'C♯', 'D♯']
E:dor     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['D', 'E', 'F♯', 'G', 'A', 'B', 'C♯']
E:phr     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['C', 'D', 'E', 'F', 'G', 'A', 'B']
E:lyd     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['B', 'C♯', 'D♯', 'E', 'F♯', 'G♯', 'A♯']
E:mix     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['A', 'B', 'C♯', 'D', 'E', 'F♯', 'G♯']
E:aeol    	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['G', 'A', 'B', 'C', 'D', 'E', 'F♯']
E:loc     	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['F', 'G', 'A', 'B♭', 'C', 'D', 'E']
---------------
E#:ion    	->	['B♯', 'C♯', 'C𝄪', 'D♯', 'D𝄪', 'E♯', 'F♯', 'F𝄪', 'G♯', 'G𝄪', 'A♯', 'B']	->	['E♯', 'F𝄪', 'G𝄪', 'A♯', 'B♯', 'C𝄪', 'D𝄪']
E#:dor    	->	['B♯', 'C♯', 'C𝄪', 'D♯', 'E', 'E♯', 'F♯', 'F𝄪', 'G♯', 'A', 'A♯', 'B']	->	['D♯', 'E♯', 'F𝄪', 'G♯', 'A♯', 'B♯', 'C𝄪']
E#:phr    	->	['B♯', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['C♯', 'D♯', 'E♯', 'F♯', 'G♯', 'A♯', 'B♯']
E#:lyd    	->	['B♯', 'C♯', 'C𝄪', 'D♯', 'D𝄪', 'E♯', 'F♯', 'F𝄪', 'G♯', 'G𝄪', 'A♯', 'A𝄪']	->	['B♯', 'C𝄪', 'D𝄪', 'E♯', 'F𝄪', 'G𝄪', 'A𝄪']
E#:mix    	->	['B♯', 'C♯', 'C𝄪', 'D♯', 'E', 'E♯', 'F♯', 'F𝄪', 'G♯', 'G𝄪', 'A♯', 'B']	->	['A♯', 'B♯', 'C𝄪', 'D♯', 'E♯', 'F𝄪', 'G𝄪']
E#:aeol   	->	['B♯', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'F𝄪', 'G♯', 'A', 'A♯', 'B']	->	['G♯', 'A♯', 'B♯', 'C♯', 'D♯', 'E♯', 'F𝄪']
E#:loc    	->	['C', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['F♯', 'G♯', 'A♯', 'B', 'C♯', 'D♯', 'E♯']
---------------
Fb:ion    	->	['C', 'D♭', 'D', 'E♭', 'F♭', 'F', 'G♭', 'G', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['F♭', 'G♭', 'A♭', 'B𝄫', 'C♭', 'D♭', 'E♭']
Fb:dor    	->	['D𝄫', 'D♭', 'E𝄫', 'E♭', 'F♭', 'G𝄫', 'G♭', 'A𝄫', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['E𝄫', 'F♭', 'G♭', 'A𝄫', 'B𝄫', 'C♭', 'D♭']
Fb:phr    	->	['D𝄫', 'D♭', 'E𝄫', 'E♭', 'F♭', 'G𝄫', 'G♭', 'A𝄫', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['D𝄫', 'E𝄫', 'F♭', 'G𝄫', 'A𝄫', 'B𝄫', 'C♭']
Fb:lyd    	->	['C', 'D♭', 'D', 'E♭', 'F♭', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'C♭']	->	['C♭', 'D♭', 'E♭', 'F♭', 'G♭', 'A♭', 'B♭']
Fb:mix    	->	['D𝄫', 'D♭', 'E𝄫', 'E♭', 'F♭', 'G𝄫', 'G♭', 'A𝄫', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['B𝄫', 'C♭', 'D♭', 'E𝄫', 'F♭', 'G♭', 'A♭']
Fb:aeol   	->	['D𝄫', 'D♭', 'E𝄫', 'E♭', 'F♭', 'G𝄫', 'G♭', 'A𝄫', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['A𝄫', 'B𝄫', 'C♭', 'D𝄫', 'E𝄫', 'F♭', 'G♭']
Fb:loc    	->	['D𝄫', 'D♭', 'E𝄫', 'E♭', 'F♭', 'G𝄫', 'G♭', 'A𝄫', 'A♭', 'B𝄫', 'C𝄫', 'C♭']	->	['G𝄫', 'A𝄫', 'B𝄫', 'C𝄫', 'D𝄫', 'E𝄫', 'F♭']
---------------
F:ion     	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['F', 'G', 'A', 'B♭', 'C', 'D', 'E']
F:dor     	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['E♭', 'F', 'G', 'A♭', 'B♭', 'C', 'D']
F:phr     	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['D♭', 'E♭', 'F', 'G♭', 'A♭', 'B♭', 'C']
F:lyd     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['C', 'D', 'E', 'F', 'G', 'A', 'B']
F:mix     	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['B♭', 'C', 'D', 'E♭', 'F', 'G', 'A']
F:aeol    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['A♭', 'B♭', 'C', 'D♭', 'E♭', 'F', 'G']
F:loc     	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'C♭']	->	['G♭', 'A♭', 'B♭', 'C♭', 'D♭', 'E♭', 'F']
---------------
F#:ion    	->	['C', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['F♯', 'G♯', 'A♯', 'B', 'C♯', 'D♯', 'E♯']
F#:dor    	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['E', 'F♯', 'G♯', 'A', 'B', 'C♯', 'D♯']
F#:phr    	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['D', 'E', 'F♯', 'G', 'A', 'B', 'C♯']
F#:lyd    	->	['B♯', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['C♯', 'D♯', 'E♯', 'F♯', 'G♯', 'A♯', 'B♯']
F#:mix    	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['B', 'C♯', 'D♯', 'E', 'F♯', 'G♯', 'A♯']
F#:aeol   	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['A', 'B', 'C♯', 'D', 'E', 'F♯', 'G♯']
F#:loc    	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['G', 'A', 'B', 'C', 'D', 'E', 'F♯']
---------------
Gb:ion    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'C♭']	->	['G♭', 'A♭', 'B♭', 'C♭', 'D♭', 'E♭', 'F']
Gb:dor    	->	['C', 'D♭', 'D', 'E♭', 'F♭', 'F', 'G♭', 'G', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['F♭', 'G♭', 'A♭', 'B𝄫', 'C♭', 'D♭', 'E♭']
Gb:phr    	->	['D𝄫', 'D♭', 'E𝄫', 'E♭', 'F♭', 'G𝄫', 'G♭', 'A𝄫', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['E𝄫', 'F♭', 'G♭', 'A𝄫', 'B𝄫', 'C♭', 'D♭']
Gb:lyd    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['D♭', 'E♭', 'F', 'G♭', 'A♭', 'B♭', 'C']
Gb:mix    	->	['C', 'D♭', 'D', 'E♭', 'F♭', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'C♭']	->	['C♭', 'D♭', 'E♭', 'F♭', 'G♭', 'A♭', 'B♭']
Gb:aeol   	->	['D𝄫', 'D♭', 'E𝄫', 'E♭', 'F♭', 'G𝄫', 'G♭', 'A𝄫', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['B𝄫', 'C♭', 'D♭', 'E𝄫', 'F♭', 'G♭', 'A♭']
Gb:loc    	->	['D𝄫', 'D♭', 'E𝄫', 'E♭', 'F♭', 'G𝄫', 'G♭', 'A𝄫', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['A𝄫', 'B𝄫', 'C♭', 'D𝄫', 'E𝄫', 'F♭', 'G♭']
---------------
G:ion     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['G', 'A', 'B', 'C', 'D', 'E', 'F♯']
G:dor     	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['F', 'G', 'A', 'B♭', 'C', 'D', 'E']
G:phr     	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['E♭', 'F', 'G', 'A♭', 'B♭', 'C', 'D']
G:lyd     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['D', 'E', 'F♯', 'G', 'A', 'B', 'C♯']
G:mix     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['C', 'D', 'E', 'F', 'G', 'A', 'B']
G:aeol    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['B♭', 'C', 'D', 'E♭', 'F', 'G', 'A']
G:loc     	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['A♭', 'B♭', 'C', 'D♭', 'E♭', 'F', 'G']
---------------
G#:ion    	->	['B♯', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'F𝄪', 'G♯', 'A', 'A♯', 'B']	->	['G♯', 'A♯', 'B♯', 'C♯', 'D♯', 'E♯', 'F𝄪']
G#:dor    	->	['C', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['F♯', 'G♯', 'A♯', 'B', 'C♯', 'D♯', 'E♯']
G#:phr    	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['E', 'F♯', 'G♯', 'A', 'B', 'C♯', 'D♯']
G#:lyd    	->	['B♯', 'C♯', 'C𝄪', 'D♯', 'E', 'E♯', 'F♯', 'F𝄪', 'G♯', 'A', 'A♯', 'B']	->	['D♯', 'E♯', 'F𝄪', 'G♯', 'A♯', 'B♯', 'C𝄪']
G#:mix    	->	['B♯', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['C♯', 'D♯', 'E♯', 'F♯', 'G♯', 'A♯', 'B♯']
G#:aeol   	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['B', 'C♯', 'D♯', 'E', 'F♯', 'G♯', 'A♯']
G#:loc    	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['A', 'B', 'C♯', 'D', 'E', 'F♯', 'G♯']
---------------
Ab:ion    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['A♭', 'B♭', 'C', 'D♭', 'E♭', 'F', 'G']
Ab:dor    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'C♭']	->	['G♭', 'A♭', 'B♭', 'C♭', 'D♭', 'E♭', 'F']
Ab:phr    	->	['C', 'D♭', 'D', 'E♭', 'F♭', 'F', 'G♭', 'G', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['F♭', 'G♭', 'A♭', 'B𝄫', 'C♭', 'D♭', 'E♭']
Ab:lyd    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['E♭', 'F', 'G', 'A♭', 'B♭', 'C', 'D']
Ab:mix    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['D♭', 'E♭', 'F', 'G♭', 'A♭', 'B♭', 'C']
Ab:aeol   	->	['C', 'D♭', 'D', 'E♭', 'F♭', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'C♭']	->	['C♭', 'D♭', 'E♭', 'F♭', 'G♭', 'A♭', 'B♭']
Ab:loc    	->	['D𝄫', 'D♭', 'E𝄫', 'E♭', 'F♭', 'G𝄫', 'G♭', 'A𝄫', 'A♭', 'B𝄫', 'B♭', 'C♭']	->	['B𝄫', 'C♭', 'D♭', 'E𝄫', 'F♭', 'G♭', 'A♭']
---------------
A:ion     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['A', 'B', 'C♯', 'D', 'E', 'F♯', 'G♯']
A:dor     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['G', 'A', 'B', 'C', 'D', 'E', 'F♯']
A:phr     	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['F', 'G', 'A', 'B♭', 'C', 'D', 'E']
A:lyd     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['E', 'F♯', 'G♯', 'A', 'B', 'C♯', 'D♯']
A:mix     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['D', 'E', 'F♯', 'G', 'A', 'B', 'C♯']
A:aeol    	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['C', 'D', 'E', 'F', 'G', 'A', 'B']
A:loc     	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['B♭', 'C', 'D', 'E♭', 'F', 'G', 'A']
---------------
A#:ion    	->	['B♯', 'C♯', 'C𝄪', 'D♯', 'E', 'E♯', 'F♯', 'F𝄪', 'G♯', 'G𝄪', 'A♯', 'B']	->	['A♯', 'B♯', 'C𝄪', 'D♯', 'E♯', 'F𝄪', 'G𝄪']
A#:dor    	->	['B♯', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'F𝄪', 'G♯', 'A', 'A♯', 'B']	->	['G♯', 'A♯', 'B♯', 'C♯', 'D♯', 'E♯', 'F𝄪']
A#:phr    	->	['C', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['F♯', 'G♯', 'A♯', 'B', 'C♯', 'D♯', 'E♯']
A#:lyd    	->	['B♯', 'C♯', 'C𝄪', 'D♯', 'D𝄪', 'E♯', 'F♯', 'F𝄪', 'G♯', 'G𝄪', 'A♯', 'B']	->	['E♯', 'F𝄪', 'G𝄪', 'A♯', 'B♯', 'C𝄪', 'D𝄪']
A#:mix    	->	['B♯', 'C♯', 'C𝄪', 'D♯', 'E', 'E♯', 'F♯', 'F𝄪', 'G♯', 'A', 'A♯', 'B']	->	['D♯', 'E♯', 'F𝄪', 'G♯', 'A♯', 'B♯', 'C𝄪']
A#:aeol   	->	['B♯', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['C♯', 'D♯', 'E♯', 'F♯', 'G♯', 'A♯', 'B♯']
A#:loc    	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['B', 'C♯', 'D♯', 'E', 'F♯', 'G♯', 'A♯']
---------------
Bb:ion    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['B♭', 'C', 'D', 'E♭', 'F', 'G', 'A']
Bb:dor    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['A♭', 'B♭', 'C', 'D♭', 'E♭', 'F', 'G']
Bb:phr    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'C♭']	->	['G♭', 'A♭', 'B♭', 'C♭', 'D♭', 'E♭', 'F']
Bb:lyd    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['F', 'G', 'A', 'B♭', 'C', 'D', 'E']
Bb:mix    	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['E♭', 'F', 'G', 'A♭', 'B♭', 'C', 'D']
Bb:aeol   	->	['C', 'D♭', 'D', 'E♭', 'E', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'B']	->	['D♭', 'E♭', 'F', 'G♭', 'A♭', 'B♭', 'C']
Bb:loc    	->	['C', 'D♭', 'D', 'E♭', 'F♭', 'F', 'G♭', 'G', 'A♭', 'A', 'B♭', 'C♭']	->	['C♭', 'D♭', 'E♭', 'F♭', 'G♭', 'A♭', 'B♭']
---------------
B:ion     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['B', 'C♯', 'D♯', 'E', 'F♯', 'G♯', 'A♯']
B:dor     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['A', 'B', 'C♯', 'D', 'E', 'F♯', 'G♯']
B:phr     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['G', 'A', 'B', 'C', 'D', 'E', 'F♯']
B:lyd     	->	['C', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['F♯', 'G♯', 'A♯', 'B', 'C♯', 'D♯', 'E♯']
B:mix     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['E', 'F♯', 'G♯', 'A', 'B', 'C♯', 'D♯']
B:aeol    	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['D', 'E', 'F♯', 'G', 'A', 'B', 'C♯']
B:loc     	->	['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['C', 'D', 'E', 'F', 'G', 'A', 'B']
---------------
B#:ion    	->	['B♯', 'C♯', 'C𝄪', 'D♯', 'D𝄪', 'E♯', 'F♯', 'F𝄪', 'G♯', 'G𝄪', 'A♯', 'A𝄪']	->	['B♯', 'C𝄪', 'D𝄪', 'E♯', 'F𝄪', 'G𝄪', 'A𝄪']
B#:dor    	->	['B♯', 'C♯', 'C𝄪', 'D♯', 'E', 'E♯', 'F♯', 'F𝄪', 'G♯', 'G𝄪', 'A♯', 'B']	->	['A♯', 'B♯', 'C𝄪', 'D♯', 'E♯', 'F𝄪', 'G𝄪']
B#:phr    	->	['B♯', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'F𝄪', 'G♯', 'A', 'A♯', 'B']	->	['G♯', 'A♯', 'B♯', 'C♯', 'D♯', 'E♯', 'F𝄪']
B#:lyd    	->	['B♯', 'C♯', 'C𝄪', 'D♯', 'D𝄪', 'E♯', 'E𝄪', 'F𝄪', 'G♯', 'G𝄪', 'A♯', 'A𝄪']	->	['F𝄪', 'G𝄪', 'A𝄪', 'B♯', 'C𝄪', 'D𝄪', 'E𝄪']
B#:mix    	->	['B♯', 'C♯', 'C𝄪', 'D♯', 'D𝄪', 'E♯', 'F♯', 'F𝄪', 'G♯', 'G𝄪', 'A♯', 'B']	->	['E♯', 'F𝄪', 'G𝄪', 'A♯', 'B♯', 'C𝄪', 'D𝄪']
B#:aeol   	->	['B♯', 'C♯', 'C𝄪', 'D♯', 'E', 'E♯', 'F♯', 'F𝄪', 'G♯', 'A', 'A♯', 'B']	->	['D♯', 'E♯', 'F𝄪', 'G♯', 'A♯', 'B♯', 'C𝄪']
B#:loc    	->	['B♯', 'C♯', 'D', 'D♯', 'E', 'E♯', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']	->	['C♯', 'D♯', 'E♯', 'F♯', 'G♯', 'A♯', 'B♯']
```
---------------
</details>

erabinov: Ok, I'll try to get the delinting done this afternoon (CA time).

For the modes, if I understand correctly, you have issues only with the `key_to_degrees` function, since when you input, say `C:phr`, you just get the corresponding major scale, say the A-flat major scale?

This seems like it could be quickly fixed by adding a dictionary that maps modes to the number of positions we have to rotate the array by. Happy to try to hack it all together.
erabinov: Ok, hopefully this most recent commit will pass the linting! It's very exciting that the modes are working as expected.
bmcfee: > Ok, I'll try to get the delinting done this afternoon (CA time).
> 
> For the modes, if I understand correctly, you have issues only with the `key_to_degrees` function, since when you input, say `C:phr`, you just get the corresponding major scale, say the A-flat major scale?
> 
> This seems like it could be quickly fixed by adding a dictionary that maps modes to the number of positions we have to rotate the array by. Happy to try to hack it all together.

Yup, that's exactly it.  Easy enough to fix as a post-hoc rotation, and doing so looks as below.  The part that I/we need to think about is where this belongs in the code, and how it will look from the API perspective.  Happy to handle that in a separate PR though.


```python
for k in 'CDEFGAB':
    for acc in ['bb', 'b', '', '#', '##']:
        root = k + acc
        for offset, mode in enumerate(['ion', 'dor', 'phr', 'lyd', 'mix', 'aeol', 'loc']):
            query = f"{root}:{mode}"
            equiv = mode_to_key(query)

            degs = np.roll(librosa.key_to_degrees(equiv), -offset)
            notes = librosa.key_to_notes(equiv)
            
            names = [notes[_] for _ in degs]
            #print(f"{query:10s}\t→\t{notes}\t→\t{names}", end='')
            print(f"{query:10s}\t→\t{names}", end='')
            if len(set([n[0] for n in names])) != 7:
                print('\t->\tFAIL')
            else:
                print()
        print('-' * 15)
```

<details>
<summary> Revised outputs, with double-accidentals even! </summary>

```
Cbb:ion   	→	['C𝄫', 'D𝄫', 'E𝄫', 'F𝄫', 'G𝄫', 'A𝄫', 'B𝄫']
Cbb:dor   	→	['C𝄫', 'D𝄫', 'E♭𝄫', 'F𝄫', 'G𝄫', 'A𝄫', 'B♭𝄫']
Cbb:phr   	→	['C𝄫', 'D♭𝄫', 'E♭𝄫', 'F𝄫', 'G𝄫', 'A♭𝄫', 'B♭𝄫']
Cbb:lyd   	→	['C𝄫', 'D𝄫', 'E𝄫', 'F♭', 'G𝄫', 'A𝄫', 'B𝄫']
Cbb:mix   	→	['C𝄫', 'D𝄫', 'E𝄫', 'F𝄫', 'G𝄫', 'A𝄫', 'B♭𝄫']
Cbb:aeol  	→	['C𝄫', 'D𝄫', 'E♭𝄫', 'F𝄫', 'G𝄫', 'A♭𝄫', 'B♭𝄫']
Cbb:loc   	→	['C𝄫', 'D♭𝄫', 'E♭𝄫', 'F𝄫', 'G♭𝄫', 'A♭𝄫', 'B♭𝄫']
---------------
Cb:ion    	→	['C♭', 'D♭', 'E♭', 'F♭', 'G♭', 'A♭', 'B♭']
Cb:dor    	→	['C♭', 'D♭', 'E𝄫', 'F♭', 'G♭', 'A♭', 'B𝄫']
Cb:phr    	→	['C♭', 'D𝄫', 'E𝄫', 'F♭', 'G♭', 'A𝄫', 'B𝄫']
Cb:lyd    	→	['C♭', 'D♭', 'E♭', 'F', 'G♭', 'A♭', 'B♭']
Cb:mix    	→	['C♭', 'D♭', 'E♭', 'F♭', 'G♭', 'A♭', 'B𝄫']
Cb:aeol   	→	['C♭', 'D♭', 'E𝄫', 'F♭', 'G♭', 'A𝄫', 'B𝄫']
Cb:loc    	→	['C♭', 'D𝄫', 'E𝄫', 'F♭', 'G𝄫', 'A𝄫', 'B𝄫']
---------------
C:ion     	→	['C', 'D', 'E', 'F', 'G', 'A', 'B']
C:dor     	→	['C', 'D', 'E♭', 'F', 'G', 'A', 'B♭']
C:phr     	→	['C', 'D♭', 'E♭', 'F', 'G', 'A♭', 'B♭']
C:lyd     	→	['C', 'D', 'E', 'F♯', 'G', 'A', 'B']
C:mix     	→	['C', 'D', 'E', 'F', 'G', 'A', 'B♭']
C:aeol    	→	['C', 'D', 'E♭', 'F', 'G', 'A♭', 'B♭']
C:loc     	→	['C', 'D♭', 'E♭', 'F', 'G♭', 'A♭', 'B♭']
---------------
C#:ion    	→	['C♯', 'D♯', 'E♯', 'F♯', 'G♯', 'A♯', 'B♯']
C#:dor    	→	['C♯', 'D♯', 'E', 'F♯', 'G♯', 'A♯', 'B']
C#:phr    	→	['C♯', 'D', 'E', 'F♯', 'G♯', 'A', 'B']
C#:lyd    	→	['C♯', 'D♯', 'E♯', 'F𝄪', 'G♯', 'A♯', 'B♯']
C#:mix    	→	['C♯', 'D♯', 'E♯', 'F♯', 'G♯', 'A♯', 'B']
C#:aeol   	→	['C♯', 'D♯', 'E', 'F♯', 'G♯', 'A', 'B']
C#:loc    	→	['C♯', 'D', 'E', 'F♯', 'G', 'A', 'B']
---------------
C##:ion   	→	['C𝄪', 'D𝄪', 'E𝄪', 'F𝄪', 'G𝄪', 'A𝄪', 'B𝄪']
C##:dor   	→	['C𝄪', 'D𝄪', 'E♯', 'F𝄪', 'G𝄪', 'A𝄪', 'B♯']
C##:phr   	→	['C𝄪', 'D♯', 'E♯', 'F𝄪', 'G𝄪', 'A♯', 'B♯']
C##:lyd   	→	['C𝄪', 'D𝄪', 'E𝄪', 'F♯𝄪', 'G𝄪', 'A𝄪', 'B𝄪']
C##:mix   	→	['C𝄪', 'D𝄪', 'E𝄪', 'F𝄪', 'G𝄪', 'A𝄪', 'B♯']
C##:aeol  	→	['C𝄪', 'D𝄪', 'E♯', 'F𝄪', 'G𝄪', 'A♯', 'B♯']
C##:loc   	→	['C𝄪', 'D♯', 'E♯', 'F𝄪', 'G♯', 'A♯', 'B♯']
---------------
Dbb:ion   	→	['D𝄫', 'E𝄫', 'F♭', 'G𝄫', 'A𝄫', 'B𝄫', 'C♭']
Dbb:dor   	→	['D𝄫', 'E𝄫', 'F𝄫', 'G𝄫', 'A𝄫', 'B𝄫', 'C𝄫']
Dbb:phr   	→	['D𝄫', 'E♭𝄫', 'F𝄫', 'G𝄫', 'A𝄫', 'B♭𝄫', 'C𝄫']
Dbb:lyd   	→	['D𝄫', 'E𝄫', 'F♭', 'G♭', 'A𝄫', 'B𝄫', 'C♭']
Dbb:mix   	→	['D𝄫', 'E𝄫', 'F♭', 'G𝄫', 'A𝄫', 'B𝄫', 'C𝄫']
Dbb:aeol  	→	['D𝄫', 'E𝄫', 'F𝄫', 'G𝄫', 'A𝄫', 'B♭𝄫', 'C𝄫']
Dbb:loc   	→	['D𝄫', 'E♭𝄫', 'F𝄫', 'G𝄫', 'A♭𝄫', 'B♭𝄫', 'C𝄫']
---------------
Db:ion    	→	['D♭', 'E♭', 'F', 'G♭', 'A♭', 'B♭', 'C']
Db:dor    	→	['D♭', 'E♭', 'F♭', 'G♭', 'A♭', 'B♭', 'C♭']
Db:phr    	→	['D♭', 'E𝄫', 'F♭', 'G♭', 'A♭', 'B𝄫', 'C♭']
Db:lyd    	→	['D♭', 'E♭', 'F', 'G', 'A♭', 'B♭', 'C']
Db:mix    	→	['D♭', 'E♭', 'F', 'G♭', 'A♭', 'B♭', 'C♭']
Db:aeol   	→	['D♭', 'E♭', 'F♭', 'G♭', 'A♭', 'B𝄫', 'C♭']
Db:loc    	→	['D♭', 'E𝄫', 'F♭', 'G♭', 'A𝄫', 'B𝄫', 'C♭']
---------------
D:ion     	→	['D', 'E', 'F♯', 'G', 'A', 'B', 'C♯']
D:dor     	→	['D', 'E', 'F', 'G', 'A', 'B', 'C']
D:phr     	→	['D', 'E♭', 'F', 'G', 'A', 'B♭', 'C']
D:lyd     	→	['D', 'E', 'F♯', 'G♯', 'A', 'B', 'C♯']
D:mix     	→	['D', 'E', 'F♯', 'G', 'A', 'B', 'C']
D:aeol    	→	['D', 'E', 'F', 'G', 'A', 'B♭', 'C']
D:loc     	→	['D', 'E♭', 'F', 'G', 'A♭', 'B♭', 'C']
---------------
D#:ion    	→	['D♯', 'E♯', 'F𝄪', 'G♯', 'A♯', 'B♯', 'C𝄪']
D#:dor    	→	['D♯', 'E♯', 'F♯', 'G♯', 'A♯', 'B♯', 'C♯']
D#:phr    	→	['D♯', 'E', 'F♯', 'G♯', 'A♯', 'B', 'C♯']
D#:lyd    	→	['D♯', 'E♯', 'F𝄪', 'G𝄪', 'A♯', 'B♯', 'C𝄪']
D#:mix    	→	['D♯', 'E♯', 'F𝄪', 'G♯', 'A♯', 'B♯', 'C♯']
D#:aeol   	→	['D♯', 'E♯', 'F♯', 'G♯', 'A♯', 'B', 'C♯']
D#:loc    	→	['D♯', 'E', 'F♯', 'G♯', 'A', 'B', 'C♯']
---------------
D##:ion   	→	['D𝄪', 'E𝄪', 'F♯𝄪', 'G𝄪', 'A𝄪', 'B𝄪', 'C♯𝄪']
D##:dor   	→	['D𝄪', 'E𝄪', 'F𝄪', 'G𝄪', 'A𝄪', 'B𝄪', 'C𝄪']
D##:phr   	→	['D𝄪', 'E♯', 'F𝄪', 'G𝄪', 'A𝄪', 'B♯', 'C𝄪']
D##:lyd   	→	['D𝄪', 'E𝄪', 'F♯𝄪', 'G♯𝄪', 'A𝄪', 'B𝄪', 'C♯𝄪']
D##:mix   	→	['D𝄪', 'E𝄪', 'F♯𝄪', 'G𝄪', 'A𝄪', 'B𝄪', 'C𝄪']
D##:aeol  	→	['D𝄪', 'E𝄪', 'F𝄪', 'G𝄪', 'A𝄪', 'B♯', 'C𝄪']
D##:loc   	→	['D𝄪', 'E♯', 'F𝄪', 'G𝄪', 'A♯', 'B♯', 'C𝄪']
---------------
Ebb:ion   	→	['E𝄫', 'F♭', 'G♭', 'A𝄫', 'B𝄫', 'C♭', 'D♭']
Ebb:dor   	→	['E𝄫', 'F♭', 'G𝄫', 'A𝄫', 'B𝄫', 'C♭', 'D𝄫']
Ebb:phr   	→	['E𝄫', 'F𝄫', 'G𝄫', 'A𝄫', 'B𝄫', 'C𝄫', 'D𝄫']
Ebb:lyd   	→	['E𝄫', 'F♭', 'G♭', 'A♭', 'B𝄫', 'C♭', 'D♭']
Ebb:mix   	→	['E𝄫', 'F♭', 'G♭', 'A𝄫', 'B𝄫', 'C♭', 'D𝄫']
Ebb:aeol  	→	['E𝄫', 'F♭', 'G𝄫', 'A𝄫', 'B𝄫', 'C𝄫', 'D𝄫']
Ebb:loc   	→	['E𝄫', 'F𝄫', 'G𝄫', 'A𝄫', 'B♭𝄫', 'C𝄫', 'D𝄫']
---------------
Eb:ion    	→	['E♭', 'F', 'G', 'A♭', 'B♭', 'C', 'D']
Eb:dor    	→	['E♭', 'F', 'G♭', 'A♭', 'B♭', 'C', 'D♭']
Eb:phr    	→	['E♭', 'F♭', 'G♭', 'A♭', 'B♭', 'C♭', 'D♭']
Eb:lyd    	→	['E♭', 'F', 'G', 'A', 'B♭', 'C', 'D']
Eb:mix    	→	['E♭', 'F', 'G', 'A♭', 'B♭', 'C', 'D♭']
Eb:aeol   	→	['E♭', 'F', 'G♭', 'A♭', 'B♭', 'C♭', 'D♭']
Eb:loc    	→	['E♭', 'F♭', 'G♭', 'A♭', 'B𝄫', 'C♭', 'D♭']
---------------
E:ion     	→	['E', 'F♯', 'G♯', 'A', 'B', 'C♯', 'D♯']
E:dor     	→	['E', 'F♯', 'G', 'A', 'B', 'C♯', 'D']
E:phr     	→	['E', 'F', 'G', 'A', 'B', 'C', 'D']
E:lyd     	→	['E', 'F♯', 'G♯', 'A♯', 'B', 'C♯', 'D♯']
E:mix     	→	['E', 'F♯', 'G♯', 'A', 'B', 'C♯', 'D']
E:aeol    	→	['E', 'F♯', 'G', 'A', 'B', 'C', 'D']
E:loc     	→	['E', 'F', 'G', 'A', 'B♭', 'C', 'D']
---------------
E#:ion    	→	['E♯', 'F𝄪', 'G𝄪', 'A♯', 'B♯', 'C𝄪', 'D𝄪']
E#:dor    	→	['E♯', 'F𝄪', 'G♯', 'A♯', 'B♯', 'C𝄪', 'D♯']
E#:phr    	→	['E♯', 'F♯', 'G♯', 'A♯', 'B♯', 'C♯', 'D♯']
E#:lyd    	→	['E♯', 'F𝄪', 'G𝄪', 'A𝄪', 'B♯', 'C𝄪', 'D𝄪']
E#:mix    	→	['E♯', 'F𝄪', 'G𝄪', 'A♯', 'B♯', 'C𝄪', 'D♯']
E#:aeol   	→	['E♯', 'F𝄪', 'G♯', 'A♯', 'B♯', 'C♯', 'D♯']
E#:loc    	→	['E♯', 'F♯', 'G♯', 'A♯', 'B', 'C♯', 'D♯']
---------------
E##:ion   	→	['E𝄪', 'F♯𝄪', 'G♯𝄪', 'A𝄪', 'B𝄪', 'C♯𝄪', 'D♯𝄪']
E##:dor   	→	['E𝄪', 'F♯𝄪', 'G𝄪', 'A𝄪', 'B𝄪', 'C♯𝄪', 'D𝄪']
E##:phr   	→	['E𝄪', 'F𝄪', 'G𝄪', 'A𝄪', 'B𝄪', 'C𝄪', 'D𝄪']
E##:lyd   	→	['E𝄪', 'F♯𝄪', 'G♯𝄪', 'A♯𝄪', 'B𝄪', 'C♯𝄪', 'D♯𝄪']
E##:mix   	→	['E𝄪', 'F♯𝄪', 'G♯𝄪', 'A𝄪', 'B𝄪', 'C♯𝄪', 'D𝄪']
E##:aeol  	→	['E𝄪', 'F♯𝄪', 'G𝄪', 'A𝄪', 'B𝄪', 'C𝄪', 'D𝄪']
E##:loc   	→	['E𝄪', 'F𝄪', 'G𝄪', 'A𝄪', 'B♯', 'C𝄪', 'D𝄪']
---------------
Fbb:ion   	→	['F𝄫', 'G𝄫', 'A𝄫', 'B♭𝄫', 'C𝄫', 'D𝄫', 'E𝄫']
Fbb:dor   	→	['F𝄫', 'G𝄫', 'A♭𝄫', 'B♭𝄫', 'C𝄫', 'D𝄫', 'E♭𝄫']
Fbb:phr   	→	['F𝄫', 'G♭𝄫', 'A♭𝄫', 'B♭𝄫', 'C𝄫', 'D♭𝄫', 'E♭𝄫']
Fbb:lyd   	→	['F𝄫', 'G𝄫', 'A𝄫', 'B𝄫', 'C𝄫', 'D𝄫', 'E𝄫']
Fbb:mix   	→	['F𝄫', 'G𝄫', 'A𝄫', 'B♭𝄫', 'C𝄫', 'D𝄫', 'E♭𝄫']
Fbb:aeol  	→	['F𝄫', 'G𝄫', 'A♭𝄫', 'B♭𝄫', 'C𝄫', 'D♭𝄫', 'E♭𝄫']
Fbb:loc   	→	['F𝄫', 'G♭𝄫', 'A♭𝄫', 'B♭𝄫', 'C♭𝄫', 'D♭𝄫', 'E♭𝄫']
---------------
Fb:ion    	→	['F♭', 'G♭', 'A♭', 'B𝄫', 'C♭', 'D♭', 'E♭']
Fb:dor    	→	['F♭', 'G♭', 'A𝄫', 'B𝄫', 'C♭', 'D♭', 'E𝄫']
Fb:phr    	→	['F♭', 'G𝄫', 'A𝄫', 'B𝄫', 'C♭', 'D𝄫', 'E𝄫']
Fb:lyd    	→	['F♭', 'G♭', 'A♭', 'B♭', 'C♭', 'D♭', 'E♭']
Fb:mix    	→	['F♭', 'G♭', 'A♭', 'B𝄫', 'C♭', 'D♭', 'E𝄫']
Fb:aeol   	→	['F♭', 'G♭', 'A𝄫', 'B𝄫', 'C♭', 'D𝄫', 'E𝄫']
Fb:loc    	→	['F♭', 'G𝄫', 'A𝄫', 'B𝄫', 'C𝄫', 'D𝄫', 'E𝄫']
---------------
F:ion     	→	['F', 'G', 'A', 'B♭', 'C', 'D', 'E']
F:dor     	→	['F', 'G', 'A♭', 'B♭', 'C', 'D', 'E♭']
F:phr     	→	['F', 'G♭', 'A♭', 'B♭', 'C', 'D♭', 'E♭']
F:lyd     	→	['F', 'G', 'A', 'B', 'C', 'D', 'E']
F:mix     	→	['F', 'G', 'A', 'B♭', 'C', 'D', 'E♭']
F:aeol    	→	['F', 'G', 'A♭', 'B♭', 'C', 'D♭', 'E♭']
F:loc     	→	['F', 'G♭', 'A♭', 'B♭', 'C♭', 'D♭', 'E♭']
---------------
F#:ion    	→	['F♯', 'G♯', 'A♯', 'B', 'C♯', 'D♯', 'E♯']
F#:dor    	→	['F♯', 'G♯', 'A', 'B', 'C♯', 'D♯', 'E']
F#:phr    	→	['F♯', 'G', 'A', 'B', 'C♯', 'D', 'E']
F#:lyd    	→	['F♯', 'G♯', 'A♯', 'B♯', 'C♯', 'D♯', 'E♯']
F#:mix    	→	['F♯', 'G♯', 'A♯', 'B', 'C♯', 'D♯', 'E']
F#:aeol   	→	['F♯', 'G♯', 'A', 'B', 'C♯', 'D', 'E']
F#:loc    	→	['F♯', 'G', 'A', 'B', 'C', 'D', 'E']
---------------
F##:ion   	→	['F𝄪', 'G𝄪', 'A𝄪', 'B♯', 'C𝄪', 'D𝄪', 'E𝄪']
F##:dor   	→	['F𝄪', 'G𝄪', 'A♯', 'B♯', 'C𝄪', 'D𝄪', 'E♯']
F##:phr   	→	['F𝄪', 'G♯', 'A♯', 'B♯', 'C𝄪', 'D♯', 'E♯']
F##:lyd   	→	['F𝄪', 'G𝄪', 'A𝄪', 'B𝄪', 'C𝄪', 'D𝄪', 'E𝄪']
F##:mix   	→	['F𝄪', 'G𝄪', 'A𝄪', 'B♯', 'C𝄪', 'D𝄪', 'E♯']
F##:aeol  	→	['F𝄪', 'G𝄪', 'A♯', 'B♯', 'C𝄪', 'D♯', 'E♯']
F##:loc   	→	['F𝄪', 'G♯', 'A♯', 'B♯', 'C♯', 'D♯', 'E♯']
---------------
Gbb:ion   	→	['G𝄫', 'A𝄫', 'B𝄫', 'C𝄫', 'D𝄫', 'E𝄫', 'F♭']
Gbb:dor   	→	['G𝄫', 'A𝄫', 'B♭𝄫', 'C𝄫', 'D𝄫', 'E𝄫', 'F𝄫']
Gbb:phr   	→	['G𝄫', 'A♭𝄫', 'B♭𝄫', 'C𝄫', 'D𝄫', 'E♭𝄫', 'F𝄫']
Gbb:lyd   	→	['G𝄫', 'A𝄫', 'B𝄫', 'C♭', 'D𝄫', 'E𝄫', 'F♭']
Gbb:mix   	→	['G𝄫', 'A𝄫', 'B𝄫', 'C𝄫', 'D𝄫', 'E𝄫', 'F𝄫']
Gbb:aeol  	→	['G𝄫', 'A𝄫', 'B♭𝄫', 'C𝄫', 'D𝄫', 'E♭𝄫', 'F𝄫']
Gbb:loc   	→	['G𝄫', 'A♭𝄫', 'B♭𝄫', 'C𝄫', 'D♭𝄫', 'E♭𝄫', 'F𝄫']
---------------
Gb:ion    	→	['G♭', 'A♭', 'B♭', 'C♭', 'D♭', 'E♭', 'F']
Gb:dor    	→	['G♭', 'A♭', 'B𝄫', 'C♭', 'D♭', 'E♭', 'F♭']
Gb:phr    	→	['G♭', 'A𝄫', 'B𝄫', 'C♭', 'D♭', 'E𝄫', 'F♭']
Gb:lyd    	→	['G♭', 'A♭', 'B♭', 'C', 'D♭', 'E♭', 'F']
Gb:mix    	→	['G♭', 'A♭', 'B♭', 'C♭', 'D♭', 'E♭', 'F♭']
Gb:aeol   	→	['G♭', 'A♭', 'B𝄫', 'C♭', 'D♭', 'E𝄫', 'F♭']
Gb:loc    	→	['G♭', 'A𝄫', 'B𝄫', 'C♭', 'D𝄫', 'E𝄫', 'F♭']
---------------
G:ion     	→	['G', 'A', 'B', 'C', 'D', 'E', 'F♯']
G:dor     	→	['G', 'A', 'B♭', 'C', 'D', 'E', 'F']
G:phr     	→	['G', 'A♭', 'B♭', 'C', 'D', 'E♭', 'F']
G:lyd     	→	['G', 'A', 'B', 'C♯', 'D', 'E', 'F♯']
G:mix     	→	['G', 'A', 'B', 'C', 'D', 'E', 'F']
G:aeol    	→	['G', 'A', 'B♭', 'C', 'D', 'E♭', 'F']
G:loc     	→	['G', 'A♭', 'B♭', 'C', 'D♭', 'E♭', 'F']
---------------
G#:ion    	→	['G♯', 'A♯', 'B♯', 'C♯', 'D♯', 'E♯', 'F𝄪']
G#:dor    	→	['G♯', 'A♯', 'B', 'C♯', 'D♯', 'E♯', 'F♯']
G#:phr    	→	['G♯', 'A', 'B', 'C♯', 'D♯', 'E', 'F♯']
G#:lyd    	→	['G♯', 'A♯', 'B♯', 'C𝄪', 'D♯', 'E♯', 'F𝄪']
G#:mix    	→	['G♯', 'A♯', 'B♯', 'C♯', 'D♯', 'E♯', 'F♯']
G#:aeol   	→	['G♯', 'A♯', 'B', 'C♯', 'D♯', 'E', 'F♯']
G#:loc    	→	['G♯', 'A', 'B', 'C♯', 'D', 'E', 'F♯']
---------------
G##:ion   	→	['G𝄪', 'A𝄪', 'B𝄪', 'C𝄪', 'D𝄪', 'E𝄪', 'F♯𝄪']
G##:dor   	→	['G𝄪', 'A𝄪', 'B♯', 'C𝄪', 'D𝄪', 'E𝄪', 'F𝄪']
G##:phr   	→	['G𝄪', 'A♯', 'B♯', 'C𝄪', 'D𝄪', 'E♯', 'F𝄪']
G##:lyd   	→	['G𝄪', 'A𝄪', 'B𝄪', 'C♯𝄪', 'D𝄪', 'E𝄪', 'F♯𝄪']
G##:mix   	→	['G𝄪', 'A𝄪', 'B𝄪', 'C𝄪', 'D𝄪', 'E𝄪', 'F𝄪']
G##:aeol  	→	['G𝄪', 'A𝄪', 'B♯', 'C𝄪', 'D𝄪', 'E♯', 'F𝄪']
G##:loc   	→	['G𝄪', 'A♯', 'B♯', 'C𝄪', 'D♯', 'E♯', 'F𝄪']
---------------
Abb:ion   	→	['A𝄫', 'B𝄫', 'C♭', 'D𝄫', 'E𝄫', 'F♭', 'G♭']
Abb:dor   	→	['A𝄫', 'B𝄫', 'C𝄫', 'D𝄫', 'E𝄫', 'F♭', 'G𝄫']
Abb:phr   	→	['A𝄫', 'B♭𝄫', 'C𝄫', 'D𝄫', 'E𝄫', 'F𝄫', 'G𝄫']
Abb:lyd   	→	['A𝄫', 'B𝄫', 'C♭', 'D♭', 'E𝄫', 'F♭', 'G♭']
Abb:mix   	→	['A𝄫', 'B𝄫', 'C♭', 'D𝄫', 'E𝄫', 'F♭', 'G𝄫']
Abb:aeol  	→	['A𝄫', 'B𝄫', 'C𝄫', 'D𝄫', 'E𝄫', 'F𝄫', 'G𝄫']
Abb:loc   	→	['A𝄫', 'B♭𝄫', 'C𝄫', 'D𝄫', 'E♭𝄫', 'F𝄫', 'G𝄫']
---------------
Ab:ion    	→	['A♭', 'B♭', 'C', 'D♭', 'E♭', 'F', 'G']
Ab:dor    	→	['A♭', 'B♭', 'C♭', 'D♭', 'E♭', 'F', 'G♭']
Ab:phr    	→	['A♭', 'B𝄫', 'C♭', 'D♭', 'E♭', 'F♭', 'G♭']
Ab:lyd    	→	['A♭', 'B♭', 'C', 'D', 'E♭', 'F', 'G']
Ab:mix    	→	['A♭', 'B♭', 'C', 'D♭', 'E♭', 'F', 'G♭']
Ab:aeol   	→	['A♭', 'B♭', 'C♭', 'D♭', 'E♭', 'F♭', 'G♭']
Ab:loc    	→	['A♭', 'B𝄫', 'C♭', 'D♭', 'E𝄫', 'F♭', 'G♭']
---------------
A:ion     	→	['A', 'B', 'C♯', 'D', 'E', 'F♯', 'G♯']
A:dor     	→	['A', 'B', 'C', 'D', 'E', 'F♯', 'G']
A:phr     	→	['A', 'B♭', 'C', 'D', 'E', 'F', 'G']
A:lyd     	→	['A', 'B', 'C♯', 'D♯', 'E', 'F♯', 'G♯']
A:mix     	→	['A', 'B', 'C♯', 'D', 'E', 'F♯', 'G']
A:aeol    	→	['A', 'B', 'C', 'D', 'E', 'F', 'G']
A:loc     	→	['A', 'B♭', 'C', 'D', 'E♭', 'F', 'G']
---------------
A#:ion    	→	['A♯', 'B♯', 'C𝄪', 'D♯', 'E♯', 'F𝄪', 'G𝄪']
A#:dor    	→	['A♯', 'B♯', 'C♯', 'D♯', 'E♯', 'F𝄪', 'G♯']
A#:phr    	→	['A♯', 'B', 'C♯', 'D♯', 'E♯', 'F♯', 'G♯']
A#:lyd    	→	['A♯', 'B♯', 'C𝄪', 'D𝄪', 'E♯', 'F𝄪', 'G𝄪']
A#:mix    	→	['A♯', 'B♯', 'C𝄪', 'D♯', 'E♯', 'F𝄪', 'G♯']
A#:aeol   	→	['A♯', 'B♯', 'C♯', 'D♯', 'E♯', 'F♯', 'G♯']
A#:loc    	→	['A♯', 'B', 'C♯', 'D♯', 'E', 'F♯', 'G♯']
---------------
A##:ion   	→	['A𝄪', 'B𝄪', 'C♯𝄪', 'D𝄪', 'E𝄪', 'F♯𝄪', 'G♯𝄪']
A##:dor   	→	['A𝄪', 'B𝄪', 'C𝄪', 'D𝄪', 'E𝄪', 'F♯𝄪', 'G𝄪']
A##:phr   	→	['A𝄪', 'B♯', 'C𝄪', 'D𝄪', 'E𝄪', 'F𝄪', 'G𝄪']
A##:lyd   	→	['A𝄪', 'B𝄪', 'C♯𝄪', 'D♯𝄪', 'E𝄪', 'F♯𝄪', 'G♯𝄪']
A##:mix   	→	['A𝄪', 'B𝄪', 'C♯𝄪', 'D𝄪', 'E𝄪', 'F♯𝄪', 'G𝄪']
A##:aeol  	→	['A𝄪', 'B𝄪', 'C𝄪', 'D𝄪', 'E𝄪', 'F𝄪', 'G𝄪']
A##:loc   	→	['A𝄪', 'B♯', 'C𝄪', 'D𝄪', 'E♯', 'F𝄪', 'G𝄪']
---------------
Bbb:ion   	→	['B𝄫', 'C♭', 'D♭', 'E𝄫', 'F♭', 'G♭', 'A♭']
Bbb:dor   	→	['B𝄫', 'C♭', 'D𝄫', 'E𝄫', 'F♭', 'G♭', 'A𝄫']
Bbb:phr   	→	['B𝄫', 'C𝄫', 'D𝄫', 'E𝄫', 'F♭', 'G𝄫', 'A𝄫']
Bbb:lyd   	→	['B𝄫', 'C♭', 'D♭', 'E♭', 'F♭', 'G♭', 'A♭']
Bbb:mix   	→	['B𝄫', 'C♭', 'D♭', 'E𝄫', 'F♭', 'G♭', 'A𝄫']
Bbb:aeol  	→	['B𝄫', 'C♭', 'D𝄫', 'E𝄫', 'F♭', 'G𝄫', 'A𝄫']
Bbb:loc   	→	['B𝄫', 'C𝄫', 'D𝄫', 'E𝄫', 'F𝄫', 'G𝄫', 'A𝄫']
---------------
Bb:ion    	→	['B♭', 'C', 'D', 'E♭', 'F', 'G', 'A']
Bb:dor    	→	['B♭', 'C', 'D♭', 'E♭', 'F', 'G', 'A♭']
Bb:phr    	→	['B♭', 'C♭', 'D♭', 'E♭', 'F', 'G♭', 'A♭']
Bb:lyd    	→	['B♭', 'C', 'D', 'E', 'F', 'G', 'A']
Bb:mix    	→	['B♭', 'C', 'D', 'E♭', 'F', 'G', 'A♭']
Bb:aeol   	→	['B♭', 'C', 'D♭', 'E♭', 'F', 'G♭', 'A♭']
Bb:loc    	→	['B♭', 'C♭', 'D♭', 'E♭', 'F♭', 'G♭', 'A♭']
---------------
B:ion     	→	['B', 'C♯', 'D♯', 'E', 'F♯', 'G♯', 'A♯']
B:dor     	→	['B', 'C♯', 'D', 'E', 'F♯', 'G♯', 'A']
B:phr     	→	['B', 'C', 'D', 'E', 'F♯', 'G', 'A']
B:lyd     	→	['B', 'C♯', 'D♯', 'E♯', 'F♯', 'G♯', 'A♯']
B:mix     	→	['B', 'C♯', 'D♯', 'E', 'F♯', 'G♯', 'A']
B:aeol    	→	['B', 'C♯', 'D', 'E', 'F♯', 'G', 'A']
B:loc     	→	['B', 'C', 'D', 'E', 'F', 'G', 'A']
---------------
B#:ion    	→	['B♯', 'C𝄪', 'D𝄪', 'E♯', 'F𝄪', 'G𝄪', 'A𝄪']
B#:dor    	→	['B♯', 'C𝄪', 'D♯', 'E♯', 'F𝄪', 'G𝄪', 'A♯']
B#:phr    	→	['B♯', 'C♯', 'D♯', 'E♯', 'F𝄪', 'G♯', 'A♯']
B#:lyd    	→	['B♯', 'C𝄪', 'D𝄪', 'E𝄪', 'F𝄪', 'G𝄪', 'A𝄪']
B#:mix    	→	['B♯', 'C𝄪', 'D𝄪', 'E♯', 'F𝄪', 'G𝄪', 'A♯']
B#:aeol   	→	['B♯', 'C𝄪', 'D♯', 'E♯', 'F𝄪', 'G♯', 'A♯']
B#:loc    	→	['B♯', 'C♯', 'D♯', 'E♯', 'F♯', 'G♯', 'A♯']
---------------
B##:ion   	→	['B𝄪', 'C♯𝄪', 'D♯𝄪', 'E𝄪', 'F♯𝄪', 'G♯𝄪', 'A♯𝄪']
B##:dor   	→	['B𝄪', 'C♯𝄪', 'D𝄪', 'E𝄪', 'F♯𝄪', 'G♯𝄪', 'A𝄪']
B##:phr   	→	['B𝄪', 'C𝄪', 'D𝄪', 'E𝄪', 'F♯𝄪', 'G𝄪', 'A𝄪']
B##:lyd   	→	['B𝄪', 'C♯𝄪', 'D♯𝄪', 'E♯𝄪', 'F♯𝄪', 'G♯𝄪', 'A♯𝄪']
B##:mix   	→	['B𝄪', 'C♯𝄪', 'D♯𝄪', 'E𝄪', 'F♯𝄪', 'G♯𝄪', 'A𝄪']
B##:aeol  	→	['B𝄪', 'C♯𝄪', 'D𝄪', 'E𝄪', 'F♯𝄪', 'G𝄪', 'A𝄪']
B##:loc   	→	['B𝄪', 'C𝄪', 'D𝄪', 'E𝄪', 'F𝄪', 'G𝄪', 'A𝄪']
---------------

```

</details>
bmcfee: Thanks again @erabinov - this is great!

I'll leave the original issue open until the second half (mode support) gets rolled in, but this goes a huge step of the way toward unblocking that.

Patch:
diff --git a/AUTHORS.md b/AUTHORS.md
index ac1b3247..7a094c6f 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -103,6 +103,7 @@ Contributors
 * Jon Petter Åsen <https://github.com/jpaasen>
 * Shin Hyun <https://github.com/kyaryunha>
 * Iliya S <https://github.com/zenitismus>
+* Eugene Rabinovich <https://github.com/erabinov>
 
 Some feature extraction code was based on <https://github.com/ronw/frontend> by Ron Weiss.
 
diff --git a/librosa/core/notation.py b/librosa/core/notation.py
index 117970d6..38f4245f 100644
--- a/librosa/core/notation.py
+++ b/librosa/core/notation.py
@@ -5,12 +5,13 @@
 import re
 import numpy as np
 from numba import jit
+from collections import Counter
 from .intervals import INTERVALS
 from .._cache import cache
 from ..util.exceptions import ParameterError
-from typing import Dict, List, Union, overload
+from typing import Dict, List, Iterable, Union, overload
 from ..util.decorators import vectorize
-from .._typing import _ScalarOrSequence, _FloatLike_co, _SequenceLike
+from .._typing import _ScalarOrSequence, _FloatLike_co, _SequenceLike, _IterableLike
 
 
 __all__ = [
@@ -122,6 +123,9 @@ MELAKARTA_MAP = {
 
 
 # Pre-compiled regular expressions for note and key parsing
+KEY_RE = re.compile(
+    r"^(?P<tonic>[A-Ga-g])" r"(?P<accidental>([#♯b!♭𝄪𝄫♮]*))" r":(?P<scale>(maj|min)(or)?)$"
+)
 NOTE_RE = re.compile(
     r"^(?P<note>[A-Ga-g])"
     r"(?P<accidental>[#♯𝄪b!♭𝄫♮]*)"
@@ -129,9 +133,7 @@ NOTE_RE = re.compile(
     r"(?P<cents>[+-]\d+)?$"
 )
 
-KEY_RE = re.compile(
-    r"^(?P<tonic>[A-Ga-g])" r"(?P<accidental>[#♯b!♭]?)" r":(?P<scale>(maj|min)(or)?)$"
-)
+ACC_MAP = {"#": 1, "♮": 0, "": 0, "b": -1, "!": -1, "♯": 1, "♭": -1, "𝄪": 2, "𝄫": -2}
 
 
 def thaat_to_degrees(thaat: str) -> np.ndarray:
@@ -471,6 +473,93 @@ def list_thaat() -> List[str]:
     """
     return list(THAAT_MAP.keys())
 
+@overload
+def __note_to_degree(key: str) -> int:
+    ...
+@overload
+def __note_to_degree(key: _IterableLike[str]) -> np.ndarray:
+    ...
+@overload
+def __note_to_degree(key: Union[str, _IterableLike[str], Iterable[str]]) -> Union[int, np.ndarray]:
+    ...
+def __note_to_degree(key: Union[str, _IterableLike[str], Iterable[str]]) -> Union[int,np.ndarray]:
+    """Take a note name and spit out the degree of that note (e.g. 'C#' -> 1). We allow possibilities like "C#b".
+
+    >>> librosa.__note_to_degree('B#')
+    0
+
+    >>> librosa.__note_to_degree('D♮##b')
+    3
+
+    >>> librosa.__note_to_degree(['B#','D♮##b'])
+    array([0,3])
+
+    """
+    if not isinstance(key, str):
+        return np.array([__note_to_degree(n) for n in key])
+
+
+    match = NOTE_RE.match(key)
+
+    if not match:
+        raise ParameterError(f"Improper key format: {key:s}")
+
+    letter = match.group('note').upper()
+    accidental = match.group('accidental')
+    pitch_map = {"C": 0, "D": 2, "E": 4, "F": 5, "G": 7, "A": 9, "B": 11}
+    counter = Counter(accidental)
+    return (pitch_map[letter]+sum([ACC_MAP[acc] * counter[acc] for acc in ACC_MAP]))%12
+
+@overload
+def __simplify_note(key: str, additional_acc: str =..., unicode: bool= ...) -> str:
+    ...
+
+@overload
+def __simplify_note(key: _IterableLike[str], additional_acc: str=..., unicode: bool = ... ) -> np.ndarray:
+    ...
+
+@overload
+def __simplify_note(key: Union[str, _IterableLike[str], Iterable[str]], additional_acc: str =..., unicode: bool = ...) -> Union[str, np.ndarray]:
+    ...
+
+def __simplify_note(key: Union[str, _IterableLike[str], Iterable[str]], additional_acc: str='', unicode: bool = True) -> Union[str, np.ndarray]:
+    """Take in a note name and simplify by canceling sharp-flat pairs, and doubling accidentals as appropriate.
+
+    >>> librosa.__simplify_note('C♭♯')
+    'C'
+
+    >>> librosa.__simplify_note('C♭♭♭')
+    'C♭𝄫'
+
+    >>> librosa.__simplify_note(['C♭♯', 'C♭♭♭'])
+    array(['C', 'C♭𝄫'])
+
+    """
+    if not isinstance(key,str):
+        return np.array([__simplify_note(n+additional_acc, unicode=unicode) for n in key])
+
+    match = NOTE_RE.match(key+additional_acc)
+
+    if not match:
+        raise ParameterError(f"Improper key format: {key:s}")
+    
+    letter = match.group('note').upper()
+    accidental = match.group('accidental')
+    counter = Counter(accidental)
+    offset = sum([ACC_MAP[acc] * counter[acc] for acc in ACC_MAP])
+
+    simplified_note = letter
+    if offset>=0:
+        simplified_note += "♯"*(offset%2)+ "𝄪"*(offset//2)
+    else:
+        simplified_note += "♭"*(offset%2)+ "𝄫"*(abs(offset)//2)
+
+    if not unicode:
+        translations = str.maketrans({"♯": "#", "𝄪": "##", "♭": "b", "𝄫": "bb"})
+        simplified_note = simplified_note.translate(translations)
+    
+    return simplified_note
+    
 
 @cache(level=10)
 def key_to_notes(key: str, *, unicode: bool = True) -> List[str]:
@@ -551,16 +640,37 @@ def key_to_notes(key: str, *, unicode: bool = True) -> List[str]:
 
     if not match:
         raise ParameterError(f"Improper key format: {key:s}")
-
+    
     pitch_map = {"C": 0, "D": 2, "E": 4, "F": 5, "G": 7, "A": 9, "B": 11}
-    acc_map = {"#": 1, "": 0, "b": -1, "!": -1, "♯": 1, "♭": -1}
 
     tonic = match.group("tonic").upper()
     accidental = match.group("accidental")
-    offset = acc_map[accidental]
+
+    offset = sum([ACC_MAP[acc] for acc in accidental])
 
     scale = match.group("scale")[:3].lower()
 
+    multiple = abs(offset)>=2
+
+    #If multiple accidentals, we use recursion, then cycle through so that the enharmonic equivalent of C is at the beginning again.
+
+    if multiple:
+        sign_map = {+1: "♯", -1: "♭"}
+        additional_acc = sign_map[np.sign(offset)]
+        intermediate_notes = key_to_notes(tonic+additional_acc*(abs(offset)-1)+':'+scale)
+        notes = [__simplify_note(note, additional_acc) for note in intermediate_notes]
+        degrees = __note_to_degree(notes)
+        notes = np.roll(notes, shift=-np.argwhere(degrees == 0)[0])
+        
+        notes = list(notes)
+
+        if not unicode:
+            translations = str.maketrans({"♯": "#", "𝄪": "##", "♭": "b", "𝄫": "bb"})
+            notes = list(n.translate(translations) for n in notes)
+
+        return notes
+            
+
     # Determine major or minor
     major = scale == "maj"
 
@@ -645,6 +755,7 @@ def key_to_notes(key: str, *, unicode: bool = True) -> List[str]:
 
     return notes
 
+# I made this work even for key signatures like 'C#b#:min'
 
 def key_to_degrees(key: str) -> np.ndarray:
     """Construct the diatonic scale degrees for a given key.
@@ -681,6 +792,9 @@ def key_to_degrees(key: str) -> np.ndarray:
     >>> librosa.key_to_degrees('A:min')
     array([ 9, 11,  0,  2,  4,  5,  7])
 
+    >>> librosa.key_to_degrees('A:min')
+    array([ 9, 11,  0,  2,  4,  5,  7])
+
     """
     notes = dict(
         maj=np.array([0, 2, 4, 5, 7, 9, 11]), min=np.array([0, 2, 3, 5, 7, 8, 10])
@@ -692,10 +806,10 @@ def key_to_degrees(key: str) -> np.ndarray:
         raise ParameterError(f"Improper key format: {key:s}")
 
     pitch_map = {"C": 0, "D": 2, "E": 4, "F": 5, "G": 7, "A": 9, "B": 11}
-    acc_map = {"#": 1, "": 0, "b": -1, "!": -1, "♯": 1, "♭": -1}
     tonic = match.group("tonic").upper()
     accidental = match.group("accidental")
-    offset = acc_map[accidental]
+    counts = Counter(accidental)
+    offset = sum([ACC_MAP[acc]*counts[acc] for acc in ACC_MAP])
 
     scale = match.group("scale")[:3].lower()
 
@@ -748,7 +862,7 @@ def fifths_to_note(*, unison: str, fifths: int, unicode: bool = True) -> str:
     """
     # Starting the circle of fifths at F makes accidentals easier to count
     COFMAP = "FCGDAEB"
-
+    
     acc_map = {
         "#": 1,
         "": 0,


Test patch:
diff --git a/tests/test_notation.py b/tests/test_notation.py
index 2b6bee83..0426df69 100644
--- a/tests/test_notation.py
+++ b/tests/test_notation.py
@@ -20,6 +20,14 @@ import librosa
 def test_key_to_notes_badkey():
     librosa.key_to_notes("not a key")
 
+@pytest.mark.xfail(raises=librosa.ParameterError)
+def test_simplify_note_badnote():
+    librosa.core.notation.__simplify_note("not a note")
+
+@pytest.mark.xfail(raises=librosa.ParameterError)
+def test_note_to_degree_badnote():
+    librosa.core.notation.__note_to_degree("not a note")
+
 
 @pytest.mark.parametrize(
     "key,ref_notes",
@@ -62,6 +70,11 @@ def test_key_to_notes_badkey():
                 "A##",
             ],
         ),
+
+        # Test for multiple accidentals in tonic name.
+        ("F##:maj", ["B#", "C#", "C##", "D#", "D##", "E#", "E##", "F##", "G#", "G##", "A#", "A##"]),
+        ("Fbb:maj", ["Dbb", "Db", "Ebb", "Fbb", "Fb", "Gbb", "Gb", "Abb", "Bbbb", "Bbb", "Cbb", "Cb"]),
+        ("A###:min", ["A###", "B##", "B###", "C###", "D##", "D###", "E##", "E###", "F###", "G##", "G###", "A##"])
     ],
 )
 def test_key_to_notes(key, ref_notes):
@@ -70,7 +83,6 @@ def test_key_to_notes(key, ref_notes):
     for (n, rn) in zip(notes, ref_notes):
         assert n == rn
 
-
 @pytest.mark.parametrize(
     "key,ref_notes",
     [
@@ -90,6 +102,31 @@ def test_key_to_notes_unicode(key, ref_notes):
     for (n, rn) in zip(notes, ref_notes):
         assert n == rn
 
+@pytest.mark.parametrize(
+    "note, ref_simplified_ascii",
+    [
+        (
+            "G####bb", "G##"
+        )
+    ],
+)
+def test_simplify_note_ascii(note, ref_simplified_ascii):
+    simplified_note = librosa.core.notation.__simplify_note(note, unicode=False)
+    for (n, rn) in zip(simplified_note, ref_simplified_ascii):
+        assert n == rn
+
+@pytest.mark.parametrize(
+    "notes, ref_simplified_array",
+    [
+        (
+            ['C♭♯', 'C♭♭♭'], ['C', 'C♭𝄫']
+        )
+    ],
+)
+def test_simplify_note_array(notes, ref_simplified_array):
+    simplified_note = librosa.core.notation.__simplify_note(notes)
+    for (n, rn) in zip(simplified_note, ref_simplified_array):
+        assert n == rn
 
 @pytest.mark.xfail(raises=librosa.ParameterError)
 def test_key_to_degrees_badkey():
@@ -103,6 +140,7 @@ def test_key_to_degrees_badkey():
         ("C:min", [0, 2, 3, 5, 7, 8, 10]),
         ("A:min", [9, 11, 0, 2, 4, 5, 7]),
         ("Gb:maj", [6, 8, 10, 11, 1, 3, 5]),
+        ("A###:maj", [0, 2, 4, 5, 7, 9, 11])
     ],
 )
 def test_key_to_degrees(key, ref_degrees):