2019-07-13 15:52:23 -07:00
|
|
|
|
#!/usr/bin/env python
|
2023-05-08 20:59:37 -07:00
|
|
|
|
# mzk music theory helper - developed by acidvegas in python (https://git.acid.vegas/mzk)
|
2019-07-13 15:52:23 -07:00
|
|
|
|
# functions.py
|
|
|
|
|
|
|
|
|
|
import constants
|
|
|
|
|
|
2023-05-08 20:59:37 -07:00
|
|
|
|
def chromatic_scale(key):
|
|
|
|
|
notes = list(constants.notes)
|
2019-07-13 15:52:23 -07:00
|
|
|
|
while notes[0] != key:
|
|
|
|
|
notes.append(notes.pop(0))
|
|
|
|
|
return notes
|
|
|
|
|
|
2023-05-08 20:59:37 -07:00
|
|
|
|
def generate_scale_string(string, scale_notes, full=False):
|
|
|
|
|
notes = chromatic_scale(string.upper())*2 if full else chromatic_scale(string.upper())
|
2019-07-24 21:58:26 -07:00
|
|
|
|
notes.append(notes[0])
|
2019-07-13 15:52:23 -07:00
|
|
|
|
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)
|
|
|
|
|
|
2023-05-18 17:29:41 -07:00
|
|
|
|
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
|
|
|
|
|
|
2024-01-20 17:48:05 -08:00
|
|
|
|
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]))
|
|
|
|
|
|
2023-05-08 20:59:37 -07:00
|
|
|
|
def scale_notes(type, key):
|
2019-07-13 15:52:23 -07:00
|
|
|
|
last = 0
|
2023-05-08 20:59:37 -07:00
|
|
|
|
all_notes = chromatic_scale(key)*2
|
|
|
|
|
scale_notes = [all_notes[0],]
|
2019-07-24 21:58:26 -07:00
|
|
|
|
for step in constants.scales[type]:
|
2019-07-13 15:52:23 -07:00
|
|
|
|
last += int(step)
|
2023-05-08 20:59:37 -07:00
|
|
|
|
scale_notes.append(all_notes[last])
|
2019-07-13 15:52:23 -07:00
|
|
|
|
return scale_notes
|
|
|
|
|
|
2023-05-08 20:59:37 -07:00
|
|
|
|
def print_chord(root, type):
|
2019-07-13 15:52:23 -07:00
|
|
|
|
# todo: finish this
|
|
|
|
|
print('◯ ⬤ ')
|
|
|
|
|
print('''╳ ╳ ╳ ╳ ╳ ╳
|
|
|
|
|
┌───┬───┬───┬───┬───┐
|
|
|
|
|
│ │ │ │ │ │
|
|
|
|
|
├───┼───┼───┼───┼───┤
|
|
|
|
|
│ │ │ │ │ │
|
|
|
|
|
├───┼───┼───┼───┼───┤
|
|
|
|
|
│ │ │ │ │ │
|
|
|
|
|
├───┼───┼───┼───┼───┤
|
|
|
|
|
│ │ │ │ │ │
|
|
|
|
|
├───┼───┼───┼───┼───┤
|
|
|
|
|
│ │ │ │ │ │
|
|
|
|
|
└───┴───┴───┴───┴───┘
|
2023-05-08 20:59:37 -07:00
|
|
|
|
E A D G B e''')
|
|
|
|
|
print('''╳ ◯ ◯
|
|
|
|
|
┌───┬───┬───┬───┬───┐
|
|
|
|
|
│ │ │ │ │ │
|
|
|
|
|
├───┼───┼───┼───⬤───┤
|
|
|
|
|
│ │ │ │ │ │
|
|
|
|
|
├───┼───⬤───┼───┼───┤
|
|
|
|
|
│ │ │ │ │ │
|
|
|
|
|
├───⬤───┼───┼───┼───┤
|
|
|
|
|
│ │ │ │ │ │
|
|
|
|
|
├───┼───┼───┼───┼───┤
|
|
|
|
|
│ │ │ │ │ │
|
|
|
|
|
└───┴───┴───┴───┴───┘
|
2019-07-13 15:52:23 -07:00
|
|
|
|
E A D G B e''')
|
|
|
|
|
|
2023-05-08 20:59:37 -07:00
|
|
|
|
|
2019-07-13 15:52:23 -07:00
|
|
|
|
def print_circle_of_fifths():
|
2019-07-24 21:58:26 -07:00
|
|
|
|
'''definition:
|
2019-07-13 15:52:23 -07:00
|
|
|
|
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
|
2019-07-24 21:58:26 -07:00
|
|
|
|
perfect fourth'''
|
2019-07-13 15:52:23 -07:00
|
|
|
|
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)
|
2019-07-24 21:58:26 -07:00
|
|
|
|
print(print_circle_of_fifths.__doc__)
|
2019-07-13 15:52:23 -07:00
|
|
|
|
|
2019-07-24 23:12:59 -07:00
|
|
|
|
def print_help():
|
2019-07-24 23:11:07 -07:00
|
|
|
|
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.')
|
|
|
|
|
|
2019-07-13 15:52:23 -07:00
|
|
|
|
def print_intervals():
|
2019-07-24 21:58:26 -07:00
|
|
|
|
'''definition:
|
2019-07-13 15:52:23 -07:00
|
|
|
|
the distance between two notes or pitches
|
|
|
|
|
|
|
|
|
|
note:
|
2019-07-24 21:58:26 -07:00
|
|
|
|
semitone - half step (b/c & e/f is a half step)
|
|
|
|
|
tone - whole step
|
2019-07-13 15:52:23 -07:00
|
|
|
|
|
|
|
|
|
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
|
2019-07-24 21:58:26 -07:00
|
|
|
|
augmented - major/perfect + 1 semitone'''
|
2019-07-24 23:11:07 -07:00
|
|
|
|
print('I N T E R V A L S'.center(38))
|
|
|
|
|
print('┌───────────┬────────────────┬───────┐')
|
|
|
|
|
print('│ semitones │ quality │ short │')
|
|
|
|
|
print('├───────────┼────────────────┼───────┤')
|
2019-07-13 15:52:23 -07:00
|
|
|
|
for interval, info in constants.intervals.items():
|
2019-07-24 23:11:07 -07:00
|
|
|
|
print('│ {0} │ {1} │ {2} │'.format(str(info['semitones']).rjust(9), interval.ljust(14), info['short_name'].ljust(5)))
|
|
|
|
|
print('└───────────┴────────────────┴───────┘')
|
2023-05-08 20:59:37 -07:00
|
|
|
|
print('\nC O M P O U N D I N T E R V A L S'.center(42))
|
2019-07-24 23:36:25 -07:00
|
|
|
|
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('└───────────┴────────────────────┴───────┘')
|
2023-05-08 20:59:37 -07:00
|
|
|
|
print(print_intervals.__doc__)
|
2019-07-13 15:52:23 -07:00
|
|
|
|
|
|
|
|
|
def print_scales():
|
2019-07-24 21:58:26 -07:00
|
|
|
|
'''definition:
|
2019-07-13 15:52:23 -07:00
|
|
|
|
any set of musical notes ordered by fundamental frequency or pitch
|
|
|
|
|
|
|
|
|
|
note:
|
|
|
|
|
1 - half step
|
|
|
|
|
2 - whole step
|
|
|
|
|
3 - whole step half step'''
|
2019-07-24 21:58:26 -07:00
|
|
|
|
print('S C A L E S'.center(43))
|
2019-07-13 15:52:23 -07:00
|
|
|
|
print('┌───────────────────────┬─────────────────┐')
|
|
|
|
|
print('│ name │ intervals │')
|
|
|
|
|
print('├───────────────────────┼─────────────────┤')
|
|
|
|
|
for name, pattern in constants.scales.items():
|
|
|
|
|
print(f'│ {name.ljust(21)} │ {get_pattern(pattern).rjust(15)} │')
|
|
|
|
|
print('└───────────────────────┴─────────────────┘')
|
2019-07-24 23:11:43 -07:00
|
|
|
|
print(print_scales.__doc__)
|