Deutsches Lilypond Forum (Archiv)

Allgemein => Fragen zu Funktionen => Thema gestartet von: xr am Samstag, 18. Februar 2017, 11:46

Titel: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
Beitrag von: xr am Samstag, 18. Februar 2017, 11:46
EDIT: DER THREADTITEL WURDE GEÄNDERT
ursprünglicher Titel: vom Grob NoteColumn einen Lyrics Grob finden

Hi,

ich versuche, Phrasierungsbögen statt unter die Noten unter die Lyrics zu setzen. Das klappt soweit (siehe Beispiel Code).
Allerdings werden die Bögen bisher nur von Note zu Note gesetzt. Ich würde sie allerdings gern vom Wortanfang bis zum Wortende, die der jeweiligen Note zugehören, setzen.

Was ich finden kann, ist den Grob der Spalte, in der der Phrasierungsbogen gesetzt wird. Weiß jemand, wie ich von dessem Parent ( Grob NoteColumn ) zu dem Grob gelangen kann, der den Lyrics Text setzt?

Hier mal mein Code:
\version "2.18.2"

%%%%%%%--------------------
% HELFER
%%%%%%%--------------------
#(define (disp vals) 
     (newline)
     (display vals)
     )

#(define (loop vals)
     (define n 0)
    (for-each (lambda (x)
                  (display n)
                  (if (list? n) (loop n) n)
                  (newline)
                  (set! n (+ 1 n))
                 (display x)
                 (newline)
                 )
        vals )
    )

#(define (nth n l)
  (if (or (> n (length l)) (< n 0))
    (error "Index out of bounds.")
    (if (eq? n 0)
      (car l)
      (nth (- n 1) (cdr l)))))
     
#(define zaehler 0)
#(define zaehler-b "0")

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


#(define (shape-slur grob)
     
     (define xy (ly:slur::calc-control-points grob))
     
     (define first-sib 0)
     (define second-sib 0)
     (define third-sib 0)
     
     (define props-a 0)
     (define props-b 0)
     (define props-c 0)

     (define newY 0)
     
         
     
   (let* (
          ;; have we been split?
          (orig (ly:grob-original grob))
          ;; if yes, get the split pieces (our siblings)
          (siblings (if (ly:grob? orig)
                        (ly:spanner-broken-into orig) '() ))
          (total-found (length siblings))                 
         
          )
       
       (disp "-------------------------------------------------") 


       
        (ly:grob-set-nested-property! grob (list 'details 'spanner-id ) zaehler)
        (ly:grob-set-property! grob  'annotation (number->string zaehler))
               
        (disp zaehler-b)
        (set! zaehler (+ zaehler 1))
        (set! zaehler-b (number->string zaehler))
       
       
        (disp  (ly:grob-parent grob 0))

       
        (cond
          ((= 0 total-found)
            (disp "");0 gefunden" )
            )
          ((= 1 total-found)
            (disp "");(disp "1 gefunden" )
            )
          ((= 2 total-found)
            (disp "");(disp "2 gefunden" )
            )
          ((= 3 total-found)
            (disp "3 gefunden" )
           
           
            (set! first-sib  (nth 0 siblings))
            (set! second-sib (nth 1 siblings))
            (set! third-sib (nth 2 siblings))
           
            (set! props-a (ly:slur::calc-control-points first-sib))
            (set! props-b (ly:slur::calc-control-points second-sib))
            (set! props-c (ly:slur::calc-control-points third-sib))
           
            (cond
                ((equal? xy props-a)
                    (disp "ist 0")
                    )
                ((equal? xy props-b)
                    (disp "ist 1")
                    )
                ((equal? xy props-c)
                    (disp "ist 2")
                    )
                (else (disp "nicht gefunden ###"))
                )
           
            (disp xy)
            (disp props-a)
            (disp props-b)
            (disp props-c)
           
            )
          (else (disp "nicht gefunden"))
            )
                   
    (disp "")
    )
    xy

)


shapeSlur =
#(define-music-function (parser location )
    () 
   
  #{       
      \override Staff.PhrasingSlur.cross-staff = ##t
      \override Staff.PhrasingSlur.outside-staff-priority = ##f
      \override Staff.PhrasingSlur.extra-offset  = #'(0 . -5)     
     
      \override PhrasingSlur.direction = #DOWN     
      \override PhrasingSlur.control-points = #shape-slur
     
  #})




\paper {
  indent = 0
  ragged-right = ##t
}


\score {
  <<
    \new Staff = "staff" {
      \new Voice = "melody" {
        \relative c'' {
              \shapeSlur
              b4\( b b2\) 
              b4 \( b b2
              b b  \break
              b b
              b b \break
              b b \) 
              b4 \( b b \) b
               
              \( b2 b
              b b \) \break
              b \( b
              b b \) 
              b \( b \break
              b b \)
             
            }
      }
    }
    \new Lyrics
    %\with { alignAboveContext = "staff" }
    {
      \lyricsto "melody" {
          Lorem ipsum dolor sit amet, consectetur adipisici elit,
          sed eiusmod tempor incidunt ut labore et dolore magna aliqua.
          Ut enim ad minim veniam, quis nostrud exercitation ullamco
          laboris nisi ut aliquid ex ea commodi consequat. Quis aute
          iure reprehenderit in voluptate velit esse cillum dolore eu
          fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non
          proident, sunt in culpa qui officia deserunt mollit anim id
          est laborum.
      }
    }
  >>
}


\layout {
  \context {
      \Score
      \remove "Bar_number_engraver"     
    }
  \context {
      \Staff
      \override Stem.direction = #UP
  }
}

Grüße,
Xaver
Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: harm6 am Samstag, 18. Februar 2017, 23:49
Hallo,

hier meine Gedanken dazu (zahlreiche Kommentare inline):

\version "2.19.52"

#(define (get-bounding-lyrics-from-note-columns note-columns)
  (let* ((bound-left (car note-columns))
         (bound-right (last note-columns))
         ;; get PaperColumns
         (pap-col-left (ly:grob-parent bound-left X))
         (pap-col-right (ly:grob-parent bound-right X))
         ;; get PaperColumns 'elements
         (pap-col-elts-left
           (ly:grob-array->list
             (ly:grob-object pap-col-left 'elements)))
         (pap-col-elts-right
           (ly:grob-array->list
             (ly:grob-object pap-col-right 'elements)))
         ;; filter for LyricText via 'lyric-syllable-interface
         (lyr-txt-left
           (filter
             (lambda (e) (grob::has-interface e 'lyric-syllable-interface))
             pap-col-elts-left))
         (lyr-txt-right
           (filter
             (lambda (e) (grob::has-interface e 'lyric-syllable-interface))
             pap-col-elts-right)))
  ;; return a pair with the found grobs, if any. Otherwise return #f
  (if (and (pair? lyr-txt-left) (pair? lyr-txt-right))
      (cons
        (car lyr-txt-left)
        (car lyr-txt-right))
      #f)))

phrasingSlurLyricsExtent =
\override PhrasingSlur.after-line-breaking =
#(lambda (grob)
   (let* (;; If 'padding' (see below) is set to a procedure th following might
          ;; be needed, hence it's only commented.
          ;(orig (ly:grob-original grob))
          ;(siblings (if (ly:grob? orig)
          ;              (ly:spanner-broken-into orig)
          ;              '()))
          (orig-cps (ly:slur::calc-control-points grob))
          (ncs (ly:grob-array->list (ly:grob-object grob 'note-columns)))
          (lyric-bounds
            (get-bounding-lyrics-from-note-columns ncs)))
           
     ;; Go further only if LyricTexts was found
     (if lyric-bounds
         (let* ((lyr-txt-left (car lyric-bounds))
                (lyr-txt-right (cdr lyric-bounds))
                (lyr-txt-left-length
                  (interval-length
                    (ly:grob-extent lyr-txt-left lyr-txt-left X)))
                (lyr-txt-right-length
                  (interval-length
                    (ly:grob-extent lyr-txt-right lyr-txt-right X)))
                (bow-x-length
                  (- (car (last orig-cps)) (car (car orig-cps))))
                ;; 'lyr-average' is the attempt to get a reasonable value for
                ;; setting 2nd and 3rd control-point x-value.
                ;; TODO find a better method/calculation
                ;;      With the calculation below, overrides/tweaks for
                ;;      PhrasingSlur will mostly be ignored...
                (lyr-average
                  (min
                    2
                    (/ bow-x-length 3)
                    (/ (+ lyr-txt-left-length lyr-txt-right-length) 2)))
                ;; TODO
                ;; 'padding' could take a procedure returning different values
                ;; whether the PhrasingSlur is broken or not and for first/last
                ;; control-points.
                (padding -0.5))
                             
         ;; set the bounds of PhrasingSlur to the found LyricText-grobs
         (ly:spanner-set-bound! grob LEFT lyr-txt-left)
         (ly:spanner-set-bound! grob RIGHT lyr-txt-right)
       
         ;; Get the new calculated control-points to base the calculation of the
         ;; final ones on this.
         ;; Actually, there's no need for it, but it makes the following
         ;; calculation  much easier.
         (let* ((cps (ly:slur::calc-control-points grob))
                (first-cp (car cps))
                (fourth-cp (fourth cps)))
       
         ;; The final control-points are calculated assuming an always flat
         ;; PrasingSlur
         (if (and (ly:grob? lyr-txt-left) (ly:grob? lyr-txt-right))
             (ly:grob-set-property! grob 'control-points
               (list
                 (cons
                   (- (car first-cp) lyr-txt-left-length padding)
                   (cdr (car orig-cps)))
                 (cons
                   (- (car first-cp) lyr-txt-left-length (- lyr-average))
                   (cdr (second orig-cps)))
                 (cons
                   (+ (car fourth-cp) lyr-txt-right-length (- lyr-average))
                   (cdr (second orig-cps)))
                 (cons
                   (+ (car fourth-cp) lyr-txt-right-length padding)
                   (cdr (car orig-cps)))))))))))

\paper {
  indent = 0
  ragged-right = ##t
}

\score {
  <<
    \new Staff = "staff" {
      \new Voice = "melody" {
        \relative c'' {
          \phrasingSlurLyricsExtent
          %% The 'extra-offset is still needed ofcourse
          %% No clue how to implement Slurs _into_ Lyrics
          \override PhrasingSlur.extra-offset = #'(0 . -4.8)
          b4\( b b2\)
          b4\( b b2
          b b  \break
          b b
          b b \break
          b2 b \)
          b4 \( b b \) b
          \( b2 b
          b b \) \break
          b \( b
          b b \)
          b \( b \break
          b b \)
        }
      }
    }
    \new Lyrics
    %\with { alignAboveContext = "staff" }
    {
      \lyricsto "melody" {
          Lorem ipsum dolor sit amet, consectetur adipisici elit,
          sed eiusmod tempor incidunt ut labore et dolore magna aliqua.
          Ut enim ad minim veniam, quis nostrud exercitation ullamco
          laboris nisi ut aliquid ex ea commodi consequat. Quis aute
          iure reprehenderit in voluptate velit esse cillum dolore eu
          fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non
          proident, sunt in culpa qui officia deserunt mollit anim id
          est laborum.
      }
    }
  >>
}

\layout {
  \context {
      \Score
      \remove "Bar_number_engraver"     
    }
  \context {
      \Staff
      \override Stem.direction = #UP
  }
}

Ein paar Worte zu Deinem Beispiel-code.

Es wird allgemein als schlechter Stil betrachtet (define whatever ...) (set! whatever ...) für lokale Variablen zu verwenden. Dafür gibts let und Verwandte.
Bitte beachte eine sinnvolle indentation und füge Kommentare ein.

All das nicht nur um es potenziellen Helfern leichter zu machen Deinen Code zu bearbeiten, sondern auch für Dich selbst.
Nach einem Jahr hast Du ansonsten dieselben Schwierigkeiten Deinen Code erneut zu verstehen wie ich anfänglich ;)

HTH,
  Harm
Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: xr am Sonntag, 19. Februar 2017, 09:18
Herzlichen Dank für deinen ausführlichen Code. Sehr hilfreich! Da muss ich jetzt erstmal durchschauen.

Eins verstehe ich aber auf Anhieb nicht:
Wieso überschreibst du after-line-breaking (\override PhrasingSlur.after-line-breaking), obwohl doch eigentlich die Control Points bearbeitet und auch zurückgegeben werden?


Was Kommentare angeht, gebe ich dir recht. Mein Code ist auch noch sehr zusammenkopiert und ich suche noch nach einem für mich einheitlichen Stil.
Diese endlosen Klammerschlüsse, die ich überall in Lily-Code sehe, finde ich allerdings sehr unübersichtlich  z.B. (cdr (car orig-cps))))))))))) . Wenn man zum ersten Mal auf den Code schaut, weiß man nie, was alles geschlossen wird. Und wenn mal eine Klammer fehlt, ist auch nicht klar welche. Daher versuche ich eine schließende Klammer für weit auseinanderliegende Ereignisse vereinzelt und auf die gleiche Einrückebene zu setzen wie ihren Anfang.

Gruß,
Xaver
Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: harm6 am Montag, 20. Februar 2017, 00:24
Zitat
Wieso überschreibst du after-line-breaking (\override PhrasingSlur.after-line-breaking), obwohl doch eigentlich die Control Points bearbeitet und auch zurückgegeben werden?

Das after-line-breaking-property ist ein Eintrittspunkt zu dem man eine Funktion ins Geschehen werfen kann, genauso wie before-line-breaking (dies allerdings zu einem früheren Zeitpunkt).
Nicht jedes property erlaubt eine procedure, manchmal ist man auch Beschränkungen unterworfen, die sich daraus ergeben wann dieses property bearbeitet wird.

So habe ich mir angewöhnt zuerst über after-line-breaking zu gehen. Es sei denn mein code würde dann zu spät bearbeitet, dann wechsel ich auf before-line-breaking. Bis auf seltene Ausnahmen klappt das immer. Für diese Ausnahmen gibts idR aber auch Lösungen...
Meistens teste ich dann allerdings, ob das eigentlich gemeinte property direkt angegangen werden kann.
Das habe ich in diesem Fall schlichtweg vergessen ...
Allerdings setzt die procedure ja nicht nur die control-points, sondern auch left- bzw right-bound des PhrasingSlurs. Ob das beim direkten override der control-points auch noch klappt habe ich nicht probiert. ;)
Aber in diesem Fall müsste man
(ly:grob-set-property! grob 'control-points MEINELISTE)
durch
MEINELISTE
ersetzen.

Zitat
Was Kommentare angeht, gebe ich dir recht. Mein Code ist auch noch sehr zusammenkopiert und ich suche noch nach einem für mich einheitlichen Stil.

Eigentlich geht es nicht um einen für Dich akzeptablen Stil, sondern um einen für die Öffentlichkeit akzeptablen Stil ;)
siehe: http://community.schemewiki.org/?scheme-style (http://community.schemewiki.org/?scheme-style) Aus dem CG kopiert.

Zitat
Diese endlosen Klammerschlüsse, die ich überall in Lily-Code sehe, finde ich allerdings sehr unübersichtlich  z.B. (cdr (car orig-cps))))))))))) .

