Deutsches Lilypond Forum (Archiv)
Allgemein => Fragen zu Funktionen => Thema gestartet von: xr am Dienstag, 31. Januar 2017, 14:24
-
Hi,
ich bin auf der Suche nach einer Funktion, mit der ich eine Wiederholungsklammer setzen kann, ohne \alternative zu benutzen.
(Da ich meine Noten mittels eines Python Skriptes erzeuge, wäre die Programmierung des gesamten Kontextes (z.B. \repeat volta 3 \alternative {...} {...} ) inklusive der Verteilung eines Gesangstextes immens kompliziert)
Gibt es die Möglichkeit z.B. mittels einer Scheme-Funktion Klammern zu erzeugen?
Wie muss die aussehen? Kann mir jemand ein einfaches Beispiel zeigen?
Ich würde gern einen Befehl erzeugen wie:
\Klammer_an '#(1 . 3) % soll Klammer 1 und 3 erzeugen
... mein Notentext ...
\Klammer_aus % soll die Klammer beenden
\Klammer_an '#(2 . 4) % soll Klammer 2 und 4 erzeugen
... mein Notentext ...
\Klammer_aus % soll die Klammer beenden
Danke für Hilfe im Voraus,
Xaver
-
Hallo,
in meiner "stradella-toolbox" (siehe https://liarchiv.joonet.de/index.php?topic=1158.0 (https://liarchiv.joonet.de/index.php?topic=1158.0)) habe ich rudimentäre Funktionen \pmpStartRepeat, \pmpStartRepeatEx, \pmpEndRepeat und \pmpStartAlternative definiert.
Anwendung: Mit \parallelMusic erzeuge ich mehrere "sequentielle Musiken" mit diesen "Kommandos" drinnen, (denn \repeat volta würde das \parallelMusic durcheinander bringen) und anschließend wird jede dieser "sequentiellen Musiken" an diesen Markierungen zerschnitten und neu (mit \repeat volta) zusammengesetzt.
Wichtig ist: Diese "Steuerkommandos" müssen genau in der gleichen Ebene der sequentiellen Musik vorkommen, nicht in Unterstrukturen gepackt!
Bisher habe ich auch kein Kommando \pmpStartAlternativeEx defniert, mit welcher man den Alternativtext für die Klammerzählung übergibt, aber der existierende Code könnte als Ausgangspunkt dienen. Und #parallel-music-results-postprocess wird sogar nur intern als Scheme-Aufruf gestartet, ist also noch nicht als normale Musikfunction definiert.
Die Anwendung sähe in Endeffekt etwa so aus, wenn man die Funktionen entsprechend erweitert:
\convertPMPRepeatMarksIntoRepeats {
\pmpStartRepeat
...
\pmpStartAlternativeEx #"1.+3."
...
\pmpStartAlternativeEx #"2.+4."
...
\pmpEndRepeat
}
Arnold
-
Prima, herzlichen Dank. Schaue ich mir direkt an.
Gruß,
Xaver
-
Das Beispiel ist wirklich kompliziert. Und da es, soweit ich es verstehe, gleich zwei Wrapper nutzt, ist es schwierig hinter die Funktionsweise zu kommen.
Ich habe deine Beispieldateien mal soweit reduziert, wie ich konnte. (s.u.)
Wird das, was in den Parser gegeben wird, jetzt soweit umgemodelt, dass für den Lilypond Parser letztlich wieder ein \repeat volta 3 \alternative {...} {...} erscheint?
Ich suche eine Möglichkeit, eine Klammer einfach wie eine Zeichnung zu setzen. Lilypond bräuchte sich um die musikalische Logik nicht weiter zu kümmern.
Hier die reduzierten Beispiele:
\version "2.15.39"
\include "stradella-toolbox--1.0.ly"
\parallelMusicWithStradella #'(AkkT) {
\pmpStartRepeat
c 4 e e e |
\pmpStartAlternative
c 4 c' c r |
\pmpStartAlternative
e 4 e e r |
\pmpStartAlternative
e4 e e e'|
\pmpStartAlternative
e4 e e r |
\mark "Mark"
\pmpStartRepeat
e'2. a'8 b' |
\pmpStartAlternative
e 4 e e e'\p |
\pmpStartAlternative
e 4 e e r |
}
\score {
\new Staff <<
\AkkT
>>
}
%{
stradella-toolbox reduced
%}
\version "2.15.30"
%§ post processing marks for the extended parallelMusic
pmpStartRepeat = #(define-music-function (parser location) () (make-music 'MarkEvent
'label "<parallel-music-results-postprocess:start repeat volta 2>"))
pmpStartRepeatEx = #(define-music-function (parser location count) (integer?) (make-music 'MarkEvent
'label (string-append "<parallel-music-results-postprocess:start repeat volta " (number->string count) ">")))
pmpEndRepeat = #(define-music-function (parser location) () (make-music 'MarkEvent
'label "<parallel-music-results-postprocess:end repeat>"))
pmpStartAlternative = #(define-music-function (parser location) () (make-music 'MarkEvent
'label "<parallel-music-results-postprocess:start alternative>"))
#(define (pmpMark mus)
(if (ly:music? mus)
(let
((eventtype (ly:music-property mus 'name))
(labeltext (ly:music-property mus 'label))
)
(if (and (eq? eventtype 'MarkEvent) (string? labeltext))
(let*
((head "<parallel-music-results-postprocess:")
(headlen (string-length head))
(labellen (string-length labeltext)))
(if (<= labellen headlen) 0
(let*
((labelhead (string-copy labeltext 0 headlen))
(labelpost (string-copy labeltext headlen))
(postlen (string-length labelpost))
(labelshortpost (string-copy labelpost 0 (min postlen 19)))
)
(if (string=? labelhead head)
(if (string=? labelshortpost "start repeat volta ") 1
(if (string=? labelpost "end repeat>") 2
(if (string=? labelpost "start alternative>") 3
0)))
0))))
0))
0))
#(define (parallel-music-results-postprocess mus)
(let ((eventtype (ly:music-property mus 'name)))
(if (eq? eventtype 'SequentialMusic)
(let
((es (ly:music-property mus 'elements))
(new-es '())
(active-dest 0)
(rep-collect '())
(alt-collect '())
(alt-list-collect '())
(rep-count 2)
)
(for-each (lambda (ese)
(let ((etype (pmpMark ese)))
(if (eq? etype 2) ;; end repeat ;;
(begin
(if (eq? active-dest 3)
(set! alt-list-collect (append alt-list-collect (cons (make-music 'SequentialMusic 'elements alt-collect) '()))))
(set! new-es (append new-es (cons
(make-music 'VoltaRepeatedMusic 'repeat-count rep-count
'element (make-music 'SequentialMusic 'elements rep-collect) 'elements alt-list-collect) '())))
(set! active-dest 0)
(set! rep-collect '())
(set! alt-collect '())
(set! alt-list-collect '())))
(if (eq? etype 1) ;; start repeat ;;
(begin
(if (eq? active-dest 3)
(set! alt-list-collect (append alt-list-collect (cons (make-music 'SequentialMusic 'elements alt-collect) '()))))
(if (>= active-dest 1) ;; terminate existing repeat and start new repeat block
(set! new-es (append new-es (cons
(make-music 'VoltaRepeatedMusic 'repeat-count rep-count
'element (make-music 'SequentialMusic 'elements rep-collect) 'elements alt-list-collect) '()))))
(set! active-dest 1)
(set! rep-collect '())
(set! alt-collect '())
(set! alt-list-collect '())))
(if (and (eq? etype 3) (>= active-dest 1)) ;; start alternative (only when repeat allready started) ;;
(if (eq? active-dest 3) ;; next alternative ;;
(begin
(set! alt-list-collect (append alt-list-collect (cons (make-music 'SequentialMusic 'elements alt-collect) '())))
(set! alt-collect '()))
(set! active-dest 3)))
(if (eq? etype 0)
(begin
(if (eq? active-dest 0)
(set! new-es (append new-es (cons ese '()))))
(if (eq? active-dest 1)
(set! rep-collect (append rep-collect (cons ese '()))))
(if (eq? active-dest 3)
(set! alt-collect (append alt-collect (cons ese '()))))))))
es)
(if (eq? active-dest 1) ;; start repeat is open ;;
(set! new-es (append new-es (cons
(make-music 'VoltaRepeatedMusic 'repeat-count rep-count
'element (make-music 'SequentialMusic 'elements rep-collect) 'elements alt-list-collect) '()))))
(if (eq? active-dest 3) ;; start alternative is open ;;
(begin
(set! alt-list-collect (append alt-list-collect (cons (make-music 'SequentialMusic 'elements alt-collect) '())))
(set! new-es (append new-es (cons
(make-music 'VoltaRepeatedMusic 'repeat-count rep-count
'element (make-music 'SequentialMusic 'elements rep-collect) 'elements alt-list-collect) '())))))
(ly:music-set-property! mus 'elements new-es)
mus)
mus)))
%§ \paralleMusic with \stradellaMode applied to the last voice
% and another postprocessor applied to all voices
parallelMusicWithStradella =
#(define-void-function (parser location voice-ids music) (list? ly:music?)
(let ((parmus (ly:parser-lookup parser 'parallelMusic)))
(let ((parex (ly:music-function-extract parmus)))
(parex parser location voice-ids music)
(let ((counter 0))
(for-each (lambda (voice-id)
(let ((pme (ly:parser-lookup parser voice-id)))
(if (eq? counter 0)
(ly:parser-define! parser voice-id
(parallel-music-results-postprocess
pme)
)
)
)
)
(reverse voice-ids))
)
)))
Xaver
-
O.K.
Und alle mit weniger Scheme-Programmier-Erfahrung durfen das hier ausprobieren:
\version "2.18.2"
pmpStartRepeat = #(define-music-function (parser location) () (make-music 'MarkEvent
'label "<parallel-music-results-postprocess:start repeat volta 2>"))
pmpStartRepeatEx = #(define-music-function (parser location count) (integer?) (make-music 'MarkEvent
'label (string-append "<parallel-music-results-postprocess:start repeat volta " (number->string count) ">")
'extra-int count))
pmpEndRepeat = #(define-music-function (parser location) () (make-music 'MarkEvent
'label "<parallel-music-results-postprocess:end repeat>"))
pmpStartAlternative = #(define-music-function (parser location) () (make-music 'MarkEvent
'label "<parallel-music-results-postprocess:start alternative>"))
pmpStartAlternativeEx = #(define-music-function (parser location s) (string?) (make-music 'MarkEvent
'label (string-append "<parallel-music-results-postprocess:start named alternative:" s ">")
'extra-text s))
#(define (pmpMark mus)
(if (ly:music? mus)
(let
((eventtype (ly:music-property mus 'name))
(labeltext (ly:music-property mus 'label))
)
(if (and (eq? eventtype 'MarkEvent) (string? labeltext))
(let*
((head "<parallel-music-results-postprocess:")
(headlen (string-length head))
(labellen (string-length labeltext)))
(if (<= labellen headlen) 0
(let*
((labelhead (string-copy labeltext 0 headlen))
(labelpost (string-copy labeltext headlen))
(postlen (string-length labelpost))
(labelshortpost (string-copy labelpost 0 (min postlen 19)))
)
(if (string=? labelhead head)
(if (string=? labelshortpost "start repeat volta ") 1
(if (string=? labelpost "end repeat>") 2
(if (string=? labelpost "start alternative>") 3
(if (string=? labelshortpost "start named alterna") 4
0))))
0))))
0))
0))
#(define (pmpMarkRepeatCount mus)
(if (ly:music? mus)
(let
((eventtype (ly:music-property mus 'name))
(labeltext (ly:music-property mus 'label)))
(if (and (eq? eventtype 'MarkEvent) (string? labeltext))
(let*
((head "<parallel-music-results-postprocess:")
(headlen (+ (string-length head) 19))
(labellen (string-length labeltext)))
(if (<= labellen headlen) 2
(let*
((labelhead (string-copy labeltext 0 headlen))
(labelpost (string-copy labeltext headlen (- labellen 1)))
(labelterm (string-copy labeltext (- labellen 1))))
(if (string=? labelterm ">") (string->number labelpost)
2))))
2))
2))
#(define (parallel-music-results-postprocess mus)
(let ((eventtype (ly:music-property mus 'name)))
(if (eq? eventtype 'SequentialMusic)
(let
((es (ly:music-property mus 'elements))
(new-es '())
(active-dest 0)
(rep-collect '())
(alt-collect '())
(alt-list-collect '())
(rep-count 2)
)
(for-each (lambda (ese)
(let ((etype (pmpMark ese)))
(if (eq? etype 2) ;; end repeat ;;
(begin
(if (eq? active-dest 3)
(set! alt-list-collect (append alt-list-collect (cons (make-music 'SequentialMusic 'elements alt-collect) '()))))
(set! new-es (append new-es (cons
(make-music 'VoltaRepeatedMusic 'repeat-count rep-count
'element (make-music 'SequentialMusic 'elements rep-collect) 'elements alt-list-collect) '())))
(set! active-dest 0)
(set! rep-collect '())
(set! alt-collect '())
(set! alt-list-collect '())))
(if (eq? etype 1) ;; start repeat ;;
(begin
(if (eq? active-dest 3)
(set! alt-list-collect (append alt-list-collect (cons (make-music 'SequentialMusic 'elements alt-collect) '()))))
(if (>= active-dest 1) ;; terminate existing repeat and start new repeat block
(set! new-es (append new-es (cons
(make-music 'VoltaRepeatedMusic 'repeat-count rep-count
'element (make-music 'SequentialMusic 'elements rep-collect) 'elements alt-list-collect) '()))))
(set! active-dest 1)
(set! rep-collect '())
(set! alt-collect '())
(set! rep-count (ly:music-property ese 'extra-int 2))
(set! alt-list-collect '())))
(if (and (eq? etype 3) (>= active-dest 1)) ;; start alternative (only when repeat allready started) ;;
(if (eq? active-dest 3) ;; next alternative ;;
(begin
(set! alt-list-collect (append alt-list-collect (cons (make-music 'SequentialMusic 'elements alt-collect) '())))
(set! alt-collect '()))
(set! active-dest 3)))
(if (and (eq? etype 4) (>= active-dest 1)) ;; start named alternative (only when repeat allready started) ;;
(begin
(if (eq? active-dest 3) ;; next alternative ;;
(set! alt-list-collect (append alt-list-collect (cons (make-music 'SequentialMusic 'elements alt-collect) '())))
(set! active-dest 3))
(set! alt-collect (cons
(make-music 'ApplyContext
'procedure (lambda (ctx)
(let ((score-ctx (ly:context-find ctx 'Score)))
(if (ly:context? score-ctx)
(let ((old-rc (ly:context-property score-ctx 'repeatCommands '())))
(ly:context-set-property! score-ctx 'repeatCommands (cons (list 'volta (ly:music-property ese 'extra-text "?")) old-rc)))))))
'()))))
(if (eq? etype 0)
(begin
(if (eq? active-dest 0)
(set! new-es (append new-es (cons ese '()))))
(if (eq? active-dest 1)
(set! rep-collect (append rep-collect (cons ese '()))))
(if (eq? active-dest 3)
(set! alt-collect (append alt-collect (cons ese '()))))))))
es)
(if (eq? active-dest 1) ;; start repeat is open ;;
(set! new-es (append new-es (cons
(make-music 'VoltaRepeatedMusic 'repeat-count rep-count
'element (make-music 'SequentialMusic 'elements rep-collect) 'elements alt-list-collect) '()))))
(if (eq? active-dest 3) ;; start alternative is open ;;
(begin
(set! alt-list-collect (append alt-list-collect (cons (make-music 'SequentialMusic 'elements alt-collect) '())))
(set! new-es (append new-es (cons
(make-music 'VoltaRepeatedMusic 'repeat-count rep-count
'element (make-music 'SequentialMusic 'elements rep-collect) 'elements alt-list-collect) '())))))
(ly:music-set-property! mus 'elements new-es)
mus)
mus)))
LinearToVoltaRepeats =
#(define-music-function (parser location m) (ly:music?)
(parallel-music-results-postprocess m))
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\LinearToVoltaRepeats {
c'1
\pmpStartRepeat
d'
\pmpStartAlternative
e'
\pmpStartAlternative
f'
\pmpStartRepeat
g'
\pmpStartAlternative
a'
\pmpStartAlternative
b'
\pmpStartRepeatEx #5
c''
\pmpStartAlternativeEx #"1., 3."
d''
\pmpStartAlternativeEx #"2., 4."
e''
\pmpStartAlternative
f''
\pmpEndRepeat
g''
}
A propos, bis jetzt sind nur Zeichenketten (Strings, unformatierte Texte) als Klammerbenennung möglich, keine markups (z. Bsp. um das Wörtchen »Fine« in eine Klammer zu setzen.)
Arnold
-
Hallo xr,
es wird die übergebene »sequentielle Musik« Schritt für Schritt durchgegangen - aber nur in der obersten Ebene - und an den »Markierungen« wird die Liste in Einzellisten unterteilt, um daraus die eingebetteten \repeat volta Strukturen einzufügen.
Dadurch kann später auch \unfoldRepeats angewandt werden, z. Bsp. um Midi zu erzeugen.
Wollte man nur die Grafik, könnte man sich eine Liste der in Handbuch nachzulesenden manuellen Wiederholungszeichen- und Voltenklammern in einer eigenen Datei zusammenstellen (und in jedes neue Projekt mittels \include einbinden). Jedes dort abgelegte Schnipsel enthält alles was an einem "Taktstrich" zu passieren hat, im einfachsten Fall nur die Taktstrichdefinition, meistens Taktstrich- und Volta-Definition zusammen.
Arnold.
-
Wollte man nur die Grafik, könnte man sich eine Liste der in Handbuch nachzulesenden manuellen Wiederholungszeichen- und Voltenklammern in einer eigenen Datei zusammenstellen (und in jedes neue Projekt mittels \include einbinden). Jedes dort abgelegte Schnipsel enthält alles was an einem "Taktstrich" zu passieren hat, im einfachsten Fall nur die Taktstrichdefinition, meistens Taktstrich- und Volta-Definition zusammen.
Das ist quasi die Antwort. Ich habe nämlich nicht gesehen, dass man eine Voltenklammer auch manuell erzeugen kann. Die Befehle lauten ganz einfach:
\set Score.repeatCommands = #'((volta "2"))
\set Score.repeatCommands = #'((volta #f))
Es ist natürlich ganz einfach, daraus eine Funktion zu basteln.
Vielen Dank,
Xaver
-
Wieder was gelernt :)
-
Da die Funktionen doch nicht so einfach zu erzeugen waren, wie ich behauptet habe - zumindest nicht für mich - poste ich nun doch noch die Lösung:
Klammer =
#(define-music-function
(parser location num)
(number?)
(define liste '(list (volta "1")) )
(set! nr (integer->char (+ 48 num)) )
(set! nr (make-string 1 nr))
(set-car! (cdr(car (cdr liste))) nr)
#{
\set Score.repeatCommands = # liste
#})
Klammer_aus =
#(define-music-function
(parser location)
()
#{
\set Score.repeatCommands = #'((volta #f))
#})
Das funktioniert für die Zahlen 1 - 9, für höhere müsste man den Code erweitern.
Gibt es in Scheme eine einfache Funktion, um Integer jeglicher Größe in Strings umzuwandeln?
-
Ja, gibt es, hier z.B. nachzulesen (https://ds26gte.github.io/tyscheme/index-Z-H-4.html#node_sec_2.2.4)
-
Ok, dann gehts doch einfacher für jede Zahl:
nr = ""
Klammer =
#(define-music-function
(parser location num)
(number?)
(define liste '(list (volta "1")) )
(set! nr (number->string num))
(set-car! (cdr(car (cdr liste))) nr)
#{
\set Score.repeatCommands = # liste
#})
-
Mein Scheme ist wieder eingerostet, aber das geht sicher noch einfacher.
Was genau macht dein Programm, hängt es num als String an die Liste?
-
Ich habe hier nochmal den vollständigen Code. Funktioniert leider immer noch nicht. Statt Klammer 1 und Klammer 2 wird 2x Klammer 2 ausgegeben. Außerdem bekomme ich folgende Fehlermeldung:
Warnung: Wiederholungsklammer bereits vorhanden, wird vorzeitig beendet
\version "2.18.2"
#(define liste '(list (volta "1")) )
txt = ""
Klammer =
#(define-music-function
(parser location num)
(number?)
(set! txt (number->string num))
(set-car! (cdr(car (cdr liste))) txt)
#{
\set Score.repeatCommands = # liste
#})
Klammer-aus =
#(define-music-function
(parser location)
()
#{
\set Score.repeatCommands = #'((volta #f))
#})
\score {
<<
\relative {
\time 4/4
f'4 g f f
\Klammer 1
e4 f e e
g4 e e e
f4 g f f
\Klammer-aus
\bar ":|."
\Klammer 2
e4 f e e
g4 e e e
\Klammer-aus
c4 d c c
}
>>
}
-
So, Stunden später habe ich jetzt endlich eine funktionierende Lösung.
Von C oder Python kommend, kann einen Scheme echt in die Verzweiflung treiben.
\version "2.18.2"
txt = ""
Klammer =
#(define-music-function
(parser location num)
(number?)
(define listeA '(list (volta "1")) )
(define listeB '( (volta #f) (volta "2")) )
(newline)
( if ( < num 2)
(begin
(set! txt (number->string num))
(set-car! (cdr(car(cdr listeA))) txt)
#{
\set Score.repeatCommands = #listeA
#}
)
(begin
(set! txt (number->string num))
(set-car!(cdr(car(cdr listeB))) txt )
#{
\set Score.repeatCommands = #listeB
#}
)
)
)
Klammer-aus =
#(define-music-function
(parser location)
()
#{
\set Score.repeatCommands = #'((volta #f))
#})
\score {
<<
\relative {
\time 4/4
f'4 g f f
\Klammer 1
e4 f e e
g4 e e e
f4 g f f
\Klammer-aus
\bar ":|."
\Klammer 3
e4 f e e
g4 e e e
\Klammer-aus
c4 d c c
}
>>
}
[code]
-
Ah - ich werde doch noch wahnsinnig!
Dummerweise überschreibt immer die letzte Zahl alle Zahlen der vorangegangenen Klammern. Das fällt natürlich erst bei mehreren Klammern auf.
Weiß irgendwer warum das so ist? Werden Variablen an den Lilypond Parser übergeben und erst am Schluss ausgewertet? Oder was mache ich falsch?
-
Meine Lösung, die nun wirklich funktioniert, wird jetzt nicht mehr berechnet, sondern ist fest vorgegeben. Geht wohl schneller, als mir noch weiter über Lösungsmöglichkeiten den Kopf zu zerbrechen.
\version "2.18.2"
Klammer =
#(define-music-function
(parser location num)
(number?)
(define listeA '((volta "1")) )
(define listeB '( (volta #f) (volta "2")) )
(define listeC '( (volta #f) (volta "3")) )
(define listeD '( (volta #f) (volta "4")) )
(define listeE '( (volta #f) (volta "5")) )
(define listeF '( (volta #f) (volta "6")) )
(define listeG '( (volta #f) (volta "7")) )
(define listeH '( (volta #f) (volta "8")) )
(define listeI '( (volta #f) (volta "9")) )
( cond
(( = num 1)
#{ \set Score.repeatCommands = #listeA #})
(( = num 2)
#{ \set Score.repeatCommands = #listeB #})
(( = num 3)
#{ \set Score.repeatCommands = #listeC #})
(( = num 4)
#{ \set Score.repeatCommands = #listeD #})
(( = num 5)
#{ \set Score.repeatCommands = #listeE #})
(( = num 6)
#{ \set Score.repeatCommands = #listeF #})
(( = num 7)
#{ \set Score.repeatCommands = #listeG #})
(( = num 8)
#{ \set Score.repeatCommands = #listeH #})
(( = num 9)
#{ \set Score.repeatCommands = #listeI #})
)
)
Klammer-aus =
#(define-music-function
(parser location)
()
#{
\set Score.repeatCommands = #'((volta #f))
#})
\score {
<<
\relative {
\time 4/4
f'4 g f f
\Klammer 1
e4 f e e
g4 e e e
f4 g f f
\Klammer-aus
\bar ":|."
\Klammer 2
e4 f e e
g4 e e e
\Klammer-aus
\bar ":|."
\Klammer 7
e4 f e e
g4 e e e
\Klammer-aus
c4 d c c
}
>>
}
-
Dummerweise überschreibt immer die letzte Zahl alle Zahlen der vorangegangenen Klammern. Das fällt natürlich erst bei mehreren Klammern auf.
Weiß irgendwer warum das so ist?
\set Score.repeatCommands setzt das context-property 'repeatCommands'.
Mehrere davon zur selben Zeit überschreiben sich gegenseitig, ist völlig normal und auch bei jedem override so.
Für overrides gibts mittlerweile eine Funktion, die den default (falls vorhanden) ausliest und dann mit dem gewünschten Wert erweitert. Für context-properties gibts das meines Wissens nach nicht.
In Deinem Fall mußt Du die bisherigen Setzungen von repeatCommands zum gegebenen Zeitpunkt auslesen, Deine Werte hinzufügen und neu zuweisen.
Such mal hier im Forum nach setRepeatCommand
Ob mein damaliger 2.16.-code heute noch funktioniert habe ich allerdings nicht getestet.
Gruß,
Harm
-
Danke für deinen Hinweis.
Darum kümmere ich mich allerdings erst wieder, wenn nochmal ein ähnliches Problem auftaucht.
Xaver
-
Hallo,
und jetzt gebe ich nochmal meinen Senf dazu - Schwerpunkt meine forgestellten Funktionen:
- Mit meinem Beispiel kann man so Unsinniges erzeugen wie ein \repeat volta mit mehr Alternativen als Wiederholungen! Meine Routine sollte dann automatisch die Wiederholungszahl erhöhen.
- Daß leider noch kein Markup statt eines Textes als Voltenzahl übergeben werden kann, habe ich in einem vorherigen Post schon erwähnt. Wird ergänzt.
- Beim Setzen der Voltenzahl (per applyContext-Funktion) habe ich bisher nur den neuen Eintrag vorangestellt, also wurde der alte (automatische) Eintrag eigentlich gar nicht gelöscht. Und bei einer zwanzigstimmigen Partitur stände der Eintrag dann auch zwanzigmal in der Liste. - applyContext-Funktion wird erweitert.
- Diese applyContext-Funktion finde ich, wäre dann allgemein sehr nützlich anzuwenden: zwar \repeat volta mit \alternative notieren, aber zu Anfang der Alternativen einfach einen abweichenden Volta-Text angeben ohne auf Beginn und Ende der Voltaklammern achten zu müssen, also etwa so:
\repeat volta 3 {
...
} \alternative { {
...
} { \setVoltaText #(make-text-markup (make-bold-markup (make-fontsize-markup 2 "Fine")))
...
\bar "|."
} { \setVoltaText #"2."
...
} } - Dann ließen sich auf ähnliche Weise auch noch "unabhängig arbeitende" Funktionen \setVoltaStart und \setVoltaEnd definieren, welche ebenfall die anderen Einträge in der repeatCommands-Liste stehen lassen.
Wenn ich das alles habe, werde ich es hier veröffentlichen. Voraussichtlich Anfang nächster Woche.
Arnold
-
So, jetzt meine aktualiserite Version:
\version "2.18.2"
#(define (string-or-markup? x) (or (string? x) (markup? x)))
pmpStartRepeat = #(define-music-function (parser location) () (make-music 'MarkEvent
'label "<parallel-music-results-postprocess:start repeat volta 2>"))
pmpStartRepeatEx = #(define-music-function (parser location count) (integer?) (make-music 'MarkEvent
'label (string-append "<parallel-music-results-postprocess:start repeat volta " (number->string count) ">")
'extra-int count))
pmpEndRepeat = #(define-music-function (parser location) () (make-music 'MarkEvent
'label "<parallel-music-results-postprocess:end repeat>"))
pmpStartAlternative = #(define-music-function (parser location) () (make-music 'MarkEvent
'label "<parallel-music-results-postprocess:start alternative>"))
pmpStartAlternativeEx = #(define-music-function (parser location s) (string-or-markup?) (make-music 'MarkEvent
'label (string-append "<parallel-music-results-postprocess:start named alternative:" (if (markup? s) "(markup)" s) ">")
'extra-text s))
#(define (pmpMark mus)
(if (ly:music? mus)
(let
((eventtype (ly:music-property mus 'name))
(labeltext (ly:music-property mus 'label))
)
(if (and (eq? eventtype 'MarkEvent) (string? labeltext))
(let*
((head "<parallel-music-results-postprocess:")
(headlen (string-length head))
(labellen (string-length labeltext)))
(if (<= labellen headlen) 0
(let*
((labelhead (string-copy labeltext 0 headlen))
(labelpost (string-copy labeltext headlen))
(postlen (string-length labelpost))
(labelshortpost (string-copy labelpost 0 (min postlen 19)))
)
(if (string=? labelhead head)
(if (string=? labelshortpost "start repeat volta ") 1
(if (string=? labelpost "end repeat>") 2
(if (string=? labelpost "start alternative>") 3
(if (string=? labelshortpost "start named alterna") 4
0))))
0))))
0))
0))
#(define (pmpMarkRepeatCount mus)
(if (ly:music? mus)
(let
((eventtype (ly:music-property mus 'name))
(labeltext (ly:music-property mus 'label)))
(if (and (eq? eventtype 'MarkEvent) (string? labeltext))
(let*
((head "<parallel-music-results-postprocess:")
(headlen (+ (string-length head) 19))
(labellen (string-length labeltext)))
(if (<= labellen headlen) 2
(let*
((labelhead (string-copy labeltext 0 headlen))
(labelpost (string-copy labeltext headlen (- labellen 1)))
(labelterm (string-copy labeltext (- labellen 1))))
(if (string=? labelterm ">") (string->number labelpost)
2))))
2))
2))
#(define (parallel-music-results-postprocess mus)
(let ((eventtype (ly:music-property mus 'name)))
(if (eq? eventtype 'SequentialMusic)
(let
((es (ly:music-property mus 'elements))
(new-es '())
(active-dest 0)
(rep-collect '())
(alt-collect '())
(alt-list-collect '())
(rep-count 2)
)
(for-each (lambda (ese)
(let ((etype (pmpMark ese)))
(if (eq? etype 2) ;; end repeat ;;
(begin
(if (eq? active-dest 3)
(set! alt-list-collect (append alt-list-collect (cons (make-music 'SequentialMusic 'elements alt-collect) '()))))
(set! new-es (append new-es (cons
(make-music 'VoltaRepeatedMusic 'repeat-count (max rep-count (length alt-list-collect))
'element (make-music 'SequentialMusic 'elements rep-collect) 'elements alt-list-collect) '())))
(set! active-dest 0)
(set! rep-collect '())
(set! alt-collect '())
(set! alt-list-collect '())))
(if (eq? etype 1) ;; start repeat ;;
(begin
(if (eq? active-dest 3)
(set! alt-list-collect (append alt-list-collect (cons (make-music 'SequentialMusic 'elements alt-collect) '()))))
(if (>= active-dest 1) ;; terminate existing repeat and start new repeat block
(set! new-es (append new-es (cons
(make-music 'VoltaRepeatedMusic 'repeat-count (max rep-count (length alt-list-collect))
'element (make-music 'SequentialMusic 'elements rep-collect) 'elements alt-list-collect) '()))))
(set! active-dest 1)
(set! rep-collect '())
(set! alt-collect '())
(set! rep-count (ly:music-property ese 'extra-int 2))
(set! alt-list-collect '())))
(if (and (eq? etype 3) (>= active-dest 1)) ;; start alternative (only when repeat allready started) ;;
(if (eq? active-dest 3) ;; next alternative ;;
(begin
(set! alt-list-collect (append alt-list-collect (cons (make-music 'SequentialMusic 'elements alt-collect) '())))
(set! alt-collect '()))
(set! active-dest 3)))
(if (and (eq? etype 4) (>= active-dest 1)) ;; start named alternative (only when repeat allready started) ;;
(begin
(if (eq? active-dest 3) ;; next alternative ;;
(set! alt-list-collect (append alt-list-collect (cons (make-music 'SequentialMusic 'elements alt-collect) '())))
(set! active-dest 3))
(set! alt-collect (cons
(make-music 'ApplyContext
'procedure (lambda (ctx)
(let ((score-ctx (ly:context-find ctx 'Score)))
(if (ly:context? score-ctx)
(let* ((old-rc (ly:context-property score-ctx 'repeatCommands '())) ; (boolean? (cadr v))
(filtered-rc (filter (lambda (v)
(or (not (pair? v)) (not (eq? (car v) 'volta)) (boolean? (cadr v)) )) old-rc)))
(ly:context-set-property! score-ctx 'repeatCommands
(cons (list 'volta (ly:music-property ese 'extra-text "?")) filtered-rc)))))))
'()))))
(if (eq? etype 0)
(begin
(if (eq? active-dest 0)
(set! new-es (append new-es (cons ese '()))))
(if (eq? active-dest 1)
(set! rep-collect (append rep-collect (cons ese '()))))
(if (eq? active-dest 3)
(set! alt-collect (append alt-collect (cons ese '()))))))))
es)
(if (eq? active-dest 1) ;; start repeat is open ;;
(set! new-es (append new-es (cons
(make-music 'VoltaRepeatedMusic 'repeat-count (max rep-count (length alt-list-collect))
'element (make-music 'SequentialMusic 'elements rep-collect) 'elements alt-list-collect) '()))))
(if (eq? active-dest 3) ;; start alternative is open ;;
(begin
(set! alt-list-collect (append alt-list-collect (cons (make-music 'SequentialMusic 'elements alt-collect) '())))
(set! new-es (append new-es (cons
(make-music 'VoltaRepeatedMusic 'repeat-count (max rep-count (length alt-list-collect))
'element (make-music 'SequentialMusic 'elements rep-collect) 'elements alt-list-collect) '())))))
(ly:music-set-property! mus 'elements new-es)
mus)
mus)))
LinearToVoltaRepeats =
#(define-music-function (parser location m) (ly:music?)
(parallel-music-results-postprocess m))
setVoltaText =
#(define-music-function (parser location on-ctx str) ((symbol? 'Score) string-or-markup?)
(make-music 'ApplyContext
'procedure (lambda (ctx)
(let ((this-ctx (ly:context-find ctx on-ctx)))
(if (ly:context? this-ctx)
(let* ((old-rc (ly:context-property this-ctx 'repeatCommands '())) ; (boolean? (cadr v))
(filtered-rc (filter (lambda (v)
(or (not (pair? v)) (not (eq? (car v) 'volta)) (boolean? (cadr v)) )) old-rc)))
(ly:context-set-property! this-ctx 'repeatCommands
(cons (list 'volta str) filtered-rc))))))))
setVoltaEndInContext =
#(define-music-function (parser location on-ctx) (symbol?)
(for-each display (list "\n on-ctx = " on-ctx " "))
(make-music 'ApplyContext
'procedure (lambda (ctx)
(let ((this-ctx (ly:context-find ctx on-ctx)))
(if (ly:context? this-ctx)
(let* ((old-rc (ly:context-property this-ctx 'repeatCommands '())) ; (boolean? (cadr v))
(already-in-rc (filter (lambda (v)
(and (pair? v) (eq? (car v) 'volta) (boolean? (cadr v)) (not (cadr v)) )) old-rc)))
(if (null? already-in-rc)
(ly:context-set-property! this-ctx 'repeatCommands
(cons (list 'volta #f) old-rc)))))))))
setVoltaEnd =
#(define-music-function (parser location) ()
(make-music 'ApplyContext
'procedure (lambda (ctx)
(let ((this-ctx (ly:context-find ctx 'Score)))
(if (ly:context? this-ctx)
(let* ((old-rc (ly:context-property this-ctx 'repeatCommands '())) ; (boolean? (cadr v))
(already-in-rc (filter (lambda (v)
(and (pair? v) (eq? (car v) 'volta) (boolean? (cadr v)) (not (cadr v)) )) old-rc)))
(if (null? already-in-rc)
(ly:context-set-property! this-ctx 'repeatCommands
(cons (list 'volta #f) old-rc)))))))))
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\LinearToVoltaRepeats {
c'1
\pmpStartRepeat
d'
\pmpStartAlternative
e'
\pmpStartAlternative
f'
\pmpStartRepeatEx #3 % impizit wird die zuletzt gestartete Wiederholung
g'
\pmpStartAlternative
a'
\pmpStartAlternativeEx #(make-text-markup (make-bold-markup (make-fontsize-markup 2 "Fine")))
b'
\bar "|."
\pmpStartAlternativeEx #"2."
c''
\pmpStartRepeatEx #5
d''
\pmpStartAlternativeEx #"1., 3."
e''
\pmpStartAlternativeEx #"2., 4."
f''
\pmpStartAlternative
g''
\pmpEndRepeat
a''
\bar "|."
\once \override Score.RehearsalMark.direction = #DOWN
\once \override Score.RehearsalMark.self-alignment-X = #RIGHT
\once \override Score.RehearsalMark.break-visibility = #begin-of-line-invisible
\mark \markup \fontsize #-2 "D.C. al Fine"
}
\LinearToVoltaRepeats {
g1
\pmpStartRepeat
a
\pmpStartRepeat
b
\pmpEndRepeat
c'
\pmpStartRepeatEx #3
d'
\pmpStartAlternative
e'
\pmpStartAlternative
f'
\pmpStartRepeat
g'
\pmpStartAlternative
a'
\pmpStartAlternative
b'
\pmpStartRepeat
c''
\pmpStartAlternative
d''
\pmpStartAlternative
e''
\pmpStartAlternative
f''
\pmpEndRepeat
\bar "|."
}
RM = {
\repeat volta 2 {
c'1 cis'
} \alternative { {
\setVoltaText #"7."
d' dis'1
} {
\setVoltaText #"3."
e'2 e' es'1
} }
}
RMx = {
\repeat volta 2 {
c'1 cis'
} \alternative { {
\setVoltaText #'Staff #"777."
d' dis'
} {
\setVoltaText #'Staff #"333."
e' es'
} }
}
RMxu = {
\repeat volta 2 {
c'1 cis'
} \alternative { {
\setVoltaText #'Staff #"777."
d' \unset Staff.repeatCommands dis'
} {
\setVoltaText #'Staff #"333."
<< { e' } { s2 \unset Staff.repeatCommands } >> es'1
} }
}
\score {
<<
\new Staff { \RM \bar "|." }
\new Staff \with {
\consists Volta_engraver
} { \RM \bar "|." }
>>
\header {
piece = \markup \justify {
Mit \bold "\\setVoltaText" kann man den Starttext
der Volte überschreiben, ohne die anderen \bold repeatCommands
zu überschreiben. Implizit bedeutet das immer auch einen Start
der Volta.
}
}
}
\defineBarLine "|-V" #'("|" "" "|")
\allowVoltaHook "|-V"
LM = {
c'1 \bar ".|:"
d'
\setVoltaText "7."
e'
\bar ":|."
\setVoltaEnd
\setVoltaText "3."
f'
\setVoltaEnd
g'
\setVoltaText #(make-text-markup (make-bold-markup (make-fontsize-markup 2 "bis")))
a' b'
\setVoltaEnd \bar "|-V"
c''
\bar "|."
}
\score {
<<
\new Staff { \LM \bar "|." }
\new Staff \with {
\consists Volta_engraver
} { \LM \bar "|." }
>>
\header {
piece = \markup \column { \justify {
Mit \bold "\\setVoltaEnd" kann man das Ende der Voltenklammer
festlegen. Legt man außderdem die Wiederholungszeichen über \bold "\\bar"
fest, so kann man damit alle (zu druckenden) Wiederholungen ohne Nutzung von
\bold "\\repeat volta" beschreiben. Folglich werden diese auch nicht
durch \bold "\\unfoldRepeats" aufgelöst.
}
\vspace #0.3
\justify {
Übrigens, \bold »bis« ist eine alte Bezeichnung, diesen (kurzen) Abschnitt
zwei Mal zu spielen. Damit die benutzte »Voltenklammer« am Schluß an dem
einfachen Taktstrich auch heruntergezogen wird, in den anderen Fällen aber
nicht, wurde eine »neuer Taktstrich-Satz« mittels \bold "\\defineBarLine"
definiert und an diesem per \bold "\\allowVoltaHook" der Schlußstrich der
Voltenklammer aktiviert.
}
}}
}
\score {
<<
\new Staff { \RM \RM \RM \bar "|." }
\new Staff \with {
\consists Volta_engraver
} { \RM \RMx \RM \bar "|." }
>>
\header {
piece = \markup \justify {
Im Prizip kann man auch in jeder Notenzeile
(d. h. in jedem Staff-Kontext)
einen anderen Text setzen, wäre da nicht noch
das eine oder andere kleine, störende Problemchen.
}
}
}
\score {
<<
\new Staff { \RM \RM \RM \bar "|." }
\new Staff \with {
\consists Volta_engraver
} { \RM \RMxu \RM \bar "|." }
>>
\header {
piece = \markup \justify {
Ein \bold "\\unset Staff.repeatCommands" kurz nach
\bold "\\setVoltaText" hilft erst einmal. Aber dieses
\italic { kurz danach } muß auch die Zeitschritte der anderen
Stimmen mit einschließen, und die Score-weit gesetzte
Bezeichnung geht auch noch verloren!
}
}
}
Dabei mußte ich feststellen, daß ich mit \setVoltaText zwar unterschiedliche Bezeichner in die Voltaklammern auf jeder Notenzeile (Staff) setzen kann, aber mit \consists Volta_engraver allein im Staff-Kontext dann doch noch eingies durcheinander kommt.
Arnold
-
Ich habe jetzt beim erneuten Probieren doch noch mal eine einfache funktionierende Lösung gefunden.
\version "2.18.2"
volta =
#(define-scheme-function
(parser location num)
(string?)
(define voltaTxt "")
(set! voltaTxt num)
#{
\set Score.repeatCommands = #(list (list 'volta voltaTxt) )
#})
volta-ende = {
\set Score.repeatCommands = #'((volta #f))
}
\relative c' {
c1
\volta "1. wandeln"
c4 b d e
\volta-ende
\bar ":|."
\volta "8,4 hallo Volta"
a4 b c d
a4 b c d
\bar ":|."
\volta-ende
a4 g f g
\volta "12, 53"
e d c g
\volta-ende
\bar "||"
}
-
Ich habe nochmal eine Verständnisfrage.
Folgender Code funktioniert:
\version "2.18.2"
volta =
#(define-scheme-function
(parser location num)
(number?)
(define nr "")
(define voltaTxt (list (list 'volta "neu") ))
;(define voltaTxt '( (volta "neu") ))
(set! nr (number->string num))
(display voltaTxt)
(set-car! (cdar voltaTxt) nr)
(newline)
#{
\set Score.repeatCommands = #voltaTxt
#})
volta-ende = {
\set Score.repeatCommands = #'((volta #f))
}
\relative c' {
c1
\volta 1
c4 b d e
\volta-ende
\bar ":|."
c4 b d e
\volta 2
a4 b c d
a4 b c d
\bar ":|."
\volta-ende
a4 g f g
\volta 5
e d c g
\volta-ende
\bar "||"
}
Wenn ich allerdings die Definition (define voltaTxt (list (list 'volta "neu") )) durch die auskommentierte Zeile (define voltaTxt '( (volta "neu") )) ersetze, funktioniert der Code nicht mehr.
(Das war mein ursprünglich nicht funktionierender Code)
Ich erhalte als Text unter der Klammer immer den zuletzt eingegebenen Wert.
(display voltaTxt) zeigt mir bei jedem Aufruf veränderte Werte, obwohl voltaTxt doch jedesmal neu definiert wird und keine globale Variable sein sollte. Das verstehe ich nicht.
Den Ausruck '( (volta "neu") ) verstehe ich ohnehin nicht. Was bedeutet das Apostroph vor der Klammer?
Wenn ich es richtig verstehe, sollte es eigentlich so sein:
Ein Apostroph vor einem String ist die Kurzform von "String".
Ein Apostroph vor einer Klammer gibt eine Liste an.
Verstehe ich das falsch?
Gruß,
Xaver
-
Hallo xr,
auch wenn ich auf die Schnelle nicht im Detail antworten kann, Ich erhalte als Text unter der Klammer immer den zuletzt eingegebenen Wert.
klingt doch sehr nach »Parameterübergabe als Zeiger, und danach wurde die Struktur wohin der Zeiger zeigt, verändert«.
Solch eine Änderung (ohne die Daten zu kopieren) machen vor allem die Scheme-Funktionen mit dem Ausrufezeichen, wie z. Bsp. set-car!.
Und die ly:music-Struktur wird ja erst dann durch die Engraver analysiert, wenn die ly:music-Datenstruktur mit allen deinen Kommandos komplett erstellt ist. \displayMusic sollte auch anzeigen, daß da jetzt das gleiche drin steht, aber genauso eine selbstgeschriebene Scheme-Funktion, welche die Daten komplett anzeigen würde (z. Bsp. mittels \musicMap).
Arnold
Arnold
-
klingt doch sehr nach »Parameterübergabe als Zeiger, und danach wurde die Struktur wohin der Zeiger zeigt, verändert«.
Für mich klingt das auch so.
Mich wundert nur, dass
(define voltaTxt (list (list 'volta "neu") ))
etwas anderes erzeugt, als
(define voltaTxt '( (volta "neu") ))
Display voltaTxt zeigt mir für beide Zeilen beim ersten Durchlauf das Gleiche an. Allerdings könnten es auch andere Datentypen sein, was ich so ja nicht sehe.
Außerdem wundert mich, wie oben geschrieben, dass beim erneuten Aufruf die zweite Zeile geändert wurde, obwohl sie doch neu definiert wurde. Meines Erachtens müsste sie also wieder den Ausgangscode beinhalten, was sie aber nicht tut.
eine selbstgeschriebene Scheme-Funktion, welche die Daten komplett anzeigen würde (z. Bsp. mittels \musicMap)
Wie funktioniert das? Gibts da ein Beispiel?
-
Hallo xr,
\musicMap ist eigentlich ganz einfach:
Definiere eine Scheme-Funktion mit einen einzigen Aufrufparameter (ly:music?), die auch wieder ein ly:music? zurückgibt.
Wenn nichts verändert wurde, gebe das Original vom Aufruf zurück, sonst die Änderung bzw. den mit make-musik erstelleten Ersatz.
In der Funktion frage zuerst nach dem Music-Property 'name, dann entscheide was du damit tun willst (z. Bsp. informationen ausgeben oder nicht)
Die Scheme-Variante von \musicMap nennt sich music-map - kaum zu glauben :-).
Deine Funktion wird dann "bottom-up" vom \musicMap angewandt.
Kurzes Rumpf-Beispiel:#(define (my-info mus)
(let ((m-name (ly:music-property mus 'name)))
(if (eq? m-name 'SequentialMusic)
...
)))
\musicMap #my-info \MeineMusik
Im anderen Fall schreibt man eine Scheme-Funktion, welche sich rekursiv aufruft - am besten dann mit zwei Aufrufparametern, einem "Tiefenzähler" der bei den Textausgaben für ein Einrücken genutzt werden kann, und dann der (obligatorische) ly:music-Parameter.
Auch hier ist die erste Aktion, den Typ des Elements herauszufinden, und bei entsprechenden Type dann 'element und/oder 'elements weiter untersuchen zu lassen.
Grob skizziert:#(define (my-struct-info indent mus)
(let ((m-name (ly:music-property mus 'name)))
(for-each display (list "\n" indent " Typ = " m-name " "))
(if (eq? m-name 'SequentialMusic)
(for-each (lambda (m) (my-struct-info (+ indent 1) x)) (ly:music-property mus 'elements)))
(if (eq? m-name 'SimultaneousMusic)
(for-each (lambda (m) (my-struct-info (+ indent 1) x)) (ly:music-property mus 'elements)))
...
))
Arnold