fluidstudio

Programmable music notation — write music as text, compile to MIDI + WAV stems + mix
Log | Files | Refs | README

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)