¿Consonancia o disonancia?


36

Con dos nombres de nota, debe escribir un programa que determine si el intervalo formado por estas dos notas es consonante o disonante.

Introducción

En la música occidental, solo hay 12 tonos "diferentes". Sus nombres, ordenados de menor a mayor, son los siguientes: C, C#, D, D#, E, F, F#, G, G#, A, A#, B. La secuencia es cíclica, es decir, continúa con otra Cdespués de la B, infinitamente.

La distancia entre dos tonos se llama intervalo . El intervalo entre dos notas que son adyacentes en la serie anterior (por ejemplo, C — C#o E — F) se llama semitono . El intervalo entre notas más distantes se define como la cantidad de pasos de semitonos necesarios para pasar de la primera a la segunda (mientras se ajusta la secuencia). Algunos ejemplos: D to E= 2 semitonos, C to G= 7 semitonos, B to D#= 4 semitonos (esto envuelve la secuencia). 1

Ahora, estos intervalos se dividen en dos categorías: consonantes (que suena agradablemente si toca las dos notas a la vez) y disonante (no tanto).

Definamos los intervalos de consonantes como: 0, 3, 4, 5, 7, 8 y 9 semitonos.

El resto de ellos es disonante, a saber: 1, 2, 6, 10 y 11 semitonos.

El reto

Escriba un "programa" (en el sentido amplio habitual de la palabra: una función está perfectamente bien) para hacer lo siguiente:

  • Tome dos nombres de notas (cadenas de la secuencia anterior) como entrada. Puede tomarlos como quiera (desde stdin, como argumentos, separados por lo que quiera, incluso puede tomarlos como una lista de caracteres (por ejemplo ["C","#"]). Sin embargo, no puede asignar ningún otro nombre a las notas (especialmente usted no puede numerarlos del 0 al 11 y usar los números).

  • Para los fanáticos de la música, las notas se especificarán sin la octava. En este caso, tampoco importa en qué orden vienen las notas y cuál es más bajo y cuál es más alto. Finalmente, no necesita manejar ningún nombre que no esté en la lista anterior. No hay otras enarmónicas como E#, sin pisos, dobles alteraciones, etc.

  • Elija dos valores diferentes. Su programa debe generar uno de ellos siempre que el intervalo formado por las dos notas en la entrada sea consonante, y el otro si no lo son. (Podría ser Truey False, pero incluso π y e si quieres :))

  • Este es un código de golf. El programa más corto en bytes en cada idioma gana. ¡Que te diviertas!

Ejemplos y casos de prueba

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

No agrego más de ellos ya que no hay casos particularmente traicioneros en esto.

Este es un primer desafío mío, por lo que cualquier crítica constructiva es calurosamente bienvenida :—). Si encuentra la explicación de la teoría descuidada, no dude en hacer preguntas. Finalmente, por favor no me digas que esto es un engaño de esto o esto . Me aseguré de que no sea así. (Esto último es bastante similar pero más complejo. Pensé que presentar un desafío un poco más simple facilitaría la participación de las personas).


1 : Traté de simplificar esta explicación lo más que pude. Hay mucha más teoría sobre los intervalos. Por favor, no me critiques por dejarlo fuera.

Respuestas:


12

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  -->  [[65, 35], 67]
 Ḣ                      -   keep the first element  -->  [65, 67]
  6×                    -   multiply by 6           -->  [390, 402]
    %21                 -   modulo 21               -->  [12, 3]
       _L               -   subtract the length     -->  [12, 3] - [2, 1] = [10, 2]
           IA           - absolute difference       -->  8
             “¬ɠṘ’      - the integer 540205
                  æ»    - right-shift               -->  540205 >> 8 = 2110
                    Ḃ   - isolate the LSB           -->  2110 & 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.


3
How do you even come up with these things?.. Are you getting these kind of 'algorithms' by hand or by brute-force? And regardless of the answer of the second question: how?!.. :S "literal integer 540205; right shifted with (ASCII code; multiply by 6; modulo 21; keep first; subtract length...); bitwise-AND 1". Your answers keep impressing me every time..
Kevin Cruijssen

