Consonance or dissonance? Given two note names, you are to write a program that determines if the interval formed by these two notes is consonant or dissonant.

Introduction

In Western music, there are only 12 "different" tones. Their names, sorted from lowest to highest, are these: C, C#, D, D#, E, F, F#, G, G#, A, A#, B. The sequence is cyclical, i. e. it continues with another C after the B, infinitely.

The distance between two tones is called an interval. The interval between any two notes that are adjacent in the series above (e. g. C — C# or E — F) is called a semitone. The interval between more distant notes is defined as the number of semitone steps needed to get from the first to the second (while possibly wrapping around the sequence). Some examples: D to E = 2 semitones, C to G = 7 semitones, B to D# = 4 semitones (this wraps around the sequence).1

Now, these intervals are divided into two categories: consonant (pleasantly sounding if you play the two notes at once) and dissonant (not so much).

Let's define the consonant intervals to be: 0, 3, 4, 5, 7, 8 and 9 semitones.

The rest of them is dissonant, namely: 1, 2, 6, 10 and 11 semitones.

The Challenge

Write a "program" (in the usual broad sense of the word: a function is perfectly OK) to do the following:


Take two note names (strings from the sequence above) as an input. You may take them however you like (from stdin, as arguments, separated by whatever you want, even feel free to take them as a list of characters (e. g. ["C","#"]). However, you may not assign any other names to the notes (especially you may not number them from 0 to 11 and use the numbers).
For you music geeks out there, the notes will be specified without the octave. In this case, it also does not matter in which order the notes come and which is lower and which is higher. Finally, you don't need to handle any names not in the list above. No other enharmonics like E#, no flats, double-alterations and so on.
Pick any two different values. Your program must output one of them whenever the interval formed by the two notes in the input is consonant, and the other if they are not. (Could be True and False, but even π and e if you want :))
This is a code-golf. The shortest program in bytes in each language wins. Have fun!


Examples and Test Cases

Note 1    Note 2    Output    Interval [semitones]
  C          D     Dissonant   2
  A#         A#    Consonant   0
  G          D     Consonant   7 (wraparound)
  D#         A     Dissonant   6
  F          E     Dissonant   11
  A          C     Consonant   3


I don't add more of them since there aren't any particularly treacherous cases in this. 

This is a first challenge of mine, so any constructive criticism is warmly welcome :—). If you find the theory explanation sloppy, feel free to ask questions. Finally, please do not tell me that this is a dupe of this or this. I made sure it's not. (The latter is quite similar but more complex. I thought that putting up a little simpler challenge will make it easier for people to join.)


1: I tried to simplify this explanation as far as I could. There's a lot more theory around intervals. Please don't bash me for leaving it out.
Jelly, 21 bytes

Takes input as a list of two strings. Returns 0 for dissonant or 1 for consonant.

OḢ6×%21_Lµ€IA“¬ɠṘ’æ»Ḃ


Try it online!

OḢ6×%21_Lµ€IA“¬ɠṘ’æ»Ḃ   - main link
         µ€             - for each note             e.g. ["A#", "C"]
O                       -   convert to ASCII codes  --&gt;  [[65, 35], 67]
 Ḣ                      -   keep the first element  --&gt;  [65, 67]
  6×                    -   multiply by 6           --&gt;  [390, 402]
    %21                 -   modulo 21               --&gt;  [12, 3]
       _L               -   subtract the length     --&gt;  [12, 3] - [2, 1] = [10, 2]
           IA           - absolute difference       --&gt;  8
             “¬ɠṘ’      - the integer 540205
                  æ»    - right-shift               --&gt;  540205 &gt;&gt; 8 = 2110
                    Ḃ   - isolate the LSB           --&gt;  2110 &amp; 1 = 0


Making-of

We first should note that the function F that we're looking for is commutative: for any pair of notes (A, B), we have F(A, B) = F(B, A).

Since there are not too many possible inputs and only 2 possible outputs to deal with, it must be possible to find a rather simple hash function H, such that |H(A) - H(B)| produces a limited range of values and is collision-free for all possible pairs of notes (A, B) with respect to the expected output.

We're going to test the set of functions H(mul, mod), which are defined as:

H(mul, mod)(s) = ((ORD(s[0]) * mul) MOD mod) - LEN(s)


Where ORD(s[0]) is the ASCII code of the first character of the note and LEN(s) is the length of the note (2 if there's a '#' and 1 if not).

Below is a commented version of the JS code that was used to find a couple of valid pairs (mul, mod) and the resulting bitmasks. There are many possible solutions, but * 6 % 21 is the shortest one with this method.



// all 12 notes
note = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'];

// reference implementation that takes 2 notes
// and returns 0 for dissonant or 1 for consonant
F = (a, b) =&gt;
  +!~[1, 2, 6, 10, 11].indexOf((note.indexOf(a) - note.indexOf(b) + 12) % 12)

for(mul = 1; mul &lt; 100; mul++) {
  for(mod = 1; mod &lt; 31; mod++) {
    // hash function
    H = s =&gt; s.charCodeAt() * mul % mod - s.length;

    table = {};

    // for all possible pairs of notes (a, b)
    if(note.every(a =&gt; note.every(b =&gt; {
      // compute the table index
      idx = Math.abs(H(a) - H(b));

      // and the expected result
      res = F(a, b);

      // if the slot is empty, store the correct value
      if(table[idx] === undefined) {
        table[idx] = res;
        return 1;
      }
      // else, make sure that the correct value is already there
      return table[idx] == res;
    }))) {
      // if all tests pass: translate the table into a binary mask
      for(n = 0, msk = 0; n &lt; 31; n++) {
        msk |= (table[n] || 0) &lt;&lt; n;
      }
      // and display the result
      console.log('mul ' + mul + ', mod ' + mod + ' --&gt; ' + msk);
    }
  }
}