Das ist nicht ly-code, sondern scheme, genauer guile.

Zitat
Wenn man zum ersten Mal auf den Code schaut, weiß man nie, was alles geschlossen wird. Und wenn mal eine Klammer fehlt, ist auch nicht klar welche. Daher versuche ich eine schließende Klammer für weit auseinanderliegende Ereignisse vereinzelt und auf die gleiche Einrückebene zu setzen wie ihren Anfang.

So geht es mit mit anderen Programmiersprachen. Bei all den fehlenden Klammern weiß ich nie wo was anfängt bzw tatsächlich aufhört.
Ich denke, daß ist eine Frage der Gewöhnung.


Gruß,
  Harm
Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: xr am Dienstag, 21. Februar 2017, 10:08
Zitat
Das after-line-breaking-property ist ein Eintrittspunkt zu dem man eine Funktion ins Geschehen werfen kann,
Das ist ein guter Tip! - Habe ich aber noch nicht umgesetzt.

Zitat
Eigentlich geht es nicht um einen für Dich akzeptablen Stil, sondern um einen für die Öffentlichkeit akzeptablen Stil ;)
:) :) :)

Ich bin in meiner Version doch erstmal dabei geblieben, die Control Points zu setzen. - Während dein Code via (ly:spanner-set-bound! grob LEFT lyr-txt-left) die Spanner setzt.
Vielleicht ist letzteres auch die bessere Idee, denn bei mir ragt die rechte Seite einer abschließenden Klammer immer noch über das Wort hinaus. Bei mir werden die Extensions via (ext  (ly:grob-extent lyr-txt note-column X)) berechnet. Vielleicht sind das noch nicht die richtigen Bezugspunkte?

Allerdings will ich noch die Form der Klammer ändern. Bei einer sich in der nächsten Zeile fortsetzenden Klammern soll sich der Bogen am Ende der Zeile nicht so stark schließen. Gleiches bei einer aus der Vorzeile kommenden Klammer. Auch hätte ich gern stärkere Bögen am Anfang und am Ende, so daß die Klammern mehr in den Text hineingreifen.
Für diese Tweaks gibt es wahrscheinlich mehrere Möglichkeiten. Muss ich noch was experementieren.

Mein Code erzeugt auch noch Fehler, wenn kein Text vorhanden ist. Klammern sollten aber auch bei fehlendem Text gesetzt werden. Diese Implementierung fehlt noch.

Hier mal mein neuer Code:
\version "2.18.2"

#(define (get-contr-pts-of-txt note-column)
     
    (let* (               
        ;; get PaperColumns
        (pap-col (ly:grob-parent note-column X))               
        ;; get PaperColumns 'elements
        (pap-col-elements (ly:grob-array->list (ly:grob-object pap-col 'elements)))               
        ;; filter for LyricText by searching for name LyricText in meta alist
        (lyr-txt
            (car (filter
                (lambda (e) (equal? 'LyricText (cdr (assoc 'name (ly:grob-property e 'meta)))))
                pap-col-elements
            ))
        )
        ;; get text extensions
        (ext  (ly:grob-extent lyr-txt note-column X))
        ;; get text
        ;(txt (ly:grob-property lyr-txt 'text))
        )
        ext
    )
)


#(define* (set-controlPoint grob nr xy-values)
     (cond
         ((= 0 nr)
              (set-car! grob xy-values))
         ((= 1 nr)
              (set-car! (cdr grob) xy-values))
         ((= 2 nr)
              (set-car! (cddr grob) xy-values))
         ((= 3 nr)
              (set-car! (cdddr grob) xy-values))
     )
)

 
#(define (set-start-point grob ncs cps)
     ;; Calculates Start-Point in splitted and in single-line slurs
     (let* (
         (ctrl-pts (get-contr-pts-of-txt (first ncs))))
         (set-controlPoint cps 0 (cons  (car ctrl-pts) -1))
     )
)

#(define (set-end-point grob ncs cps)
     ;; Calculates End-Point in splitted and in single-line slurs
     (let* (
          (ctrl-pts2 (get-contr-pts-of-txt (last ncs)))
          (val (+ (cdr ctrl-pts2) (car (last cps))))
         )
         (set-controlPoint cps 3 (cons  val -1) )
     )
)

#(define (shape-single-slur grob ncs cps)
     ;; Sets unsplitted single-line slur
     (set-start-point grob ncs cps)
     (set-end-point grob ncs cps)
)

#(define (shape-start-slur grob ncs cps)
     (set-start-point grob ncs cps)
     ;; Height of Third-Point is written to Fourth-Point
     (set-controlPoint cps 3 (cons  (car (fourth cps))  (cdr (third cps) )))
)

#(define (shape-middle-slur grob ncs cps)
     ;; doesn't do anything
     ;; TODO: what looks an empty procedure like?
     (let (( x 0)) (set! x 1)))

#(define (shape-last-slur grob ncs cps)
    (set-end-point grob ncs cps)
    ;; ;; Height of Second-Point is written to First-Point
    (set-controlPoint cps 0 (cons  (car (first cps))  (cdr (second cps))))
)
     

