Autor Thema: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver  (Gelesen 4221 mal)

xr

  • Gast
Re: vom Grob NoteColumn einen Lyrics Grob finden
« Antwort #15 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

harm6

  • Gast
Re: vom Grob NoteColumn einen Lyrics Grob finden
« Antwort #16 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
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

xr

  • Gast
Re: vom Grob NoteColumn einen Lyrics Grob finden
« Antwort #17 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?
« Letzte Änderung: Dienstag, 28. Februar 2017, 21:45 von xr »

harm6

  • Gast
Re: vom Grob NoteColumn einen Lyrics Grob finden
« Antwort #18 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
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://archiv.lilypondforum.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




xr

  • Gast
Re: vom Grob NoteColumn einen Lyrics Grob finden
« Antwort #19 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)



xr

  • Gast
Re: vom Grob NoteColumn einen Lyrics Grob finden
« Antwort #20 am: Mittwoch, 1. März 2017, 12:46 »
Zitat
Siehe:
https://archiv.lilypondforum.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 ...

Manuela

  • Gast
Re: vom Grob NoteColumn einen Lyrics Grob finden
« Antwort #21 am: Mittwoch, 1. März 2017, 13:16 »
Zitat
Siehe:
https://archiv.lilypondforum.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.

xr

  • Gast
Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
« Antwort #22 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
« Letzte Änderung: Mittwoch, 1. März 2017, 18:09 von xr »

harm6

  • Gast
Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
« Antwort #23 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

xr

  • Gast
Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
« Antwort #24 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

harm6

  • Gast
Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
« Antwort #25 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

harm6

  • Gast
Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
« Antwort #26 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

xr

  • Gast
Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
« Antwort #27 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

xr

  • Gast
Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
« Antwort #28 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?

harm6

  • Gast
Re: Phrasierungsbögen an Text ausrichten / Custom TextSpannerEngraver
« Antwort #29 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

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
Ich würde direkt in die regression-tests verlinken:
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