Deutsches Lilypond Forum (Archiv)
Allgemein => Fragen zu Funktionen => Thema gestartet von: iugin am Montag, 27. Oktober 2014, 18:28
-
Liebe alle
ich habe mich jetzt wieder beschlossen mich mit Scheme zu befassen, und dank die Posts (https://liarchiv.joonet.de/index.php?topic=1747.msg9682#msg9682) von Harm und dieser (http://lilypondblog.org/2014/03/music-functions-1-getting-to-grips-with-scheme-in-lilypond/) Artikel konnte ich beginnen, und etwas basteln.
Was ich machen möchte ist folgendes:
in Chor- und Orchesterpartituren, gruppiere ich falls nötig die Takte in Gruppen, und schreibe, wie ich sie dirigieren muss. Z.B.: ein schneller Stück in 3/8, wo die Gruppierung klar 3 + 3 + 4 ist (in Vivaldi gibt es oft solche komische Gruppierungen) dirigiere ich in eins, aber die Geste ist zweimal ein dreier und dann ein vierer. Ich weiss nicht, ob verständlich ist. :)
Ich zeichne oben an der Partitur die Grafik der Bewegung, wenn die Gruppe sich ändert, und wollte versuchen, dasselbe mit Lilypond zu machen.
Ich habe folgendes gebastelt:
\version "2.18.2"
vierviertel =
#'((moveto 0 0)
(curveto 0 0 -0.5 4 -4 4)
(lineto 4 4)
(curveto 4 4 0.5 4 0 8)
(closepath))
dreiviertel =
#'((moveto 0 4)
(curveto 0 4 -0.5 -4 -4 -4)
(curveto -4 -4 0.5 -.5 4 -4)
(curveto 4 -4 -0.5 -.5 0 4)
)
zweiviertel =
#'((moveto 0 4)
(curveto 0 4 0.5 -4 2 0)
(curveto 2 -3 -1 -4 0 4)
)
vier = \markup {
\path #0.25 #vierviertel
}
drei = \markup {
\path #0.25 #dreiviertel
}
zwei = \markup {
\path #0.25 #zweiviertel
}
writeMeasure = #(define-music-function (parser location measure shape)
(number? number?)
(if (= 0 measure)
; wenn keine Takte mehr, end Schleife
#{ #}
; sonst einen Takt schreiben
; und wenn die Gruppe fertig ist
; einen dicken Taktstrich setzen
#{ s1
#(set! measure (- measure 1))
#(if (= 0 (remainder measure shape))
#{
\once \override Staff.BarLine #'hair-thickness = #7
#})
\writeMeasure #measure #shape
#}
)
)
setGroup = #(define-music-function (parser location measure shape)
(number? number?)
; rechnet wieviele Takte man schreiben muss.
; 3 Gruppen à 2 Takte -> 6 Takte
(set! measure (* shape measure))
(cond
((= 2 shape) #{ \mark \zwei \writeMeasure #measure #shape #})
((= 3 shape) #{ \mark \drei \writeMeasure #measure #shape #})
((= 4 shape) #{ \mark \vier \writeMeasure #measure #shape #})))
structure = {
\setGroup #3 #2
\setGroup #1 #4
}
music = \relative c' {
\repeat unfold 40 { c8 d8 }
}
\score {
\new Staff {<< \music \structure>>}
}
Jetzt: mir ist absolut klar, dass dieser Code lächerlich ist, und gar nicht schön. Ich bin aber am lernen, und werde es dann optimieren.
Meine Frage ist aber:
in der Zeile 34 schreibe ich
#{ s1und Lilypond setzt ganz brav einen leeren Takt mit einer ganzen unsichtbaren Pause.
Wenn aber das Stück in einem anderen Metrum wäre, wie kann ich Lilypond sagen "schreib eine Pause, die den Takt füllt"? Sprich: in 3/8 s4. oder so.
Gibt es eine getTimeSignature-Funktion?
Wie geschrieben: der Code ist sicher furchtbar, ich weiss vieles noch nicht, ich werde ihn aber schon in Ordnung bringen.
Danke vielmals für eure Hilfe und einen schönen Abend
Eugenio
-
Hallo Eugenio,
ich habe im Moment keine Lösung für Deine eigentliche Frage, habe mir aber mal Deine 'writeMeasure'-Funktion angesehen.
der Code ist sicher furchtbar, [...]
Nö, überhaupt nicht!
Du definierst eine sich selbst aufrufende music-function.
Zwar bin ich mir nicht ganz sicher, ob es wirklich ein Problem geben könnte. Aber die Art und Weise wie Du es gemacht hast führt zu einer stark verschachtelten 'elements-list.
Ich habe mal den output von
\displayMusic \structure
kopiert und der besseren Sichtbarkeit wegen eingefärbt.
(make-music
'SequentialMusic
'elements
(list (make-music
'SequentialMusic
'elements
(list (make-music
'MarkEvent
'label
(markup
#:line
(#:path
0.25
(list (list (quote moveto) 0 4)
(list (quote curveto) 0 4 0.5 -4 2 0)
(list (quote curveto) 2 -3 -1 -4 0 4)))))
(make-music <-------------- ab hier ist der output des originalen 'writeMeasure' zu sehen (1.Aufruf)
'SequentialMusic
'elements
(list (make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'SequentialMusic
'elements
(list (make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'ContextSpeccedMusic
'context-type
'Staff
'element
(make-music
'OverrideProperty
'once
#t
'pop-first
#t
'grob-property-path
(list (quote hair-thickness))
'grob-value
7
'symbol
'BarLine))
(make-music
'SequentialMusic
'elements
(list (make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'SequentialMusic
'elements
(list (make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'ContextSpeccedMusic
'context-type
'Staff
'element
(make-music
'OverrideProperty
'once
#t
'pop-first
#t
'grob-property-path
(list 'hair-thickness)
'grob-value
7
'symbol
'BarLine))
(make-music
'SequentialMusic
'elements
(list (make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'SequentialMusic
'elements
(list (make-music
'SkipEvent
'duration
(ly:make-duration
0))
(make-music
'ContextSpeccedMusic
'context-type
'Staff
'element
(make-music
'OverrideProperty
'once
#t
'pop-first
#t
'grob-property-path
(list 'hair-thickness)
'grob-value
7
'symbol
'BarLine))
(make-music
'Music)))))))))))))))
(make-music
'SequentialMusic
'elements
(list (make-music
'MarkEvent
'label
(markup
#:line
(#:path
0.25
(list (list (quote moveto) 0 0)
(list (quote curveto) 0 0 -0.5 4 -4 4)
(list (quote lineto) 4 4)
(list (quote curveto) 4 4 0.5 4 0 8)
(list (quote closepath))))))
(make-music <-------------- ab hier ist der output des originalen 'writeMeasure' zu sehen (2.Aufruf)
'SequentialMusic
'elements
(list (make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'SequentialMusic
'elements
(list (make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'SequentialMusic
'elements
(list (make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'SequentialMusic
'elements
(list (make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'ContextSpeccedMusic
'context-type
'Staff
'element
(make-music
'OverrideProperty
'once
#t
'pop-first
#t
'grob-property-path
(list 'hair-thickness)
'grob-value
7
'symbol
'BarLine))
(make-music
'Music)))))))))))))
Ich denke es wäre cleaner auf eine mehr oder wenige "glatte" Liste abzuzielen.
Insoweit habe ich Deine Funktion wie folgt verändert ('setGroup' ist dem natürlich angepaßt):
writeMeasure =
#(define-music-function (parser location measure shape elts)
(number? number? list?)
(let* ((m
#{ s1
#(if (= 0 (remainder measure shape))
#{
\once \override Staff.BarLine #'hair-thickness = #7
#})
#})
(m-elts (ly:music-property m 'elements)))
(if (= 0 measure)
; wenn keine Takte mehr, end Schleife
; und gebe sequential-music mit den neuen 'elements aus
(make-sequential-music elts)
; sonst einen Takt schreiben
; und wenn die Gruppe fertig ist
; einen dicken Taktstrich setzen
; mittels Aufruf der lokalen 'm-elts'-Variablen
#{
\writeMeasure #(- measure 1) #shape #(append m-elts elts)
#})))
setGroup =
#(define-music-function (parser location measure shape)
(number? number?)
; (* shape measure) rechnet wieviele Takte man schreiben muss.
; 3 Gruppen à 2 Takte -> 6 Takte
(cond
((= 2 shape) #{ \mark \zwei \writeMeasure #(* shape measure) #shape #'() #})
((= 3 shape) #{ \mark \drei \writeMeasure #(* shape measure) #shape #'() #})
((= 4 shape) #{ \mark \vier \writeMeasure #(* shape measure) #shape #'() #})))
Unten wieder der output von
\displayMusic \structure
(make-music
'SequentialMusic
'elements
(list (make-music
'SequentialMusic
'elements
(list (make-music
'MarkEvent
'label
(markup
#:line
(#:path
0.25
(list (list (quote moveto) 0 4)
(list (quote curveto) 0 4 0.5 -4 2 0)
(list (quote curveto) 2 -3 -1 -4 0 4)))))
(make-music <-------------- ab hier ist der output des veränderten 'writeMeasure' zu sehen (1.Aufruf)
'SequentialMusic
'elements
(list (make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'ContextSpeccedMusic
'context-type
'Staff
'element
(make-music
'OverrideProperty
'once
#t
'pop-first
#t
'grob-property-path
(list (quote hair-thickness))
'grob-value
7
'symbol
'BarLine))
(make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'ContextSpeccedMusic
'context-type
'Staff
'element
(make-music
'OverrideProperty
'once
#t
'pop-first
#t
'grob-property-path
(list (quote hair-thickness))
'grob-value
7
'symbol
'BarLine))
(make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'ContextSpeccedMusic
'context-type
'Staff
'element
(make-music
'OverrideProperty
'once
#t
'pop-first
#t
'grob-property-path
(list (quote hair-thickness))
'grob-value
7
'symbol
'BarLine))))))
(make-music
'SequentialMusic
'elements
(list (make-music
'MarkEvent
'label
(markup
#:line
(#:path
0.25
(list (list (quote moveto) 0 0)
(list (quote curveto) 0 0 -0.5 4 -4 4)
(list (quote lineto) 4 4)
(list (quote curveto) 4 4 0.5 4 0 8)
(list (quote closepath))))))
(make-music <-------------- ab hier ist der output des veränderten 'writeMeasure' zu sehen (2.Aufruf)
'SequentialMusic
'elements
(list (make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'SkipEvent
'duration
(ly:make-duration 0))
(make-music
'ContextSpeccedMusic
'context-type
'Staff
'element
(make-music
'OverrideProperty
'once
#t
'pop-first
#t
'grob-property-path
(list (quote hair-thickness))
'grob-value
7
'symbol
'BarLine))))))))
Man sieht jetzt eine "glatte" Liste für den Funktionsaufruf.
(Die anderen Verschachtelungen resultieren aus dem Aufruf von 'setGroup'.)
Ob es eine Möglichkeit gibt den aktuellen Takt auszulesen weiß ich noch nicht. Vor dem Wochenende werde ich mich wohl nicht damit beschäftigen können ...
Gruß,
Harm
-
Lieber Harm
nur kurz bevor ich gehe.
Zuerst: danke vielmals.
Ich werde heute Abend dein Code genau anschauen, und hoffe, ich verstehe, was du machst. Sonst frage ich einfach.
Dann: ich habe eine Lösung selber gebastelt. Es wird zwar nicht die \time-Angabe gelesen (ich habe sehr lange in Internet gesucht und nichts gefunden), aber es funktioniert.
Der Code wie es jetzt aussieht ist da unten:
\version "2.16.2"
vierviertel =
#'((moveto 0 0)
(curveto 0 0 -0.5 4 -4 0)
(curveto -4 0 0 3 3 0.5)
(curveto 3 0.5 3.5 0 4 1)
(curveto 4 0 0 1 0 8)
(closepath))
dreiviertel =
#'((moveto 0 8)
(curveto 0 8 0 0 -1 0)
(curveto -1 0 0 2 3 0.5)
(curveto 3 0.5 3.5 0 4 1)
(curveto 3 0.5 3.5 0 4 1)
(curveto 4 0 0 1 0 8)
)
zweiviertel =
#'((moveto 0 8)
(curveto 0 4 0.5 -4 2 0)
(curveto 2 -3 -1 -4 0 8)
)
vier = \markup {
\path #0.25 #vierviertel
}
drei = \markup {
\path #0.25 #dreiviertel
}
zwei = \markup {
\path #0.25 #zweiviertel
}
#(
; definiert 4/4 als default
define tm '(4 . 4))
setTime = #(define-void-function (parser location time) (pair?)
;
; Speichert die time-Angaben
; car -> numerator
; cdr -> denominator
;
; Sagt WriteMeasure wie lang
; jeder Takt ist
(set! tm time)
)
createRest = #(define-music-function (parser location) ()
;
; kreiert eine unsichtbare Pause aufgrund der Werten
; von setTime
;
(make-music
'SequentialMusic
'elements
(list (make-music
'SkipEvent
'duration
(ly:make-duration (log2 (cdr tm)) 0 (car tm)))))
)
writeMeasure = #(define-music-function (parser location measure shape)
(number? number?)
;
; Schreibt eine Reihe von unsichtbaren Pausen
; mit der Form shape
; und setzt dicke Taktstriche
;
(if (= 0 measure)
; wenn keine Takte mehr, end Schleife
#{ #}
; sonst einen Takt schreiben
; und wenn die Gruppe fertig ist
; einen dicken Taktstrich setzen
#{ \createRest
#(set! measure (- measure 1))
#(if (= 0 (remainder measure shape))
#{
\once \override Staff.BarLine #'hair-thickness = #7
#})
\writeMeasure #measure #shape
#}
)
)
skipGroup = #(define-music-function (parser location measure)
(number?)
(if (= 0 measure)
#{
\once \override Staff.BarLine #'hair-thickness = #7
#} ;then
(begin (set! measure (- measure 1))
#{
\createRest
\skipGroup #measure #}
)); end begin -> else
)
setGroup = #(define-music-function (parser location measure shape)
(number? number?)
;
; rechnet wieviele Takte man schreiben muss.
; 3 Gruppen à 2 Takte -> 6 Takte
;
; Verwendung:
; \setGroup #3 #2
; kreierst drei Gruppen à zwei Takten
;
(set! measure (* shape measure))
(cond
((= 0 shape) #{ \writeMeasure #measure #})
((= 2 shape) #{ \mark \zwei \writeMeasure #measure #shape #})
((= 3 shape) #{ \mark \drei \writeMeasure #measure #shape #})
((= 4 shape) #{ \mark \vier \writeMeasure #measure #shape #})))
structure = {
\setTime #'(3 . 8)
\setGroup #1 #4
\setTime #'(3 . 16)
\setGroup #1 #3
\setGroup #1 #2
}
music = \relative c' {
\time 3/8
c8 d e
c d e
c d e
c d e
\time 3/16
c16 d e
c d e
c d e
c d e
c d e
\bar"|."
}
\score {
\new Staff {<< \music \structure>>}
}
Ich habe ausserdem eine art globale Variable, tm, welche die Taktarten speichert. Ist sowas verpönt? Normalerweise sollte man globale Variablen vermeiden, wenn möglich, ich wusste aber nicht, wie ich sonst das Problem hätte lösen können.
Auf jeden Fall funktioniert es.
Besten dank, und ich melde mich wieder, wenn ich Fragen haben.
Einen lieben Gruss
Eugenio
-
Lieber Harm
ich verstehe den Code, danke vielmals!
Ich habe da aber noch eine Frage: wenn ich den Beispiel da unten kompiliere, kriege ich den dicken Taktstrich, nur in dem System, wo ich die Struktur implementiere.
\version "2.16.2"
vierviertel =
#'((moveto 0 0)
(curveto 0 0 -0.5 4 -4 0)
(curveto -4 0 0 3 3 0.5)
(curveto 3 0.5 3.5 0 4 1)
(curveto 4 0 0 1 0 8)
(closepath))
dreiviertel =
#'((moveto 0 8)
(curveto 0 8 0 0 -1 0)
(curveto -1 0 -3 2 -.5 1)
(curveto -.5 1 2 0 4 1)
(curveto 4 1 0 -1.5 0 8)
)
zweiviertel =
#'((moveto 0 8)
(curveto 0 8 0.5 -3 4 3)
(curveto 4 3 -1 -3 0 8)
)
vier = \markup {
\path #0.25 #vierviertel
}
drei = \markup {
\path #0.25 #dreiviertel
}
zwei = \markup {
\path #0.25 #zweiviertel
}
#(
; definiert 4/4 als default
define tm '(4 . 4))
setTime = #(define-void-function (parser location time) (pair?)
;
; Speichert die time-Angaben
; car -> numerator
; cdr -> denominator
;
; Sagt WriteMeasure wie lang
; jeder Takt ist
(set! tm time)
)
createRest = #(define-music-function (parser location) ()
;
; kreiert eine unsichtbare Pause aufgrund der Werten
; von setTime
;
(make-music
'SequentialMusic
'elements
(list (make-music
'SkipEvent
'duration
(ly:make-duration (log2 (cdr tm)) 0 (car tm)))))
)
writeMeasure =
#(define-music-function (parser location measure shape elts)
(number? number? list?)
(let* ((m
#{ \createRest
#(if (= 0 (remainder measure shape))
#{
\once \override Staff.BarLine #'hair-thickness = #7
#})
#})
(m-elts (ly:music-property m 'elements)))
(if (= 0 measure)
; wenn keine Takte mehr, end Schleife
; und gebe sequential-music mit den neuen 'elements aus
(make-sequential-music elts)
; sonst einen Takt schreiben
; und wenn die Gruppe fertig ist
; einen dicken Taktstrich setzen
; mittels Aufruf der lokalen 'm-elts'-Variablen
#{
\writeMeasure #(- measure 1) #shape #(append m-elts elts)
#})))
skipGroup = #(define-music-function (parser location measure)
(number?)
(if (= 0 measure)
#{
\once \override Staff.BarLine #'hair-thickness = #7
#} ;then
(begin (set! measure (- measure 1))
#{
\createRest
\skipGroup #measure #}
)); end begin -> else
)
setGroup =
#(define-music-function (parser location measure shape)
(number? number?)
; (* shape measure) rechnet wieviele Takte man schreiben muss.
; 3 Gruppen à 2 Takte -> 6 Takte
(cond
((= 0 shape) #{ \writeMeasure #measure #})
((= 2 shape) #{ \mark \zwei \writeMeasure #(* shape measure) #shape #'() #})
((= 3 shape) #{ \mark \drei \writeMeasure #(* shape measure) #shape #'() #})
((= 4 shape) #{ \mark \vier \writeMeasure #(* shape measure) #shape #'() #})))
struktur = {
\setTime #'(3 . 8)
\setGroup #1 #3
}
one = \relative c' {
\time 3/8
c8 d e d c d e d c d e d c
}
two = \relative c {
\clef bass
c8 d e d c d e d c d e d c
}
\score {
\new PianoStaff <<
\new Staff << \one \struktur >>
\new Staff \two
>>
}Wie kann den dicken Taktstrich durch die ganze Partitur haben?
Danke vielmals und einen lieben Gruss
Eugenio
-
Gefunden!
\once \override Score.BarLine #'hair-thickness = #7statt
\once \override Staff.BarLine #'hair-thickness = #7
...und so kann ich ruhig schlafen gehen ;)
Gute Nacht
Eugenio
-
Hallo Eugenio,
schön das es erstmal klappt. :)
Ich habe aber noch etwas weiter über das Problem nachgedacht.
Prinzipiell willst Du ja an zu bestimmende Stellen eine Grafik (via \mark) einfügen, sowie bestimmte Taktstriche verdeutlichen (via 'hair-thickness).
Ist es wirklich sinnvoll den Weg über eine zweite Stimme zu gehen?
Eigentlich schreit das förmlich nach einem custom-engraver.
Ich hab auch schon ein paar Versuche in diese Richtung unternommen, hab aber nicht die Zeit sie zu vollenden. Vielleicht nächstes Wochenende.
Gruß,
Harm
-
Hallo Harm
ist genauso wie du sagst.
Ob es ein anderer Weg gibt, weiss ich nicht. So funktioniert es, ich bin schon froh (vor allem, dass ich selber was basteln konnte).
Danke für deine Hilfe.
Liebe Grüsse
Eugenio