Deutsches Lilypond Forum (Archiv)
Allgemein => Fragen zu Funktionen => Thema gestartet von: Manuela am Donnerstag, 24. März 2016, 10:27
-
Hi,
ich kämpfe noch immer :-[
Ich möchte eine Notenfolge automatisch in verschiedene Tonlagen transponieren
Die Funktion TuWasO liefert den Output für eine Transponierung, for-each liefert zwar keine Fehlermeldung, bewirkt jedoch auch keine Ausgabe.
Wahrscheinlich geht das sowieso viel eleganter und einfacher...
\version "2.18.2"
myMusik= \relative c' { e c d e f }
myPitch = des
#(begin
(define TuWas
(lambda Musik Pitch
(define-music-function (parser location Musik Pitch)
(ly:music? ly:pitch?)
#{ \transpose c $Pitch { $Musik } #}
)
"Blödsinn"
)
)
(define TuWasO
(define-music-function (parser location Musik Pitch)
(ly:music? ly:pitch?)
#{ \transpose c $Pitch { $Musik } #}
)
)
(define PitchListe
(list
(ly:make-pitch 0 0 NATURAL)
(ly:make-pitch 0 1 NATURAL)
(ly:make-pitch 0 2 NATURAL)
)
)
(define n-Musik (make-list 3 myMusik))
(for-each TuWas n-Musik PitchListe)
)
\TuWasO \myMusik \myPitch
-
Hallo Manuela,
zunächst die Frage nach dem Ziel der potenziellen Funktion.
Soll die finale Ausgabe äquivalent
mus = \relative c' { c4 d e2 }
\new Staff
{
\transpose c cis \mus
\transpose c d \mus
\transpose c dis \mus
}
sein oder eher
mus = \relative c' { c4 d e2 }
\new Staff \transpose c cis \mus
\new Staff \transpose c d \mus
\new Staff \transpose c dis \mus
?
Zu `for-each' hier ein paar kommentierte Beispiele:
%% Zu jeden Element der Liste ls wird 1 addiert
%% _und_ neue Liste wird ausgegeben.
#(display
(let ((ls '(1 2 3)))
(map 1+ ls)))
%% Zu jeden Element der Liste ls wird 1 addiert
%% Sonst passiert nichts
%% Die Ausgabe ist #<unspecified>
#(display
(let ((ls '(1 2 3)))
(for-each 1+ ls)))
%% (1) `mus' wird lokal definiert indem die Noten c, e und g aus einem
%% Akkord ausgelesen werden
%% (2) Dann läuft eine procedure ab, die in das 'tweak-property eine Frabe
%% setzt.
%% Allerdings wird in der procedure `ly:music-set-property!' verwendet,
%% es setzt das property aber dessen Ausgabe ist #<unspecified>
%% Insoweit ist die Gesamtausgabe:
%% (list #<unspecified>
%% #<unspecified>
%% #<unspecified>)
%% Hier sollte man `for-each' verwenden nicht `map'
#(display-scheme-music
(let ((mus (event-chord-notes #{ <c e g> #})))
(map
(lambda (m col)
(ly:music-set-property! m 'tweaks (list (cons 'color col))))
mus
(list red green cyan))))
%% Selbst wenn man `mus' nachher ausgibt, so hat man doch erst eine Liste
%% erstellt und diese dann weggeworfen.
%% Schlechter Programmierstil!!
%% Besser:
#(display-scheme-music
(let ((mus (event-chord-notes #{ <c e g> #})))
(for-each
(lambda (m col)
(ly:music-set-property! m 'tweaks (list (cons 'color col))))
mus
(list red green cyan))
mus))
%% Um das Ergebnis auch gedruckt zu sehen, muß man es in sequentielle Musik
%% überführen:
$(make-sequential-music
(let ((mus (event-chord-notes #{ <c e g> #})))
(for-each
(lambda (m col)
(ly:music-set-property! m 'tweaks (list (cons 'color col))))
mus
(list red green cyan))
mus))
Zu Deinem Code:
(1)
Es ist nicht nötig alles in einen Ausdruck (begin ...) zu packen.
(2)
Insbesondere music-functions kann man auf toplevel-niveau definieren, d.h.
myFunction = #(define-music-function ...)
(3)
(lambda Musik Pitch ...)
ist tatsächlich "Blödsinn"
entweder
(lambda (Musik Pitch) ...) als eine procedure mit zwei Argumenten
oder
(lambda Musik ...) als `named-let', also eine rekursive Geschichte. Ich glaube aber nicht, daß Du das willst ...
(4)
(define PitchListe
(list
(ly:make-pitch 0 0 NATURAL)
(ly:make-pitch 0 1 NATURAL)
(ly:make-pitch 0 2 NATURAL)
)
)
läßt sich einfacher definieren mit der LilyPond-eigenen procedure `event-chord-pitches'
(define PitchListe (event-chord-pitches #{ <c' d' e'> #}))
Soviel fürs erste.
Ich denke es ist Dir nicht geholfen, wenn ich eine Lösung für das Problem poste.
Insoweit wie weit soll ich gehen mit Verbesserungsvorschlägen?
Viel Erfolg,
Harm
-
Ich klinke mich mal ein, weil ich das Thema interessant finde und selbst auf keine Lösung komme … Vorausgesetzt, es ist so gemeint, dass mehrere Scores erzeugt werden sollen.
%% Um das Ergebnis auch gedruckt zu sehen, muß man es in sequentielle Musik
%% überführen:
$(make-sequential-music
Warum braucht man hier $ und nicht #? Ich kenn die Verwendung und Bedeutung der beiden innerhalb von Musikfunktionen, aber irgendwie wirds mir hier nicht ganz klar.
(define PitchListe (event-chord-pitches #{ <c' d' e'> #}))
Interessant, das war mir neu. Aber vielleicht gut zu wissen in anderen Anwendungsfällen: Auch einzelne pitches muss man nicht mit (ly:make-pitch 0 0 NATURAL) ausschreiben, sondern kann die einfach als #{ c' #} schreiben (da braucht mann dann keine weitere Funktion, event-chord-pitches ist nur einfach dafür da, die pitches aus dem Akkord zu extrahieren).
Eigentlich lässt sich die Sache auch ohne die ganzen Funktionen TuWas und TuWasO hinschreiben. (Nachdem harm da nichts gesagt hat, halte ich mich auch mal zurück mit Code, sind aber ca. 5 Zeilen, wenn man mal die pitch-Liste als gegeben nimmt.) Man kriegt dann aber eine Liste von Scores und da bin auch ich überfragt: Wie krieg ich denn diese Scores jetzt ausgegeben? Hab mit make-bookpart und make-book rumprobiert, aber 1. glaub ich kaum, dass es so kompliziert sein muss und 2. hab ich das nicht auf die Reihe gekriegt …
-
(lambda Musik Pitch ...)
ist tatsächlich "Blödsinn"
entweder
(lambda (Musik Pitch) ...) als eine procedure mit zwei Argumenten
oder
(lambda Musik ...) als `named-let', also eine rekursive Geschichte. Ich glaube aber nicht, daß Du das willst ...
(4)
(define PitchListe
(list
(ly:make-pitch 0 0 NATURAL)
(ly:make-pitch 0 1 NATURAL)
(ly:make-pitch 0 2 NATURAL)
)
)
läßt sich einfacher definieren mit der LilyPond-eigenen procedure `event-chord-pitches'
(define PitchListe (event-chord-pitches #{ <c' d' e'> #}))
Soviel fürs erste.
Ich denke es ist Dir nicht geholfen, wenn ich eine Lösung für das Problem poste.
Insoweit wie weit soll ich gehen mit Verbesserungsvorschlägen?
Viel Erfolg,
Harm
Hallo Harm,
du einsames Liliypond- und Scheme-Genie (wenn es einen Smiley "Thumbs up" oder "Klatschen" gäbe, würde ich ihn jetzt einfügen) ,
danke für deine Vorschläge und Anregungen, die mir wieder Stoff zum Nachdenken geben :D
Tatsächlich wäre mir mit einer Lösung schon sehr geholfen, ich bezwecke nämlich etwas ganz bestimmtes damit.
Trotz ausgiebigen Studiums diverser Scheme-Handbücher und Tutorials tue ich mir immer noch unglaublich schwer mit der Klammernsetzung, und dann geht sie wieder daneben wie in meinem Beispiel. Natürlich wollte ich eine Prozedur mit 2 Variablen haben.
-
Könntest du harms erste Frage beantworten? Davon hängt ab, ob ich ne Lösung hätte ;)
-
Könntest du harms erste Frage beantworten? Davon hängt ab, ob ich ne Lösung hätte ;)
O.k. klar :D
es sollen verschiedene Scores werden
Im Idealfall würde ich auch noch Text innerhalb der Ausgabe abhängig von der verwendeten Tonart gestalten (wofür ich keine andere Lösung gefunden habe)
-
Hm, dann weiß ich leider nichts, da müssen wir wohl auf harm warten ;)
Was für ein Text denn genau? Bzw. wie soll der von der Tonart abhängen?
-
Tatsächlich wäre mir mit einer Lösung schon sehr geholfen
Hier so einiges:
\version "2.18.2"
\markup "Basic, one Staff"
myMusik = \relative c' { e c d e }
%% Alles in einen Staff, basic :)
\new Staff
$(make-sequential-music
(map
(lambda (p)
#{ { \transpose c $p \myMusik } #})
(event-chord-pitches #{ < f g > #})))
\markup "All new Scores"
%% Jeweils neue Scores:
%% Das ist 2.18.-code, für neuere lily-versionen muß `parser' gelöscht werden
%% `add-score' und `scorify-music' finden sich in lily-library.scm
%% `add-score' Kommentar:
%% ;; Add a score to the current bookpart, book or toplevel
%% `scorify-music' verwendet `ly:make-score', doku dafür:
%% "Return score with music encapsulated in it."
%% Man ist jetzt natürlich komplett auf scheme-level
%% Neue Elemente müssen dann auch via scheme eingeführt werden
%% Dazu gibt es einige weitere Funktionen in lily-library.scm
#(for-each
(lambda (p)
(add-score parser
(scorify-music #{ { \transpose c $p \myMusik } #} parser)))
(event-chord-pitches #{ < f g > #}))
\markup "Sophisticated `multipleTransposes'"
%% Thanks David Kastrup
multipleTransposes =
#(define-music-function (parser location m music)(ly:music? ly:music?)
(music-clone m
'elements
(map (lambda (pitch)
(ly:music-property #{ \transpose c $pitch $music #} 'element))
(event-chord-pitches m))))
%% Examples
\new Staff
\multipleTransposes { c cis } \relative c'' { g a }
\new StaffGroup
\multipleTransposes << c cis >> \relative c'' \new Staff { g a }
\new Staff
\multipleTransposes <c cis> g''!
Im Idealfall würde ich auch noch Text innerhalb der Ausgabe abhängig von der verwendeten Tonart gestalten
Was für Text?
Als \header? mark? markup? (im Staff oder toplevel?)
Gruß,
Harm
-
Hier so einiges:
Was für Text?
Als \header? mark? markup? (im Staff oder toplevel?)
Gruß,
Harm
Hui, das geht ja in Windeseile :)
Da hätte ich mich nicht tagelang plagen müssen...
Markup täte es, glaube ich.
Ganz herzlichen Dank :-*
Ich sehe gerade, das ist so sophisticated, da wäre ich ohnehin chancenlos gewesen...
-
Warum braucht man hier $ und nicht #?
#(make-sequential-music (list #{ c'1 d' #}))
ist ein scheme-Ausdruck, der erst noch Musik in einem .ly-file werden muß.
Das kann auf verschieden Weise passieren. Z.B.:
foo =
#(make-sequential-music (list #{ c'1 d' #}))
\new Staff \foo
Auch
\displayMusic
#(make-sequential-music (list #{ c'1 d' #}))
reicht schon, da \displayMusic auch die Musik ausgibt.
Aber die vielleicht simpelste Methode ist $ zu verwenden
$(make-sequential-music (list #{ c'1 d' #}))
HTH,
Harm
-
\version "2.18.2"
\markup "Basic, one Staff"
myMusik = \relative c' { e c d e }
%% Alles in einen Staff, basic :)
\new Staff
$(make-sequential-music
(map
(lambda (p)
#{ { \transpose c $p \myMusik } #})
(event-chord-pitches #{ < f g > #})))
\markup "All new Scores"
%% Jeweils neue Scores:
%% Das ist 2.18.-code, für neuere lily-versionen muß `parser' gelöscht werden
%% `add-score' und `scorify-music' finden sich in lily-library.scm
%% `add-score' Kommentar:
%% ;; Add a score to the current bookpart, book or toplevel
%% `scorify-music' verwendet `ly:make-score', doku dafür:
%% "Return score with music encapsulated in it."
%% Man ist jetzt natürlich komplett auf scheme-level
%% Neue Elemente müssen dann auch via scheme eingeführt werden
%% Dazu gibt es einige weitere Funktionen in lily-library.scm
#(for-each
(lambda (p)
(add-score parser
(scorify-music #{ { \transpose c $p \myMusik } #} parser)))
(event-chord-pitches #{ < f g > #}))
\markup "Sophisticated `multipleTransposes'"
%% Thanks David Kastrup
multipleTransposes =
#(define-music-function (parser location m music)(ly:music? ly:music?)
(music-clone m
'elements
(map (lambda (pitch)
(ly:music-property #{ \transpose c $pitch $music #} 'element))
(event-chord-pitches m))))
%% Examples
\new Staff
\multipleTransposes { c cis } \relative c'' { g a }
\new StaffGroup
\multipleTransposes << c cis >> \relative c'' \new Staff { g a }
\new Staff
\multipleTransposes <c cis> g''!
Wow. Woher weiß man sowas? Da hilft ja nichtmal die Internals Reference, hast du einfach den kompletten Code gelesen und intus? music-clone und add-score hab ich vorher noch nie gesehn.
Und eine Frage hab ich noch: Wie funktioniert multipleTransposes? Scheint ja 'elements von nem EventChord, SimultaneousMusic oder SequentialMusic zu nehmen und jedes Element dieser Liste durch
(ly:music-property [mach was mit dem einzelnen pitch und dem Muster] 'element)zu ersetzen. Ich versteh nicht ganz, warum man da dieses 'element erstmal rausholt. Geht doch auch ohne bzw. warum funktioniert es überhaupt mit?
Das dritte Beispiel brauchts anscheinend, aber das kapier ich auch überhaupt nicht (weder, warum es überhaupt geht, noch, warum es nur mit diesem Trick geht) …
Edit: Danke für deine Erklärung zum Thema $. Ich glaub, ich habs verstanden :)
-
Wieso finde ich event-chord-pitches nicht in der Internals-Referenz?
Könnte man damit folgendes machen: eine Liste von Akkorden, wobei jeder Akkord durch eine Liste von Pitches dargestellt wird.
Und eine Funktion, die als Argument eine Liste von Pitches hat und alle Akkorde aus obiger Liste auswirft, in denen die Pitches des Arguments vorkommen.
Das sollte wahrscheinlich nicht allzu schwierig ist, aber ich verrenne mich bestimmt wieder irgendwie.
-
Wow. Woher weiß man sowas? Da hilft ja nichtmal die Internals Reference
Da kann ich mich nur anschließen :D
-
Markup täte es, glaube ich.
\version "2.18.2"
%% Mit hinzugefügtem toplevel-markup
#(for-each
(lambda (p)
(begin
(add-text parser
(format #f
"tranposed to ~a"
(cadr (string-split (format #f "~a" p) #\sp))))
(add-score parser
(scorify-music #{ { \transpose c $p \myMusik } #} parser))))
(event-chord-pitches #{ < f g > #}))
HTH,
Harm
P.S.
Für die anderen Fragen, ich brauch mal ne' Pause ;)
-
Für die anderen Fragen, ich brauch mal ne' Pause ;)
Die hast du dir verdient :) Aber schön, wie viel Neues man hier lernt ;)
-
Für die anderen Fragen, ich brauch mal ne' Pause ;)
Die hast du dir mehr als verdient :D
Nur um zu illustrieren, worum es mir geht. Das folgende Gebilde würde ich gerne transponieren, und es wäre schön, wenn die Beschriftungen, die jetzt hart verdrahtet sind, auch mittransponiert würden.
Aber lass dir bitte Zeit, ich drehe jetzt auch gleich den Blechtrottel ab und widme mich meinem Piano.
\version "2.19.32"
FootLeft = #(string-append "" )
FootCenter = #(string-append "")
FootRight = #(string-append "gesetzt mit LILYPOND " (lilypond-version) " am " (strftime "%d.%m.%Y %H:%M:%S" (localtime (current-time))))
\language "deutsch"
\include "Schemes.ily"
\paper {
#(set-paper-size "a4")
left-margin = #30
top-margin = #20
%line-width = #50
indent = #'0
%ragged-right=##f
% annotate-spacing = ##t
oddFooterMarkup = \markup \fill-line {
\abs-fontsize #9 { \FootLeft }
% \abs-fontsize #7.0 { \italic { \FootCenter } }
\abs-fontsize #9 { \FootRight }
}
#(define page-breaking ly:minimal-breaking)
}
\bookpart
{
\markuplist
{
\vspace#1
\abs-fontsize #12
{
\bold \smallCaps
"Der verminderte Septakkord auf His/Dis/Fis/A"
}
\vspace#1
\justify {
\override #'(line-width . 40)
\score {
\new StaffGroup
\with
{
\remove "System_start_delimiter_engraver"
}
<<
\new Staff
\with {
\remove "Time_signature_engraver"
%\remove "Clef_engraver"
%\remove "Bar_engraver"
}
\relative c' {
\override Staff.InstrumentName.font-size = #1
\override Staff.InstrumentName.self-alignment-X = #RIGHT
\set Staff.instrumentName= "Grundton His"
< \tweak color #blue his dis fis a > < dis fis a \tweak color #red his >
< fis a \tweak color #red his dis > < a \tweak color #red his dis fis >
\stopStaff
\override TextScript #'padding = #-1
\override TextScript #'staff-padding = #'()
\override TextScript #'outside-staff-priority = ##f
s1^\markup {
\override #'(line-width . 10)
\fill-line {
\column {
\line {
\fontsize #'2 "Cis-Moll "
}
}
}
} s1 s1 \key cis \minor
}
\new Staff
\with {
\remove "Time_signature_engraver"
%\remove "Clef_engraver"
%\remove "Bar_engraver"
}
\relative c' {
\override Staff.InstrumentName.font-size = #1
\override Staff.InstrumentName.self-alignment-X = #RIGHT
\set Staff.instrumentName= "Grundton Dis"
< c \tweak color #red dis fis a > < \tweak color #blue dis fis a c >
< fis a c \tweak color #red dis > < a c \tweak color #red dis fis >
\stopStaff
\override TextScript #'padding = #-1
\override TextScript #'staff-padding = #'()
\override TextScript #'outside-staff-priority = ##f
s1^\markup {
\override #'(line-width . 10)
\fill-line {
\column {
\line {
\fontsize #'2 "E-Moll "
}
}
}
} s1 s1 \key e \minor s1
}
\new Staff
\with {
\remove "Time_signature_engraver"
%\remove "Clef_engraver"
%\remove "Bar_engraver"
}
\relative c' {
\override Staff.InstrumentName.font-size = #1
\override Staff.InstrumentName.self-alignment-X = #RIGHT
\set Staff.instrumentName= "Grundton A"
< c es ges \tweak color #red a > < es ges \tweak color #red a c >
< ges \tweak color #red a c es > < \tweak color #blue a c es ges >
\stopStaff
\override TextScript #'padding = #-1
\override TextScript #'staff-padding = #'()
\override TextScript #'outside-staff-priority = ##f
s1^\markup {
\override #'(line-width . 10)
\fill-line {
\column {
\line {
\fontsize #'2 "B-Moll "
}
}
}
} s1 s1 \key b \minor s1
}
\new Staff
\with {
\remove "Time_signature_engraver"
%\remove "Clef_engraver"
%\remove "Bar_engraver"
}
\relative c' {
\override Staff.InstrumentName.font-size = #1
\override Staff.InstrumentName.self-alignment-X = #RIGHT
\set Staff.instrumentName= "Grundton Fis"
< c es \tweak color #red fis a > < es \tweak color #red fis a c >
< \tweak color #blue fis a c es > < a c es \tweak color #red fis >
\stopStaff
\override TextScript #'padding = #-1
\override TextScript #'staff-padding = #'()
\override TextScript #'outside-staff-priority = ##f
s1^\markup {
\override #'(line-width . 10)
\fill-line {
\column {
\line {
\fontsize #'2 "G-Moll "
}
}
}
} s1 s1 \key g \minor s1
}
>>
\layout { indent = #'30 }
}
}
}
}
Und hier noch die Include Datei (die habe ich von irgendwoher):
\version "2.19.32"
#(define (string->string-list strg)
(define (helper-1 strg ls)
"
Converts a string into a list of strings,
every string of the list has string-length 1
e.g "1234" -> '("1" "2" "3" "4")
"
(if (= (string-length strg) 0)
(begin
(set! ls '(""))
ls)
(begin
(set! ls (cons (substring strg 0 1) ls))
(if (>= (string-length (string-drop strg 1)) 1)
(helper-1 (string-drop strg 1) ls)
(reverse ls)))))
(if (string? strg)
(helper-1 strg '())
strg))
#(define (proc l1 l2)
"
l1 is supposed to be a list of strings.
proc will return a new list l2, build of the
elements of l1.
Every string of l2 has string-length 1
e.g '("12" "34") -> '("1" "2" "3" "4")
"
(if (null? l1)
l2
(begin
(set! l2 (append l2 (string->string-list (car l1))))
(proc (cdr l1) l2))))
#(define (stack-chars stencil stils kern)
(set! stencil (ly:stencil-combine-at-edge stencil X RIGHT (car stils) kern))
(if (null? (cdr stils))
stencil
(stack-chars stencil (cdr stils) kern)))
#(define-markup-command (char-space layout props nmbr args)(number? markup-list?)
(let* ((new-args (list-join args " "))
(args+ (reverse (cons " " (reverse new-args))))
(argls (proc args+ '()))
(stils (map (lambda (x)(interpret-markup layout props x)) argls))
(new-stils (reverse (cdr (reverse stils)))))
(stack-chars empty-stencil new-stils nmbr)))
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)