#(define (shape-slurs grob)
   
    (let* (
            ;; original ControlPoints
            (cps (ly:slur::calc-control-points grob))
            ;; get NoteColumns
            (ncs (ly:grob-array->list (ly:grob-object grob 'note-columns)))
           
            ;; have we been split?
            (orig (ly:grob-original grob))
            ;; if yes, get the split pieces (our siblings)
            (siblings (if (ly:grob? orig)
                    (ly:spanner-broken-into orig)
                    '()))
           
            ;; Find control-points and map with enumeration to list.
            ;; The mapped enumeration helps to find the
            ;; position of a sibling inside a splitted slur.
            (n -1)
            (sib-points (map
               (lambda (gr)
                  (begin
                  (set! n (+ 1 n))
                  (list n (ly:slur::calc-control-points gr))
                  )
               )
               siblings))
           
            ;; Find position if slur is splitted.
            ;; Compares control points of actual calculated grob
            ;; to the control points of the siblings of the original grob.
            ;; Returns an empty list if slur is not splitted.
            (orig-pts (ly:slur::calc-control-points grob))
            (slur-position
                (filter
                    (lambda (e) (equal? (cadr e) orig-pts))
                    sib-points)) 
        )
        ;; call procedures to set cps positions
        (cond
            ((equal? slur-position '())
                (shape-single-slur grob ncs cps))
            ((equal? 0 (caar slur-position ))
                (shape-start-slur grob ncs cps))
            ((equal? (- (length siblings) 1) (caar slur-position ))
                (shape-last-slur grob ncs cps))
            (else
                (shape-middle-slur grob ncs cps))
        )
        cps
    )
   
)


shapeSlur =
#(define-music-function (parser location )
    ()
   
  #{       
      \override Staff.PhrasingSlur.cross-staff = ##t
      \override Staff.PhrasingSlur.outside-staff-priority = ##f
      \override Staff.PhrasingSlur.extra-offset  = #'(0 . -5)     
     
      \override PhrasingSlur.direction = #DOWN     
      \override PhrasingSlur.control-points = #shape-slurs
  #})


\paper {
  indent = 0
  ragged-right = ##t
}


\score {
  <<
    \new Staff = "staff" {
      \new Voice = "melody" {
        \relative c'' {
              \shapeSlur
             
                b4 b b2
              b4  b \( b2
              b b  \break
              b b
              b b
              b b \break
              b b \)
              b4  b \( b  b \)
               
               b2 b
              b b  \break
              b \( b
              b b
              b  b \break
              b \)  b
             
            }
      }
    }
    \new Lyrics
    %\with { \override LyricText.color = #test }
    {
      \lyricsto "melody" {
          Lorem ipsum dolor sit amet, consectetur adipisici elit,
          sed eiusmod tempor incidunt ut labore et doloredolore magna aliqua.
          Ut enim ad minim veniam, quis nostrud exercitation ullamco
          laboris nisi ut aliquid ex ea commodi consequat. Quis aute
          iure reprehenderit in voluptate velit esse cillum dolore eu
          fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non
          proident, sunt in culpa qui officia deserunt mollit anim id
          est laborum.
      }
    }
  >>
}


\layout {
  \context {
      \Score
      \remove "Bar_number_engraver"     
    }
  \context {
      \Staff
      \override Stem.direction = #UP
  }
}

Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: harm6 am Dienstag, 21. Februar 2017, 12:06
Hallo,

leider muss ich mich gleich um meinen regulären Job kümmern, insoweit kann ich mich jetzt nicht mit Deinen Fragen bzw Deinem Code auseinandersetzen.
Doch hier:
Zitat
Auch hätte ich gern stärkere Bögen am Anfang und am Ende, so daß die Klammern mehr in den Text hineingreifen.
weiß ich schlichtweg nicht was Du meinst.
Kannst Du das ausführlicher beschreiben bzw ein Bild davon posten (evtl. gefaked mit einem Grafik-Programm)?

Gruß,
  Harm
Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: xr am Mittwoch, 22. Februar 2017, 11:42
Ich habe nun erstmal Anfänge und Enden des Bogens händisch eingefügt. Das erzeugt allerdings Probleme - siehe letzte Zeile beim Wort "commodi".
Um Verhältnisse zu berechnen, müßte ich an den linken bzw rechten Nachbarn der aktuellen PaperColumn kommen. Dafür kann ich aber leider keinen Zugang finden.
Vergleiche auch das zweite Bild im Anhang, das die Columns nummeriert (6,7,9,11). Kann man diese Nummern auslesen?

Mittels (ly:grob-parent grob X) bekomme ich den Parent. Wie aber bekomme ich mögliche Kinder?
Und welcher Grob enthält alle horizontalen Objekte?
(Ich habe mal ein Snippet aus der Snippet Repository eingefügt, das die Hierarchie eines Grobs anzeigt.)

Im Übrigen habe ich den Score so geändert, das man sieht, wofür meine Einstellung eigentlich gebraucht wird: Um Sprache rhythmisch zu setzen. Die Bögen sollen später zusammengehörige Phrasen verdeutlichen und auch noch zwischen weiteren Stimmen hin und hergehen.

\version "2.18.2"

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% HELFER
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

#(define (disp vals)
     (newline)
     (display vals)
     )

#(define (dloop vals)
    (define n 0)
    (for-each
        (lambda (x)
            (newline)
            (display n)
            (if (list? n) (loop n) n)
            (newline)
            (set! n (+ 1 n))
            (display x)
           
        )
        vals
    )
)

#(define (nth n l)
    (if
        (or (> n (length l)) (< n 0))       
        (error "Index out of bounds.")
        (if
            (eq? n 0)
            (car l)
            (nth (- n 1) (cdr l))
        )
    )
)
     
#(define zaehler 0)
#(define (draw-annotation grob)
    ;; for debugging:
    ;; draws a number to the grob
    (ly:grob-set-nested-property! grob (list 'details 'spanner-id ) zaehler)
    (ly:grob-set-property! grob  'annotation (number->string zaehler))     
    (disp '----------------- )
    (disp zaehler)
    (set! zaehler (+ zaehler 1))
)
#(define (props grob)
     (dloop  (ly:grob-properties grob ) )
     )
#(define (bprops grob)
     (dloop  (ly:grob-basic-properties grob ) )
     )
     
     
     
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% from the snippet repository http://lsr.di.unimi.it/LSR/Item?id=622
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#(define (grob::name grob)
  (assq-ref (ly:grob-property grob 'meta) 'name))

#(define (get-ancestry grob)
  (if (not (null? (ly:grob-parent grob X)))
      (list (grob::name grob)
            (get-ancestry (ly:grob-parent grob X))
            (get-ancestry (ly:grob-parent grob Y)))
      (grob::name grob)))

#(define (format-ancestry lst padding)
   (string-append
    (symbol->string (car lst))
    "\n"
    (let ((X-ancestry
           (if (list? (cadr lst))
               (format-ancestry (cadr lst) (+ padding 3))
               (symbol->string (cadr lst))))
          (Y-ancestry
           (if (list? (caddr lst))
               (format-ancestry (caddr lst) (+ padding 3))
               (symbol->string (caddr lst)))))
      (if (equal? X-ancestry Y-ancestry)
          (string-append
           (format #f "~&")
           (make-string padding #\space)
           "X,Y: "
           (if (list? (cadr lst))
               (format-ancestry (cadr lst) (+ padding 5))
               (symbol->string (cadr lst))))
          (string-append
           (format #f "~&")
           (make-string padding #\space)
           "X: " X-ancestry
           "\n"
           (make-string padding #\space)
           "Y: " Y-ancestry
           (format #f "~&"))))
    (format #f "~&")))

#(define (display-ancestry grob)
   (format (current-error-port)
      "~3&~a~2%~a~&"
      (make-string 36 #\-)
      (if (ly:grob? grob)
          (format-ancestry (get-ancestry grob) 0)
          (format #f "~a is not a grob" grob))))
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

#(define (get-cp cps nr xy)   
    (define wert
        (lambda (x)
            (cond
                ((= xy 0)
                     (car x))
                ((= xy 1)
                     (cdr x))
            )
        )
    )
         
      (cond
          ((= 0 nr)
               (wert (car cps)))
          ((= 1 nr)
               (wert (cadr cps)))
          ((= 2 nr)
               (wert (caddr cps)))
          ((= 3 nr)
               (wert (cadddr cps)))
      )
)



#(define (get-contr-pts-of-txt note-column)
     
    (let* (               
        ;; get PaperColumns
        (pap-col (ly:grob-parent note-column X))               
        ;; get PaperColumns 'elements
        (pap-col-elements (ly:grob-array->list (ly:grob-object pap-col 'elements)))               
        ;; filter for LyricText by searching for name LyricText in meta alist
        (lyr-txt
             (filter
                (lambda (e) (equal? 'LyricText (cdr (assoc 'name (ly:grob-property e 'meta)))))
                pap-col-elements
            )
        )
        ;; set default Extensions in case
        ;; no lyrics are present
        (ext (cons -1 1))
        ;; optional: for displaying the text belonging to the slur
        (txt '-)
        (parent (ly:grob-parent pap-col Y))
        )
       
        (if (= 1 (length lyr-txt))
            (begin
                (set! lyr-txt (car lyr-txt))
                ;; get text extensions
                (set! ext  (ly:grob-extent lyr-txt note-column X))
                ;; optional: for displaying the text belonging to the slur
                (set! txt (ly:grob-property lyr-txt 'text))
                ;(disp txt) 
               
               
                ;; DEBUGGING
               
                (if (equal? txt "amet," )
                    (display-ancestry note-column))
               
                ;(display-ancestry note-column)
                ;(set! txt (ly:grob-parent note-column Y))
                ;(set! txt (ly:grob-parent txt X))
                ;(set! txt ( ly:grob- note-column ))
                ;(display-ancestry txt)
                ;(props txt )
                ;(set! txt ( ly:axis-group-interface::calc-pure-y-common pap-col))
                ;(bprops txt)
               
            )
        )
        ext
    )
)


#(define* (set-controlPoint grob nr xy-values)
     (cond
         ((= 0 nr)
              (set-car! grob xy-values))
         ((= 1 nr)
              (set-car! (cdr grob) xy-values))
         ((= 2 nr)
              (set-car! (cddr grob) xy-values))
         ((= 3 nr)
              (set-car! (cdddr grob) xy-values))
     )
)


 
#(define (set-start-point grob ncs cps)
     ;; Calculates Start-Point in splitted and in single-line slurs
     ;; TODO: exact calculation of 1st and 2nd ControlPoint
     (let* (
         (ctrl-pts (get-contr-pts-of-txt (first ncs)))
         
         (oldX1 (car (second cps)))
         (oldY1 (cdr (second cps)))
         
         (newX0 (- (car ctrl-pts) 1) )
         (newX1 newX0);(/ (+ (* newX0 7) oldX1) 8 ))
         (newY1 (- oldY1 1))
         )
         (set-controlPoint cps 0 (cons  newX0 .5))
         (set-controlPoint cps 1 (cons  newX1 newY1))
         
     )
)

#(define (set-end-point grob ncs cps)
     ;; Calculates End-Point in splitted and in single-line slurs
     ;; TODO: exact calculation of 3rd and 4th ControlPoint
     (let* (
          (ctrl-pts (get-contr-pts-of-txt (last ncs)))
         
          (oldX2 (car (third cps)))
          (oldY2 (cdr (third cps)))
         
          (newX3 (+ (+ (cdr ctrl-pts) (car (last cps))) .5 ))
          (newX2 newX3); (/ (+ (* newX3 7) oldX2) 8 ))         
          (newY2 (- oldY2 1))
         )
         (set-controlPoint cps 3 (cons  newX3 .5) )
         (set-controlPoint cps 2 (cons  newX2 newY2))
     )
)



#(define (shape-single-slur grob ncs cps)
     ;; Sets unsplitted single-line slur
     (set-start-point grob ncs cps)
     (set-end-point grob ncs cps)
)

#(define (shape-start-slur grob ncs cps)
     (set-start-point grob ncs cps)
     ;; Height of Third-Point is written to Fourth-Point
     (set-controlPoint cps 3 (cons  (car (fourth cps))  (cdr (third cps) )))
)

#(define (shape-middle-slur grob ncs cps)
     ;; Y of 1st and 4th ControilPoint are calculated for
     ;; flattening the slur
     (let* (
          (oldY0 (cdr (first cps)))
          (oldX0 (car (first cps)))
          (oldY1 (cdr (second cps)))
          (oldY2 (cdr (third cps)))
          (oldX3 (car (fourth cps)))
          (oldY3 (cdr (fourth cps)))
         
          (newY0 (/ (+ oldY0 oldY1) 2) )
          (newY3 (/ (+ oldY2 oldY3) 2) )
         )
         (set-controlPoint cps 0 (cons  oldX0 newY0) )
         (set-controlPoint cps 3 (cons  oldX3 newY3))
     )
)

#(define (shape-last-slur grob ncs cps)
    (set-end-point grob ncs cps)
    ;; ;; Height of Second-Point is written to First-Point
    (set-controlPoint cps 0 (cons  (car (first cps))  (cdr (second cps))))
)
     

#(define (shape-slurs grob)
   
    (let* (
            ;; original ControlPoints
            (cps (ly:slur::calc-control-points grob))
            ;; get NoteColumns
            (ncs (ly:grob-array->list (ly:grob-object grob 'note-columns)))
            ;; have we been split?
            (orig (ly:grob-original grob))
            ;; if yes, get the split pieces (our siblings)
            (siblings (if (ly:grob? orig)
                    (ly:spanner-broken-into orig)
                    '()))
           
            ;; Find control-points and map with enumeration to list.
            ;; The mapped enumeration helps to find the
            ;; position of a sibling inside a splitted slur.
            (n -1)
            (sib-points (map
               (lambda (gr)
                  (begin
                  (set! n (+ 1 n))
                  (list n (ly:slur::calc-control-points gr))
                  )
               )
               siblings))
           
            ;; Find position if slur is splitted.
            ;; Compares control points of actual calculated grob
            ;; to the control points of the siblings of the original grob.
            ;; Returns an empty list if slur is not splitted.
            (orig-pts (ly:slur::calc-control-points grob))
            (slur-position
                (filter
                    (lambda (e) (equal? (cadr e) orig-pts))
                    sib-points)) 
        )
       
        ;; call procedures to set cps positions
        (cond
            ((equal? slur-position '())
                (shape-single-slur grob ncs cps))
            ((equal? 0 (caar slur-position ))
                (shape-start-slur grob ncs cps))
            ((equal? (- (length siblings) 1) (caar slur-position ))
                (shape-last-slur grob ncs cps))
            (else
                (shape-middle-slur grob ncs cps))
        )
        cps
    )   
)


shapeSlur =
#(define-music-function (parser location )
    ()
   
  #{       
      \override Staff.PhrasingSlur.cross-staff = ##t
      \override Staff.PhrasingSlur.outside-staff-priority = ##f
      %% extra-offset might be set inside #shape-slurs
      \override Staff.PhrasingSlur.extra-offset  = #'(0 . -3.6)     
      %\override Staff.PhrasingSlur.details.free-head-distance = #1
      \override PhrasingSlur.direction = #DOWN     
      \override PhrasingSlur.control-points = #shape-slurs
  #})


\paper {
  indent = 0
  %ragged-right = ##t
  ragged-last = ##t
  system-system-spacing.basic-distance = #18 
}


notes = {
    b4 b b2
    b4 b \( b2
    b b
    b b \break
    b b
    b b
    b b \break
    b b \)
    b4 b
    \( b  b \)   
    b2 b
    b b \break
    b b \(
    b b
    b b \break
    b \) b   
}


\score {
  <<
    \new Staff = "staff" {
       
      \new Voice = "melody" {
         
        \relative c'' {
              \shapeSlur
              \notes
            }
      }
    }
    \new Lyrics

    {
      \lyricsto "melody" {
          Lorem ipsum dolor sit amet, consectetur adipisici elit,
          sed eiusmod tempor incidunt ut labore et doloredolore magna aliqua.
          Ut enim ad minim veniam, quis nostrud exercitation ullamco
          laboris nisi ut aliquid ex ea commodi consequat. Quis aute
          iure reprehenderit in voluptate velit esse cillum dolore eu
          fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non
          proident, sunt in culpa qui officia deserunt mollit anim id
          est laborum.
      }
    }
  >>
}


\layout {
   
  \context {
      \Score
      \remove "Bar_number_engraver" 
    }
  \context {
      \Staff
      \override Stem.direction = #UP
      \remove "Time_signature_engraver"
      \remove "Clef_engraver"
      \override StaffSymbol.line-count = #1
  }

  \context {
      \Lyrics
       \override VerticalAxisGroup.
       nonstaff-relatedstaff-spacing = #'((basic-distance . 3.5))
  }
}


Noch eine allgemeine Frage zu Scheme:
Gibt es eine Möglichkeit, Variablen wie in Python zu bestimmen, z.B.
x,y = 0,1 ( dann ist x = 0 und y =1)
Das fände ich praktisch, da man es bei Koordinaten oft mit pairs zu tun hat.

Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: xr am Mittwoch, 22. Februar 2017, 21:49
Wie man die Nachbarn einer PaperColumn findet, weiss ich inzwischen: Über die Elemente des System Grobs.
Der Befehl sähe etwa so aus:
(Nachbarn-einer-PaperColumn (ly:grob-array->list (ly:grob-object system-grob 'elements)))
Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: harm6 am Freitag, 24. Februar 2017, 22:08
Hallo,

was mich an der ganzen Sache stört ist der nach wie vor nötige override für extra-offset. Das geht fürchterlich schnell krachen.
Insoweit habe ich versucht den PhrasingSlur tatsächlich innerhalb von Lyrics ans laufen zu bekommen, allerdings ohne Erfolg.
Aber einen TextSpanner bekommt man hin. Dessen stencil könnte man dann mit hinreichenden Werten versorgen, um ihn durch make-bow-stencil zu ersetzen.

Aber bevor ich diesen Weg weiter beschreite:
Zitat
Die Bögen sollen später [...] auch noch zwischen weiteren Stimmen hin und hergehen.
Wie soll das nachher aussehen?

Gruß,
  Harm
Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: xr am Sonntag, 26. Februar 2017, 09:53
Hallo,

ein Beispiel, wie es aussehen könnte, ist im Anhang.
Die Bögen sind mit Inkscape gesetzt.
In dem Beispiel sieht man drei Staves, wobei oberer und unterer nur die Lyrics zeigen, der mittlere nur noch für die Anzeige einer Mittellinie gebraucht wird.

Bögen setze ich zusätzlich noch zur Anzeige von Tonhöhenverläufen. Ich brauche also manchmal mehrere Bögen aufeinmal.

Zitat
Insoweit habe ich versucht den PhrasingSlur tatsächlich innerhalb von Lyrics ans laufen zu bekommen, allerdings ohne Erfolg.

Das fände ich spannend.
Ideal wäre für mich, Bögen frei setzen zu können (mal mit, mal ohne Spanner). Auch ohne die Beschränkung, dass nur jeweils ein Bogen eines Typs - Phrasing, Bindung - gesetzt werden kann. Und mal mit, mal ohne Beachtung von Kollisionen.
Ich habe ein Beispiel von dir gefunden, das Bögen setzt. https://liarchiv.joonet.de/index.php?topic=1422.msg7841#msg7841
Allerdings als markup und ohne Spanner.

Ich habe darüber nachgedacht, ob und wie man einen eigenen Grob erzeugen kann.
Wäre es etwa möglich, sich vom PhrasingSlur_engraver einen Grob erzeugen zu lassen und den zu setzen?
Oder könnte man den PhrasingSlur_engraver kopieren und die Kopie im Voice Kontext einbinden?

Die Beispiele, die ich bisher für das Erstellen eines Engravers gefunden habe, sind mir leider noch zu kompliziert.

Gruß,
Xaver
Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: harm6 am Sonntag, 26. Februar 2017, 10:35
Hallo,

vielen Dank für das Bild, sehr hilfreich für mich, um eine bessere Vorstellung zu bekommen was Dein Ziel ist.
Kann das Geschehen noch komplexer werden oder ist das Dein Maximalziel?

Zitat
Ich habe ein Beispiel von dir gefunden, das Bögen setzt. https://liarchiv.joonet.de/index.php?topic=1422.msg7841#msg7841 (https://liarchiv.joonet.de/index.php?topic=1422.msg7841#msg7841)
Allerdings als markup und ohne Spanner.
Mittlerweile ist \undertie im source-code, also direkt nutzbar.
Aber ja, es ist ein markup, nichts sonst.

Zitat
Ich habe darüber nachgedacht, ob und wie man einen eigenen Grob erzeugen kann.
Wäre es etwa möglich, sich vom PhrasingSlur_engraver einen Grob erzeugen zu lassen und den zu setzen?
Oder könnte man den PhrasingSlur_engraver kopieren und die Kopie im Voice Kontext einbinden?

Die Beispiele, die ich bisher für das Erstellen eines Engravers gefunden habe, sind mir leider noch zu kompliziert.

Tatsächlich habe ich auch schon über ein neues grob nachgedacht.
Aber das ist eine komplizierte Sache, mit einem engraver allein ist es nicht getan.
Hier ein Beispiel:
http://lists.gnu.org/archive/html/lilypond-user/2016-11/msg00012.html (http://lists.gnu.org/archive/html/lilypond-user/2016-11/msg00012.html)
Hat mich wesentliche Teile der letzten Sommerferien gekostet, das so Hinzukriegen.


Der PhrasingSlur_engraver selbst ist in C++ geschrieben, da kommt man als user ohne den sorce-code dann neu zu kompilieren nicht dran.
Natürlich kann man versuchen ihn in scheme neu zu schreiben. Das ist allerdings jenseits meiner Fähigkeiten, da ich C++ nicht kann.


Könntest Du mal den code für das Bild posten, ohne die Bögen?
Mich interessiert wie Du das ansonsten angelegt hast.

Gruß,
  Harm
Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: xr am Sonntag, 26. Februar 2017, 11:00
Kann das Geschehen noch komplexer werden oder ist das Dein Maximalziel?
Maximalziele ändern sich leider mit jedem neuen Stück. Und ja, manchmal sind auch noch Noten oder markups daneben/dazwischen.
Aber ich wäre schon zufrieden, wenn ich Bögen in Lilypond bereits anlegen kann. Und je mehr ich beachten kann und je weniger ich in der Ausgabedatei noch nachträglich bearbeiten muss, desto besser. Momentan kann ich halt gar keine Bögen eintragen. Wenn sich dann Stücke in der Probephase häufiger ändern, muss ich jedesmal alle Bögen von Hand wieder nachtragen. Das dauert und nervt. Und ich vergesse auch schonmal welche.

Zitat
Hier ein Beispiel:
http://lists.gnu.org/archive/html/lilypond-user/2016-11/msg00012.html
Hat mich wesentliche Teile der letzten Sommerferien gekostet, das so Hinzukriegen.

Schaue ich mir an.
 ;D  Ob Computer wirklich eine Zeitersparnis sind, wenn man besondere Lösungen braucht? (frage ich mich häufiger) Handarbeit ist ja auch was Tolles.

Zitat
Könntest Du mal den code für das Bild posten, ohne die Bögen?
Mich interessiert wie Du das ansonsten angelegt hast.

Das muss ich erst aus mehreren Dateien auseinanderklamüsern. kommt die Tage ...
Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: xr am Sonntag, 26. Februar 2017, 12:27
Zitat
Hier ein Beispiel:
http://lists.gnu.org/archive/html/lilypond-user/2016-11/msg00012.html
Hat mich wesentliche Teile der letzten Sommerferien gekostet, das so Hinzukriegen.

Habe ich gerade reingeschaut. Da brauche ich bis zu den Sommerferien, um da was zu verstehen. 8)
Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: xr am Sonntag, 26. Februar 2017, 17:51
So, habe den Code für das Bild doch eben zusammenkopiert.
Da können durchaus doppelte oder unwirksame Einstellungen drin sein. Der Code ist schon was älter, und so lange er funktionierte, hatte ich kein Bedarf ihn zu überarbeiten.

Außerdem hatte ich doch falsch in Erinnerung, was welche Stimme macht.
Der oberste Staff enthält die Noten für StimmeA (sind in diesem Beispiel ausgeblendet, können aber bei Bedarf wieder eingeblendet werden)
Der mittlere Staff enthält Noten und Lyrics für StimmeA und StimmeB. Noten sind ausgeblendet.
Der unterste wie oberste für StimmeB.

Heute würde ich Scores für Duos wohl anders konzipieren, damit sie einfacher um Stimmen zu erweitern sind und jede Stimme auch gleich funktioniert. Bei Scores für mehr als zwei Stimmen habe ich das auch schon gemacht.

Töne und Text wurden mit einem Python Script erzeugt, das mir eine andere Eingabedatei übersetzt. Daher das gestreckte Format.

\version "2.18.2"


\header {
  title = "Example"
}

\paper {
  system-system-spacing.padding = #0
  system-system-spacing.basic-distance = #0
  system-system-spacing.minimal-distance = #0
  score-markup-spacing.basic-distance = #0
  score-markup-spacing.minimal-distance = #0
  score-system-spacing.minimal-distance = #0
  score-system-spacing.basic-distance = #0
  indent = 0.0\cm
 
  print-page-number = ##t
  print-first-page-number = ##t
  oddHeaderMarkup = \markup \null
  evenHeaderMarkup = \markup \null
  oddFooterMarkup = \markup {
    \fill-line {
      \on-the-fly #print-page-number-check-first
      \fromproperty #'page:page-number-string
    }
  }
 
  evenFooterMarkup = \oddFooterMarkup

    myStaffSize = #18
    #(define fonts
        (make-pango-font-tree "Trebuchet MS"
        "Nimbus Sans"
        "Luxi Mono"
        (/ myStaffSize 20))) 
}

dummy = {
  \once \omit Score.TimeSignature
  \once \omit Score.Clef
  \partial 64
  \once  \hideNotes
  b64   
  \once \omit Score.BarLine
}

farbe =  #(x11-color 'grey65)

Layout-Noten = {

  % Farben
  \override Voice.NoteHead.color = \farbe
  \override Voice.Stem.color = \farbe
  \override Voice.Beam.color = \farbe
  \override Voice.TupletNumber.color = \farbe
  \override Voice.Dots.color = \farbe
  \override Voice.Rest.color = \farbe
  \override Staff.BarLine.color = #white
  \override Voice.DynamicText.color = #red

  \override Staff.StaffSymbol.line-count = #0
  \override Staff.NoteHead.font-size = #-4

  % Hals
  \override Voice.Stem.length = #3
  \override Voice.Stem.thickness = #1
  % Hals mit Balken
  \override Voice.Stem.length-fraction = #0.6
  \override Voice.Flag.font-size = #-5
  \override Voice.Beam.beam-thickness = #0.3
  \override Voice.Beam.length-fraction = #0.6
  \override Voice.Beam.auto-knee-gap = #0.1
  \override Voice.Beam.gap = #0.
 
  \override TupletBracket.stencil = ##f
 
  % Abstand Akzente
  \override Staff.Script.padding = #1
 
  \override Voice.Script.color = #red
}

Layout = {
  \override Staff.StaffSymbol.line-count = #1
  \override Staff.StaffSymbol.thickness = #0.2
  \override Staff.Stem.transparent = ##t
  % Hals
  \override Voice.Stem.length = #2
  % Hals mit Balken
  \override Voice.Stem.length-fraction = #0
  \override Staff.NoteHead.font-size = #-100
  % Balken
  \override Voice.Beam.beam-thickness = #0
  \override Voice.Beam.length-fraction = #0
  \override Voice.Dots.stencil = ##f
  \override Voice.Stem.stencil = ##f
  \override Voice.Flag.stencil = ##f
  \override Voice.TupletNumber.stencil = ##f
  \override Staff.TextScript.stencil  = ##f
  \override Staff.Rest.stencil  = ##f
 
  \override Staff.BarLine.bar-extent = #'(-.8 . .8)
  \override Staff.BarLine.hair-thickness = #'1.5

}


abstand = {
  \override
      VerticalAxisGroup.default-staff-staff-spacing =
      #'((basic-distance . 0)
         (minimum-distance . 0)
         (padding . 0)
         )     
}

us-oT = {
  \override Voice.Beam.stencil = ##f
  \override Voice.Stem.stencil = ##f
  \override Voice.Flag.stencil = ##f
  \override Voice.NoteHead.transparent = ##t
  \override Voice.Dots.transparent = ##t
  \override Staff.BarLine.stencil = ##f 
  \override Voice.Script.Y-offset = #0
}

 
%% StimmeA-Toene bis SimmeB-Worte werden mit Code erzeugt,
%% daher der langgestreckte Satz.
StimmeA-Toene = \relative c''
    {\stemUp \dummy

  \us-oT
\time 1/4
b4 |
b4 |
b4 |
b4 |
b4 |
b4 |
b4 |
s4 |

  s4 |
b4 |
b4 \break
|
s4 |
b4 |
s4 |
b4 |
b4 |

  s4 |
b4 |
s4 |
b4 \break
|
s4 |
b4 |
s4 |
b4 |

  s4 |
b4 |
s4 |
b4 |
s4 |
b4 |
s4 |
b4 |

  s4 \break
|

     }



StimmeB-Toene = \relative c''
    {\stemDown \dummy

  \us-oT
\time 1/4
s4 |
s4 |
s4 |
s4 |
s4 |
s4 |
s4 |
b4 |

  b4 |
s4 |
s4 |
b4 |
s4 |
b4 |
s4 |
s4 |

  b4 |
s4 |
b4 |
s4 |
b4 |
s4 |
b4 |
s4 |

  b4 |
s4 |
b4 |
s4 |
b4 |
s4 |
b4 |
s4 |

  b4 |

     }



StimmeA-Worte = {
    \lyricsto first {
        dummi \new dummy{}
       
        \new standard {   
}
\new fly {
nach dem Schleicher durch den gelben Sand
ist also ungerichtet die Reihe
ziel ist leicht es
wa ss au ma
       
        }
    }
}


StimmeB-Worte = {
    \lyricsto second {
        dummi \new dummy{}
       
        \new standard {   
}
\new fly {
ihr
Suchen weitestgehend da
so los vie ist
das s ie s
cht       
        }
    }
}


\score {
 
  <<
   
    \new Staff   
        \with {
            \Layout-Noten
            \abstand
            \override Staff.Rest.Y-offset = #0.8
            \override Staff.Script.direction = #UP
            \dynamicUp
        }
        \StimmeA-Toene
   
    \new Staff = "staff_mitte"
        \with {
            \Layout
            \abstand
        }

    <<
      \Layout
      \override Staff.StaffSymbol.color = #farbe
     
      \new Voice = "first"
          \StimmeA-Toene
      \new Voice = "second"
          \StimmeB-Toene
    >>
   
    \new Lyrics
        \with { alignAboveContext = "staff_mitte" }
        \StimmeA-Worte
   
    \new Lyrics
        \with { \override LyricText.extra-offset = #'(0 . -.6) }
        \StimmeB-Worte

    \new Staff
        \with {\Layout-Noten
              \override Staff.Rest.Y-offset = #-0.8 }
        \StimmeB-Toene
       
  >>

  \layout  {
   
    \context {
      \Score
      \remove "Bar_number_engraver"
      \remove "Bar_engraver"
     
    }
    \context {
        \Staff
        \remove "Time_signature_engraver"
        \remove "Clef_engraver"
    }
   
    \context {
      \Lyrics
     
      \remove "Hyphen_engraver"
      \remove "Lyric_engraver"
     
      \accepts "fly"
      \accepts "standard"
      \accepts "dummy"
    }
       
   
    \context {
      \name standard
      \type "Engraver_group"     
      \consists "Bar_engraver"
      \consists "Lyric_engraver"
      \consists "Hyphen_engraver"
     
      \override LyricText.self-alignment-X = #0     
      \alias Lyrics     
    }
   
    \context {
      \name fly
      \type "Engraver_group"           
      \consists "Lyric_engraver"
      \consists "Hyphen_engraver"
     
      \override LyricText.self-alignment-X = #-1
      \override LyricSpace.minimum-distance = #.6
      \override BarLine.bar-extent = #'(0 . 0)
      \alias Lyrics
    }
   
    \context {     
      \name dummy
      \type "Engraver_group"     
      \alias Lyrics
    }   
  }
}


Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: xr am Montag, 27. Februar 2017, 11:06
Ich habe noch zwei weitere Lösungsansätze.

Der erste ist, mehrere Phrasing_slur_engraver zu registrieren mit:
\layout  {     
        \context {
            \Voice
            \consists "Phrasing_slur_engraver"
            \consists "Phrasing_slur_engraver"
            \consists "Phrasing_slur_engraver"
            \override PhrasingSlur.avoid-slur = #'inside   
        }
    }

Beispiel:

\version "2.18.2"

#(define counter 0)

#(define (shape-slurs grob)
   
    (let* (
            ;; original ControlPoints
            (cps (ly:slur::calc-control-points grob)))
       
         ; shift Control-Points x and y by counter
         (set! cps (map
                    (lambda (xy)
                        (cons (+ (car xy) counter) (+ (cdr xy) counter))
                        )
                    cps)
         )

        (ly:grob-set-property! grob 'control-points cps)
        ; kill grob nr 3
        (if (= counter 3)
            (ly:grob-suicide! grob)
            )
        (set! counter (+ counter 1))
    )   
)

shapeSlur =
#(define-music-function (parser location)
    ()
   #{     
      \override Staff.PhrasingSlur.cross-staff = ##t   
      \override Staff.PhrasingSlur.after-line-breaking = #shape-slurs
  #})

 
txtB = {
    \lyricsto melody {
      Ut enim ad minim veniam,  quis nostrud exercitation ullamco
    laboris nisi ut aliquid ex ea commodi consequat. Quis aute
    }
}

noten = 
\relative c'' {
    \shapeSlur
    c1 c c c \break
    c c c  c
    c \(c c c
    c c c\) c
}

\score {
<<   
    \new Staff
     
    \new Voice = "melody"
        \noten
    \new Lyrics
        \txtB
>>   

    \layout  {     
        \context {
            \Voice
            \consists "Phrasing_slur_engraver"
            \consists "Phrasing_slur_engraver"
            \consists "Phrasing_slur_engraver"
            \override PhrasingSlur.avoid-slur = #'inside   
        }
    }
}

Hier wird jeder Phrasierungsbogen 4x gesetzt. Die Phrasierungsbögen können mit Scheme abgefangen und wieder gelöscht werden.
Auf diese Art ist es möglich, gleichzeitig mehrere Phrasierungsbögen zu setzen.
Allerdings scheinen mir in der Zählzeit versetzte Bögen einen komplizierten Rattenschwanz an Abfragen im Code nach sich zu ziehen.
Und den Vorteil, den Phrasierungsbögen haben, nämlich dass sie Kollisionen vermeiden, hat man mit der Einstellung \override Staff.PhrasingSlur.cross-staff = ##t  ohnehin schon aufgegeben.

Daher werde ich jetzt wohl mal einen zweiten Lösungsansatz verfolgen:
Direktes Zeichnen der Bögen in einem Markup.

z.B.
samplePath =
  #'((moveto 0 0)
     (lineto 0 0)
     (curveto 0 0 5 3.3 10 1 )
     (curveto 10 1 5 3 0 0 )
     (closepath)
     )
%% und einfügen mit:
\markup  { \override #'(filled . #t) \path #0.1 #samplePath  }

Ich werde jetzt mal schauen, ob und wie ich die Punkte berechnen kann.

Könnte man vielleicht eigene Spanner erzeugen und mit zwei Befehlen Anfang und Ende eines Spanners in den VerticalAxisGroups eintragen, an denen sich dann die ControlPoints einer Kurve orientieren lassen?

Gruß,
Xaver
Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: xr am Dienstag, 28. Februar 2017, 18:56
@harm,

ich habe versucht, dein Beispiel des bend-engravers zu verstehen, vor allem bend-grob-defs.ly
Gleichzeitig habe ich noch dieses Beispiel gefunden:
https://github.com/starrynte/lilypond/blob/92d671669c1aa7e54655c118b37c4a2f191aaded/input/regression/scheme-text-spanner.ly
Die beiden sind sich sehr ähnlich.

Ich habe zunächst einmal versucht, einen zweiten Engraver zu entwerfen und einfache Umbenennungen vorzunehmen. Das ist leider direkt gescheitert.
Wenn ich versuche, das text-spanner-event umzubenennen, bekomme ich den Fehler: Programmierfehler: Not a music type
( Ich habe SchemeTextSpanEvent in SchemeTextSpanEventB umbenannt. github Zeilen 68, 172, 175,
oder in deiner Datei: BendSpanEvent in BendSpanEventA. Zeilen 131, 353, 356)

Ich habe keine Stelle gesehen, an der der Name nochmals auftauchen würde. Warum geht das nicht?

Wenn ich mir music-descriptions und all-grob-descriptions anzeigen lasse, scheinen der Grob und der Event-Spanner eingetragen worden zu sein.

Könntest du mir da nochmal Auskunft geben?

Gruß,
Xaver
Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: harm6 am Dienstag, 28. Februar 2017, 21:02
Zitat
ich habe versucht, dein Beispiel des bend-engravers zu verstehen, vor allem bend-grob-defs.ly
Gleichzeitig habe ich noch dieses Beispiel gefunden:
https://github.com/starrynte/lilypond/blob/92d671669c1aa7e54655c118b37c4a2f191aaded/input/regression/scheme-text-spanner.ly
Die beiden sind sich sehr ähnlich.

Der schemeTextSpannerEngraver ist aus unseren regression-tests. Der direkte link:
lilypond.org/doc/v2.19/input/regression/6c/lily-35f36acf.ly (http://lilypond.org/doc/v2.19/input/regression/6c/lily-35f36acf.ly)
Der link, den Du oben angegeben hast ist glaub' ich ein backup.

Und ja, mein BendSpannerEngraver ist der kaum veränderte schemeTextSpannerEngraver.

Zitat
Ich habe zunächst einmal versucht, einen zweiten Engraver zu entwerfen und einfache Umbenennungen vorzunehmen. Das ist leider direkt gescheitert.
Wenn ich versuche, das text-spanner-event umzubenennen, bekomme ich den Fehler: Programmierfehler: Not a music type

Du müsstest SchemeTextSpanEvent in SchemeTextSpanEventB umbenennen und scheme-text-span-event in scheme-text-span-event-b.
Das eine ist class, das andere type.

Siehe:
Zitat
#(define scheme-event-spanner-types
   '(
     (SchemeTextSpanEvent
      . ((description . "Used to signal where scheme text spanner brackets
start and stop.")
         (types . (post-event scheme-text-span-event span-event event))
         ))
     ))
Da kommen beide vor. Die Namen müssen passen!

Zitat
Könntest du mir da nochmal Auskunft geben?

Ich hab' ne zeitlang nicht geantwortet, da mir eine wirklich zündende Idee nicht kommen will. Sobald ich was habe werd' ich mich melden.
Eine Schwierigkeit sehe ich jedoch jetzt schon: der zu schaffende spanner muß Deinen Beispielen nach cross-context funktionieren, immer eine heikle Sache...

Gruß,
  Harm
Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: xr am Dienstag, 28. Februar 2017, 21:42
Herzlichen Dank für die Antwort. Da wäre ich allein nie drauf gekommen!

Meine momentane Idee ist, Text Spanner zu benutzen, um die Punkte zu setzen, an denen die Bögen anfangen, enden und evtl. nur durchlaufen sollen. Das Problem bei den Spannern ist allerdings, dass, soweit ich es verstehe, von jedem Typ immer nur je einer gleichzeitig genutzt werden kann. Bzw. dass ein Spanner automatisch endet, sobald ein neuer begonnen wird.

Daher brauche ich mehrere. Und da ich die Funktionen von Lilypond nicht einschränken will - man könnte ja auch unterschiedliche Spanner "mißbrauchen", also Text-, Slur- und Phrasing beispielsweise, die dann anderweitig aber nicht mehr zur Verfügung stünden - , will ich benutzerdefinierte Spanner schreiben. Der PhrasingSlur fällt einstweilen aus, da mir eine Übersetzung aus C++ noch zu langwierig ist. Deswegen versuch ich mich jetzt mal am TextSpanner.

Zwei Fragen bezüglich der Scheme Syntax habe ich noch. Vor allem zu ersterer habe ich gar keine Einträge gefunden:
- Was genau macht dieses Hochkomma (Gravis) vor einer Klammer? Es wandelt wohl Nachfolgendes in eine Liste um. Aber gibts da Regeln?
- Wofür steht das Komma in folgender Konstrukion: (direction . ,UP) Fließkomma?

Cross-Voice: Du kennst bestimmt diesen Beitrag:
http://lilypondblog.org/2016/08/google-summer-of-code-2016-cross-voice-spanners/
Allerdings funktioniert sein angegebener Code in 2.19.54  bei mir entweder nicht, oder man sieht keine Änderung gegenüber gewöhnlicher Eingabe.

Gruß,
Xaver

P.S: Bei dem Fehler Not a music type hatte ich keine Möglichkeit, irgendeine Ausgabe zu bekommen. Auch kein #(write 'Fehler) in den Anfangszeilen des Codes wurde ausgegeben. Gibt es da noch mitteilsamere Lilipond Varianten?
Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: harm6 am Dienstag, 28. Februar 2017, 23:26
Zitat
Meine momentane Idee ist, Text Spanner zu benutzen, um die Punkte zu setzen, an denen die Bögen anfangen, enden und evtl. nur durchlaufen sollen. Das Problem bei den Spannern ist allerdings, dass, soweit ich es verstehe, von jedem Typ immer nur je einer gleichzeitig genutzt werden kann. Bzw. dass ein Spanner automatisch endet, sobald ein neuer begonnen wird.
Nicht ganz.
Siehe:
http://lilypond.org/doc/v2.19/Documentation/changes/index.html (http://lilypond.org/doc/v2.19/Documentation/changes/index.html)
Such nach:
Zitat
There is a new command \= for specifying the spanner-id for simultaneous slurs and phrasing slurs.


Zitat
Zwei Fragen bezüglich der Scheme Syntax habe ich noch. Vor allem zu ersterer habe ich gar keine Einträge gefunden:
- Was genau macht dieses Hochkomma (Gravis) vor einer Klammer? Es wandelt wohl Nachfolgendes in eine Liste um. Aber gibts da Regeln?
- Wofür steht das Komma in folgender Konstrukion: (direction . ,UP) Fließkomma?

Siehe:
https://liarchiv.joonet.de/index.php?topic=1762.msg9753#msg9753 (https://liarchiv.joonet.de/index.php?topic=1762.msg9753#msg9753)


Zitat
Cross-Voice: Du kennst bestimmt diesen Beitrag:
http://lilypondblog.org/2016/08/google-summer-of-code-2016-cross-voice-spanners/
Allerdings funktioniert sein angegebener Code in 2.19.54  bei mir entweder nicht, oder man sieht keine Änderung gegenüber gewöhnlicher Eingabe.
Aufgrund des dortigen C++ codes ist eine Neukompilierung von LilyPond nötig. Hab ich nie getestet.

Zitat
Bei dem Fehler Not a music type hatte ich keine Möglichkeit, irgendeine Ausgabe zu bekommen. Auch kein #(write 'Fehler) in den Anfangszeilen des Codes wurde ausgegeben. Gibt es da noch mitteilsamere Lilipond Varianten?
Nicht das ich wüsste.

Die Ausgabe einer sinvollen und nützlichen Fehlermeldung ist eine Kunst.
LilyPond ist da noch stark verbesserungsfähig ...

Allerdings ist man beim Vorhaben ein neues grob zu erschaffen auch tief, tief in Herz und Gehirn von LilyPond...

Gruß,
  Harm



Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: xr am Mittwoch, 1. März 2017, 12:29
Zitat
Nicht ganz.
Siehe:
http://lilypond.org/doc/v2.19/Documentation/changes/index.html
Such nach:

Hätte ich das mal früher gesehen, dann hätte ich den gestrigen Abend anders verbracht.
Damit sind ja richtig komplizierte Konstruktionen möglich.

\version "2.19.54"

\fixed c' {
 c1\=3^(\=5( <c~ f\=1( a\=4( g'^\=2( >2 <c e\=1)   > < d,\=3) e'\=4)\=5) > r\=2)
}

Wahrscheinlich werde ich das auch als Lösung weiterverfolgen.

Mein Abend und Teile meines Morgens sind jetzt allerdings für folgendes eingesetzt worden:
Ein Template zum Erstellen von TextSpannerEngravern.

Beispieldatei:

\version "2.19.52"
%% Example for creating multiple custom text spanners
%% Based on:
% http://lilypond.org/doc/v2.18/input/regression/collated-files.html
% look for:
% ‘scheme-text-spanner.ly’


custom-engraver = "Hallo"
% rename path to your file, which holds the engraver code
\include "template-scheme-text-spanner-engraver2.ly"

custom-engraver = "Welt"
\include "template-scheme-text-spanner-engraver2.ly"

custom-engraver = "A"
\include "template-scheme-text-spanner-engraver2.ly"



\layout {
  \context {
    \Global
    \grobdescriptions #all-grob-descriptions
  }
  \context {
    \Voice
    \consists #HalloTextSpanner
    \consists #WeltTextSpanner
    \consists #ATextSpanner
  }
}

\relative {
  \override HalloTextSpanner.to-barline = ##f
 
  \override HalloTextSpanner.bound-details.left.text = #"hal"
  \override HalloTextSpanner.bound-details.right.text = #"lo"
  \override WeltTextSpanner.bound-details.left.text = #"we"
  \override WeltTextSpanner.bound-details.right.text = #"lt"
  \override ATextSpanner.bound-details.left.text = #"A"
  \override ATextSpanner.bound-details.right.text = #"A"
 
  a4 b\hallo-start c\welt-start d
  a4 b c d
  a4 \a-start b c\hallo-stop d \welt-stop \break
 
  a4\hallo-start \welt-start b d \a-stop c
  a4 b\hallo-stop c d  \welt-stop
  a1
}

Und der folgende Code muss in eine andere Datei gespeichert werden und der Pfad in obiger Datei bei \include entsprechend angepaßt werden.

%% Example for creating multiple custom text spanners
%% Based on:
% http://lilypond.org/doc/v2.18/input/regression/collated-files.html
% look for:
% ‘scheme-text-spanner.ly’

\version "2.19.54"

%% define vars to be used to formate text
#(define ev-class-name 0)
#(define ev-type-name 0)
#(define grob-def-name 0)
#(define engraver-name 0)


#(define (create-custom-engraver name)
    (set! ev-class-name (string-append (string-downcase name) "-text-span-event"))
    (set! ev-type-name  (string-append name "TextSpanEvent"))
    (set! grob-def-name (string-append name "TextSpanner"))
    (set! engraver-name (string-append name "TextSpannerEngraver" ))
)
#(create-custom-engraver custom-engraver)


#(define template-list (list "
(begin

(define-event-class '" ev-class-name " 'span-event)

(define (add-grob-definition grob-name grob-entry)
   (let* ((meta-entry   (assoc-get 'meta grob-entry))
          (class        (assoc-get 'class meta-entry))
          (ifaces-entry (assoc-get 'interfaces meta-entry)))
     (set-object-property! grob-name 'translation-type? ly:grob-properties?)
     (set-object-property! grob-name 'is-grob? #t)
     (set! ifaces-entry (append (case class
                                  ((Item) '(item-interface))
                                  ((Spanner) '(spanner-interface))
                                  ((Paper_column) '((item-interface
                                                     paper-column-interface)))
                                  ((System) '((system-interface
                                               spanner-interface)))
                                  (else '(unknown-interface)))
                                ifaces-entry))
     (set! ifaces-entry (uniq-list (sort ifaces-entry symbol<?)))
     (set! ifaces-entry (cons 'grob-interface ifaces-entry))
     (set! meta-entry (assoc-set! meta-entry 'name grob-name))
     (set! meta-entry (assoc-set! meta-entry 'interfaces
                                  ifaces-entry))
     (set! grob-entry (assoc-set! grob-entry 'meta meta-entry))
     (set! all-grob-descriptions
           (cons (cons grob-name grob-entry)
                 all-grob-descriptions))))

(add-grob-definition
  '" grob-def-name
 " `(
    (bound-details . ((left . ((Y . 0)
                               (padding . 0.25)
                               (attach-dir . ,LEFT)
                               ))
                      (left-broken . ((end-on-note . #t)))
                      (right . ((Y . 0)
                                (padding . 0.25)
                                ))
                      ))
    (dash-fraction . 0.2)
    (dash-period . 3.0)
    (direction . ,UP)
    (font-shape . italic)
    (left-bound-info . ,ly:line-spanner::calc-left-bound-info)
    (outside-staff-priority . 350)
    (right-bound-info . ,ly:line-spanner::calc-right-bound-info)
    (staff-padding . 0.8)
    (stencil . ,ly:line-spanner::print)
    (style . dashed-line)

    (meta . ((class . Spanner)
             (interfaces . (font-interface
                            line-interface
                            line-spanner-interface
                            outside-staff-interface
                            side-position-interface))))))

(define scheme-event-spanner-types
   '(
     (" ev-type-name
     " . ((description . \"Used to signal where scheme text spanner brackets
start and stop.\")
         (types . (post-event " ev-class-name " span-event event))
         ))
     ))

(set!
  scheme-event-spanner-types
  (map (lambda (x)
         (set-object-property! (car x)
                               'music-description
                               (cdr (assq 'description (cdr x))))
         (let ((lst (cdr x)))
           (set! lst (assoc-set! lst 'name (car x)))
           (set! lst (assq-remove! lst 'description))
           (hashq-set! music-name-to-property-table (car x) lst)
           (cons (car x) lst)))
       scheme-event-spanner-types))

(set! music-descriptions
       (append scheme-event-spanner-types music-descriptions))

(set! music-descriptions
       (sort music-descriptions alist<?))

(define (add-bound-item spanner item)
   (if (null? (ly:spanner-bound spanner LEFT))
       (ly:spanner-set-bound! spanner LEFT item)
       (ly:spanner-set-bound! spanner RIGHT item)))

(define (axis-offset-symbol axis)
   (if (eqv? axis X) 'X-offset 'Y-offset))

(define (set-axis! grob axis)
  (if (not (number? (ly:grob-property grob 'side-axis)))
      (begin
        (set! (ly:grob-property grob 'side-axis) axis)
        (ly:grob-chain-callback
         grob
         (if (eqv? axis X)
             ly:side-position-interface::x-aligned-side
             side-position-interface::y-aligned-side)
         (axis-offset-symbol axis)))))

(define " engraver-name "
(lambda (context)
   (let ((span '())
         (finished '())
         (event-start '())
         (event-stop '()))
     (make-engraver
      (listeners ((" ev-class-name " engraver event)
                  (if (= START (ly:event-property event 'span-direction))
                      (set! event-start event)
                      (set! event-stop event))))
      (acknowledgers ((note-column-interface engraver grob source-engraver)
                      (if (ly:spanner? span)
                          (begin
                            (ly:pointer-group-interface::add-grob span 'note-columns grob)
                            (add-bound-item span grob)))
                      (if (ly:spanner? finished)
                          (begin
                            (ly:pointer-group-interface::add-grob finished 'note-columns grob)
                            (add-bound-item finished grob)))))
      ((process-music trans)
       (if (ly:stream-event? event-stop)
           (if (null? span)
               (ly:warning \"You're trying to end a scheme text spanner but you haven't started one.\")
               (begin (set! finished span)
                      (ly:engraver-announce-end-grob trans finished event-start)
                      (set! span '())
                      (set! event-stop '()))))
       (if (ly:stream-event? event-start)
           (begin (set! span (ly:engraver-make-grob trans '" grob-def-name " event-start))
                  (set-axis! span Y)
                  (set! event-start '()))))
      ((stop-translation-timestep trans)
       (if (and (ly:spanner? span)
                (null? (ly:spanner-bound span LEFT)))
           (ly:spanner-set-bound! span LEFT
             (ly:context-property context 'currentMusicalColumn)))
       (if (ly:spanner? finished)
           (begin
             (if (null? (ly:spanner-bound finished RIGHT))
                 (ly:spanner-set-bound! finished RIGHT
                   (ly:context-property context 'currentMusicalColumn)))
             (set! finished '())
             (set! event-start '())
             (set! event-stop '()))))
      ((finalize trans)
       (if (ly:spanner? finished)
           (begin
             (if (null? (ly:spanner-bound finished RIGHT))
                 (ly:spanner-set-bound! finished RIGHT
                   (ly:context-property context 'currentMusicalColumn)))
             (set! finished '())))
       (if (ly:spanner? span)
           (begin
             (ly:warning \"I think there's a dangling scheme text spanner :-(\")
             (ly:grob-suicide! span)
     (set! span '()))))))))

)
"))


%% make one long string from text-list
#(define template-txt (string-concatenate template-list))
%% evaluate expression
#(define tmp (eval (read (open-input-string  template-txt))
       (current-module)))

%% create Variable, which holds the Engraver -> <Name>TextSpanner
#(module-define! (current-module)  (string->symbol grob-def-name)
     (eval (read (open-input-string  engraver-name))(current-module)))

%% Create Variable which holds the start command of the engraver -> <name>-start
#(define var (string-append (string-downcase custom-engraver) "-start"))
start = #(make-span-event (string->symbol ev-type-name) START)

#(module-define! (current-module)
     (string->symbol var)
     start)

%% Create Variable which holds the stop command of the engraver -> <name>-stop
#(define var (string-append (string-downcase custom-engraver) "-stop"))
start = #(make-span-event (string->symbol ev-type-name) STOP)

#(module-define! (current-module)
     (string->symbol var)
     start)

Das ganze ist nur so lang und umständlich geworden, weil ich im make-engraver den Klassennamen scheme-text-span-event nicht durch eine Variable ersetzen konnte:
(listeners ((scheme-text-span-event engraver event)


Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: xr am Mittwoch, 1. März 2017, 12:46
Zitat
Siehe:
https://liarchiv.joonet.de/index.php?topic=1762.msg9753#msg9753
( Thema: scheme - very basic: quote, unquote, quasiquote bzw ' oder , oder ` )

Das sollte unbedingt in die Dokumentation. Via Google ist das nicht zu finden.

Und dann habe ich den Threadtitel verändert. Der ursprüngliche Titel hat ja mit dem weiteren Verlauf überhaupt nichts mehr zu tun. Und wo ich gerade bei Googlebarkeit bin ...
Titel: Re: vom Grob NoteColumn einen Lyrics Grob finden
Beitrag von: Manuela am Mittwoch, 1. März 2017, 13:16
Zitat
Siehe:
https://liarchiv.joonet.de/index.php?topic=1762.msg9753#msg9753
( Thema: scheme - very basic: quote, unquote, quasiquote bzw ' oder , oder ` )

Das sollte unbedingt in die Dokumentation. Via Google ist das nicht zu finden.


Das findet man aber in diversen Scheme-Dokus und Tutorials.
Titel: Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
Beitrag von: xr am Mittwoch, 1. März 2017, 15:08
Hallo Manuela,

stimmt, zu quote, unquote, quasiquote findet man sofort was.
Zum Komma und zum Gravis habe ich aber nichts gefunden, zumindest nicht in den einführenden Tutorial.
"Scheme Gravis" z.B. liefert auch eher Links zu einem Autoverleih. ;)

Deshalb ernsthaft: Komma und Gravis kommen in Lilypond häufiger vor, daher wärs gut, wenn die in der Lilypond Scheme Dokumentation auch mal erwähnt würden.

EDIT: Dass Komma und Gravis auch Kurzschreibweisen sind, habe ich tatsächlich erst jetzt gelesen - wahrscheinlich war ich mal wieder blind.

Gruß,
Xaver
Titel: Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
Beitrag von: harm6 am Donnerstag, 2. März 2017, 02:16
Hallo Xaver,

hier mein Versuch.
Ich fand Dein Vorgehen zu un-schemish :))
Woher kommt nur die Manie alles in einen string zu hauen... lol

\version "2.19.52"
%% Example for creating multiple custom text spanners
%% Based on:
% http://lilypond.org/doc/v2.18/input/regression/collated-files.html
% look for:
% ‘scheme-text-spanner.ly’


#(define name-list '(Hallo Welt A))
% rename path to your file, which holds the engraver code
\include "template-scheme-text-spanner-engraver2-harm.ly"

\layout {
  \context {
    \Global
    \grobdescriptions #all-grob-descriptions
  }
  \context {
    \Voice
    \consists #HalloTextSpanner
    \consists #WeltTextSpanner
    \consists #ATextSpanner
  }
}

\relative {
  \override HalloTextSpanner.to-barline = ##f
 
  \override HalloTextSpanner.bound-details.left.text = #"hall"
  \override HalloTextSpanner.bound-details.right.text = #"lo"
  \override WeltTextSpanner.bound-details.left.text = #"we"
  \override WeltTextSpanner.bound-details.right.text = #"lt"
  \override ATextSpanner.bound-details.left.text = #"A"
  \override ATextSpanner.bound-details.right.text = #"A"
 
  a4 b\hallo-start c\welt-start d
  a4 b c d
  a4 \a-start b c\hallo-stop d \welt-stop \break
 
  a4\hallo-start \welt-start b d \a-stop c
  a4 b\hallo-stop c d  \welt-stop
  a1

}

%% Example for creating multiple custom text spanners
%% Based on:
% http://lilypond.org/doc/v2.18/input/regression/collated-files.html
% look for:
% ‘scheme-text-spanner.ly’

\version "2.19.52"

#(define (add-grob-definition grob-name grob-entry)
   (let* ((meta-entry   (assoc-get 'meta grob-entry))
          (class        (assoc-get 'class meta-entry))
          (ifaces-entry (assoc-get 'interfaces meta-entry)))
     (set-object-property! grob-name 'translation-type? ly:grob-properties?)
     (set-object-property! grob-name 'is-grob? #t)
     (set! ifaces-entry (append (case class
                                  ((Item) '(item-interface))
                                  ((Spanner) '(spanner-interface))
                                  ((Paper_column) '((item-interface
                                                     paper-column-interface)))
                                  ((System) '((system-interface
                                               spanner-interface)))
                                  (else '(unknown-interface)))
                                ifaces-entry))
     (set! ifaces-entry (uniq-list (sort ifaces-entry symbol<?)))
     (set! ifaces-entry (cons 'grob-interface ifaces-entry))
     (set! meta-entry (assoc-set! meta-entry 'name grob-name))
     (set! meta-entry (assoc-set! meta-entry 'interfaces
                                  ifaces-entry))
     (set! grob-entry (assoc-set! grob-entry 'meta meta-entry))
     (set! all-grob-descriptions
           (cons (cons grob-name grob-entry)
                 all-grob-descriptions))))
                 
#(define (add-bound-item spanner item)
   (if (null? (ly:spanner-bound spanner LEFT))
       (ly:spanner-set-bound! spanner LEFT item)
       (ly:spanner-set-bound! spanner RIGHT item)))

#(define (axis-offset-symbol axis)
   (if (eqv? axis X) 'X-offset 'Y-offset))

#(define (set-axis! grob axis)
  (if (not (number? (ly:grob-property grob 'side-axis)))
      (begin
        (set! (ly:grob-property grob 'side-axis) axis)
        (ly:grob-chain-callback
         grob
         (if (eqv? axis X)
             ly:side-position-interface::x-aligned-side
             side-position-interface::y-aligned-side)
         (axis-offset-symbol axis)))))

%% TODO find easier method to create this list, better structure, etc
#(define (namings names)
  (map
    (lambda (name)
      (list
        (cons
          (string->symbol (format #f "~aTextSpanEvent" name))
          (ly:camel-case->lisp-identifier
            (string->symbol (format #f "~aTextSpanEvent" name))))
        (cons
          (string->symbol (format #f "~aTextSpanner" name))
          (string->symbol (format #f "~aTextSpannerEngraver" name)))
        (cons
          (string->symbol (string-downcase (format #f "~a-start" name)))
          (string->symbol (string-downcase (format #f "~a-stop" name))))))
        names))

#(for-each
  (lambda (text-span-event-name)
    (define-event-class
     text-span-event-name
     'span-event))
   (map cdar (namings name-list)))

#(for-each
   (lambda (g-name)
    (add-grob-definition
      g-name
      `(
        (bound-details . ((left . ((Y . 0)
                                   (padding . 0.25)
                                   (attach-dir . ,LEFT)
                                   ))
                          (left-broken . ((end-on-note . #t)))
                          (right . ((Y . 0)
                                    (padding . 0.25)
                                    ))
                          ))
        (dash-fraction . 0.2)
        (dash-period . 3.0)
        (direction . ,UP)
        (font-shape . italic)
        (left-bound-info . ,ly:line-spanner::calc-left-bound-info)
        (outside-staff-priority . 350)
        (right-bound-info . ,ly:line-spanner::calc-right-bound-info)
        (staff-padding . 0.8)
        (stencil . ,ly:line-spanner::print)
        (style . dashed-line)
   
        (meta . ((class . Spanner)
                 (interfaces . (font-interface
                                line-interface
                                line-spanner-interface
                                outside-staff-interface
                                side-position-interface)))))))
     (map caadr (namings name-list)))
   
#(define scheme-event-spanner-types
   (map
    (lambda (x)
     (cons
      (car x)
       (list
         '(description . "Used to signal where scheme text spanner brackets start and stop.")
          (cons 'types 
                (list 'post-event
                      (cdr x)
                      'span-event
                      'event)))))
    (map car (namings name-list))))
     
#(set!
  scheme-event-spanner-types
  (map (lambda (x)
         (set-object-property! (car x)
                               'music-description
                               (cdr (assq 'description (cdr x))))
         (let ((lst (cdr x)))
           (set! lst (assoc-set! lst 'name (car x)))
           (set! lst (assq-remove! lst 'description))
           (hashq-set! music-name-to-property-table (car x) lst)
           (cons (car x) lst)))
       scheme-event-spanner-types))
       
#(set! music-descriptions
       (append scheme-event-spanner-types music-descriptions))

#(set! music-descriptions
       (sort music-descriptions alist<?))
       
#(for-each
  (lambda (name-event)
    (module-define! (current-module)  (car name-event)
      (lambda (context)
         (let ((span '())
               (my-ev (cdr name-event))
               (finished '())
               (event-start '())
               (event-stop '()))
          `(
            (listeners
              (,my-ev
                .
                ,(lambda (engraver event)
                  (if (= START (ly:event-property event 'span-direction))
                      (set! event-start event)
                      (set! event-stop event)))))
            (acknowledgers
              (note-column-interface
              .
              ,(lambda (engraver grob source-engraver)
                (if (ly:spanner? span)
                    (begin
                      (ly:pointer-group-interface::add-grob span 'note-columns grob)
                      (add-bound-item span grob)))
                (if (ly:spanner? finished)
                    (begin
                      (ly:pointer-group-interface::add-grob finished 'note-columns grob)
                      (add-bound-item finished grob))))))
            (process-music
              .
              ,(lambda (trans)
                 (if (ly:stream-event? event-stop)
                     (if (null? span)
                         (ly:warning "You're trying to end a scheme text spanner but you haven't started one.")
                         (begin (set! finished span)
                                (ly:engraver-announce-end-grob trans finished event-start)
                                (set! span '())
                                (set! event-stop '()))))
                 (if (ly:stream-event? event-start)
                     (begin (set! span (ly:engraver-make-grob trans (car name-event) event-start))
                            (set-axis! span Y)
                            (set! event-start '())))))
            (stop-translation-timestep
              .
              ,(lambda (trans)
                 (if (and (ly:spanner? span)
                          (null? (ly:spanner-bound span LEFT)))
                     (ly:spanner-set-bound! span LEFT
                       (ly:context-property context 'currentMusicalColumn)))
                 (if (ly:spanner? finished)
                     (begin
                       (if (null? (ly:spanner-bound finished RIGHT))
                           (ly:spanner-set-bound! finished RIGHT
                             (ly:context-property context 'currentMusicalColumn)))
                       (set! finished '())
                       (set! event-start '())
                       (set! event-stop '())))))
            (finalize
              .
              ,(lambda (trans)
                 (if (ly:spanner? finished)
                     (begin
                       (if (null? (ly:spanner-bound finished RIGHT))
                           (ly:spanner-set-bound! finished RIGHT
                             (ly:context-property context 'currentMusicalColumn)))
                       (set! finished '())))
                 (if (ly:spanner? span)
                     (begin
                       (ly:warning "I think there's a dangling scheme text spanner :-)")
                       (ly:grob-suicide! span)
               (set! span '()))))))))))
           
      (map
        (lambda (p) (cons (caadr p) (cdar p)))
        (namings name-list)))

%% TODO merge the next two def (too tired right now)
#(for-each
  (lambda (elt)
    (module-define! (current-module) (car elt)
      (make-span-event (cdr elt) START)))
  (map (lambda (p) (cons (car (last p)) (caar p)))(namings name-list)))
   
#(for-each
  (lambda (elt)
    (module-define! (current-module) (car elt)
      (make-span-event (cdr elt) STOP)))
  (map (lambda (p) (cons (cdr (last p)) (caar p)))(namings name-list)))


Ich bin zu müde für irgenwelche Erklärungen ...
Morgen abend wieder.

Gruß,
  Harm
Titel: Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
Beitrag von: xr am Donnerstag, 2. März 2017, 06:10
Hallo harm,

Zitat
Woher kommt nur die Manie alles in einen string zu hauen... lol

Kommt daher, weil ich für den Ausdruck  (listeners ((scheme-text-span-event engraver event) keine Möglichkeit gefunden habe, das scheme-text-span-event zu ersetzen. Also habe ich es kurzerhand in einen String umgewandelt, dort ausgetauscht und dann wieder evaluiert. Und ihm dann verboten, mich weiterhin zu nerven und aufzuhalten. ;D
Derart kann man natürlich schlecht in dem Code weitere Änderungen vornehmen.


Aber du hast ja jetzt eine prima Lösung gepostet. Könntest du genau diesen Teil später nochmal genauer kommentieren?

#(for-each
  (lambda (name-event)
    (module-define! (current-module)  (car name-event)
      (lambda (context)
         (let ((span '())
               (my-ev (cdr name-event))
               (finished '())
               (event-start '())
               (event-stop '()))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Hier hätte ich gerne nähere Erläuterungen über das Folgende
;;;; Auch gern zu Komma und gravis. Ist das wirklich nur die Kurzform von quasiquote und unquote?
          `(
            (listeners
              (,my-ev
                .
                ,(lambda (engraver event)

Mich interessiert, was die Konstruktion mit quasiquote und ,my-ev aus my-ev macht. Ich konnte my-ev nicht abfangen und erhielt bei (display my-ev) einen Fehler. Welche ein Typ ist das? Mir schien, als würde das im Parser wie ein String oder Symbol interpretiert, ohne jedoch wirklich Symbol oder String zu sein.

Grüße,
Xaver
Titel: Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
Beitrag von: harm6 am Donnerstag, 2. März 2017, 23:34
Hallo Xaver,

Zitat
Aber du hast ja jetzt eine prima Lösung gepostet. Könntest du genau diesen Teil später nochmal genauer kommentieren?
[...]
Mich interessiert, was die Konstruktion mit quasiquote und ,my-ev aus my-ev macht. Ich konnte my-ev nicht abfangen und erhielt bei (display my-ev) einen Fehler. Welche ein Typ ist das?

Lass uns mal ein einfacheres Beispiel betrachten. Einen simplen listener. Er soll solch eine Variable annehmen, als Ausgabe nur melden, falls eine Note gefunden wurde.

Man sollte sich klar machen, daß ein engraver erstmal nur eine Liste ist, die via \consist in einen Context eingefügt wird.
Das make-engraver-makro vereinfacht die Syntax, vernebelt aber die Basis...

Im Code unten habe ich 3 solche engraver definiert.
test-engr benutzt make-engraver
test-I-engr verwendet eine direkt sichtbare Liste aber die Kürzel für quote, quasiquote und unquote
test-II-engr verwendet `list` und `cons` um die Liste zu erstellen quote nur für tatsächliche symbols
Alle drei greifen auf ein vordefiniertes symbol zu, `ev`, versuchen es zu verarbeiten und ins terminal zu schreiben.

Resultat:
Der erste, mit make-engraver erstellte scheitert. Die beiden anderen funktionieren.

Konsequenzen:
(1) Beim Vergleich von test-I-engr mit test-II-engr sollte klar werden, daß das unquote wirklich nur eben das macht. De Versuch es im Terminal anzeigen zu lassen sollte an geeigneter Stelle erfolgen.
(2) test-engr mit make-engraver ist gescheitert die Variable zu nutzen.
make-engraver ist ein noch nicht so altes makro und wird von nicht vielen Personen verwendet. Es ist nicht überraschend, daß man irgendwann eine Schwäche findet.
Ich werde dieses Problem mal auf der devel-mailing-list vorstellen.

Hier der Testcode:
\version "2.19.52"

#(define ev 'note-event)

#(define test-engr
  (lambda (context)
    (make-engraver
      (listeners
        (
         (ev engraver event)
         ;(note-event engraver event)
           (write ev)
           (format #t "\n\nnote-event found from control-engraver\n"))))))
       
#(define test-I-engr
  (lambda (context)
    `((listeners
        (
         ,ev
         ;note-event
          .
          ,(lambda (engraver event)
             (write ev)
             (format #t "\n\nnote-event found from test-I-engraver\n")))))))     
             
#(define test-II-engr
  (lambda (context)
    (list
      (cons 'listeners
        (list
          (cons
            ev
            ;'note-event
            (lambda (engraver event)
               (write ev)
               (format #t "\n\nnote-event found from test-II-engraver\n"))))))))
\layout {
  \context {
    \Voice
    \consists #test-engr
    \consists #test-I-engr
    \consists #test-II-engr
  }
}

{
  c'4
}

HTH,
  Harm
Titel: Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
Beitrag von: harm6 am Donnerstag, 2. März 2017, 23:39
Ansonsten habe ich den code noch mal überarbeitet. Die ganze Sache wird jetzt über ein aufrufbare Funktion geregelt.

Das Test-file:
\version "2.19.52"
%% Example for creating multiple custom text spanners
%% Based on:
% http://lilypond.org/doc/v2.18/input/regression/collated-files.html
% look for:
% ‘scheme-text-spanner.ly’

% rename path to your file, which holds the engraver code
\include "multiple-spanner-grobs-engravers.ly"

\createSpannersAndEngravers  #'(Hallo Welt A)

\layout {
  \context {
    \Global
    \grobdescriptions #all-grob-descriptions
  }
  \context {
    \Voice
    \consists \HalloTextSpannerEngraver
    \consists \WeltTextSpannerEngraver
    \consists \ATextSpannerEngraver
  }
}

\relative {
  \override HalloTextSpanner.to-barline = ##f
 
  \override HalloTextSpanner.bound-details.left.text = #"hall"
  \override HalloTextSpanner.bound-details.right.text = #"lo"
  \override WeltTextSpanner.bound-details.left.text = #"we"
  \override WeltTextSpanner.bound-details.right.text = #"lt"
  \override ATextSpanner.bound-details.left.text = #"A"
  \override ATextSpanner.bound-details.right.text = #"A"
 
  a4 b\hallo-start c\welt-start d
  a4 b c d
  a4 \a-start b c\hallo-stop d \welt-stop \break
 
  a4\hallo-start \welt-start b d \a-stop c
  a4 b\hallo-stop c d  \welt-stop
  a1

}

Das zu inkludierende file (auch im Anhang):
%% Example for creating multiple custom text spanners
%% Based on:
% http://lilypond.org/doc/v2.18/input/regression/collated-files.html
% look for:
% ‘scheme-text-spanner.ly’

\version "2.19.52"

#(define (add-grob-definition grob-name grob-entry)
   (let* ((meta-entry   (assoc-get 'meta grob-entry))
          (class        (assoc-get 'class meta-entry))
          (ifaces-entry (assoc-get 'interfaces meta-entry)))
     (set-object-property! grob-name 'translation-type? ly:grob-properties?)
     (set-object-property! grob-name 'is-grob? #t)
     (set! ifaces-entry (append (case class
                                  ((Item) '(item-interface))
                                  ((Spanner) '(spanner-interface))
                                  ((Paper_column) '((item-interface
                                                     paper-column-interface)))
                                  ((System) '((system-interface
                                               spanner-interface)))
                                  (else '(unknown-interface)))
                                ifaces-entry))
     (set! ifaces-entry (uniq-list (sort ifaces-entry symbol<?)))
     (set! ifaces-entry (cons 'grob-interface ifaces-entry))
     (set! meta-entry (assoc-set! meta-entry 'name grob-name))
     (set! meta-entry (assoc-set! meta-entry 'interfaces
                                  ifaces-entry))
     (set! grob-entry (assoc-set! grob-entry 'meta meta-entry))
     (set! all-grob-descriptions
           (cons (cons grob-name grob-entry)
                 all-grob-descriptions))))
                 
#(define (add-bound-item spanner item)
   (if (null? (ly:spanner-bound spanner LEFT))
       (ly:spanner-set-bound! spanner LEFT item)
       (ly:spanner-set-bound! spanner RIGHT item)))

#(define (axis-offset-symbol axis)
   (if (eqv? axis X) 'X-offset 'Y-offset))

#(define (set-axis! grob axis)
  (if (not (number? (ly:grob-property grob 'side-axis)))
      (begin
        (set! (ly:grob-property grob 'side-axis) axis)
        (ly:grob-chain-callback
         grob
         (if (eqv? axis X)
             ly:side-position-interface::x-aligned-side
             side-position-interface::y-aligned-side)
         (axis-offset-symbol axis)))))
         
#(define (namings names)
  (map
    (lambda (name)
      (list
        (string->symbol (format #f "~aTextSpanEvent" name))
        (ly:camel-case->lisp-identifier
          (string->symbol (format #f "~aTextSpanEvent" name)))
        (string->symbol (format #f "~aTextSpanner" name))
        (string->symbol (format #f "~aTextSpannerEngraver" name))
        (string->symbol (string-downcase (format #f "~a-start" name)))
        (string->symbol (string-downcase (format #f "~a-stop" name)))))
    names))
   
#(define (scheme-event-spanner-types-proc lst)
  (map
    (lambda (x y)
      (cons
        x
        (list
          '(description . "Used to signal where scheme text spanner brackets start and stop.")
           (cons 'types 
                 (list 'post-event
                       y
                       'span-event
                       'event)))))
    (map car (namings lst))
    (map second (namings lst))))
   
#(define spanner-props
  `(
    (bound-details . ((left . ((Y . 0)
                               (padding . 0.25)
                               (attach-dir . ,LEFT)
                               ))
                      (left-broken . ((end-on-note . #t)))
                      (right . ((Y . 0)
                                (padding . 0.25)
                                ))
                      ))
    (dash-fraction . 0.2)
    (dash-period . 3.0)
    (direction . ,UP)
    (font-shape . italic)
    (left-bound-info . ,ly:line-spanner::calc-left-bound-info)
    (outside-staff-priority . 350)
    (right-bound-info . ,ly:line-spanner::calc-right-bound-info)
    (staff-padding . 0.8)
    (stencil . ,ly:line-spanner::print)
    (style . dashed-line)
 
    (meta . ((class . Spanner)
             (interfaces . (font-interface
                            line-interface
                            line-spanner-interface
                            outside-staff-interface
                            side-position-interface))))))

#(define define-engraver
  (lambda (engr-name g-name event-types)
    (module-define! (current-module) engr-name
      (lambda (context)
        (let ((span '())
              (finished '())
              (event-start '())
              (event-stop '()))
          `(
            (listeners
              (,event-types
                .
                ,(lambda (engraver event)
                  (if (= START (ly:event-property event 'span-direction))
                      (set! event-start event)
                      (set! event-stop event)))))
            (acknowledgers
              (note-column-interface
              .
              ,(lambda (engraver grob source-engraver)
                (if (ly:spanner? span)
                    (begin
                      (ly:pointer-group-interface::add-grob span 'note-columns grob)
                      (add-bound-item span grob)))
                (if (ly:spanner? finished)
                    (begin
                      (ly:pointer-group-interface::add-grob finished 'note-columns grob)
                      (add-bound-item finished grob))))))
            (process-music
              .
              ,(lambda (trans)
                 (if (ly:stream-event? event-stop)
                     (if (null? span)
                         (ly:warning "You're trying to end a scheme text spanner but you haven't started one.")
                         (begin
                           (set! finished span)
                           (ly:engraver-announce-end-grob trans finished event-start)
                           (set! span '())
                           (set! event-stop '()))))
                 (if (ly:stream-event? event-start)
                     (begin
                       (set! span (ly:engraver-make-grob trans g-name event-start))
                       (set-axis! span Y)
                       (set! event-start '())))))
            (stop-translation-timestep
              .
              ,(lambda (trans)
                 (if (and (ly:spanner? span)
                          (null? (ly:spanner-bound span LEFT)))
                     (ly:spanner-set-bound! span LEFT
                       (ly:context-property context 'currentMusicalColumn)))
                 (if (ly:spanner? finished)
                     (begin
                       (if (null? (ly:spanner-bound finished RIGHT))
                           (ly:spanner-set-bound! finished RIGHT
                             (ly:context-property context 'currentMusicalColumn)))
                       (set! finished '())
                       (set! event-start '())
                       (set! event-stop '())))))
            (finalize
              .
              ,(lambda (trans)
                 (if (ly:spanner? finished)
                     (begin
                       (if (null? (ly:spanner-bound finished RIGHT))
                           (ly:spanner-set-bound! finished RIGHT
                             (ly:context-property context 'currentMusicalColumn)))
                       (set! finished '())))
                 (if (ly:spanner? span)
                     (begin
                       (ly:warning "I think there's a dangling scheme text spanner :-)")
                       (ly:grob-suicide! span)
               (set! span '())))))))))))
%%%%%%%%%%%%%%%%%%%%%%%%%%

createSpannersAndEngravers =
#(define-void-function (name-list)(list?)
 (let ((naming-lst (namings name-list)))

  (for-each
    (lambda (text-span-event-name)
      (define-event-class text-span-event-name 'span-event))
    (map second naming-lst))
 
  (for-each
    (lambda (g-name) (add-grob-definition g-name spanner-props))
    (map third naming-lst))
 
  (let* ((new-scheme-event-spanner-types
           (scheme-event-spanner-types-proc name-list))
         (scheme-event-spanner-types
           (map (lambda (x)
                  (set-object-property! (car x)
                                        'music-description
                                        (cdr (assq 'description (cdr x))))
                  (let ((lst (cdr x)))
                    (set! lst (assoc-set! lst 'name (car x)))
                    (set! lst (assq-remove! lst 'description))
                    (hashq-set! music-name-to-property-table (car x) lst)
                    (cons (car x) lst)))
                 new-scheme-event-spanner-types)))
 
    (set! music-descriptions
           (append scheme-event-spanner-types music-descriptions))
   
    (set! music-descriptions
           (sort music-descriptions alist<?)))
         
  ;; Create engravers
  (for-each
    define-engraver
    (map fourth naming-lst)
    (map third naming-lst)
    (map second naming-lst))
       
  ;; Create spanner-start/stop-commands
  ;; Example a-start/a-stop
  ;;
  ;; cmd-start: <spanner-name>-start
  ;; cmd-stop: <spanner-name>-stop
  ;; evt: <SpannerName>TextSpanEvent 
  (for-each
    (lambda (cmd-start cmd-stop evt)
      (module-define! (current-module) cmd-start
        (make-span-event evt START))
      (module-define! (current-module) cmd-stop
        (make-span-event evt STOP)))
    (map fifth naming-lst)
    (map sixth naming-lst)
    (map car naming-lst))

  (newline)
  (pretty-print
  (cons
    "The following events (class and type), grobs, engravers and
start/stop-commands are created"
    naming-lst))))

HTH,
  Harm
Titel: Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
Beitrag von: xr am Freitag, 3. März 2017, 23:16
Hallo harm,

zunächst einmal mein herzlichster Dank für deine wirklich hervorragende Hilfe! Deine Beispiele sind ganz prima und ich habe in kurzer Zeit wirklich einiges gelernt! Danke.

Mit den Engravern habe ich auch noch weiter herumgespielt. Und in den regression tests fand ich auch noch dies. Ebenfalls fürs Verständnis hilfreich.
https://github.com/lilypond/lilypond/blob/master/input/regression/scheme-engraver.ly

Allerdings glaube ich, dass Phrasierungsbögen mit id für meine Belange doch ausreichend sind. Ich habe mir mal die Regeln notiert, an denen ich entlangprogrammieren will.
tmp={\mark"
---- REGELN -------

Phrasing-Slur spanner-id

leer Bogen keine Änderung
\=1 Bogen wechselt Stimme
\=2 übergreifender Bogen, sitzt oberhalb

Abkürzungen
X:
    kW  (kein Wechsel)
    VN  (Vorgänger oder Nachfolger, je nach Seite)
    aR  (am Rand - NonMusicPaperColumn)
    WR  (Wortrand)
    WR+ (Wortrand plus geringer Abstand)
    max (Hälfte des Zwischenraumes, max Maximalabstand)

Y:
    WU  (Wechsel nach unten)
    WO  (Wechsel nach oben)
    ein (Y zur Mittelzeile hin eingezogen)


- Bogen in einer Zeile:
    -links + rechts:
        -kW,VN: x:max, Y:ein
        -kw,aR: x:WR+,  Y:ein
    -links:
        -WU x:WR Y:WR+
        -WO,VN X:max Y:ein
        -WO,aR X:WR Y:ein
    -rechts:
        -WU,VN X:max Y:ein
        -WU,aR X:WR+ Y:ein
        -WO X:WR Y:WR+

- Bogen gesplittet, Anfang:
    -links wie Bogen in einer Zeile
    -rechts:
        - Krümmung verringert

- Bogen gesplittet, Ende:
    wie Anfang seitenverkehrt

- Bogen gesplittet, Mittelzeile
    - Krümmung an Seiten verringert

- übergreifender Bogen:
    -immer oben
    -gleiche Regeln wie bei Vorgängern,
     sollte etwas höher sitzen

"}

Allerdings wird das nun wohl was länger dauern, da ich gerade anderweitig viel zu tun habe. Wenn ich damit durch bin, poste ich das Beispiel.

Grüße,
Xaver
Titel: Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
Beitrag von: xr am Freitag, 3. März 2017, 23:25
Ich dachte, ich hätte mal eine Property gesehen, die mir anzeigt, in welcher Systemzeile ich mich befinde. Aber ich kann die nicht mehr finden.
Gibts die oder habe ich das falsch in Erinnerung?
Titel: Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
Beitrag von: harm6 am Samstag, 4. März 2017, 01:03
Zitat von: harm
Ich werde dieses Problem mal auf der devel-mailing-list vorstellen.
http://lilypond.1069038.n5.nabble.com/limitation-in-make-engraver-td200649.html (http://lilypond.1069038.n5.nabble.com/limitation-in-make-engraver-td200649.html)

Zitat von: xr
zunächst einmal mein herzlichster Dank für deine wirklich hervorragende Hilfe! Deine Beispiele sind ganz prima und ich habe in kurzer Zeit wirklich einiges gelernt! Danke.
Gern geschehen.

Zitat von: xr
Mit den Engravern habe ich auch noch weiter herumgespielt. Und in den regression tests fand ich auch noch dies. Ebenfalls fürs Verständnis hilfreich.
https://github.com/lilypond/lilypond/blob/master/input/regression/scheme-engraver.ly (https://github.com/lilypond/lilypond/blob/master/input/regression/scheme-engraver.ly)
Ich würde direkt in die regression-tests (http://lilypond.org/doc/v2.19/input/regression/collated-files.html) verlinken:
lilypond.org/doc/v2.19/input/regression/b1/lily-58429201.ly (http://lilypond.org/doc/v2.19/input/regression/b1/lily-58429201.ly)

Zitat von: xr
Allerdings glaube ich, dass Phrasierungsbögen mit id für meine Belange doch ausreichend sind.
Ich bin gespannt auf das Ergebnis ;)

Zitat von: xr
Ich dachte, ich hätte mal eine Property gesehen, die mir anzeigt, in welcher Systemzeile ich mich befinde. Aber ich kann die nicht mehr finden.
Gibts die oder habe ich das falsch in Erinnerung?
Kannst Du den Zusammenhang erläutern, d.h. wo brauchst Du das? In einer Funktion?

Ansonsten hab ich den engraver-code nochmal bearbeitet, schien mir noch nicht elegant genug zu sein. lol

%% Example for creating multiple custom text spanners
%% Based on:
% http://lilypond.org/doc/v2.18/input/regression/collated-files.html
% look for:
% ‘scheme-text-spanner.ly’

\version "2.19.52"

#(define (add-grob-definition grob-name grob-entry)
   (let* ((meta-entry   (assoc-get 'meta grob-entry))
          (class        (assoc-get 'class meta-entry))
          (ifaces-entry (assoc-get 'interfaces meta-entry)))
     (set-object-property! grob-name 'translation-type? ly:grob-properties?)
     (set-object-property! grob-name 'is-grob? #t)
     (set! ifaces-entry (append (case class
                                  ((Item) '(item-interface))
                                  ((Spanner) '(spanner-interface))
                                  ((Paper_column) '((item-interface
                                                     paper-column-interface)))
                                  ((System) '((system-interface
                                               spanner-interface)))
                                  (else '(unknown-interface)))
                                ifaces-entry))
     (set! ifaces-entry (uniq-list (sort ifaces-entry symbol<?)))
     (set! ifaces-entry (cons 'grob-interface ifaces-entry))
     (set! meta-entry (assoc-set! meta-entry 'name grob-name))
     (set! meta-entry (assoc-set! meta-entry 'interfaces
                                  ifaces-entry))
     (set! grob-entry (assoc-set! grob-entry 'meta meta-entry))
     (set! all-grob-descriptions
           (cons (cons grob-name grob-entry)
                 all-grob-descriptions))))
                 
#(define (add-bound-item spanner item)
   (if (null? (ly:spanner-bound spanner LEFT))
       (ly:spanner-set-bound! spanner LEFT item)
       (ly:spanner-set-bound! spanner RIGHT item)))

#(define (axis-offset-symbol axis)
   (if (eqv? axis X) 'X-offset 'Y-offset))

#(define (set-axis! grob axis)
  (if (not (number? (ly:grob-property grob 'side-axis)))
      (begin
        (set! (ly:grob-property grob 'side-axis) axis)
        (ly:grob-chain-callback
         grob
         (if (eqv? axis X)
             ly:side-position-interface::x-aligned-side
             side-position-interface::y-aligned-side)
         (axis-offset-symbol axis)))))
   
#(define (scheme-event-spanner-types-proc class-lst type-lst)
  (map
    (lambda (x y)
      (cons
        x
        (list
          '(description
            .
            "Used to signal where scheme text spanner brackets start and stop.")
           (cons 'types 
                 (list 'post-event
                       y
                       'span-event
                       'event)))))
    class-lst
    type-lst))
   
#(define scheme-event-spanner-type
  (lambda (x)
    (set-object-property! (car x)
                          'music-description
                          (cdr (assq 'description (cdr x))))
    (let ((lst (cdr x)))
      (set! lst (assoc-set! lst 'name (car x)))
      (set! lst (assq-remove! lst 'description))
      (hashq-set! music-name-to-property-table (car x) lst)
      (cons (car x) lst))))
   
#(define spanner-props
  `(
    (bound-details . ((left . ((Y . 0)
                               (padding . 0.25)
                               (attach-dir . ,LEFT)
                               ))
                      (left-broken . ((end-on-note . #t)))
                      (right . ((Y . 0)
                                (padding . 0.25)
                                ))
                      ))
    (dash-fraction . 0.2)
    (dash-period . 3.0)
    (direction . ,UP)
    (font-shape . italic)
    (left-bound-info . ,ly:line-spanner::calc-left-bound-info)
    (outside-staff-priority . 350)
    (right-bound-info . ,ly:line-spanner::calc-right-bound-info)
    (staff-padding . 0.8)
    (stencil . ,ly:line-spanner::print)
    (style . dashed-line)
 
    (meta . ((class . Spanner)
             (interfaces . (font-interface
                            line-interface
                            line-spanner-interface
                            outside-staff-interface
                            side-position-interface))))))

#(define define-engraver
  (lambda (engr-name g-name event-types)
    (module-define! (current-module) engr-name
      (lambda (context)
        (let ((span '())
              (finished '())
              (event-start '())
              (event-stop '()))
          `((listeners
              (,event-types
                .
                ,(lambda (engraver event)
                  (if (= START (ly:event-property event 'span-direction))
                      (set! event-start event)
                      (set! event-stop event)))))
            (acknowledgers
              (note-column-interface
              .
              ,(lambda (engraver grob source-engraver)
                (if (ly:spanner? span)
                    (begin
                      (ly:pointer-group-interface::add-grob
                        span 'note-columns grob)
                      (add-bound-item span grob)))
                (if (ly:spanner? finished)
                    (begin
                      (ly:pointer-group-interface::add-grob
                        finished 'note-columns grob)
                      (add-bound-item finished grob))))))
            (process-music
              .
              ,(lambda (trans)
                 (if (ly:stream-event? event-stop)
                     (if (null? span)
                         (ly:warning "You're trying to end a scheme text spanner
but you haven't started one.")
                         (begin
                           (set! finished span)
                           (ly:engraver-announce-end-grob
                             trans finished event-start)
                           (set! span '())
                           (set! event-stop '()))))
                 (if (ly:stream-event? event-start)
                     (begin
                       (set! span
                             (ly:engraver-make-grob trans g-name event-start))
                       (set-axis! span Y)
                       (set! event-start '())))))
            (stop-translation-timestep
              .
              ,(lambda (trans)
                 (if (and (ly:spanner? span)
                          (null? (ly:spanner-bound span LEFT)))
                     (ly:spanner-set-bound! span LEFT
                       (ly:context-property context 'currentMusicalColumn)))
                 (if (ly:spanner? finished)
                     (begin
                       (if (null? (ly:spanner-bound finished RIGHT))
                           (ly:spanner-set-bound! finished RIGHT
                             (ly:context-property
                               context
                               'currentMusicalColumn)))
                       (set! finished '())
                       (set! event-start '())
                       (set! event-stop '())))))
            (finalize
              .
              ,(lambda (trans)
                 (if (ly:spanner? finished)
                     (begin
                       (if (null? (ly:spanner-bound finished RIGHT))
                           (ly:spanner-set-bound! finished RIGHT
                             (ly:context-property
                               context
                               'currentMusicalColumn)))
                       (set! finished '())))
                 (if (ly:spanner? span)
                     (begin
                       (ly:warning
                         "I think there's a dangling scheme text spanner :-)")
                       (ly:grob-suicide! span)
               (set! span '())))))))))))

createSpannersAndEngravers =
#(define-void-function (name-list)(list?)
 (let* ((naming-lst
          (map
            (lambda (name)
              (list
                (string->symbol (format #f "~aTextSpanEvent" name))
                (ly:camel-case->lisp-identifier
                  (string->symbol (format #f "~aTextSpanEvent" name)))
                (string->symbol (format #f "~aTextSpanner" name))
                (string->symbol (format #f "~aTextSpannerEngraver" name))
                (string->symbol (string-downcase (format #f "~a-start" name)))
                (string->symbol (string-downcase (format #f "~a-stop" name)))))
            name-list))
        (new-scheme-event-spanner-types
           (scheme-event-spanner-types-proc
             (map car naming-lst)
             (map second naming-lst)))
        (scheme-event-spanner-types
          (map
            scheme-event-spanner-type
            new-scheme-event-spanner-types)))

    (set! music-descriptions
          (append scheme-event-spanner-types music-descriptions))
   
    (set! music-descriptions
          (sort music-descriptions alist<?))

    (for-each
      (lambda (evt
               text-span-event-name
               g-name
               engr-name
               cmd-start
               cmd-stop)
        (define-event-class text-span-event-name 'span-event)
        (add-grob-definition g-name spanner-props)
        (define-engraver engr-name g-name text-span-event-name)
        (module-define! (current-module) cmd-start
          (make-span-event evt START))
        (module-define! (current-module) cmd-stop
          (make-span-event evt STOP)))
      (map car naming-lst)
      (map second naming-lst)
      (map third naming-lst)
      (map fourth naming-lst)
      (map fifth naming-lst)
      (map sixth naming-lst))

    (newline)
    (pretty-print
      (cons
        "The following events (class and type), grobs, engravers and
start/stop-commands are created"
        naming-lst))))
     
%%%%%%%%%%%%%%%%%%%%%%%
%% Example
%%%%%%%%%%%%%%%%%%%%%%%
%{
       
%% rename path to your file, which holds the engraver code
% \include "multiple-spanner-grobs-engravers.ly"

\createSpannersAndEngravers  #'(Hallo Welt A)

\layout {
  \context {
    \Global
    \grobdescriptions #all-grob-descriptions
  }
  \context {
    \Voice
    \consists \HalloTextSpannerEngraver
    \consists \WeltTextSpannerEngraver
    \consists \ATextSpannerEngraver
  }
}

\relative {
  \override HalloTextSpanner.to-barline = ##f
 
  \override HalloTextSpanner.bound-details.left.text = #"hall"
  \override HalloTextSpanner.bound-details.right.text = #"lo"
  \override WeltTextSpanner.bound-details.left.text = #"we"
  \override WeltTextSpanner.bound-details.right.text = #"lt"
  \override ATextSpanner.bound-details.left.text = #"A"
  \override ATextSpanner.bound-details.right.text = #"A"
 
  a4 b\hallo-start c\welt-start d
  a4 b c d
  a4 \a-start b c\hallo-stop d \welt-stop \break
 
  a4\hallo-start \welt-start b d \a-stop c
  a4 b\hallo-stop c d  \welt-stop
  a1

}
%}
   


Gruß,
  Harm





Titel: Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
Beitrag von: xr am Samstag, 4. März 2017, 08:06
Zitat
Zitat
Zitat von: xr

    Ich dachte, ich hätte mal eine Property gesehen, die mir anzeigt, in welcher Systemzeile ich mich befinde. Aber ich kann die nicht mehr finden.
    Gibts die oder habe ich das falsch in Erinnerung?

Kannst Du den Zusammenhang erläutern, d.h. wo brauchst Du das? In einer Funktion?

Um bei der Bestimmung der Nachbarn einer PaperColumn herauszufinden, ob der gefundene Nachbar noch in derselben Zeile sitzt oder doch eine Zeile davor/danach.

Mit folgender Funktion zähle ich alle PaperColumns, trage sie in eine hash map ein und annotiere sie:

#(define counterPC 0)
% hash table, holding PaperColumns
% key is the counter
#(define ht-pap-cols (make-hash-table))

% mit \consists \set_annotations_to_paper_columns_engraver
% in einen Staff Context eintragen
set_annotations_to_paper_columns_engraver =
#(make-engraver

    (acknowledgers
         ;; get all PaperColums, count them
         ;; set annotation of PCs to counter
         ((paper-column-interface engraver grob source-engraver)
             (let* (
                 (mom (ly:moment-main (t->m engraver)))
                 
                 (pap-col (if (equal? (grob-name grob) 'PaperColumn)
                     grob
                     #f))
                 (annotation (number->string counterPC))
                 )
                 
                 (if pap-col
                     (begin
                     (ly:grob-set-property! grob 'annotation annotation)
                     (hashv-set! ht-pap-cols counterPC (list grob mom))
                     (set! counterPC (1+ counterPC ))
                     )
                 )
             )
         )
     )
)

Danach kann in einer Funktion, die die Slurs durchläuft, von jedem Slur die PaperColumn gefunden werden, über deren annotation ihre Nummer ausgelesen werden, und über die hash map kann man dann den Nachbarn bestimmen.
Der könnte aber auch eine Zeile drüber/drunter sitzen.

EDIT: Parent eines Slurs kann allerdings auch eine NonMusicalPaperColumn sein, wenn er am Rand sitzt. Das macht die Sache etwas komplizierter. Aber das Prinzip bleibt wahrscheinlich gleich.
Titel: Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
Beitrag von: xr am Freitag, 17. März 2017, 11:48
Die Lösung, die ich gefunden habe, hätte ich gern allgemeiner formuliert, aber dann hätte ich wohl bis Ostern daran gesessen. Nun ist sie erstmal speziell auf meine eigenen Bedürfnisse abgestimmt.

Ich weiß nicht, ob ich den gesamten Code hier einstellen soll. Ist doch ziemlich umfangreich geworden. Hier kann er eingesehen und runtergeladen werden:
https://github.com/XRoemer/Lilypond/tree/master/Custom_Slurs
Download als zip:
https://github.com/XRoemer/Lilypond

Ich habe den Code in mehrere Dateien unterteilt, damit man klarer sieht, wo sich welche Methoden befinden und (hoffentlich) eine schnellere Übersicht bekommt.

Gestartet wird das Beispiel von custom_slurs_example.ly.
Und der Einstiegspunkt für alle Methoden, die dort aufgerufen werden, ist in shape_slurs.ly . Von da aus verzweigen sich die weiteren Methoden.

Insgesamt ist es leider eine mehrschrittige und auch nicht besonders übersichtliche Programmierung geworden. Die Lilypond Datei wird 2 x gerendert.
Beim ersten Mal werden die Abstände ausgelesen und in einem Hash Table gespeichert.
Beim zweiten Durchlauf werden erst mittels Engraver Slurs, Noten und PaperColumns ausgelesen, annotiert und im Hash Table ht-columns gespeichert.
Dann werden die Phrasierungsbögen bearbeitet. Gleichzeitig läuft ein Zähler für die Zeilen mit.

Was mich noch ernsthaft stört, ist der verzweigte Code in der Startdatei. Ich hätte gern einen Befehl wie \use-custom-slurs an einer einzigen Stelle in custom_slurs_example.ly
Momentan muss ich aber an mehreren Stellen was einsetzen. Bis zum Sommer, wenn ich es wirklich brauchen werde, habe ich es bestimmt wieder vergessen.


Im Anhang sind zwei Bilder: Einmal mit unbearbeiteten Bögen, einmal mit bearbeiteten.
In Frescobaldi darf man übrigens nicht vergessen, die Pdf Ansicht umzuschalten. Es wird ja 2x gerendert, automatisch zeigt aber Frescobaldi die erste unbearbeitete Version.

Gruß,
Xaver


P.S. Die Bögen gewinnen wohl noch keinen Schönheitswettbewerb. Ihre Endpunkte sitzen jetzt zwar an den richtigen Stellen, aber die Handles könnten besser eingestellt werden. Naja, Ostern ...
Titel: Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
Beitrag von: xr am Freitag, 17. März 2017, 15:15
Die Reduktion der zusätzlichen Einträge für die Custom Slurs geht teils auch.

Wo ich aber noch nicht weiter komme, ist bei der Umwandlung folgenden Codes in eine Scheme Funktion.

compile-book =
    \book {
      \my-score
    }

#(define start-second-pass #f)
\compile-book
#(set! start-second-pass #t)
#(set! annot-zaehler 0)
#(define output-suffix "second")
\compile-book

Ich würde diesen Code gerne so umwandeln, dass ich \compile-book mit einem Argument aufrufen kann. Aber ganz gleich wie ich \book \my-score auch klammere oder welche Funktion ich auch nehme, ich bekomme immer Fehlermeldungen. Weiß jemand, wie ich das anstellen muß?

Gruß,
Xaver