Piano Chords on White Keys

MATL, 31 bytes

Thanks to Jonathan Allan for a correction.

'BAGFEDC'"GYs12X\110BQX@YSYsm?@

Try it online! Or verify all test cases.

Explanation

The pattern 2 2 1 2 2 2 1 specifies the intervals between consecutive white keys. The program uses a loop that applies all cyclic shifts to this basic pattern, in order to test each key as a potential lowest note of the input chord. For each shift, the cumulative sum of the pattern is obtained. For example, for B as potential lowest note, the pattern has been shifted to 1 2 2 1 2 2 2 and its cumulative sum is 1 3 5 6 8 10 12.

Now, to see if this can support a 4 3 3 chord we compute the cumulative sum of the chord intervals, which is 4 7 10; reduce it via 1-based modulo 12 (an interval of 14 would give 2); and check if those numbers are all members of the allowed values 1 3 5 6 8 10 12. That's not the case in this example. Had it been the case, we would output the letter B.

The correspondence between cyclic shifts and output letters is defined by the string 'BAGFEDC'. This indicates that 'B' (first character) corresponds to a cyclic shift by 1; 'A' (second character) corresponds to a cyclic shift by 2 etc.

'BAGFEDC'  % Push this string
"          % For each character from the string
  G        %   Push input array
  Ys       %   Cumulative sum
  12X\     %   1-based modulo 12, element-wise (1,12,13,14 respectively give 1,12,1,2)
  110BQ    %   Push 110, convert to binary, add 1 element-wise: gives [2 2 1 2 2 2 1]
  X@       %   Push current iteration index, starting at 1
  YS       %   Cyclic shift to the right by that amount
  Ys       %   Cumulative sum
  m        %   Ismember. Gives an array of true of false entries
  ?        %   If all true
    @      %     Push current character
           %   End (implicit)
           % End (implicit)
           % Display (implicit)

Mathematica, 110 bytes (ISO 8859-1 encoding)

±i_:=#&@@@Select["A#BC#D#EF#G#"~StringTake~{Mod[#,12,1]}&/@#&/@(Accumulate[i~Prepend~#]&/@Range@12),FreeQ@"#"]

Defines a unary function ± taking a list of integers as input (no restrictions on the size or signs of the integers, actually) and returns a list of one-character strings. For example, ±{3,4} returns {"A","D","E"}.

"A#BC#D#EF#G#"~StringTake~{Mod[#,12,1]}&/@# is a function that turns a list of integers into the corresponding note names, except that # stands for any black key. This is applied to each element of Accumulate[i~Prepend~#]&/@Range@12, which builds up a list of note values from the list input list of note intervals, starting with each possible note from 1 to 12. We filter out all such note-name lists that contain "#" using Select[...,FreeQ@"#"], and then return the first note in each remaining list using #&@@@.


Python 2, 159 155 bytes

(Posting this after making sure there's a valid submission that's shorter than this one)

import numpy
s='C.D.EF.G.A.B'
def k(y):return lambda x:s[(x+y)%12]
for i in range(12):
    if s[i]!='.'and'.'not in map(k(i),numpy.cumsum(input())):print s[i]

Pretty much just the trivial solution. Inputs as a list of integers and outputs with each character on an individual line.

-4 bytes by removing an unnecessary variable