chromatic_check.py (1596B)
1 #!/usr/bin/env python3 2 """Chromatic check — list all valid chords per degree for a given root/mode. 3 4 Usage: 5 python chromatic_check.py 7 misheberak 6 python chromatic_check.py 0 dorian 7 """ 8 9 import sys 10 import os 11 from pathlib import Path as _Path 12 sys.path.insert(0, str(_Path(__file__).resolve().parent.parent)) 13 14 from tools.parser import MODES, CHORDS 15 16 NOTES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] 17 18 19 def check(root: int, mode: str): 20 if mode not in MODES: 21 print(f"Unknown mode: {mode}") 22 print(f"Available: {', '.join(MODES.keys())}") 23 return 24 25 intervals = MODES[mode] 26 scale_set = set((root + i) % 12 for i in intervals) 27 28 print(f"Root: {NOTES[root]} | Mode: {mode}") 29 print(f"Scale: {' '.join(NOTES[(root + i) % 12] for i in intervals)}") 30 print() 31 32 for deg, offset in enumerate(intervals): 33 degree_root = (root + offset) % 12 34 valid = [] 35 for chord_name, chord_intervals in CHORDS.items(): 36 chord_notes = [(degree_root + ci) % 12 for ci in chord_intervals] 37 if all(n in scale_set for n in chord_notes): 38 valid.append(chord_name) 39 40 if valid: 41 print(f"- {deg}: {', '.join(valid)}") 42 else: 43 print(f"- {deg}: (no scale-pure chords)") 44 45 46 if __name__ == "__main__": 47 if len(sys.argv) < 3: 48 print("Usage: python chromatic_check.py <root_semitone> <mode>") 49 print(" root_semitone: 0=C, 1=C#, 2=D, ..., 7=G, ..., 11=B") 50 sys.exit(1) 51 52 root = int(sys.argv[1]) 53 mode = sys.argv[2] 54 check(root, mode)