\version "2.22.0" \include "dodeka.ily" % ---------------------- Forte number dictionaries ------------------------------ forte-distinct-inverses = % Taken from https://en.wikipedia.org/wiki/List_of_pitch-class_sets #'((0-1 . ()) (1-1 . (0)) (2-1 . (0 1)) (2-2 . (0 2)) (2-3 . (0 3)) (2-4 . (0 4)) (2-5 . (0 5)) (2-6 . (0 6)) (3-1 . (0 1 2)) (3-2A . (0 1 3)) (3-2B . (0 2 3)) (3-3A . (0 1 4)) (3-3B . (0 3 4)) (3-4A . (0 1 5)) (3-4B . (0 4 5)) (3-5A . (0 1 6)) (3-5B . (0 5 6)) (3-6 . (0 2 4)) (3-7A . (0 2 5)) (3-7B . (0 3 5)) (3-8A . (0 2 6)) (3-8B . (0 4 6)) (3-9 . (0 2 7)) (3-10 . (0 3 6)) (3-11A . (0 3 7)) (3-11B . (0 4 7)) (3-12 . (0 4 8)) (4-1 . (0 1 2 3)) (4-2A . (0 1 2 4)) (4-2B . (0 2 3 4)) (4-3 . (0 1 3 4)) (4-4A . (0 1 2 5)) (4-4B . (0 3 4 5)) (4-5A . (0 1 2 6)) (4-5B . (0 4 5 6)) (4-6 . (0 1 2 7)) (4-7 . (0 1 4 5)) (4-8 . (0 1 5 6)) (4-9 . (0 1 6 7)) (4-10 . (0 2 3 5)) (4-11A . (0 1 3 5)) (4-11B . (0 2 4 5)) (4-12A . (0 2 3 6)) (4-12B . (0 3 4 6)) (4-13A . (0 1 3 6)) (4-13B . (0 3 5 6)) (4-14A . (0 2 3 7)) (4-14B . (0 4 5 7)) (4-z15A . (0 1 4 6)) (4-z15B . (0 2 5 6)) (4-16A . (0 1 5 7)) (4-16B . (0 2 6 7)) (4-17 . (0 3 4 7)) (4-18A . (0 1 4 7)) (4-18B . (0 3 6 7)) (4-19A . (0 1 4 8)) (4-19B . (0 3 4 8)) (4-20 . (0 1 5 8)) (4-21 . (0 2 4 6)) (4-22A . (0 2 4 7)) (4-22B . (0 3 5 7)) (4-23 . (0 2 5 7)) (4-24 . (0 2 4 8)) (4-25 . (0 2 6 8)) (4-26 . (0 3 5 8)) (4-27A . (0 2 5 8)) (4-27B . (0 3 6 8)) (4-28 . (0 3 6 9)) (4-z29A . (0 1 3 7)) (4-z29B . (0 4 6 7)) (5-1 . (0 1 2 3 4)) (5-2A . (0 1 2 3 5)) (5-2B . (0 2 3 4 5)) (5-3A . (0 1 2 4 5)) (5-3B . (0 1 3 4 5)) (5-4A . (0 1 2 3 6)) (5-4B . (0 3 4 5 6)) (5-5A . (0 1 2 3 7)) (5-5B . (0 4 5 6 7)) (5-6A . (0 1 2 5 6)) (5-6B . (0 1 4 5 6)) (5-7A . (0 1 2 6 7)) (5-7B . (0 1 5 6 7)) (5-8 . (0 2 3 4 6)) (5-9A . (0 1 2 4 6)) (5-9B . (0 2 4 5 6)) (5-10A . (0 1 3 4 6)) (5-10B . (0 2 3 5 6)) (5-11A . (0 2 3 4 7)) (5-11B . (0 3 4 5 7)) (5-z12 . (0 1 3 5 6)) (5-13A . (0 1 2 4 8)) (5-13B . (0 2 3 4 8)) (5-14A . (0 1 2 5 7)) (5-14B . (0 2 5 6 7)) (5-15 . (0 1 2 6 8)) (5-16A . (0 1 3 4 7)) (5-16B . (0 3 4 6 7)) (5-z17 . (0 1 3 4 8)) (5-z18A . (0 1 4 5 7)) (5-z18B . (0 2 3 6 7)) (5-19A . (0 1 3 6 7)) (5-19B . (0 1 4 6 7)) (5-20A . (0 1 5 6 8)) (5-20B . (0 2 3 7 8)) (5-21A . (0 1 4 5 8)) (5-21B . (0 3 4 7 8)) (5-22 . (0 1 4 7 8)) (5-23A . (0 2 3 5 7)) (5-23B . (0 2 4 5 7)) (5-24A . (0 1 3 5 7)) (5-24B . (0 2 4 6 7)) (5-25A . (0 2 3 5 8)) (5-25B . (0 3 5 6 8)) (5-26A . (0 2 4 5 8)) (5-26B . (0 3 4 6 8)) (5-27A . (0 1 3 5 8)) (5-27B . (0 3 5 7 8)) (5-28A . (0 2 3 6 8)) (5-28B . (0 2 5 6 8)) (5-29A . (0 1 3 6 8)) (5-29B . (0 2 5 7 8)) (5-30A . (0 1 4 6 8)) (5-30B . (0 2 4 7 8)) (5-31A . (0 1 3 6 9)) (5-31B . (0 2 3 6 9)) (5-32A . (0 1 4 6 9)) (5-32B . (0 3 5 8 9)) (5-33 . (0 2 4 6 8)) (5-34 . (0 2 4 6 9)) (5-35 . (0 2 4 7 9)) (5-z36A . (0 1 2 4 7)) (5-z36B . (0 3 5 6 7)) (5-z37 . (0 3 4 5 8)) (5-z38A . (0 1 2 5 8)) (5-z38B . (0 3 6 7 8)) (6-1 . (0 1 2 3 4 5)) (6-2A . (0 1 2 3 4 6)) (6-2B . (0 2 3 4 5 6)) (6-z3A . (0 1 2 3 5 6)) (6-z3B . (0 1 3 4 5 6)) (6-z4 . (0 1 2 4 5 6)) (6-5A . (0 1 2 3 6 7)) (6-5B . (0 1 4 5 6 7)) (6-z6 . (0 1 2 5 6 7)) (6-7 . (0 1 2 6 7 8)) (6-8 . (0 2 3 4 5 7)) (6-9A . (0 1 2 3 5 7)) (6-9B . (0 2 4 5 6 7)) (6-z10A . (0 1 3 4 5 7)) (6-z10B . (0 2 3 4 6 7)) (6-z11A . (0 1 2 4 5 7)) (6-z11B . (0 2 3 5 6 7)) (6-z12A . (0 1 2 4 6 7)) (6-z12B . (0 1 3 5 6 7)) (6-z13 . (0 1 3 4 6 7)) (6-14A . (0 1 3 4 5 8)) (6-14B . (0 3 4 5 7 8)) (6-15A . (0 1 2 4 5 8)) (6-15B . (0 3 4 6 7 8)) (6-16A . (0 1 4 5 6 8)) (6-16B . (0 2 3 4 7 8)) (6-z17A . (0 1 2 4 7 8)) (6-z17B . (0 1 4 6 7 8)) (6-18A . (0 1 2 5 7 8)) (6-18B . (0 1 3 6 7 8)) (6-z19A . (0 1 3 4 7 8)) (6-z19B . (0 1 4 5 7 8)) (6-20 . (0 1 4 5 8 9)) (6-21A . (0 2 3 4 6 8)) (6-21B . (0 2 4 5 6 8)) (6-22A . (0 1 2 4 6 8)) (6-22B . (0 2 4 6 7 8)) (6-z23 . (0 2 3 5 6 8)) (6-z24A . (0 1 3 4 6 8)) (6-z24B . (0 2 4 5 7 8)) (6-z25A . (0 1 3 5 6 8)) (6-z25B . (0 2 3 5 7 8)) (6-z26 . (0 1 3 5 7 8)) (6-27A . (0 1 3 4 6 9)) (6-27B . (0 3 5 6 8 9)) (6-z28 . (0 1 3 5 6 9)) (6-z29 . (0 2 3 6 7 9)) (6-30A . (0 1 3 6 7 9)) (6-30B . (0 2 3 6 8 9)) (6-31A . (0 1 4 5 7 9)) (6-31B . (0 2 4 5 8 9)) (6-32 . (0 2 4 5 7 9)) (6-33A . (0 2 3 5 7 9)) (6-33B . (0 2 4 6 7 9)) (6-34A . (0 1 3 5 7 9)) (6-34B . (0 2 4 6 8 9)) (6-35 . (0 2 4 6 8 10)) (6-z36A . (0 1 2 3 4 7)) (6-z36B . (0 3 4 5 6 7)) (6-z37 . (0 1 2 3 4 8)) (6-z38 . (0 1 2 3 7 8)) (6-z39A . (0 2 3 4 5 8)) (6-z39B . (0 3 4 5 6 8)) (6-z40A . (0 1 2 3 5 8)) (6-z40B . (0 3 5 6 7 8)) (6-z41A . (0 1 2 3 6 8)) (6-z41B . (0 2 5 6 7 8)) (6-z42 . (0 1 2 3 6 9)) (6-z43A . (0 1 2 5 6 8)) (6-z43B . (0 2 3 6 7 8)) (6-z44A . (0 1 2 5 6 9)) (6-z44B . (0 3 4 7 8 9)) (6-z45 . (0 2 3 4 6 9)) (6-z46A . (0 1 2 4 6 9)) (6-z46B . (0 3 5 7 8 9)) (6-z47A . (0 1 2 4 7 9)) (6-z47B . (0 2 5 7 8 9)) (6-z48 . (0 1 2 5 7 9)) (6-z49 . (0 1 3 4 7 9)) (6-z50 . (0 1 4 6 7 9)) (7-1 . (0 1 2 3 4 5 6)) (7-2A . (0 1 2 3 4 5 7)) (7-2B . (0 2 3 4 5 6 7)) (7-3A . (0 1 2 3 4 5 8)) (7-3B . (0 3 4 5 6 7 8)) (7-4A . (0 1 2 3 4 6 7)) (7-4B . (0 1 3 4 5 6 7)) (7-5A . (0 1 2 3 5 6 7)) (7-5B . (0 1 2 4 5 6 7)) (7-6A . (0 1 2 3 4 7 8)) (7-6B . (0 1 4 5 6 7 8)) (7-7A . (0 1 2 3 6 7 8)) (7-7B . (0 1 2 5 6 7 8)) (7-8 . (0 2 3 4 5 6 8)) (7-9A . (0 1 2 3 4 6 8)) (7-9B . (0 2 4 5 6 7 8)) (7-10A . (0 1 2 3 4 6 9)) (7-10B . (0 2 3 4 5 6 9)) (7-11A . (0 1 3 4 5 6 8)) (7-11B . (0 2 3 4 5 7 8)) (7-z12 . (0 1 2 3 4 7 9)) (7-13A . (0 1 2 4 5 6 8)) (7-13B . (0 2 3 4 6 7 8)) (7-14A . (0 1 2 3 5 7 8)) (7-14B . (0 1 3 5 6 7 8)) (7-15 . (0 1 2 4 6 7 8)) (7-16A . (0 1 2 3 5 6 9)) (7-16B . (0 1 3 4 5 6 9)) (7-z17 . (0 1 2 4 5 6 9)) (7-z18A . (0 1 4 5 6 7 9)) (7-z18B . (0 1 4 6 7 8 9)) (7-19A . (0 1 2 3 6 7 9)) (7-19B . (0 1 2 3 6 8 9)) (7-20A . (0 1 2 5 6 7 9)) (7-20B . (0 1 2 5 7 8 9)) (7-21A . (0 1 2 4 5 8 9)) (7-21B . (0 1 3 4 5 8 9)) (7-22 . (0 1 2 5 6 8 9)) (7-23A . (0 2 3 4 5 7 9)) (7-23B . (0 2 4 5 6 7 9)) (7-24A . (0 1 2 3 5 7 9)) (7-24B . (0 2 4 6 7 8 9)) (7-25A . (0 2 3 4 6 7 9)) (7-25B . (0 2 3 5 6 7 9)) (7-26A . (0 1 3 4 5 7 9)) (7-26B . (0 2 4 5 6 8 9)) (7-27A . (0 1 2 4 5 7 9)) (7-27B . (0 2 4 5 7 8 9)) (7-28A . (0 1 3 5 6 7 9)) (7-28B . (0 2 3 4 6 8 9)) (7-29A . (0 1 2 4 6 7 9)) (7-29B . (0 2 3 5 7 8 9)) (7-30A . (0 1 2 4 6 8 9)) (7-30B . (0 1 3 5 7 8 9)) (7-31A . (0 1 3 4 6 7 9)) (7-31B . (0 2 3 5 6 8 9)) (7-32A . (0 1 3 4 6 8 9)) (7-32B . (0 1 3 5 6 8 9)) (7-33 . (0 1 2 4 6 8 10)) (7-34 . (0 1 3 4 6 8 10)) (7-35 . (0 1 3 5 6 8 10)) (7-z36A . (0 1 2 3 5 6 8)) (7-z36B . (0 2 3 5 6 7 8)) (7-z37 . (0 1 3 4 5 7 8)) (7-z38A . (0 1 2 4 5 7 8)) (7-z38B . (0 1 3 4 6 7 8)) (8-1 . (0 1 2 3 4 5 6 7)) (8-2A . (0 1 2 3 4 5 6 8)) (8-2B . (0 2 3 4 5 6 7 8)) (8-3 . (0 1 2 3 4 5 6 9)) (8-4A . (0 1 2 3 4 5 7 8)) (8-4B . (0 1 3 4 5 6 7 8)) (8-5A . (0 1 2 3 4 6 7 8)) (8-5B . (0 1 2 4 5 6 7 8)) (8-6 . (0 1 2 3 5 6 7 8)) (8-7 . (0 1 2 3 4 5 8 9)) (8-8 . (0 1 2 3 4 7 8 9)) (8-9 . (0 1 2 3 6 7 8 9)) (8-10 . (0 2 3 4 5 6 7 9)) (8-11A . (0 1 2 3 4 5 7 9)) (8-11B . (0 2 4 5 6 7 8 9)) (8-12A . (0 1 3 4 5 6 7 9)) (8-12B . (0 2 3 4 5 6 8 9)) (8-13A . (0 1 2 3 4 6 7 9)) (8-13B . (0 2 3 5 6 7 8 9)) (8-14A . (0 1 2 4 5 6 7 9)) (8-14B . (0 2 3 4 5 7 8 9)) (8-z15A . (0 1 2 3 4 6 8 9)) (8-z15B . (0 1 3 5 6 7 8 9)) (8-16A . (0 1 2 3 5 7 8 9)) (8-16B . (0 1 2 4 6 7 8 9)) (8-17 . (0 1 3 4 5 6 8 9)) (8-18A . (0 1 2 3 5 6 8 9)) (8-18B . (0 1 3 4 6 7 8 9)) (8-19A . (0 1 2 4 5 6 8 9)) (8-19B . (0 1 3 4 5 7 8 9)) (8-20 . (0 1 2 4 5 7 8 9)) (8-21 . (0 1 2 3 4 6 8 10)) (8-22A . (0 1 2 3 5 6 8 10)) (8-22B . (0 1 2 3 5 7 9 10)) (8-23 . (0 1 2 3 5 7 8 10)) (8-24 . (0 1 2 4 5 6 8 10)) (8-25 . (0 1 2 4 6 7 8 10)) (8-26 . (0 1 3 4 5 7 8 10)) (8-27A . (0 1 2 4 5 7 8 10)) (8-27B . (0 1 2 4 6 7 9 10)) (8-28 . (0 1 3 4 6 7 9 10)) (8-z29A . (0 1 2 3 5 6 7 9)) (8-z29B . (0 2 3 4 6 7 8 9)) (9-1 . (0 1 2 3 4 5 6 7 8)) (9-2A . (0 1 2 3 4 5 6 7 9)) (9-2B . (0 2 3 4 5 6 7 8 9)) (9-3A . (0 1 2 3 4 5 6 8 9)) (9-3B . (0 1 3 4 5 6 7 8 9)) (9-4A . (0 1 2 3 4 5 7 8 9)) (9-4B . (0 1 2 4 5 6 7 8 9)) (9-5A . (0 1 2 3 4 6 7 8 9)) (9-5B . (0 1 2 3 5 6 7 8 9)) (9-6 . (0 1 2 3 4 5 6 8 10)) (9-7A . (0 1 2 3 4 5 7 8 10)) (9-7B . (0 1 2 3 4 5 7 9 10)) (9-8A . (0 1 2 3 4 6 7 8 10)) (9-8B . (0 1 2 3 4 6 8 9 10)) (9-9 . (0 1 2 3 5 6 7 8 10)) (9-10 . (0 1 2 3 4 6 7 9 10)) (9-11A . (0 1 2 3 5 6 7 9 10)) (9-11B . (0 1 2 3 5 6 8 9 10)) (9-12 . (0 1 2 4 5 6 8 9 10)) (10-1 . (0 1 2 3 4 5 6 7 8 9)) (10-2 . (0 1 2 3 4 5 6 7 8 10)) (10-3 . (0 1 2 3 4 5 6 7 9 10)) (10-4 . (0 1 2 3 4 5 6 8 9 10)) (10-5 . (0 1 2 3 4 5 7 8 9 10)) (10-6 . (0 1 2 3 4 6 7 8 9 10)) (11-1 . (0 1 2 3 4 5 6 7 8 9 10)) (12-1 . (0 1 2 3 4 5 6 7 8 9 10 11))) forte = #'((0-1 . ()) (1-1 . (0)) (2-1 . (0 1)) (2-2 . (0 2)) (2-3 . (0 3)) (2-4 . (0 4)) (2-5 . (0 5)) (2-6 . (0 6)) (3-1 . (0 1 2)) (3-10 . (0 3 6)) (3-11 . (0 3 7)) (3-11 . (0 4 7)) (3-12 . (0 4 8)) (3-2 . (0 1 3)) (3-2 . (0 2 3)) (3-3 . (0 1 4)) (3-3 . (0 3 4)) (3-4 . (0 1 5)) (3-4 . (0 4 5)) (3-5 . (0 1 6)) (3-5 . (0 5 6)) (3-6 . (0 2 4)) (3-7 . (0 2 5)) (3-7 . (0 3 5)) (3-8 . (0 2 6)) (3-8 . (0 4 6)) (3-9 . (0 2 7)) (4-1 . (0 1 2 3)) (4-10 . (0 2 3 5)) (4-11 . (0 1 3 5)) (4-11 . (0 2 4 5)) (4-12 . (0 2 3 6)) (4-12 . (0 3 4 6)) (4-13 . (0 1 3 6)) (4-13 . (0 3 5 6)) (4-14 . (0 2 3 7)) (4-14 . (0 4 5 7)) (4-16 . (0 1 5 7)) (4-16 . (0 2 6 7)) (4-17 . (0 3 4 7)) (4-18 . (0 1 4 7)) (4-18 . (0 3 6 7)) (4-19 . (0 1 4 8)) (4-19 . (0 3 4 8)) (4-20 . (0 1 5 8)) (4-21 . (0 2 4 6)) (4-22 . (0 2 4 7)) (4-22 . (0 3 5 7)) (4-23 . (0 2 5 7)) (4-24 . (0 2 4 8)) (4-25 . (0 2 6 8)) (4-26 . (0 3 5 8)) (4-27 . (0 2 5 8)) (4-27 . (0 3 6 8)) (4-28 . (0 3 6 9)) (4-2 . (0 1 2 4)) (4-2 . (0 2 3 4)) (4-3 . (0 1 3 4)) (4-4 . (0 1 2 5)) (4-4 . (0 3 4 5)) (4-5 . (0 1 2 6)) (4-5 . (0 4 5 6)) (4-6 . (0 1 2 7)) (4-7 . (0 1 4 5)) (4-8 . (0 1 5 6)) (4-9 . (0 1 6 7)) (4-z15 . (0 1 4 6)) (4-z15 . (0 2 5 6)) (4-z29 . (0 1 3 7)) (4-z29 . (0 4 6 7)) (5-1 . (0 1 2 3 4)) (5-10 . (0 1 3 4 6)) (5-10 . (0 2 3 5 6)) (5-11 . (0 2 3 4 7)) (5-11 . (0 3 4 5 7)) (5-13 . (0 1 2 4 8)) (5-13 . (0 2 3 4 8)) (5-14 . (0 1 2 5 7)) (5-14 . (0 2 5 6 7)) (5-15 . (0 1 2 6 8)) (5-16 . (0 1 3 4 7)) (5-16 . (0 3 4 6 7)) (5-19 . (0 1 3 6 7)) (5-19 . (0 1 4 6 7)) (5-20 . (0 1 5 6 8)) (5-20 . (0 2 3 7 8)) (5-21 . (0 1 4 5 8)) (5-21 . (0 3 4 7 8)) (5-22 . (0 1 4 7 8)) (5-23 . (0 2 3 5 7)) (5-23 . (0 2 4 5 7)) (5-24 . (0 1 3 5 7)) (5-24 . (0 2 4 6 7)) (5-25 . (0 2 3 5 8)) (5-25 . (0 3 5 6 8)) (5-26 . (0 2 4 5 8)) (5-26 . (0 3 4 6 8)) (5-27 . (0 1 3 5 8)) (5-27 . (0 3 5 7 8)) (5-28 . (0 2 3 6 8)) (5-28 . (0 2 5 6 8)) (5-29 . (0 1 3 6 8)) (5-29 . (0 2 5 7 8)) (5-2 . (0 1 2 3 5)) (5-2 . (0 2 3 4 5)) (5-30 . (0 1 4 6 8)) (5-30 . (0 2 4 7 8)) (5-31 . (0 1 3 6 9)) (5-31 . (0 2 3 6 9)) (5-32 . (0 1 4 6 9)) (5-32 . (0 3 5 8 9)) (5-33 . (0 2 4 6 8)) (5-34 . (0 2 4 6 9)) (5-35 . (0 2 4 7 9)) (5-3 . (0 1 2 4 5)) (5-3 . (0 1 3 4 5)) (5-4 . (0 1 2 3 6)) (5-4 . (0 3 4 5 6)) (5-5 . (0 1 2 3 7)) (5-5 . (0 4 5 6 7)) (5-6 . (0 1 2 5 6)) (5-6 . (0 1 4 5 6)) (5-7 . (0 1 2 6 7)) (5-7 . (0 1 5 6 7)) (5-8 . (0 2 3 4 6)) (5-9 . (0 1 2 4 6)) (5-9 . (0 2 4 5 6)) (5-z12 . (0 1 3 5 6)) (5-z17 . (0 1 3 4 8)) (5-z18 . (0 1 4 5 7)) (5-z18 . (0 2 3 6 7)) (5-z36 . (0 1 2 4 7)) (5-z36 . (0 3 5 6 7)) (5-z37 . (0 3 4 5 8)) (5-z38 . (0 1 2 5 8)) (5-z38 . (0 3 6 7 8)) (6-1 . (0 1 2 3 4 5)) (6-14 . (0 1 3 4 5 8)) (6-14 . (0 3 4 5 7 8)) (6-15 . (0 1 2 4 5 8)) (6-15 . (0 3 4 6 7 8)) (6-16 . (0 1 4 5 6 8)) (6-16 . (0 2 3 4 7 8)) (6-18 . (0 1 2 5 7 8)) (6-18 . (0 1 3 6 7 8)) (6-20 . (0 1 4 5 8 9)) (6-21 . (0 2 3 4 6 8)) (6-21 . (0 2 4 5 6 8)) (6-22 . (0 1 2 4 6 8)) (6-22 . (0 2 4 6 7 8)) (6-27 . (0 1 3 4 6 9)) (6-27 . (0 3 5 6 8 9)) (6-2 . (0 1 2 3 4 6)) (6-2 . (0 2 3 4 5 6)) (6-30 . (0 1 3 6 7 9)) (6-30 . (0 2 3 6 8 9)) (6-31 . (0 1 4 5 7 9)) (6-31 . (0 2 4 5 8 9)) (6-32 . (0 2 4 5 7 9)) (6-33 . (0 2 3 5 7 9)) (6-33 . (0 2 4 6 7 9)) (6-34 . (0 1 3 5 7 9)) (6-34 . (0 2 4 6 8 9)) (6-35 . (0 2 4 6 8 10)) (6-5 . (0 1 2 3 6 7)) (6-5 . (0 1 4 5 6 7)) (6-7 . (0 1 2 6 7 8)) (6-8 . (0 2 3 4 5 7)) (6-9 . (0 1 2 3 5 7)) (6-9 . (0 2 4 5 6 7)) (6-z10 . (0 1 3 4 5 7)) (6-z10 . (0 2 3 4 6 7)) (6-z11 . (0 1 2 4 5 7)) (6-z11 . (0 2 3 5 6 7)) (6-z12 . (0 1 2 4 6 7)) (6-z12 . (0 1 3 5 6 7)) (6-z13 . (0 1 3 4 6 7)) (6-z17 . (0 1 2 4 7 8)) (6-z17 . (0 1 4 6 7 8)) (6-z19 . (0 1 3 4 7 8)) (6-z19 . (0 1 4 5 7 8)) (6-z23 . (0 2 3 5 6 8)) (6-z24 . (0 1 3 4 6 8)) (6-z24 . (0 2 4 5 7 8)) (6-z25 . (0 1 3 5 6 8)) (6-z25 . (0 2 3 5 7 8)) (6-z26 . (0 1 3 5 7 8)) (6-z28 . (0 1 3 5 6 9)) (6-z29 . (0 2 3 6 7 9)) (6-z36 . (0 1 2 3 4 7)) (6-z36 . (0 3 4 5 6 7)) (6-z37 . (0 1 2 3 4 8)) (6-z38 . (0 1 2 3 7 8)) (6-z39 . (0 2 3 4 5 8)) (6-z39 . (0 3 4 5 6 8)) (6-z3 . (0 1 2 3 5 6)) (6-z3 . (0 1 3 4 5 6)) (6-z4 . (0 1 2 4 5 6)) (6-z40 . (0 1 2 3 5 8)) (6-z40 . (0 3 5 6 7 8)) (6-z41 . (0 1 2 3 6 8)) (6-z41 . (0 2 5 6 7 8)) (6-z42 . (0 1 2 3 6 9)) (6-z43 . (0 1 2 5 6 8)) (6-z43 . (0 2 3 6 7 8)) (6-z44 . (0 1 2 5 6 9)) (6-z44 . (0 3 4 7 8 9)) (6-z45 . (0 2 3 4 6 9)) (6-z46 . (0 1 2 4 6 9)) (6-z46 . (0 3 5 7 8 9)) (6-z47 . (0 1 2 4 7 9)) (6-z47 . (0 2 5 7 8 9)) (6-z48 . (0 1 2 5 7 9)) (6-z49 . (0 1 3 4 7 9)) (6-z50 . (0 1 4 6 7 9)) (6-z6 . (0 1 2 5 6 7)) (7-1 . (0 1 2 3 4 5 6)) (7-10 . (0 1 2 3 4 6 9)) (7-10 . (0 2 3 4 5 6 9)) (7-11 . (0 1 3 4 5 6 8)) (7-11 . (0 2 3 4 5 7 8)) (7-13 . (0 1 2 4 5 6 8)) (7-13 . (0 2 3 4 6 7 8)) (7-14 . (0 1 2 3 5 7 8)) (7-14 . (0 1 3 5 6 7 8)) (7-15 . (0 1 2 4 6 7 8)) (7-16 . (0 1 2 3 5 6 9)) (7-16 . (0 1 3 4 5 6 9)) (7-19 . (0 1 2 3 6 7 9)) (7-19 . (0 1 2 3 6 8 9)) (7-20 . (0 1 2 5 6 7 9)) (7-20 . (0 1 2 5 7 8 9)) (7-21 . (0 1 2 4 5 8 9)) (7-21 . (0 1 3 4 5 8 9)) (7-22 . (0 1 2 5 6 8 9)) (7-23 . (0 2 3 4 5 7 9)) (7-23 . (0 2 4 5 6 7 9)) (7-24 . (0 1 2 3 5 7 9)) (7-24 . (0 2 4 6 7 8 9)) (7-25 . (0 2 3 4 6 7 9)) (7-25 . (0 2 3 5 6 7 9)) (7-26 . (0 1 3 4 5 7 9)) (7-26 . (0 2 4 5 6 8 9)) (7-27 . (0 1 2 4 5 7 9)) (7-27 . (0 2 4 5 7 8 9)) (7-28 . (0 1 3 5 6 7 9)) (7-28 . (0 2 3 4 6 8 9)) (7-29 . (0 1 2 4 6 7 9)) (7-29 . (0 2 3 5 7 8 9)) (7-2 . (0 1 2 3 4 5 7)) (7-2 . (0 2 3 4 5 6 7)) (7-30 . (0 1 2 4 6 8 9)) (7-30 . (0 1 3 5 7 8 9)) (7-31 . (0 1 3 4 6 7 9)) (7-31 . (0 2 3 5 6 8 9)) (7-32 . (0 1 3 4 6 8 9)) (7-32 . (0 1 3 5 6 8 9)) (7-33 . (0 1 2 4 6 8 10)) (7-34 . (0 1 3 4 6 8 10)) (7-35 . (0 1 3 5 6 8 10)) (7-3 . (0 1 2 3 4 5 8)) (7-3 . (0 3 4 5 6 7 8)) (7-4 . (0 1 2 3 4 6 7)) (7-4 . (0 1 3 4 5 6 7)) (7-5 . (0 1 2 3 5 6 7)) (7-5 . (0 1 2 4 5 6 7)) (7-6 . (0 1 2 3 4 7 8)) (7-6 . (0 1 4 5 6 7 8)) (7-7 . (0 1 2 3 6 7 8)) (7-7 . (0 1 2 5 6 7 8)) (7-8 . (0 2 3 4 5 6 8)) (7-9 . (0 1 2 3 4 6 8)) (7-9 . (0 2 4 5 6 7 8)) (7-z12 . (0 1 2 3 4 7 9)) (7-z17 . (0 1 2 4 5 6 9)) (7-z18 . (0 1 4 5 6 7 9)) (7-z18 . (0 1 4 6 7 8 9)) (7-z36 . (0 1 2 3 5 6 8)) (7-z36 . (0 2 3 5 6 7 8)) (7-z37 . (0 1 3 4 5 7 8)) (7-z38 . (0 1 2 4 5 7 8)) (7-z38 . (0 1 3 4 6 7 8)) (8-1 . (0 1 2 3 4 5 6 7)) (8-10 . (0 2 3 4 5 6 7 9)) (8-11 . (0 1 2 3 4 5 7 9)) (8-11 . (0 2 4 5 6 7 8 9)) (8-12 . (0 1 3 4 5 6 7 9)) (8-12 . (0 2 3 4 5 6 8 9)) (8-13 . (0 1 2 3 4 6 7 9)) (8-13 . (0 2 3 5 6 7 8 9)) (8-14 . (0 1 2 4 5 6 7 9)) (8-14 . (0 2 3 4 5 7 8 9)) (8-16 . (0 1 2 3 5 7 8 9)) (8-16 . (0 1 2 4 6 7 8 9)) (8-17 . (0 1 3 4 5 6 8 9)) (8-18 . (0 1 2 3 5 6 8 9)) (8-18 . (0 1 3 4 6 7 8 9)) (8-19 . (0 1 2 4 5 6 8 9)) (8-19 . (0 1 3 4 5 7 8 9)) (8-20 . (0 1 2 4 5 7 8 9)) (8-21 . (0 1 2 3 4 6 8 10)) (8-22 . (0 1 2 3 5 6 8 10)) (8-22 . (0 1 2 3 5 7 9 10)) (8-23 . (0 1 2 3 5 7 8 10)) (8-24 . (0 1 2 4 5 6 8 10)) (8-25 . (0 1 2 4 6 7 8 10)) (8-26 . (0 1 3 4 5 7 8 10)) (8-27 . (0 1 2 4 5 7 8 10)) (8-27 . (0 1 2 4 6 7 9 10)) (8-28 . (0 1 3 4 6 7 9 10)) (8-2 . (0 1 2 3 4 5 6 8)) (8-2 . (0 2 3 4 5 6 7 8)) (8-3 . (0 1 2 3 4 5 6 9)) (8-4 . (0 1 2 3 4 5 7 8)) (8-4 . (0 1 3 4 5 6 7 8)) (8-5 . (0 1 2 3 4 6 7 8)) (8-5 . (0 1 2 4 5 6 7 8)) (8-6 . (0 1 2 3 5 6 7 8)) (8-7 . (0 1 2 3 4 5 8 9)) (8-8 . (0 1 2 3 4 7 8 9)) (8-9 . (0 1 2 3 6 7 8 9)) (8-z15 . (0 1 2 3 4 6 8 9)) (8-z15 . (0 1 3 5 6 7 8 9)) (8-z29 . (0 1 2 3 5 6 7 9)) (8-z29 . (0 2 3 4 6 7 8 9)) (9-1 . (0 1 2 3 4 5 6 7 8)) (9-10 . (0 1 2 3 4 6 7 9 10)) (9-11 . (0 1 2 3 5 6 7 9 10)) (9-11 . (0 1 2 3 5 6 8 9 10)) (9-12 . (0 1 2 4 5 6 8 9 10)) (9-2 . (0 1 2 3 4 5 6 7 9)) (9-2 . (0 2 3 4 5 6 7 8 9)) (9-3 . (0 1 2 3 4 5 6 8 9)) (9-3 . (0 1 3 4 5 6 7 8 9)) (9-4 . (0 1 2 3 4 5 7 8 9)) (9-4 . (0 1 2 4 5 6 7 8 9)) (9-5 . (0 1 2 3 4 6 7 8 9)) (9-5 . (0 1 2 3 5 6 7 8 9)) (9-6 . (0 1 2 3 4 5 6 8 10)) (9-7 . (0 1 2 3 4 5 7 8 10)) (9-7 . (0 1 2 3 4 5 7 9 10)) (9-8 . (0 1 2 3 4 6 7 8 10)) (9-8 . (0 1 2 3 4 6 8 9 10)) (9-9 . (0 1 2 3 5 6 7 8 10)) (10-1 . (0 1 2 3 4 5 6 7 8 9)) (10-2 . (0 1 2 3 4 5 6 7 8 10)) (10-3 . (0 1 2 3 4 5 6 7 9 10)) (10-4 . (0 1 2 3 4 5 6 8 9 10)) (10-5 . (0 1 2 3 4 5 7 8 9 10)) (10-6 . (0 1 2 3 4 6 7 8 9 10)) (11-1 . (0 1 2 3 4 5 6 7 8 9 10)) (12-1 . (0 1 2 3 4 5 6 7 8 9 10 11))) % Setup of forte-dictionary as a vector of alists in which the % keys are normal/prime forms of PC sets. #(define forte-dictionary (make-vector 13)) #(define (set-pc-style all-sets) (for-each (lambda (n) (let ((all-n-sets (filter (lambda (entry) (eq? (length (cdr entry)) n)) all-sets))) (vector-set! forte-dictionary n (map (lambda (number-set-pair) (cons (cdr number-set-pair) (symbol->string (car number-set-pair)))) all-n-sets)))) (iota 13))) #(set-pc-style forte) % ---------------------- General tools ---------------------------------------- #(define (assoc-list alist keys) "Return the first entry in alist whose for the first possible key in keys. Keys are compared using equal?" ; "Vertical" searching (first choose a key and use assoc for this key) seems to be faster ; than "horizontal" searching. (if (pair? keys) (let* ((first-key (car keys)) (other-keys (cdr keys)) (instance (assoc first-key alist))) (if instance instance (assoc-list alist other-keys))) #f)) #(define* (comma-separate-strings lst #:optional (separator ",")) (if (pair? lst) (if (pair? (cdr lst)) (string-append (car lst) separator (comma-separate-strings (cdr lst) separator)) (car lst)) "")) #(define (group-classes lst equivalent?) ; groups list in sublists of equivalent elements. ; equivalent? is assumed to be an equivalence relation ; TODO: Keep order (at the moment, reversed list is returned) ; (Solution: Wrap in let loop?) (if (pair? lst) (let* ((head (car lst)) (tail-groups (group-classes (cdr lst) equivalent?)) (group-of-head (list-index (lambda (group) (equivalent? (car group) head)) tail-groups))) (if group-of-head (begin (list-set! tail-groups group-of-head (cons head (list-ref tail-groups group-of-head))) tail-groups) (cons (list head) tail-groups))) '())) #(define (all-numbers from to) (iota (1+ (abs (- to from))) (min from to))) #(define (sort-with-valuation lst val less) ; val is a valuation function on lst ; returns sorted list of pairs (x . (val x)) ; The sort is stable. (stable-sort! (map (lambda (x) (cons x (val x))) lst) (lambda (p q) (less (cdr p) (cdr q))))) #(define (sort-by-valuation lst val less) (map car (sort-with-valuation lst val less))) #(define (print-and-return val) (pretty-print val) val) #(define (range set) (- (last set) (first set))) #(define (set-find-pattern set pattern) ; given a pattern (a b ...), return the first sublist ; of set starting with an element x such that ; x+a, x+b etc. are also in set. ; set is assumed to be sorted! ; Returns #f (not empty list) if pattern is not found! (if (pair? set) (if (every (lambda (k) (member (+ (car set) k) set)) pattern) set (set-find-pattern (cdr set) pattern)) #f)) #(define (set-replace! set old new) ; replaces old by new in set. ; set is assumed to be (strictly) sorted and will be sorted! ; may operate destructively, but is NOT guaranteed to ; modify set in-place as desired! ; TODO: At the moment, no check is new is already in set! (let ((sublist (member old set))) (if (pair? sublist) (begin (list-set! sublist 0 new) (sort! set <)) set))) #(define (mark-duplicates els) ; given a list of elements, produces a same-length list of ; booleans indicating, for each element of the given list; ; if it has a twin in the list. ; e.g. input '(1 2 3 4 1 5 6 3) ; output '(#t #f #t #f #t #f #f #t) (if (pair? els) (let* ((head (car els)) (tail (cdr els)) (tail-duplicates (mark-duplicates tail)) (twin-in-tail (list-index (lambda (n) (eq? n head)) tail))) (if twin-in-tail (begin (list-set! tail-duplicates twin-in-tail #t) (cons #t tail-duplicates)) (cons #f tail-duplicates))) '())) #(define (lexicographic less) ; should less be assumed to be strictly less or less-or-equal? (lambda (x y) (cond ((null? x) #t) ((null? y) #f) ((equal? (car x) (car y)) ; which notion of equality should be used? ((lexicographic less) (cdr x) (cdr y))) (else (less (car x) (car y)))))) #(define (lexicographic-by-size less) ; should less be assumed to be strictly less or less-or-equal? (lambda (x y) (let* ((length-x (length x)) (length-y (length y))) (if (eq? length-x length-y) (if (null? x) #t ; arbitrary, might also be #f? (if (equal? (car x) (car y)) ; which notion of equality should be used? ((lexicographic-by-size less) (cdr x) (cdr y)) (less (car x) (car y)))) (< length-x length-y))))) #(define (strictly-sorted? numlist) (cond ((null? numlist) #t) ((null? (cdr numlist)) #t) (else (and (< (car numlist) (cadr numlist)) (strictly-sorted? (cdr numlist)))))) #(define (vector-increase-at! pos vec) (vector-set! vec pos (1+ (vector-ref vec pos))) vec) % ---------------------- The chromatic_clash_engraver ---------------------------------- % This engraver, to be used in voice or staff contexts, forces the use of % accidentals if diatonic clashes (such as c + cis) occur. chromatic_clash_engraver = #(lambda (ctx) ; writing the engraver as a context-dependent lambda ; makes sure that the notes are collected per-staff ; if the engraver is \consist'ed to all \Staff contexts. (let ((note-events '())) (make-engraver (listeners ((note-event engraver event) (set! note-events (cons event note-events)))) ((process-music translator) (let* ((pitches (map (lambda (ev) (ly:event-property ev 'pitch)) note-events)) (pitch-steps (map ly:pitch-steps pitches)) (clashes (mark-duplicates pitch-steps))) (for-each (lambda (ev clash?) (if clash? (ly:event-set-property! ev 'force-accidental #t))) note-events clashes)) (set! note-events '()))))) % ---------------------- Basic PC set definitions ----------------------------- % "Chromatic pitches" are integers where 0 = middle c. % A PC-set shall always be % a) be sorted, % b) made up of elements {0,...,11}, % c) without duplicates. % normalize-pc-set guarantees a) and b). % make-pc-set guarantees a)-c). % PC sets made by make-pc-set are guaranteed to fulfill a)-c). % A PC-set is _not_ assumed to always be rooted in 0. % (i.e. a PC-set is an ordered subset of Z/12, but without % identification of translates). #(define (pitch-class? n) (and (index? n) (< n 12))) #(define (pc-set? set) (and (every pitch-class? set) (strictly-sorted? set))) % May be used instead of make-pc-set if input set is guaranteed % not to contain duplicate pitch classes #(define (normalize-pc-set set) (sort (map (lambda (p) (modulo p 12)) set) <)) #(define (make-pc-set notes) (delete-duplicates (normalize-pc-set notes))) % ---------------------- Elementary PC set operations -------------------------- #(define (set+ . sets) ; robust (make-pc-set (apply append sets))) #(define (set- set-a set-b) ; assumes conditions a)-c) (filter (lambda (n) (not (memq n set-b))) set-a)) #(define chromatic-total (iota 12)) #(define (set-complement set) ; assumes conditions a)-c) (filter (lambda (n) (not (memq n set))) chromatic-total)) % all-subsets does not produce a natural ordering of subsets. % Maybe lexicographic-by-size? #(define (all-subsets set) (if (null? set) (list set) (let ((head (car set)) (subsets-without-head (all-subsets (cdr set)))) (append (map (lambda (subset) (cons head subset)) subsets-without-head) subsets-without-head)))) #(define (all-supersets set) (map (lambda (summand) (set+ set summand)) (all-subsets (set-complement set)))) % ---------------------- Interval vector operations ---------------------------- #(define (interval-class p1 p2) ; Calculates the intervall class in {0,...,6} between two chromatic pitches (let ((diff (modulo (- p1 p2) 12))) (if (> diff 6) (- 12 diff) diff))) #(define (interval-vector set) (if (pair? set) (let* ((head (car set)) (tail (cdr set)) (work-vector (interval-vector tail))) (for-each (lambda (k) (vector-increase-at! (1- (interval-class head k)) work-vector)) tail) work-vector) (make-vector 6 0))) #(define (format-interval-vector vect) (string-append "<" (string-concatenate (map number->string (vector->list vect))) ">")) #(define (format-interval-vector-with-comma vect) (string-append "<" (comma-separate-strings (map number->string (vector->list vect))) ">")) % ---------------------- Unsorted operations ----------------------------------- #(define (zerobase-pc-set set) (map (lambda (p) (- p (car set))) set)) #(define (rotate-pc-set set) ; TODO: Does not accept empty set at the moment. (if (and (pair? set) ; test if set is non-empty ... (pair? (cdr set))) ; ... and contains more than one element. (normalize-pc-set (map (lambda (p) (- p (cadr set))) set)) set)) #(define (all-rotations set) ; generates list of all rotations of pitch class set, each zero-based. (if (null? set) (list set) (fold (lambda (n lst) (cons (rotate-pc-set (car lst)) lst)) ; ignore counter, add rotated version of list-head to list (list (zerobase-pc-set set)) ; start with zerobased-version of set (iota (1- (length set)))))) #(define (normal-forms set) ; Returns list of normal forms, i.e. rotations with minimal overall interval range ; expects a zero-based set! ; The list is unsorted (whereas a recursive lexicocraphic sorting might produce some kind of prime form as well). ; TODO: This function works with last's a lot; it might be more efficient to produce reversed rotations and work with car's. (let* ((rotations (all-rotations set)) (ranges (map (lambda (rotation) (last rotation)) rotations)) (minimum-range (apply min ranges))) (filter (lambda (rotation) (= (last rotation) minimum-range)) rotations))) % What's the difference between sort and sort-list? #(define (recurse-sets add-length base-part proc) ; In this function, pc-sets are ordered right-to-left for recursion. ; A right-hand base-part is amended at the start ; If recursion is complete, proc is called for the completed pc sets. (if (zero? add-length) (proc (reverse base-part)) (let ((new-minimum (1+ (car base-part)))) (for-each (lambda (n) (recurse-sets (1- add-length) (cons n base-part) proc)) (iota (- 12 new-minimum) new-minimum))))) #(define (for-each-zerobased-pc-set proc n) (if (positive? n) (recurse-sets (1- n) '(0) proc) (proc '()))) #(define (set-equivalent? set-a set-b) ; tests transposition equivalence ; expects sets with condition a), b) ; returns empty list as #f value (member (zerobase-pc-set set-b) (all-rotations set-a))) #(define (set-invert set) (zerobase-pc-set (normalize-pc-set (map - set)))) #(define (set-transpose set k) (normalize-pc-set (map (lambda (p) (+ p k)) set))) #(define (prime-form set) ; This is not the actual prime form in case of ambiguity! (car (sort (normal-forms set) (lexicographic <)))) #(define (lookup-primeform-fortenumber set) (let* ((rotations (all-rotations (make-pc-set set))) (dictionary (vector-ref forte-dictionary (length (car rotations))))) (assoc-list dictionary rotations))) #(define (lookup-fortenumber set) (cdr (lookup-primeform-fortenumber set))) #(define (lookup-primeform set) (car (lookup-primeform-fortenumber set))) #(define (format-set set) (string-append "[" (comma-separate-strings (map number->string set)) "]")) #(define (format-hex-set set) (string-upcase (string-concatenate (map (lambda (x) (number->string x 16)) set)))) #(define (generate-js-dictionary port) (define* (format-js-variable-assignment variable-value) (let ((variable (car variable-value)) (value (cdr variable-value))) (if (list? value) (string-append (format #f " ~s: [\n" variable) (comma-separate-strings (map (lambda (entry) (format #f " ~s" entry)) value) ",\n") (format #f "\n ]")) (format #f " ~s: ~s" variable value)))) (define (format-js-variable-assignments variable-value-list) (display (comma-separate-strings (map format-js-variable-assignment variable-value-list) ",\n") port)) (define (hex-prime-form set) (format-hex-set (lookup-primeform set))) (format port "export default {\n") (map (lambda (n) (map (lambda (primeform-fortename) (let* ((primeform (car primeform-fortename)) (fortename (cdr primeform-fortename)) (subsets (delete-duplicates (map hex-prime-form (all-subsets primeform)))) (supersets (delete-duplicates (map hex-prime-form (all-supersets primeform))))) (format port " ~s: {\n" (format-hex-set primeform)) (format-js-variable-assignments (list (cons "forteName" fortename) (cons "complement" (lookup-fortenumber (set-complement primeform))) (cons "intervalVector" (format-interval-vector-with-comma (interval-vector (car primeform-fortename)))) (cons "subSets" subsets) (cons "superSets" supersets))) (format port "\n },\n"))) (vector-ref forte-dictionary n))) (iota 13)) (format port "};\n") (ly:warning "Don't forget to remove last comma in .js output!")) % ---------------------- LilyPond interface ------------------------------------ forteNumber = #(define-scheme-function (set) (pc-set?) (lookup-fortenumber set)) tablePrimeForm = #(define-scheme-function (set) (pc-set?) (markup (format-set (car (lookup-primeform-fortenumber set))))) addChordForteNumber = #(define-music-function (mus) (ly:music?) (if (music-is-of-type? mus 'event-chord) #{ $mus - \forteNumber \PCset #mus #} mus)) addChordPrimeForm = #(define-music-function (mus) (ly:music?) (if (music-is-of-type? mus 'event-chord) #{ << $mus \context Lyrics = "pf" \lyricmode { \markup { \rotate #30 \tiny \typewriter \tablePrimeForm \PCset #mus } } >> #} mus)) addChordPrimeFormAndIntervalVector = #(define-music-function (mus) (ly:music?) (if (music-is-of-type? mus 'event-chord) (let ((set (PCset mus))) #{ << $mus \context Lyrics = "pf" \lyricmode { \markup { \rotate #30 \teeny \bold \typewriter \tablePrimeForm #set } } \context Lyrics = "iv" \lyricmode { \markup { \rotate #30 \teeny \typewriter #(format-interval-vector (interval-vector set)) } } >> #}) mus)) PCset = #(define-scheme-function (notes) (ly:music?) (make-pc-set (map ly:pitch-semitones (music-pitches notes)))) % ---------------------- Circle of fifths tools -------------------------------- % PC set = set of chromatic pitch classes in Z/12Z (c = 0, c# = 1 etc.) % CFPC set = circle of fifth chromatic pitch classes in Z/12Z (d = 0, a = 1 etc.) % SFPC set = stack of fifth diatonic pitch classes in Z (d = 0) % % C/SFPC pitch classes are centered on 0 = d since that way % the white keys are % { f = -3, c = -2, g = -1, d = 0, a = 1, e = 2, b = 3 } #(define cfpc-set? pc-set?) % Internally, both sets are represented by (wlog ordered) sets in Z/12Z #(define (sfpc-set? set) (and (every integer? set) (strictly-sorted? set))) #(define make-cfpc-set make-pc-set) #(define (pc-set->cfpc-set set) (make-cfpc-set (map (lambda (p) (* (- p 2) 7)) set))) #(define (cfpc-set->pc-set set) (make-pc-set (map (lambda (p) (+ (* p 7) 2)) set))) #(define (accidental-count sfpc) ; sfpc is a stack-of-fifth class, 0 = d. (round (/ sfpc 7))) #(define (accidental-weight n) ; 0 accidentals: weight 0 ; 1 accidental: weight 3 ; 2 accidentals: weight 15 (1- (ash 1 (* 2 (abs n))))) #(define (sfpc-set-accidental-weight set) (apply + (map (lambda (k) (accidental-weight (accidental-count k))) set))) #(define (all-sfpc-liftings-of-cfpc-set set) ; expects a chromatic fifths set, sorted, elements in 0..11. ; the resulting list is sorted by range (last - first) (sort (fold (lambda (n prev) (cons (cdr (append (car prev) (list (+ 12 (caar prev))))) prev)) (list set) (iota (1- (length set)))) (lambda (p q) (< (range p) (range q))))) #(define (cfpc-set->sfpc-set set) ; Lifts a CFPC set to a SFPC set. ; The lifted set is guaranteed to map to cfpc-set via Z -> Z/12Z ; The lifting is chosen as follows: ; - First, a lifting with least possible fifth ranges is chosen ; - Then, we try to eliminate doubly-chromatic clashes ; - Lastly, we move by multiples of 12 in order to center around 0 ; as far as possible (let* ((lifted-set (car (all-sfpc-liftings-of-cfpc-set set)))) ; (range (last lifted-set)) ; (shift (truncate (/ range 2)))) (do ((i 1 (1+ i))) ((> i 3)) ; Three rounds of optimization seem to suffice ... ; TODO: The following four operations repeat code in a bad way... ; Turn (c cis d) into (his cis d), i.e. (0 2 7) into (2 7 12) (let ((c-cis-d (set-find-pattern lifted-set '(2 7)))) (if c-cis-d (set! lifted-set (set-replace! lifted-set (car c-cis-d) (+ 12 (car c-cis-d)))))) ; Turn (h c cis) into (h c des), i.e. (0 5 7) into (0 5 -5) (let ((c-h-cis (set-find-pattern lifted-set '(5 7)))) (if c-h-cis (set! lifted-set (set-replace! lifted-set (+ 7 (car c-h-cis)) (+ -5 (car c-h-cis)))))) ; Turn (c cis dis) into (his cis dis), i.e. (0 7 9) into (7 9 12) (let ((c-cis-dis (set-find-pattern lifted-set '(7 9)))) (if c-cis-dis (set! lifted-set (set-replace! lifted-set (car c-cis-dis) (+ 12 (car c-cis-dis)))))) ; Turn (c d dis) into (c d es), i.e. (0 2 9) into (0 2 3) (let ((c-d-dis (set-find-pattern lifted-set '(2 9)))) (if c-d-dis (set! lifted-set (set-replace! lifted-set (+ 9 (car c-d-dis)) (+ -3 (car c-d-dis)))))) ) ; end of stupid loop to do 4 passes of optimization (let* ((mean (/ (+ (first lifted-set) (last lifted-set)) 2)) (correction (* 12 (round (/ mean 12))))) (map (lambda (p) (- p correction)) lifted-set)))) #(define (whitecenter-sfpc-set set) ; transposes an sfpc set in such a way that we prefer white ; keys and make double accidentals expensive. ; Idea: Given a sorted sfpc-set (a b ... z), ; left-most candidate arises by shifting by z to the left, ; right-most candidate arises by shifting by a to the left. ; Hence: Add all indices from (-z, ..., -a) (first (sort-by-valuation (map (lambda (shift) (map (lambda (x) (- x shift)) set)) (all-numbers (first set) (last set))) sfpc-set-accidental-weight <))) #(define (sfpc->pitch p) (let* ((mittle-d (ly:make-pitch 0 1)) (fifth-up (ly:make-pitch 0 4)) (fifth-down (ly:make-pitch -1 3)) (step (if (negative? p) fifth-down fifth-up))) (car (fold (lambda (n lst) (cons (ly:pitch-transpose (car lst) step) lst)) (list mittle-d) (iota (abs p)))))) chordFromCFPCset = #(define-music-function (set) (cfpc-set?) #{ % Should add a function to sanitize chords % in order to remove f-fis-clashes. < $@(map normalize-pitch (map sfpc->pitch (whitecenter-sfpc-set (cfpc-set->sfpc-set set)))) > #}) niceChordFromPCset = #(define-music-function (set) (pc-set?) (chordFromCFPCset (pc-set->cfpc-set set))) % ---------------------- Tests and examples ------------------------------------ %{ #(map (lambda (entry) (let ((prime-form (car entry)) (number (cdr entry))) (format #t "~a ~a\n" number (lookup-fortenumber (set-complement prime-form))))) (vector-ref forte-dictionary 5)) %} #(set-pc-style forte-distinct-inverses) %{ \fixed c' { \musicMap #addChordForteNumber { \textLengthOn \accidentalStyle modern \time 1/4 \omit Stem \omit Score.TimeSignature \omit Score.BarLine \override TextScript.baseline-skip = 2.4 ^Molldreiklang ^Durdreiklang ^\markup\column{übermäßiger Dreiklang} ^\markup\column{verminderter Dreiklang} ^"sus2" ^"sus4" ^Doppelquarte ^\markup\column{Viennese Trichord} ^\markup\column{Viennese Trichord?} % % % % \once\undo\omit Score.BarLine \bar "." ^"Halbverminderter" ^"Dominantseptakkord" ^"moll7" ^"Verminderter" ^"maj7" ^"add9" ^"m-add9" \once\undo\omit Score.BarLine \bar "." } } %} PCsetChord = #(define-music-function (set) (pc-set?) #{ < $@(map (lambda (n) (make-music 'NoteEvent 'duration (ly:make-duration 2) 'pitch (normalize-and-respell-pitch (ly:make-pitch 0 0 (/ n 2))))) set) > #} ) \paper { system-system-spacing.padding = 5 } \layout { \context { \Staff \consists \chromatic_clash_engraver } \accidentalStyle modern \textLengthOn \omit Score.TimeSignature \override TextScript.direction = #UP \override TextScript.self-alignment-X = #CENTER \override TextScript.parent-alignment-X = #CENTER \override TextScript.X-align-on-main-noteheads = ##t \override TextScript.font-size = -1 \omit Score.BarNumber } %{ \new Staff { \time 1/4 $@ (map addChordForteNumber (map addChordPrimeForm (map niceChordFromPCset (map car (vector-ref forte-dictionary 5))))) } %} #(define (list-of-pc-set-tuples n) ; Yields the list of all n-sets in the current forte-dictionary ; in groups of set, inverse and (if n = 6) complement. (group-classes (map car (vector-ref forte-dictionary n)) (if (equal? n 6) (lambda (set-a set-b) (or (set-equivalent? set-a set-b) (set-equivalent? set-a (set-invert set-b)) (set-equivalent? set-a (set-complement set-b)) (set-equivalent? set-a (set-complement (set-invert set-b))))) (lambda (set-a set-b) (or (set-equivalent? set-a set-b) (set-equivalent? set-a (set-invert set-b))))))) allSets = #(define-void-function (n) (index?) (add-score #{ \score { \layout { indent = 0 ragged-last = ##t \override Lyrics.VerticalAxisGroup.nonstaff-nonstaff-spacing.padding = 0.7 \override Lyrics.VerticalAxisGroup.nonstaff-nonstaff-spacing.minimum-distance = 0 } \new Staff { \set Timing.defaultBarType = "" \textLengthOn \time 1/4 $@(map (lambda (list-of-sets) #{ $@ (map addChordPrimeFormAndIntervalVector (map addChordForteNumber (map niceChordFromPCset list-of-sets))) \bar "|" #}) (list-of-pc-set-tuples n)) } } #} )) %{ "027": { "rahnPrimeForm": "027", "fortePrimeForm": "027", "intervalVector": "010020", "forteName": "3-9", "cardinality": 3, "zMate": null } %} #(format #t "~a ~a\n" (get-internal-real-time) (get-internal-run-time)) #(let ((port (open-output-file "list.js"))) (generate-js-dictionary port) (close-port port)) #(format #t "~a ~a\n" (get-internal-real-time) (get-internal-run-time)) %{ \book { \paper { page-breaking = #ly:one-page-breaking score-system-spacing.padding = 15 } \header { tagline = ##f } #(for-each allSets (iota 12 1)) } %}