@KevinCruijssen I've added the original JS code that was used to find these values.
Arnauld

Thanks for the added explanation. I'm still as impressed as first, but you gave a clear explanation of how you came up with it. Too bad I can only upvote once.
Kevin Cruijssen

9

APL (Dyalog), 62 39 bytes

Uses ⎕IO←0; 0 is consonant, 1 is dissonant. Takes list of base note chars as left argument and list of sharps as right argument.

{⎕A[|-/('C D EF G A '⍳⍺)+⍵=⍕#]∊'BCGKL'}

Try it online!

{} anonymous function where is the left argument and is the right argument

⎕A[]∊'BCGKL' is the Alphabet, indexed by the following, a member of the string?

  ⍕# format the root namespace (yields the sharp character)

  ⍵= are the right argument chars (the sharps) equal to that?

  ()+ add the following:

   'C D EF G A '⍳⍺ indices of the left argument chars in the string

  -/ difference between those

  | absolute value


Would you mind adding an explanation for those of us who are unfamiliar with APL?
Draconis

@Draconis Explanation added.
Adám

9

MATL, 30 27 26 bytes

,j'DJEFPGIALBC'&mQs]ZP7Mdm

Inputs the two notes in different lines. Outputs 0 for consonant, 1 for dissonant.

Try it online! Or verify all test cases.

Explanation

The 11-character string

DJEFPGIALBC

encodes both the notes and the dissonant intervals, as follows.

The program first finds the 1-based indices of the input characters in the above string. A non-sharp input like D will give 1, E will give 3, ..., C will give 11. These numbers can also be considered 1×1 numeric arrays. A sharp input like C# will give the 1×2 array [11 0], meaning that C was found at position 11 and # was not found.

Observe that letters JPIL will never be present in the input. For now they are only used as placeholders, so that for example note E is two semitones above D. But they will also be useful to define dissonant intervals.

The numbers in the first entry of the 1×1 or 1×2 array correspond to note pitch in semitones, not counting sharp symbols (yet). Observe that the scale defined by these numbers doesn't start at C; but that doesn't matter because we only want intervals, that is, differences between notes. Subtracting the obtained numbers would give either the interval or 12 minus the interval. But first we need to consider the sharp symbol.

To consider sharp notes, a golfy way (in MATL) is to add 1 to each entry of the 1×1 or 1×2 array obtained previously and then sum the array (2 bytes). Thus non-sharp notes are increased by 1 and sharp notes by 2. This makes sharp notes 1 semitone higher than non-sharp notes, as required. We are also adding an extra semitone to all notes, but that doesn't change the intervals between them. So now note D will give pitch number 2, D# will give 3, ..., C will give 12, C# will give 13.

Dissonant intervals are 1, 2, 6, 10, or 11. These have a modulo-12 symmetry: an interval between two notes is dissonant if and only if the interval with the notes in reverse order, modulo 12, is dissonant.

If we compute the consecutive differences of the string 'DJEFPGIALBC' we get the numeric vector

6 -5 1 10 -9 2 -8 11 -10 1

which contains precisely the dissonant intervals, in addition to some negative values, which will be neither useful nor harmful. Observe that it is the choice of additional letters JPIL in the string 'DJEFPGIALBC' that defines (via consecutive differences) the dissonant intervals.

To see if the two input notes are dissonant we take the absolute difference of their pitch numbers. For example, C and D# will give numbers 12 and 3 respectively, and the absolute difference is 9. The actual difference would be -9, and the actual interval would be 3 (obtained as -9 modulo 12). But thanks to the symmetry referred to above, we can consider 9 instead of 3. Since 9 is not present in the vector of consecutive differences, the notes are consonant.


2
I like the way how you encoded both the notes and the dissonant intervals in the same string.
celtschk

8

JavaScript (ES6), 68 64 bytes

Takes the notes as two strings in currying syntax (a)(b). Returns 0 for dissonant or 1 for consonant.

a=>b=>488055>>(g=s=>'C D EF G A'.search(s[0])-!s[1])(a)-g(b)+9&1

Test cases

Formatted and commented

a => b =>                       // given the two notes 'a' and 'b'
  488055 >>                     // 19-bit lookup bitmask: 1110111001001110111
    (g = s =>                   // we use g() to convert a note 's' into a semitone index
      'C D EF G A'.search(s[0]) // position of the note: -1 for 'B' (not found) to 9 for 'A'
      - !s[1]                   // subtract 1 semitone if the '#' is not there
    )(a)                        // compute the result for 'a'  --> [ -2 ...  9]
    - g(b)                      // subtract the result for 'b' --> [-11 ... 11]
    + 9                         // add 9                       --> [ -2 ... 20]
  & 1                           // test the bitmask at this position (0 if negative or > 18)

7

Jelly, 26 bytes

i@€ØAo.SḤ’d5ḅ4µ€ạ/“¢£©½¿‘ċ

A monadic link taking a list of the two notes (as lists of characters) and returning 0 for consonant and 1 for dissonant.

Try it online! or see all inputs in the test-suite.

How?

i@€ØAo.SḤ’d5ḅ4µ€ạ/“¢£©½¿‘ċ - Link: list of lists of characters, notes
              µ€           - for €ach note in notes: (call the resulting list x)
   ØA                      -   yield the uppercase alphabet
i@€                        -   first index of c in ^ for €ach character, c
                           -     ...note '#' is not there so yields 0 (A->1, B->2,...)
      .                    -   literal one half
     o                     -   or (vectorised)  - e.g. "C#" -> [3, 0] -> [3, 0.5]
       S                   -   sum
        Ḥ                  -   double - that is ...  C C#  D D#  E  F F#  G G#  A A#  B
                                                 ->  6  7  8  9 10 12 13 14 15  2  3  4
         ’                 -   decrement         ->  5  6  7  8  9 11 12 13 14  1  2  3
           5               -   literal five
          d                -   divmod                (e.g. 9 -> [1,4] or 11 -> [2,1])
             4             -   literal four
            ḅ              -   convert from base     (e.g. [1,4] -> 8 or [2,1] -> 9)
                                                 ->  4  5  6  7  8  9 10 11 12  1  2  3
                 /         - reduce x with:
                ạ          -   absolute difference   (e.g. ["G#", "A"] -> [12, 1] -> 11)
                  “¢£©½¿‘  - code-page indices = [1, 2, 6, 10, 11]
                         ċ - count occurrences (1 if in the list, 0 if not)

5

Jelly, 31 bytes

O_65ị“¢[ḋṃ’b⁴¤+L$€Ḣ€ạ/e“cṾ’b12¤

Try it online!

wheeeeee 32 bytes too long

Explanation

O_65ị“¢[ḋṃ’b⁴¤+L$€Ḣ€ạ/e“cṾ’b12¤  Main link
O                                Cast each character to an int using Python `ord`
 _65                             Subtract 65 (A is 0, G is 7)
     “¢[ḋṃ’b⁴¤                   [2, 3, 5, 7, 9, 10, 0]
     “¢[ḋṃ’                      37058720
           b                     Digits in base
            ⁴                    16
    ị                            Index into this list; this creates the gaps for sharps
                 €               For each sublist
              +L$                Add the length to each element (Sharpens sharp notes)
              +                  Add
               L                 Length
                   €             For each sublist
                  Ḣ              Take the first element
                    ạ/           Absolute difference between the two (unoctaved) pitches # It's convenient that every interval's inverse (?) has the same consonance/dissonance
                      e          Is the semitone difference in
                       “cṾ’b12¤  [1, 2, 6, 10, 11]?
                       “cṾ’      25178
                           b     base
                            12   12

Hey, that's a great answer! I was wondering if someone makes use of the symmetry, and you did. And I like your method for matching the note names with numbers as well! +1.
Ramillies

The semitone difference may be symmetrical but you still get duff results - for example "G#", "A" (dissonant) yields a difference of 11 which is not in [1,2,6].
Jonathan Allan

@JonathanAllan oh uh well that's embarrassing; I thought that absolute difference fixed that... ._. will fix lol
HyperNeutrino

1
@JonathanAllan fixed for a couple extra bytes (3 IIRC)
HyperNeutrino

4

Mathematica, 55 bytes

function                                                  arguments        bytes

FreeQ[1|2|6|10|11]@Abs[#-#2&@@Sound`PitchToNumber/@#]&    [{"C","F#"}]     55

Map the internal built-in Sound`PitchToNumber on the input (list of two strings), take the absolute difference, then pattern match for dissonant interval numbers.


Just for fun (non-competing)

Here are some shorter functions that violate the restriction “you may not assign any other names to the notes.” The rudimentary Music` package has predefined note constants (like A4 = 440.) and the function HertzToCents (which can be golfed). Instead of strings, we will use the note constants as arguments, but given in a different format for each function.

FreeQ[1|2|6|10|11]@Abs@@Round[.01HertzToCents@#]&         [{C3,Fsharp3}]   50+9=59
FreeQ[1|2|6|10|11]@Abs@Round[17Log[#2/#]]&                [C3,Fsharp3]     43+9=52
FreeQ[1|2|6|10|11]@Abs@Round[17Log@#]&                    [C3/Fsharp3]     39+9=48

The package import <<Music`; takes 9 bytes.

This function converts a string (like "F#") into a note constant (like Fsharp3):

Symbol[StringReplace[#,"#"->"sharp"]<>"3"]&                                44

To accept intervals larger than an octave, replace Abs[…] with Mod[…,12].


Why are some intervals considered dissonant? An interval is a ratio of two frequencies. If the ratio has a “simple” numerator and denominator, it tends to be more consonant. In 5-limit tuning, ratios can be factored into integer powers of only prime numbers less than or equal to 5. No interval in equal temperament, besides the octave, is a just interval; they are merely close approximations using powers of the 12th root of 2.

Instead of hard-coding which interval numbers are dissonant, we can find a rational approximation of the interval and then determine if its numerator and denominator are “simple” (meaning that the denominator is less than 5 and the ratio does not divide 7).

This table shows each of the steps in that process.

Table[
  Module[{compoundInterval,simpleInterval,rationalApprox,denomLeq5,div7,consonant},
    compoundInterval = Power[2, i/12];
    simpleInterval   = 2^Mod[Log2[compoundInterval], 1];
    rationalApprox   = Rationalize[N@simpleInterval, 1/17];
    denomLeq5        = Denominator[rationalApprox]<=5;
    div7             = Denominator[rationalApprox]>1 && rationalApprox\[Divides]7;
    consonant        = FreeQ[1|2|6|10|11][Mod[i,12]];

    InputForm/@{
      i, simpleInterval, rationalApprox, 
      denomLeq5, div7, denomLeq5 && !div7,
      consonant
    }
  ], {i, 0, 12}
]

i   sInterval  ratio   denomLeq5  div7       den&&!div  | consonant?

0   1          1       True       False      True       | True
1   2^(1/12)   17/16   False      False      False      | False
2   2^(1/6)    9/8     False      False      False      | False
3   2^(1/4)    6/5     True       False      True       | True
4   2^(1/3)    5/4     True       False      True       | True
5   2^(5/12)   4/3     True       False      True       | True
6   Sqrt[2]    7/5     True       True       False      | False
7   2^(7/12)   3/2     True       False      True       | True
8   2^(2/3)    8/5     True       False      True       | True
9   2^(3/4)    5/3     True       False      True       | True
10  2^(5/6)    7/4     True       True       False      | False
11  2^(11/12)  11/6    False      False      False      | False
12  1          1       True       False      True       | True

The rational approximation lies within 1/17 of the interval because that is the largest threshold that distinguishes between all 12 equal tempered intervals. We match rational numbers with the pattern Rational[a_,b_] (or just a_~_~b_) first, and then match integers with only _.

This culminates in the following rather short function that determines if an arbitrary frequency ratio (greater than 1) is consonant or dissonant.

Rationalize[#,1/17]/.{a_~_~b_:>b<=5&&!a∣7,_->True}&       [Fsharp3/C3]     51+9=60

1
Gosh, don't tell me Mathematica has a builtin even for this... :D
Ramillies

3

Mathematica, 118 bytes

FreeQ[{1,2,6,10,11},Min@Mod[Differences[Min@Position["C|C#|D|D#|E|F|F#|G|G#|A|A#|B"~StringSplit~"|",#]&/@{#,#2}],12]]&


Input form

["A#","D"]

Outputs

True->Consonant  
False->Dissonant   

thanks @JonathanFrech -16 bytes


Just a remark: you don't need to output strings Consonant and Dissonant. You may output any two values instead of them (0/1,... whatever). That could save some bytes.
Ramillies

1
Can you not omit the If[...,0,1] and define True->Consonant; False->Dissonant?
Jonathan Frech

1
StringCases["CC#DD#EFF#GG#AA#B",_~~"#"...] – 42 Bytes
celtschk

1
Also, 2 bytes can be saved by replacing {1,2,6,10,11} with 1|2|6|10|11
celtschk

1
@Skyler See the answer below.
hftf

3

Charcoal, 30 bytes

≔B#A#G#FE#D#C槔o∧⌈ς”⁻⌕ζ⮌θ⌕ζ⮌η

Try it online! Link is to verbose version of code. Outputs 1 for consonant, 0 for dissonant. Explanation:

≔B#A#G#FE#D#Cζ                  Store reversed note names in z
                        θ       First input
                       ⮌        Reversed
                     ⌕ζ         Find index in z
                            η   Second input
                           ⮌    Reversed
                         ⌕ζ     Find index in z
                     ⁻          Subtract
               ”o∧⌈ς”           Compressed string 100111011100
              §                 Circularly index
                                Implicitly print

out of curiosity, is there a mnemonic reason the glyph ⌕ζ is used for "find index"?
Jonah

@Jonah ζ is the variable assigned to earlier.
Neil

2

J, 68 bytes

[:e.&1 2 6 10 11[:(12| -~/)(<;._1',C,C#,D,D#,E,F,F#,G,G#,A,A#,B')i.]

explanation

A straightforward, not super golfed implementation in J:

  • Input is given as boxed itemized notes (produced using cut), in order.

  • Find their indexes in the range of notes: (<;._1',C,C#,D,D#,E,F,F#,G,G#,A,A#,B')i.]

  • Subtract the first from the second: -~/

  • Take the remainder when divided by 12: 12|

  • Check if it's one of the dissonant notes: e.&1 2 6 10 11

Try it online!


2

///, 90 88 bytes

/^/"\///^\/\///C^D/##"E/DD"F/E#"G/FD"A/GD"B/AD"#,#/,"B#^B/#"A#/#"A^G#^G^F#/#"F^E^D#^D/#/

Try it online! (all test cases at once)

  • Put the input after the code.
  • Separate the note names with ,B# in each test case.
  • The output is , for consonant, ,# for dissonant.
  • Support for double-alterations (##) or E# in some particular cases. Otherwise the output is , for consonant, #, for dissonant (thanks to modulo 12 symmetry)
  • Can handle multiple test cases at once (if separated reasonably)
  • Lowercase characters are printed exactly.

2

C (gcc), 91 bytes

g(char*s){return (s[1]&1|2**s&15)*4/5;}f(char*x,char*y){return (1952220<<g(x)>>g(y))&2048;}

call: f("A#", "D")

Return value:

  • Consonant: 2048
  • Dissonant: 0

Bonus: The function is case insensitive.

Try it online!


Are there not two unnecessary spaces in both return (s?
Jonathan Frech

You may try g(char*s){s=(s[1]&1|2**s&15)*4/5;}f(char*x,char*y){x=1952220<<g(x)>>g(y)&2048;} and nice solution!
Keyu Gan

1

Python 2, 125 117 83 78 77 bytes

a,b=map("C C#D D#E F F#G G#A A#B".index,input())
print chr(abs(a-b))in""

Where the "" at the end actually contains the characters "\x02\x04\x0c\x14\x16"

Try it Online!

(+3 because I forgot 11 or 22 in the list to begin with)

-8 bytes from Jonathan Frech and switching to Python 2.

-34 bytes with suggestions from Jonathan Frech and using str's index instead of list's.

-4 bytes from inlining i and Neil's reversing the string suggestion (only -2 really, as i forgot () around a generator)

-5 bytes from un-inlining i & changing the input format

-1 bytes from Jonathan Frech with map() and unprintables.

Takes input in one line of stdin in the format:

'C','C#'

True is dissonant, False is consonant.

Old Explanation:

i='C C#D D#E F F#G G#A A#B'.index
a,b=input()
print abs(i(a)-i(b))in[2,4,12,20]

Python str.index returns the lowest (positive) starting index of a matching substring, so "ABACABA".index("A") == 0 and "ABACABA".index("BA") == 1. Because of this, we can put the note names evenly spaced in a string, and as long as (for example) A comes before A#, the shared A won't be a problem.

i='C C#D D#E F F#G G#A A#B'.index

i is now a function that returns the index in 'C C#D D#E F F#G G#A A#B' its argument (a note name) is, which is 2 * (the number of semitones that note is up from C)

a,b=input()

Python 2's input() is (mostly) equivalent to eval(input()) in Python3, so with a valid input of the format 'C#','F' (for example), a='C#' and b='F'

print abs(i(a)-i(b))in[2,4,12,20]

If the disttance between the first note and the second note in the string is not 2, 4, 12, or 20 (since the note names are represented in 2 characters), then the interval is dissonant, print True, else it is consonant, print False.


Since the input format is not strict, you can use eval(input()) (13 bytes) instead of input().split() (15 bytes).
Jonathan Frech




1
You could use Unicode characters () instead of an emtpy string.
Jonathan Frech

1

C (gcc), 115 117 120 bytes

g(char*a){a=*a-65+!!a[1]*(7-*a/70-*a/67);}f(x,y)char*x,*y;{x="(pP$HL<lt<X"[g(x)]*32+"=ZukW-^h1F6"[g(x)]>>g(y)&1;}

Try it online!

Return 1/0 for consonat and dissonat. It is always interesting to do string manipulation with pure C. Take input as f("A#", "C")


0

PowerShell, 107 bytes

param($a,$b)[math]::abs(($x=-split'C C# D D# E F F# G G# A A# B').indexof($b)-$x.indexof($a))-in1,2,6,10,11

Try it online!

Outputs True for dissonant and False for consonant.

Takes input $a and $b, the two notes, as strings. Performs a -split operation on the scale, which splits on whitespace, to create an array of the notes, stores that into $x. Finds the .indexof $b in that array, subtracts the index of $a, and then takes the absolute value thereof. Checks whether that number is -in the dissonant ranges.



0

SQL, 582 bytes

SQL Fiddle

I still have some golfing to do on it, but I wanted to get it down here before I end up breaking it completely.

If the input is in a letter format, then putting those letters in a table with values is okay, right?

CREATE TABLE N(N char(2),v int)
Insert Into N values('A',1),('A#',2),('B',3),('C',4),('C#',5),('D',6),('D#',7),('E',8),('F',9),('F#',10),('G',11),('G#',12);
CREATE TABLE D(D char(9),v int) 
Insert Into D values('C',0),('D',1),('D',2),('C',3),('C',4),('C',5),('D',6);
CREATE FUNCTION I(@A char(2),@B char(2))
RETURNS char(9) as
BEGIN
DECLARE @E int=(SELECT v from N where n=@A),@F int=(SELECT v from N where n=@B)
DECLARE @C char(9) = (SELECT case D when 'D' then 'Dissonant' when 'C' then 'Consonant' END from D where v in(abs(@e-@f),12-abs(@e-@f)))
RETURN isnull(@C,'NotANote')
END

0

Perl 5, 106 bytes

("C,C#,D,D#,E,F,F#,G,G#,A,A#,B,"x2)=~/$F[0],(.*?)$F[1],/;$r=(1+($1=~y/,//))%12;say(grep/$r/,(0,3..5,7..9))

Try it online!

Returns false for dissonant, true for consonant.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.