191 lines
7.8 KiB
Python
191 lines
7.8 KiB
Python
#!/usr/bin/env python
|
||
# mzk music theory helper - developed by acidvegas in python (https://git.acid.vegas/mzk)
|
||
# functions.py
|
||
|
||
import constants
|
||
|
||
def chromatic_scale(key):
|
||
notes = list(constants.notes)
|
||
while notes[0] != key:
|
||
notes.append(notes.pop(0))
|
||
return notes
|
||
|
||
def generate_scale_string(string, scale_notes, full=False):
|
||
notes = chromatic_scale(string.upper())*2 if full else chromatic_scale(string.upper())
|
||
notes.append(notes[0])
|
||
for index,note in enumerate(notes):
|
||
if note in scale_notes:
|
||
notes[index] = notes[index].center(5, '-')
|
||
else:
|
||
notes[index] = '-'*5
|
||
return notes
|
||
|
||
def get_pattern(pattern):
|
||
new_pattern = list()
|
||
for step in pattern:
|
||
if step == '1' : new_pattern.append('H')
|
||
elif step == '2' : new_pattern.append('W')
|
||
elif step == '3' : new_pattern.append('WH')
|
||
return ' '.join(new_pattern)
|
||
|
||
def chord_notes(type, key):
|
||
notes = scale_notes(type, key)
|
||
pattern = constants.chords[type]['pattern']
|
||
_notes = [key,]
|
||
for step in pattern.split()[1:]: #1 b3 5
|
||
if len(step) == 2:
|
||
if step[:1] == 'b':
|
||
_notes.append(notes[int(step)-2])
|
||
elif step[:1] == '#':
|
||
_notes.append(notes[int(step)])
|
||
else:
|
||
_notes.append(notes[int(step)-1])
|
||
return _notes
|
||
|
||
def print_scale(root, type, full=False, chord=False):
|
||
frets = (24,147) if full else (12,75)
|
||
print(f'{root.upper()} {type.upper()} SCALE'.center(frets[1]))
|
||
print(' ┌' + '┬'.join('─'*5 for x in range(frets[0])) + '┐')
|
||
print('0 │' + '│'.join(str(x).center(5) for x in range(1,frets[0]+1)) + '│')
|
||
print(' ├' + '┼'.join('─'*5 for x in range(frets[0])) + '┤')
|
||
notes = chord_notes(type, root) if chord else scale_notes(type, root)
|
||
for string in ('eBGDAE'):
|
||
string_notes = generate_scale_string(string, notes, full)
|
||
print(string + ' │' + '│'.join(note.center(5, '-') for note in string_notes[1:]) + '│')
|
||
print(' └' + '┴'.join('─'*5 for x in range(frets[0])) + '┘')
|
||
print((', '.join(notes) + ' / ' + get_pattern(constants.scales[type])).rjust(frets[1]))
|
||
|
||
def scale_notes(type, key):
|
||
last = 0
|
||
all_notes = chromatic_scale(key)*2
|
||
scale_notes = [all_notes[0],]
|
||
for step in constants.scales[type]:
|
||
last += int(step)
|
||
scale_notes.append(all_notes[last])
|
||
return scale_notes
|
||
|
||
def print_chord(root, type):
|
||
# todo: finish this
|
||
print('◯ ⬤ ')
|
||
print('''╳ ╳ ╳ ╳ ╳ ╳
|
||
┌───┬───┬───┬───┬───┐
|
||
│ │ │ │ │ │
|
||
├───┼───┼───┼───┼───┤
|
||
│ │ │ │ │ │
|
||
├───┼───┼───┼───┼───┤
|
||
│ │ │ │ │ │
|
||
├───┼───┼───┼───┼───┤
|
||
│ │ │ │ │ │
|
||
├───┼───┼───┼───┼───┤
|
||
│ │ │ │ │ │
|
||
└───┴───┴───┴───┴───┘
|
||
E A D G B e''')
|
||
print('''╳ ◯ ◯
|
||
┌───┬───┬───┬───┬───┐
|
||
│ │ │ │ │ │
|
||
├───┼───┼───┼───⬤───┤
|
||
│ │ │ │ │ │
|
||
├───┼───⬤───┼───┼───┤
|
||
│ │ │ │ │ │
|
||
├───⬤───┼───┼───┼───┤
|
||
│ │ │ │ │ │
|
||
├───┼───┼───┼───┼───┤
|
||
│ │ │ │ │ │
|
||
└───┴───┴───┴───┴───┘
|
||
E A D G B e''')
|
||
|
||
|
||
def print_circle_of_fifths():
|
||
'''definition:
|
||
the relationship among the 12 tones of the chromatic scale, their corresponding key signatures, & the associated major/minor keys
|
||
|
||
accidentals:
|
||
sharps - F, C, G, D, A, E, B
|
||
flats - B, E, A, D, G, C, F
|
||
|
||
intervals:
|
||
unison
|
||
perfect fifth
|
||
major sencond
|
||
major sixth
|
||
major third
|
||
major seventh
|
||
augmented fourth
|
||
minor second
|
||
minor sixth
|
||
minor third
|
||
minor seventh
|
||
perfect fourth'''
|
||
circle = constants.circle.replace('\n',' \n') + ' ' # todo: fix this
|
||
for note in ('major','C','F','B♭','E♭','A♭','D♭','C♯','G♭/F♯','B','C♭','E','A','D','G'): # todo: reverse
|
||
circle = circle.replace(f' {note} ', f' \033[91m{note}\033[0m ')
|
||
for item in ('♮','1♭','2♭','3♭','4♭','5♭/7♯','6♭/6♯','7♭/5♯','4','3♯','2♯','1♯'):
|
||
circle = circle.replace(f' {item} ', f' \033[90m{item}\033[0m ')
|
||
for note in ('minor','a','d','g','c','f','b♭','e♭/d♯','g♯','c♯','f♯','b','c'):
|
||
circle = circle.replace(f' {note} ', f' \033[92m{note}\033[0m ')
|
||
print(circle)
|
||
print(print_circle_of_fifths.__doc__)
|
||
|
||
def print_help():
|
||
print('usage: python mzk.py [OPTIONS]')
|
||
print('\noptions:')
|
||
print('--chord=KEY_TYPE │ print a TYPE chord in the key of KEY')
|
||
print('--circle │ print the circle of fifths')
|
||
print('--intervals │ print list of intervals')
|
||
print('--scale=KEY_TYPE │ print a TYPE scale in the key of KEY')
|
||
print('--scales │ print list of scale types & patterns')
|
||
print('\nnote: KEY_TYPE must be formatted as such: c_major, f#_mixolydian, etc.')
|
||
|
||
def print_intervals():
|
||
'''definition:
|
||
the distance between two notes or pitches
|
||
|
||
note:
|
||
semitone - half step (b/c & e/f is a half step)
|
||
tone - whole step
|
||
|
||
types:
|
||
harmonic interval - notes played simultaneously
|
||
melodic interval - notes played successively
|
||
|
||
makeup:
|
||
quantity - distance between two notes
|
||
quality - number of semitones between notes
|
||
|
||
qualities:
|
||
major/minor - 2nds, 3rds, 6ths, 7ths
|
||
perfect - 4ths, 5ths, octaves
|
||
diminished - minor/perfect - 1 semitone
|
||
augmented - major/perfect + 1 semitone'''
|
||
print('I N T E R V A L S'.center(38))
|
||
print('┌───────────┬────────────────┬───────┐')
|
||
print('│ semitones │ quality │ short │')
|
||
print('├───────────┼────────────────┼───────┤')
|
||
for interval, info in constants.intervals.items():
|
||
print('│ {0} │ {1} │ {2} │'.format(str(info['semitones']).rjust(9), interval.ljust(14), info['short_name'].ljust(5)))
|
||
print('└───────────┴────────────────┴───────┘')
|
||
print('\nC O M P O U N D I N T E R V A L S'.center(42))
|
||
print('┌───────────┬────────────────────┬───────┐')
|
||
print('│ semitones │ quality │ short │')
|
||
print('├───────────┼────────────────────┼───────┤')
|
||
for interval, info in constants.compound_intervals.items():
|
||
print('│ {0} │ {1} │ {2} │'.format(str(info['semitones']).rjust(9), interval.ljust(18), info['short_name'].ljust(5)))
|
||
print('└───────────┴────────────────────┴───────┘')
|
||
print(print_intervals.__doc__)
|
||
|
||
def print_scales():
|
||
'''definition:
|
||
any set of musical notes ordered by fundamental frequency or pitch
|
||
|
||
note:
|
||
1 - half step
|
||
2 - whole step
|
||
3 - whole step half step'''
|
||
print('S C A L E S'.center(43))
|
||
print('┌───────────────────────┬─────────────────┐')
|
||
print('│ name │ intervals │')
|
||
print('├───────────────────────┼─────────────────┤')
|
||
for name, pattern in constants.scales.items():
|
||
print(f'│ {name.ljust(21)} │ {get_pattern(pattern).rjust(15)} │')
|
||
print('└───────────────────────┴─────────────────┘')
|
||
print(print_scales.__doc__) |