Skip to content

Instantly share code, notes, and snippets.

@kevinadi
Last active June 27, 2017 21:54
Show Gist options
  • Save kevinadi/67b089e9e166d00e5cedc93cffbd9c62 to your computer and use it in GitHub Desktop.
Save kevinadi/67b089e9e166d00e5cedc93cffbd9c62 to your computer and use it in GitHub Desktop.
Shows guitar scales & chords
#!/usr/bin/python
'''
Print guitar scales to stdout
'''
import argparse
import textwrap
MAXFRET = 16
NOTES_FLAT = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B']
NOTES_SHARP = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
# Scale dictionary
SCALEDICT = [
['Scale', 'Chromatic', '1,b2,2,b3,3,4,b5,5,b6,6,b7,7'],
['Scale', 'Major', '1,2,3,4,5,6,7'],
['Scale', 'Minor Natural', '1,2,b3,4,5,b6,b7'],
['Scale', 'Minor Harmonic', '1,2,b3,4,5,b6,7'],
['Scale', 'Major Pentatonic', '1,2,3,5,6'],
['Scale', 'Minor Pentatonic', '1,b3,4,5,b7'],
['Scale', 'Blues Pentatonic', '1,b3,4,b5,5,b7'],
['Mode', 'Ionian', '1,2,3,4,5,6,7'],
['Mode', 'Dorian', '1,2,b3,4,5,6,b7'],
['Mode', 'Phrygian', '1,b2,b3,4,5,b6,b7'],
['Mode', 'Lydian', '1,2,3,#4,5,6,7'],
['Mode', 'Mixolydian', '1,2,3,4,5,6,b7'],
['Mode', 'Aeolian', '1,2,b3,4,5,b6,b7'],
['Mode', 'Locrian', '1,b2,b3,4,b5,b6,b7'],
['Chord', 'Major', '1,3,5'],
['Chord', 'Minor', '1,b3,5'],
['Chord', '7th', '1,3,5,b7'],
['Chord', 'Major 7th', '1,3,5,7'],
['Chord', 'Minor 7th', '1,b3,5,b7'],
['Chord', '6th', '1,3,5,6'],
['Chord', 'Augmented', '1,3,#5'],
['Chord', 'Diminished', '1,b3,b5']
]
class GuitarScale(object):
''' Determines scales & notes on a fretboard
'''
def __init__(self, key, scale, chord, tuning):
scale = scale.title()
scaleinput = [
n for n, l in enumerate(SCALEDICT)
if l[1].title().startswith(scale) and (l[0] == 'Chord') is chord][0]
scalevalues = SCALEDICT[scaleinput][2]
key = key.title()
if key.find('b') >= 0 or scalevalues.find('b') >= 0:
notes = NOTES_FLAT
else:
notes = NOTES_SHARP
# get the scale & transpose to key
scale = [(x + notes.index(key)) %
12 for x in GuitarScale.interval2idx(scalevalues)]
strings = list(reversed([NOTES_FLAT.index(z) for z in tuning]))
self.params = {
'fretboard': None,
'scalenotes': None,
'intervals': None,
'notes': notes,
'scalevalues': scalevalues,
'scaleinput': scaleinput
}
self.inputs = {
'key': key,
'scale': scale,
'chord': chord,
'tuning': tuning,
'strings': strings
}
@staticmethod
def interval2idx(intervalinput):
''' Convert from interval numbers to notes '''
interval_flat = ['1', 'b2', '2', 'b3', '3',
'4', 'b5', '5', 'b6', '6', 'b7', '7']
interval_sharp = ['1', '#1', '2', '#2', '3',
'4', '#4', '5', '#5', '6', '#6', '7']
if repr(intervalinput).find('b') >= 0:
interval = interval_flat
else:
interval = interval_sharp
return [interval.index(i) for i in intervalinput.split(',')]
@staticmethod
def description():
''' Return a description of the class '''
return 'available scales/chords:\n ' + \
'\n '.join(' - '.join(x[:2]) for x in SCALEDICT)
@staticmethod
def calculate_notes(notes, scale, strings):
''' Calculate the notes '''
scalenotes = [notes[x] for x in scale]
fretboard = [[notes[(x + y) % 12] for x in range(MAXFRET)] for y in strings]
for string in xrange(len(strings)): # eliminate notes not in scale
for fret in range(MAXFRET):
if fretboard[string][fret] not in scalenotes:
fretboard[string][fret] = ''
return fretboard, scalenotes
@staticmethod
def calculate_intervals(scalevalues, scalenotes):
''' Calculate the intervals '''
idx = [x for x in GuitarScale.interval2idx(scalevalues)] + [12]
intervals = ' - '.join(str((idx[x] - idx[x - 1]) / 2.)
for x in range(1, len(idx)))
return scalenotes, intervals
def construct_fretboard(self):
''' Construct the fretboard '''
strings = self.inputs.get('strings')
scale = self.inputs.get('scale')
notes = self.params.get('notes')
scalevalues = self.params.get('scalevalues')
fretboard, scalenotes = GuitarScale.calculate_notes(notes, scale, strings)
scalenotes, intervals = GuitarScale.calculate_intervals(scalevalues, scalenotes)
self.params['fretboard'] = fretboard
self.params['scalenotes'] = scalenotes
self.params['intervals'] = intervals
def printable_fretboard(self):
''' Pretty print '''
key = self.inputs.get('key')
strings = self.inputs.get('strings')
tuning = self.inputs.get('tuning')
scaleinput = self.params.get('scaleinput')
scalenotes = self.params.get('scalenotes')
scalevalues = self.params.get('scalevalues')
intervals = self.params.get('intervals')
fretboard = self.params.get('fretboard')
scalename = key + ' ' + SCALEDICT[scaleinput][1] + ' ' + SCALEDICT[scaleinput][0]
output = textwrap.dedent('''
{dashline}
{scalename}
{dashline}
Scale notes: {scalenotes}
{scalevalues}
Intervals: {intervals}
Tuning: {tuning}
'''.format(
dashline='-'*len(scalename),
scalename=scalename,
scalenotes=' - '.join(x.center(3) for x in scalenotes),
scalevalues=' - '.join(x.center(3) for x in scalevalues.split(',')),
intervals=intervals,
tuning=' - '.join(tuning)
))
for current_string in xrange(len(strings)):
fret_sym, nut_sym = '|', '||'
if fretboard[current_string][0] == '':
output += ' ' * 2 + nut_sym
else:
output += fretboard[current_string][0].center(2) + nut_sym
for current_fret in range(1, MAXFRET):
if fretboard[current_string][current_fret] == '':
output += '-' * 5 + fret_sym
elif fretboard[current_string][current_fret] == scalenotes[0] and scaleinput != 0:
output += (
'(' + fretboard[current_string][current_fret] + ')').center(5, '-') + \
fret_sym
else:
output += fretboard[current_string][current_fret].center(5, '-') + \
fret_sym
output += '\n'
output += '\n 3 5 7' +\
' 9 o o 15'
return output
if __name__ == '__main__':
PARSER = argparse.ArgumentParser(
description=GuitarScale.description(),
formatter_class=argparse.RawTextHelpFormatter)
PARSER.add_argument('key', type=str, help='The key')
PARSER.add_argument('scale', type=str, help='The scale')
PARSER.add_argument('--chord', '-c', help='Display chord', action='store_true')
PARSER.add_argument('--tuning', '-t', help='String tuning', default='EADGBE', type=str)
ARGS = PARSER.parse_args()
SCALE = GuitarScale(key=ARGS.key, scale=ARGS.scale, chord=ARGS.chord, tuning=ARGS.tuning)
SCALE.construct_fretboard()
print SCALE.printable_fretboard()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment