some time ago I had the idea to write a kind of harmony analyzer. It works in the following manner: you enter some notes (myMusik) and you get a list of chords which contain these notes.
I had no idea of Scheme, so I studies several manuals, and Harm from the German Lilypond forum helped me a lot but still I am sometimes lost in parentheses. The code as it is now is working and I am very glad about that. But there is much room for improvement, and the chordNameExceptions don't work, so I ask for your help
Basically I would prefer to enter a list of chords like I do in Lilypond, such as c:7 c:m7 c:maj7 ... and the program should do the rest, create list etc. My scheme knowledge is not good enough for that.
Please excuse the German comments.
Hi altogether,
some time ago I had the idea to write a kind of harmony analyzer. It works in the following manner: you enter some notes and you get a list of chords which contain these notes.
I had no idea of Scheme, so I studies several manuals, and Harm from the German Lilypond forum helped me a lot but still I am sometimes lost in parentheses. The code as it is now is working and I am very glad about that. But there is much room for improvement, and the chordNameExceptions don't work, so I ask for your help
Basically I would prefer to enter a list of chords like I do in Lilypond, such as c:7 c:m7 c:maj7 ... and the program should do the rest, create list etc. My scheme knowledge is not good enough for that.
Please excuse the German comments.
Here is the code:
\version "2.19.37"
\language "deutsch"
myMusik= < c' e' >1_"Input"
Tonleitern=< c des d es e f fis ges g as a b h >
chExceptionMusic = {
<c es ges>1-\markup { \super "dim" }
<c e gis>1-\markup { \super "maj" }
<c e geses>1-\markup { \super "ddim" }
<c eses ges >1-\markup { \super "dddim" }
<c e g h>1-\markup { \super "maj7" }
<c es g h>1-\markup { "m" \super "maj7" }
<c es ges h>1-\markup { "m" \super { "maj7" \flat "5" } }
<c e ges h>1-\markup { \super { "maj7" \flat "5" } }
<c es ges heses>1-\markup { \super "dim7" }
<c e g h d'>1-\markup { \super "maj9" }
<c e g b d' f a' >1-\markup { \super "13" }
%<c e g d' >1-\markup { \super "add9" }
<c e g b des' as' >1-\markup { \super { \flat "9" \flat 13 } }
<c e g b d' a' >1-\markup { \super "13" }
<c e g a d'>1-\markup { \super "6(add9)" }
}
% Convert music to list and prepend to existing exceptions.
chExceptions = #( append
( sequential-music-to-chord-exceptions chExceptionMusic #t)
ignatzekExceptions)
chExceptions = #( append
( sequential-music-to-chord-exceptions chExceptionMusic #t)
ignatzekExceptions)
ChordI = \chordmode { \germanChords
\set chordNameExceptions = #chExceptions c:1 }
ChordII = \chordmode { \germanChords
\set chordNameExceptions = #chExceptions c:2 }
ChordIII = \chordmode { \germanChords
\set chordNameExceptions = #chExceptions c:3 }
ChordIV = \chordmode { \germanChords
\set chordNameExceptions = #chExceptions c:4 }
ChordV = \chordmode { \germanChords
\set chordNameExceptions = #chExceptions c:5 }
ChordVI = \chordmode { \germanChords
\set chordNameExceptions = #chExceptions c:6 }
ChordVII = \chordmode { \germanChords
\set chordNameExceptions = #chExceptions c:7 }
ChordVIII = \chordmode { \germanChords
\set chordNameExceptions = #chExceptions c:8 }
#(define MusLis (list
ChordI
ChordII
ChordIII
ChordIV
ChordV
ChordVI
ChordVII
ChordVIII
))
#(define (pitch-equals? p1 p2)
(and
(= (ly:pitch-alteration p1) (ly:pitch-alteration p2))
(= (ly:pitch-notename p1) (ly:pitch-notename p2))))
#(define myInput (sort (music-pitches myMusik) ly:pitch<?) )
#(define mk-pitch
;; wir erschaffen eine Pitchliste aus der Musikliste
(lambda (ls)
(cond
;; wir haben fertig, wenn Liste leer ist
((null? ls) '())
;; wenn das Listenelement ein gültiger Musikausdruck ist
;; dann fügen wir die sortierte Pitchliste als Listenelement hinzu
((ly:music? (car ls))
(cons (sort (music-pitches (car ls) ) ly:pitch<?)
;(cons (car l2) (vvv l1 (cdr l2)))
(mk-pitch (cdr ls)))
)
;; ansonsten machen wir mit dem Rest der Liste weiter
(else (mk-pitch (cdr ls)))
)
)
)
#(define is-in?
;; wir versuchen zu eruieren
;; ob alle Elemente der Liste l1 in l2 enthalten sind
;; genauer gesagt müssen nur notename und alteration stimmen
;; die Oktavlage interessiert uns nicht
(lambda ( l1 l2 )
( cond
;; zunächst prüfen wir, ob die Testliste bereits erschöpft ist
;; wenn ja dann ist es wahr
((null? l1) #t
)
;; wenn die Zielliste erschöpft ist
;; ist es falsch
((null? l2) #f
)
;; wir überprüfen das erste Element der Testliste
;; wenn es gleich dem ersten Element der Zielliste ist
;; dann können wir mit dem restlichen Teil der Testliste fortsetzen
((pitch-equals? (car l1) (car l2))
(is-in? (cdr l1) l2)
;'equalPitch
)
;; ansonsten versuchen wir es mit dem folgenden Element der Zielliste
(else (is-in? l1 (cdr l2))
)
)
)
)
#(begin
(define (ist-in? l1 l2)
(every (lambda (x) (->bool (member x l2 pitch-equals?))) l1)
)
)
#(define www
;; wir wollen jetzt die Musikausdrücke in eine Liste hängen
;; l1 ist unsere Testliste
;; l2 enthält die Pitches
;; l3 enthält die Musik (in diesem Fall die Akkorde bzw. Akkordnamen
(lambda (l1 l2 l3)
( cond
;; wenn die Akkordliste leer ist, dann verabschieden wir uns
((null? l2) '()
)
;; wenn die Testliste leer ist, dann ist es auch aus
((null? l1) '() )
;; wenn die Noten der Testliste im ersten Akkord enthalten sind,
;; wird der Akkord hinzugefügt
((is-in? l1 (car l2))
(cons (car l3) (www l1 (cdr l2) (cdr l3)))
)
;; wenn die Noten von l1 nicht im ersten Akkort enthalten sind
;; machen wir mit der restlichen Akkordliste weiter
(else (www l1 (cdr l2) (cdr l3))
)
)
)
)
%% Liste der transponierten AKkorde
%% in zwei Stufen
%% die innere Funktion durchläuft die Akkordliste
%% die äußere die Tonleiterliste
%% das hat den Sinn, dass ich die Liste der Akkorde insgesamt transponieren will
%% und nicht jeden Akkord in alle Tonarten und dann den nächsten Akkord etc.
#(define ( Akkorde-in-allen-Tonleitern Pitch Musik)
(append-map
(lambda (p)
(map
(lambda (music)
#{ { \transpose c $p $music } #})
Musik
)
)
(event-chord-pitches Pitch)
)
)
#(define MusikListe (Akkorde-in-allen-Tonleitern Tonleitern MusLis))
#(define PitLis (mk-pitch MusikListe))
#(define Ergebnis (www myInput PitLis MusikListe )) % just for interest
#(display (length Ergebnis))
\score {
\new Staff
<<
\new Voice $(make-sequential-music (append (list myMusik) Ergebnis))
\new ChordNames {
s1
$(make-sequential-music Ergebnis)
}
>>
}