Deutsches Lilypond Forum (Archiv)
Allgemein => Fragen zu Funktionen => Thema gestartet von: stefanhuglfing am Mittwoch, 3. Februar 2016, 15:47
-
Ich würde gern mit einer Funktion viele Strophentexte unter die Melodie setzen.
Mein code unten ist natürlich in vielerlei Hinsicht fehlerhaft bis unsinnig.
Mag mir jemand helfen? Ist mir überhaupt noch zu helfen?
\version "2.18.2"
Noten = { c'4 d' e' f' }
Text1 = { \lyricmode { ers -- te Stro -- phe } }
Text2 = { \lyricmode { zwei -- te Stro -- phe } }
Text3 = { \lyricmode { drit -- te Stro -- phe } }
#(define (Strophen Anzahl)
(do ((i 1 (1+ i)))
((> i Anzahl))
(\addlyrics #(string-append "Text" (number->string #\Nummer)))))
\score { \new Staff <<
\new Voice {
\Noten
#(Strophen 3) } >> }
-
Hallo!
Ein Problem ist, dass in Lilypond Variablen mit Zahlen nicht zulässig sind. Text1 geht nicht, hingegen z.B. TextOne schon.
So kannst du nicht, durch eine Schleife Zahlen an Strings anhängen, und so neue Variablen erzeugen.
Dann: ich kann Scheme nicht, aber trotzdem versucht etwas zu basteln. Ich sollte vielleicht ein neuer Thread öffnen, aber ich versuche mal hier.
Mein Code geht nicht, aber, bin ich sicher, bin ich ganz nah an der Lösung.
Vielleicht kann jemand einfach meinen Code korrigieren, und dann haben wir beide was gelernt :-)
\version "2.18.2"
Noten = { c'4 d' e' f' }
TextOne = { \lyricmode { ers -- te Stro -- phe } }
TextTwo = { \lyricmode { zwei -- te Stro -- phe } }
TextThree = { \lyricmode { drit -- te Stro -- phe } }
addText = #(define-music-function (parser location voice text)(string? list?)
(if (null? text)
#{ #}
)
#{
\new Lyrics \lyricsto $voice $(car text)
\addText $voice #'(cdr text)
#}
)
\score {
<<
\new Staff { \new Voice = "music" { \Noten }}
\addText #"music" #(list TextOne TextTwo TextThree)
>>
}
Lieber Gruss
Eugenio
-
ich denke einen Klammerfehler habe ich gefunden.
Die Klammer vor if muss nicht hinter #{ #} geschlossen werden,
sondern hinter dem zweiten #{ ... #}-Block:
\version "2.18.2"
Noten = { c'4 d' e' f' }
TextOne = { \lyricmode { ers -- te Stro -- phe } }
TextTwo = { \lyricmode { zwei -- te Stro -- phe } }
TextThree = { \lyricmode { drit -- te Stro -- phe } }
addText = #(define-music-function (parser location voice text)(string? list?)
(if (null? text)
#{ #}
#{
\new Lyrics \lyricsto $voice $(car text)
\addText $voice #'(cdr text)
#}
)
)
\score {
<<
\new Staff { \new Voice = "music" { \Noten }}
\addText #"music" #(list TextOne TextTwo TextThree)
>>
}
Jetzt gibt es andere Fehlermeldungen: ...list erwartet...
-
Ach ja, du hast recht! :)
Ich habe jetzt diese Fassung, die kompiliert, bringt aber nichts:
\version "2.18.2"
Noten = { c'4 d' e' f' }
TextOne = { \lyricmode { ers -- te Stro -- phe } }
TextTwo = { \lyricmode { zwei -- te Stro -- phe } }
TextThree = { \lyricmode { drit -- te Stro -- phe } }
addText = #(define-music-function (parser location voice text)(string? list?)
(if (null? text)
#{ #}
#{
\new Lyrics \lyricsto $voice $(car text)
\addText $voice $(cdr text)
#}
)
)
\score {
<<
\new Staff { \new Voice = "music" { \Noten }}
\addText #"music" #(list TextOne TextTwo TextThree)
>>
}
Alle Scheme-Gurus werden sich totlachen, weil es sicher eine Kleinigkeit ist...
Lieber Gruss
Eugenio
-
So einfach ists nicht...
Aber folgendes scheint meinen begrenzten Tests zu Folge zu klappen:
\version "2.18.2"
Noten = { c'4( d' e') f' g'2 a' }
TextOne = \lyricmode { ers -- te Stro -- phe }
TextTwo = \lyricmode { zwei -- te Stro -- phe }
TextThree = \lyricmode { drit -- te Stro -- phe }
addText =
#(define-music-function (parser location voice text)
(string? ly:music?)
#{
<<
$@(map
(lambda (txt) #{ \new Lyrics \lyricsto $voice $txt #})
(if (music-is-of-type?
(car (ly:music-property text 'elements)) 'sequential-music)
(ly:music-property text 'elements)
(list text)))
>>
#})
\score { \displayMusic
<<
\new Staff \new Voice = "music" \Noten
\addText "music"
{ \TextOne \TextTwo \TextThree }
>>
}
\score {
<<
\new Staff \new Voice = "music" \Noten
\addText "music"
\TextOne
>>
}
\score {
<<
\new Staff \new Voice = "music" \Noten
\addText "music"
{ \TextOne }
>>
}
HTH,
Harm
-
Das ist sogar mehr, als ich wollte. Ich muss der Funktion nicht einmal sagen, wie viele Strophen es sein sollen.
Das erspart mir nochmal einigen Aufwand beim Erstellen von Partituren mit mehreren Sätzen und mehreren Stimmen.
Ich frage mich nur:
Bastelt für komplexe Partituren jeder selber irgendwie rum,
oder gibt es ein übliches Verfahren, wie man die Dateistruktur aufbaut usw.?
Sollte ich dazu ein neues Thema eröffnen?
-
Hallo Harm
dass ich so weit weg von der Lösung war, dachte ich nicht :-)
Wieso geht es nicht, dieselbe Funktion mit cdr aufzurufen?
Einen lieben Gruss
Eugenio
-
dass ich so weit weg von der Lösung war, dachte ich nicht :-)
Warst Du doch gar nicht ;)
Wieso geht es nicht, dieselbe Funktion mit cdr aufzurufen?
Zunächst, Stefan hatte es mit einer (do ...)-Schleife versucht, Du mit einer Rekursion.
Ich fand das alles zu kompiliziert und unnötig, deshalb mein einfaches (map ...)
Aber natürlich geht es!
Hier Deine Rekursion mit Anmerkungen:
\version "2.18.2"
Noten = { c'4 d' e' f' }
TextOne = { \lyricmode { ers -- te Stro -- phe } }
TextTwo = { \lyricmode { zwei -- te Stro -- phe } }
TextThree = { \lyricmode { drit -- te Stro -- phe } }
addText = #(define-music-function (parser location voice text)(string? list?)
;; Die Abbruchbedingung ist zwar ok, aber was wird denn ausgegeben? Nur: #{ #}
;; Aber:
;; #(display-scheme-music #{ #})
;; -> (make-music (quote Music))
;; Also im wesentlichen also gar nix ;)
(if (null? text)
#{ #}
;; Der folgende Ausdruck ist auch nicht zielführend da ein geschachtelter Ausdruck entsteht:
;;
;; (make-music
;; 'SequentialMusic
;; 'elements
;; ;; first Lyrics
;; (list (make-music
;; 'ContextSpeccedMusic
;; 'create-new
;; #t
;; ...)
;; ;; second Lyrics
;; (make-music
;; 'SequentialMusic
;; 'elements
;; (list (make-music
;; 'ContextSpeccedMusic
;; 'create-new
;; #t
;; ...)))
;; ;; third Lyrics
;; (make-music
;; 'SequentialMusic
;; 'elements
;; (list (make-music
;; 'ContextSpeccedMusic
;; 'create-new
;; #t
;; ...)))
;; (make-music (quote Music))))))))
;; dieser müßte erst durch die music-function ausgegeben, entfaltet und in sinnvoller Weise in den Score integriert werden.
;; Letzteres funktioniert, falls man es als simultaneous-music oder innerhalb von << >> ausgibt.
#{
\new Lyrics \lyricsto $voice $(car text)
\addText $voice $(cdr text)
#}
)
)
\score {
<<
\new Staff { \new Voice = "music" { \Noten }}
\addText #"music" #(list TextOne TextTwo TextThree)
>>
}
Obwohl ich wie gesagt die Anwendung des einfachen (map ...) hier bevorzuge, habe ich Deinen Ansatz mal überarbeitet. Das führt zu:
\version "2.18.2"
Noten = { c'4 d' e' f' }
TextOne = \lyricmode { ers -- te Stro -- phe }
TextTwo = \lyricmode { zwei -- te Stro -- phe }
TextThree = \lyricmode { drit -- te Stro -- phe }
addText =
#(define-music-function (parser location rl voice text)
(list? string? list?)
(if (null? text)
(make-simultaneous-music (reverse rl))
#{
\addText
$(cons #{ \new Lyrics \lyricsto $voice $(car text) #} rl)
$voice
$(cdr text)
#}))
\score {
<<
\new Staff { \new Voice = "music" { \Noten }}
\addText #'() "music" #(list TextOne TextTwo TextThree)
>>
}
EDIT: define-scheme-function zu define-music-function geändert (Tatsächlich funktioniert beides, es gibt aber keinen besonderen Grund define-scheme-function zu benutzen.)
Du siehst ein Argument mehr ,`rl', welches später als '() gesetzt wird, eine leere Liste in der alle Lyrics-contexte akkumuliert und dann via make-simultaneous-music ausgegeben werden.
Es entsteht auch keine geschachtelte Liste, da `cons' verwendet ist.
In meiner eigenen Funktion habe ich noch ein bißchen mehr Aufwand betrieben um eine möglichst LilyPond-artige Eingabe zu ermöglichen.
Wie aus meinen angefügten Beispielen hervorgeht kann man \TextOne oder { \TextOne } für einen einzelnen Lyrics-context benutzen oder z.B. { \TextOne \TextTwo \TextThree } für mehrere.
HTH,
Harm
-
Lieber Harm
danke vielmals für die Erklärungen... ich bin leider anscheinend zu dumm, um sie zu verstehen... :)
Ich schaue sie morgen wieder in allem Ruhe an, vielleicht hilft es...
Einen lieben Gruss
Eugenio
-
ich bin leider anscheinend zu dumm, um sie zu verstehen
Es ist noch nicht allzu lange her, daß ich jede gelungene Rekursion rot im Kalender vermerkt habe.
Manchmal kriegt man Knoten im Hirn, wenn man es verstehen bzw schreiben will.
Falls Du Fragen hast, werd' ich versuchen genauere Erklärungen zu geben.
Hier noch ein Coding welches die Methode aus dem ersten Post dieses Threads verwendet, eine do-Schleife.
Es hat natürlich die Schwäche, daß sowohl die Basis-Identifier-Namen, als auch der Context-Name hard-coded sind.
Ich füge es trotzdem an, um eine do-Schleife, sowie eine Methode Zahlen in Identifier-Namen zu haben zu demonstrieren.
\version "2.18.2"
Noten = { c'4 d' e' f' }
"Text1" = \lyricmode { ers -- te Stro -- phe }
"Text2" = \lyricmode { zwei -- te Stro -- phe }
"Text3" = \lyricmode { drit -- te Stro -- phe }
#(define (Strophen Anzahl)
(make-simultaneous-music
(do ((i 1 (1+ i))
(l '()))
((> i Anzahl)
(reverse l))
(set! l
(cons
#{
\new Lyrics \lyricsto ""
{ #(eval-string (string-append "Text" (number->string i))) }
#}
l)))))
\score {
<<
\new Staff
\new Voice \Noten
#(Strophen 3)
>>
}
Gruß,
Harm