Last active
March 8, 2018 03:53
-
-
Save rvzzz/1d8f28ede8a26aad68087a0a2e9cc294 to your computer and use it in GitHub Desktop.
script that generates random chords for you to practice... An efficient alternative for dice rolling...
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
#!/usr/bin/python3 | |
from random import choice | |
from termcolor import colored | |
from time import sleep as wait_a_few_seconds_between_chords | |
######################################################## | |
# Feel free to comment/uncomment any key and quality | |
# that you want (or not) to practice... | |
######################################################## | |
keys = [ | |
'C ' , | |
'Db' , | |
'D ' , | |
'Eb' , | |
'E ' , | |
'F ' , | |
'Gb' , | |
'G ' , | |
'Ab' , | |
'A ' , | |
'Bb' , | |
'B ' | |
] | |
qualities = [ | |
#################### | |
#? Triads | |
#################### | |
"Major ", | |
"minor ", | |
"diminished", | |
"Augmented ", | |
#################### | |
#? Sus2/Sus4 | |
#################### | |
# "Sus2 ", | |
# "Sus4 ", | |
# "Dom 7 Sus4", | |
#################### | |
#? 6ths | |
#################### | |
# "Major 6 ", | |
# "minor 6 ", | |
#################### | |
#? 7ths (Total of 8) | |
#################### | |
# "Major 7 ", | |
# "minor 7 ", | |
# "Dom 7 ", | |
# "min 7 b5 ", | |
# "dimini 7 ", | |
# "minMaj 7 ", | |
# "AugMaj 7 ", | |
# "Augmen 7 ", | |
#################### | |
#? 9ths | |
#################### | |
# "Major 9 ", | |
# "minor 9 ", | |
# "Dom 9 ", | |
#################### | |
#? 11ths | |
#################### | |
# "Dom 11 ", | |
#################### | |
#? 13ths | |
#################### | |
# "Dom 13 ", | |
########################## | |
#? Other Altered Chords | |
########################## | |
# "Major b5 ", # Ex: C(-5) | |
# "Dom 7 #5 ", # Ex: C7(+5) | |
# "Dom 7 b5 ", # Ex: C7(-5) | |
# "Major 7 b3b5 ", # Ex: CM7(b3b5) | |
# "Major 7 b3#5 ", # Ex: CM7(b3#5) | |
# "Dom 9 #5 ", # Ex: C9(+5) | |
# "Dom 9 b5 ", # Ex: C9(-5) | |
# "minor 9 #5 ", # Ex: Cm9(+5) | |
# "minor 9 b5 ", # Ex: Cm9(-5) | |
# "Dom 7 b9 ", # Ex: C7(b9) | |
# "Dom 11 b9 ", # Ex: C11(b9) | |
# "Dom 11 #5 ", # Ex: C11(#5) | |
# "Dom 13 b9 ", # Ex: C13(b9) | |
# "Dom 7 Add 6 ", # Ex: C7(6) | |
# "Major 6 Add 9", # Ex: C6(9) | |
] | |
positions = [ | |
"Root Position", | |
"1st Inversion", | |
"2nd Inversion", | |
"3rd Inversion" | |
] | |
# this variable will hold the generated quality to compare | |
# with the next one...at the beginning is "None" obviously! | |
previous_generated_chord_quality = None | |
# this list will hold all randomly generated chords | |
CHORDS_THAT_WERE_GENERATED_AND_PRACTICED = [] | |
######################################################## | |
# Functions (total of 6) | |
######################################################## | |
def _chord_position_highlight(generated_position: str) -> str: | |
if generated_position == "in 1st Inversion": | |
return "on_blue" | |
elif generated_position == "in 2nd Inversion": | |
return "on_red" | |
elif generated_position == "in 3rd Inversion": | |
return "on_green" | |
def _is_not_a_triad(generated_quality: str) -> bool: | |
check_4_note_chord = ["6", "7", "9", "11", "13"] | |
return any(degree in check_4_note_chord for degree in generated_quality) | |
def generate_and_print_random_chord() -> None: | |
key = choice(keys) | |
quality = choice(qualities) | |
######################################################## | |
# don't let qualities repeat one after the other | |
######################################################## | |
global previous_generated_chord_quality | |
while previous_generated_chord_quality == quality: | |
quality = choice(qualities) | |
previous_generated_chord_quality = quality | |
######################################################## | |
# Check if it's a 4 note chord or a triad and give | |
# the respective valide inversions for it. | |
######################################################## | |
if _is_not_a_triad(quality): | |
position = "in " + choice(positions) | |
# add chord for frequency analysis | |
CHORDS_THAT_WERE_GENERATED_AND_PRACTICED.append( | |
key + " " + quality + " " + position) | |
print(colored(key, "magenta"), | |
colored(quality, "magenta"), | |
colored(text=position, | |
color="white", | |
on_color=_chord_position_highlight(position), | |
attrs=["bold"])) | |
else: | |
# Since it's a Triad exclude 3rd Inversion with slice [:-1] | |
# And for now just practice Root Position for Augmented chords | |
# but don't specify any position | |
position = "in " + choice(positions[:-1]) \ | |
if quality != "Augmented " \ | |
else "" | |
# add chord for frequency analysis | |
CHORDS_THAT_WERE_GENERATED_AND_PRACTICED.append( | |
key + " " + quality + " " + (position if quality != "Augmented " else "in Root Position")) | |
print(colored(key, "yellow"), | |
colored(quality, "yellow"), | |
colored(text=position, | |
color="white", | |
on_color=_chord_position_highlight(position), | |
attrs=["bold"])) | |
def _chord_frequency_counter() -> None: | |
remove_duplicate_chords = list(set(CHORDS_THAT_WERE_GENERATED_AND_PRACTICED)) | |
remove_duplicate_chords.sort() | |
for each in remove_duplicate_chords: | |
print( colored(each[:14], "magenta") if _is_not_a_triad(each) else each[:14], _nicely_highlited(each[14:]) , ":", colored(CHORDS_THAT_WERE_GENERATED_AND_PRACTICED.count(each), "cyan")) | |
wait_a_few_seconds_between_chords(1) | |
def _nicely_highlited(each_chord_position: str) -> None: | |
return colored(text=each_chord_position, | |
color="white", | |
on_color=_chord_position_highlight(each_chord_position), | |
attrs=["bold"]) | |
def final_stats_practice_session_report() -> None: | |
################################################################# | |
# friendly message | |
################################################################# | |
print(colored("-" * 45, "cyan")) | |
print(colored("Nice...Time to rest!", "green")) | |
################################################################# | |
# overview stats | |
################################################################# | |
print(colored("-" * 45, "cyan")) | |
print(colored("Here is some stats of your practice session:", "red")) | |
print("You practiced", colored(round_counter, "red"), | |
"round!" if round_counter == 1 else "rounds!") | |
print("That equates to", colored(20 * round_counter, "red"), | |
"random chords practiced...") | |
print("And without redundant frequency:", | |
colored(len(set(CHORDS_THAT_WERE_GENERATED_AND_PRACTICED)), "red")) | |
print(colored("-" * 45, "cyan")) | |
wait_a_few_seconds_between_chords(9) | |
################################################################# | |
# chord frequency stats | |
################################################################# | |
print(colored("And here is the frequency of each chord:", "green")) | |
_chord_frequency_counter() | |
print() # just a final separator | |
######################################################## | |
print() # initial separator | |
######################################################## | |
round_counter = 1 | |
while "Practicing Chords": | |
# ROUND START ########################################################### | |
print(colored("*" * 10, "red"), "ROUND:", round_counter, colored("*" * 10, "red")) | |
######################################################################### | |
for _ in range(4): | |
print(colored("-" * 30, "cyan")) | |
for _ in range(5): | |
generate_and_print_random_chord() | |
wait_a_few_seconds_between_chords(7) | |
# ROUND END ############################################################ | |
print(colored("+" * 30, color="red", attrs=["blink"])) | |
######################################################################## | |
wait_between_rounds = str(input()) # click <return> to continue | |
# ctrl-D to kill the program | |
# or write some specific input in order to see the stats report | |
allowed_input_text_from_user_to_break_loop = ["q" , "quit", | |
"s" , "stop", "enough", | |
"tired" , "tired as fuck" ] | |
if allowed_input_text_from_user_to_break_loop.__contains__(wait_between_rounds.lower()): | |
break | |
round_counter+=1 | |
final_stats_practice_session_report() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment