Created
November 17, 2008 14:16
-
-
Save arnar/25774 to your computer and use it in GitHub Desktop.
Chord guesser for major keys
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re | |
NOTE_NUMBERS = { | |
'A': 0, | |
'A#': 1, | |
'Bb': 1, | |
'B': 2, | |
'C': 3, | |
'C#': 4, | |
'Db': 4, | |
'D': 5, | |
'D#': 6, | |
'Eb': 6, | |
'E': 7, | |
'F': 8, | |
'F#': 9, | |
'Gb': 9, | |
'G': 10, | |
'G#': 11, | |
'Ab': 11, | |
} | |
NOTE_NAMES = { | |
'flats': ('A','Bb','B','C','Db','D','Eb','E','F','Gb','G','Ab'), | |
'sharps': ('A','A#','B','C','C#','D','D#','E','F','F#','G','G#') | |
} | |
KEYS_FLAT_SHARP = { | |
'A': 'sharps', | |
'A#': 'flats', | |
'Bb': 'flats', | |
'B': 'sharps', | |
'C': 'sharps', | |
'C#': 'flats', | |
'Db': 'flats', | |
'D': 'sharps', | |
'D#': 'flats', | |
'Eb': 'flats', | |
'E': 'sharps', | |
'F': 'flats', | |
'F#': 'sharps', | |
'Gb': 'flats', | |
'G': 'sharps', | |
'G#': 'flats', | |
'Ab': 'flats', | |
} | |
def note_number(note): | |
return NOTE_NUMBERS[note] | |
def number_note(number, key='C'): | |
return NOTE_NAMES[KEYS_FLAT_SHARP[key]][number % 12] | |
def generate_chords(key): | |
root = note_number(key) | |
bases = [(root + i) % 12 for i in [0,2,4,5,7,9,11]] | |
major = ['','m','m','','','m','dim'] | |
return [number_note(b) + m for (b,m) in zip(bases,major)] | |
KEY_CHORDS = dict([(key, generate_chords(key)) for key in NOTE_NUMBERS.keys()]) | |
KEY_CHORD_SETS = dict([(key, set(chords)) for (key, chords) in KEY_CHORDS.items()]) | |
def normalize_chord(chord): | |
chord = re.sub(r'(sus|add|maj|7).*$', '', chord) | |
chord = re.sub(r'moll.*$', 'm', chord) | |
if chord[0].islower(): | |
chord = chord[0].upper() + "m" + chord[1:] | |
if chord[0] == 'H': | |
chord = 'B' + chord[1:] | |
assert re.match(r'^[ABCDEFG](#|b)?(m|dim)?$', chord) | |
return chord | |
def guess_key(chords): | |
# find the number of intersecting chords with each set | |
chords = map(normalize_chord, chords) | |
chord_set = set(chords) | |
max_overlap = 0 | |
found_keys = [] | |
for key, key_chords in KEY_CHORD_SETS.items(): | |
overlap = len(chord_set & key_chords) | |
if overlap >= max_overlap: | |
if overlap > max_overlap: | |
found_keys = [] | |
found_keys.append(key) | |
max_overlap = overlap | |
return found_keys | |
if __name__ == '__main__': | |
while True: | |
chords = raw_input('What chords? ').split() | |
if len(chords) == 0: | |
break | |
print "Likely keys: ", ','.join(guess_key(chords)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment