Allgemein > Fragen zu Funktionen

Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver

(1/7) > >>

xr:
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:

--- 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
  }
}

--- Ende Code ---

Grüße,
Xaver

harm6:
Hallo,

hier meine Gedanken dazu (zahlreiche Kommentare inline):


--- Code: ---\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
  }
}

--- Ende Code ---

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

xr:
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

harm6:

--- Zitat ---Wieso überschreibst du after-line-breaking (\override PhrasingSlur.after-line-breaking), obwohl doch eigentlich die Control Points bearbeitet und auch zurückgegeben werden?

--- Ende Zitat ---

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.

--- Ende Zitat ---

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 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))))))))))) .

--- Ende Zitat ---

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.
--- Ende Zitat ---

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

xr:

--- Zitat ---Das after-line-breaking-property ist ein Eintrittspunkt zu dem man eine Funktion ins Geschehen werfen kann,
--- Ende Zitat ---
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 ;)
--- Ende Zitat ---
:) :) :)

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:

--- 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
  }
}


--- Ende Code ---

Navigation

[0] Themen-Index

[#] Nächste Seite

Zur normalen Ansicht wechseln