arnar (owner)

Revisions

gist: 25774 Download_button fork
public
Description:
Chord guesser for major keys
Public Clone URL: git://gist.github.com/25774.git
Embed All Files: show embed
Python #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
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))