Historische Notation "übergebundener Noten" [GELÖST]

Begonnen von ingmar, Montag, 1. Mai 2017, 20:13

« vorheriges - nächstes »

ingmar

Hallo,


im alten Forum hatte ich schonmal danach gefragt, aber leider keine Antwort erhalten. Hab selber ein bisschen herumprobiert, aber keine Lösung gefunden; daher hole ich das Thema hier nochmal hoch.

In der ersten Hälfte des 17. Jahrhunderts wurden Überbindungen noch gar nicht oder nur sehr wenig eingesetzt. Wenn sich der Komponist entschied, Taktstriche einzusetzen - und das geschah eher in Partituren als in Stimmen - dann wurden Töne, deren Notenwert über den Taktstrich hinausging, zur Verdeutlichung so geschrieben, dass der Notenkopf mitten in diesem Taktstrich lag. Eine präzise vertikale Ausrichtung gleichzeitiger Noten wurde in diesen Fällen nicht vorgenommen (angehängt sind zwei Beispiele, für eine ganze Note und eine halbe Note).

Ich brauche keine Lösung für den Fall dass der Taktwechsel auf einen Zeilenwechsel fällt; dann wurde schon ein Bindebogen verwendet (oder der Zeilenwechsel vermieden). Folgende Notenlängen treten auf: Breve, Ganze, Halbe, Viertel.

Minimalbeispiel, als Start:

\version "2.19.37"

shft = { } % tja, was müsste hier drinstehen...?

music = \relative {
c'2 \shft d1 e2
f4 g a \shft bes2
      a4 \shft g2
f2. \shft e2 d4 e8 f g \shft a4
   g8 a b c4 \shft d2
   c8 b c4. bes8 a1
}

\score { \music }


Herzlichen Dank!

--ingmar

Malte

\version "2.19.56"
\language "deutsch"

\relative { 
  \override BreathingSign.stencil = #ly:text-interface::print
  \time 4/2
  % 'extra-offset je nach Notenkopfbreite
  % 'text für Notenkopf und -hals, UP/DOWN für Halsrichtung
  % 'Y-offset für Tonhöhe (0 für mittlere Linie = h' im Violinschlüssel)
  \override BreathingSign.extra-offset = #'(1.25 . 0)
  \override BreathingSign.text = \markup { \note #"breve" #UP }
  \override BreathingSign.Y-offset = -1
  e'1*2 \breathe s1 e
  \override BreathingSign.extra-offset = #'(1.1 . 0)
  \override BreathingSign.text = \markup { \note #"1" #UP }
  \override BreathingSign.Y-offset = -1.5
  d e2*2 \breathe s2 e d1
  \override BreathingSign.extra-offset = #'(0.9 . 0)
  \override BreathingSign.text = \markup { \note #"2" #UP }
  c1 d2 e4*2 \breathe s4 e d2 c1
  \override BreathingSign.extra-offset = #'(0.9 . 0)
  \override BreathingSign.text = \markup { \note #"4" #DOWN }
  \override BreathingSign.Y-offset = 0.5
  c1 d2 e4 g8*2 \breathe s8 g e4 d2 c1
}

Ich glaube, es ist schwierig, die Position einer Note an den Taktstrich zu binden. Man kann nicht einfach X-offset oder extra-offset hernehmen, weil die Taktbreite sich ja ändern kann. Ich hab hier jetzt mal Atemzeichen mißbraucht. Das ist natürlich noch ein ganzes Stück von Benutzbarkeit entfernt, aber vielleicht kann man da ja was draus machen ;) Ich bin mir übrigens nicht sicher, ob Atemzeichen vom Taktstrich immer genau gleich weit entfernt sind oder ob das auch davon abhängt, wie dicht der Satz sonst ist. Dann könnte auch diese Lösung keine sein ...

Das horizontale Spacing ist hier auch suboptimal, z. B. ist nach dem ersten Achtel zu viel Platz, finde ich.

harm6


\version "2.19.56"

NhOnBar =
  \once \override NoteHead.after-line-breaking =
#(lambda (grob)
  (let* ((sys (ly:grob-system grob))
         (all-elts (ly:grob-object sys 'all-elements))
         (stem (ly:grob-object grob 'stem))
         (nhs&bls
           (filter
             (lambda (elt)
               (or
                 (grob::has-interface elt 'note-head-interface)
                 (grob::has-interface elt 'bar-line-interface)))
             (ly:grob-array->list all-elts)))
         (nh&bl (take (member grob nhs&bls) 2))
         (exts
           (map (lambda (g) (ly:grob-extent g sys X)) (member grob nh&bl))))

  (ly:grob-set-property! grob 'extra-offset
    (cons
      (- (interval-center (cadr exts)) (interval-center (car exts)))
      0))
  (ly:grob-set-property! stem 'extra-offset
    (cons
      (- (interval-center (cadr exts)) (interval-center (car exts)))
      0))))

     

{
\time 4/2
c''1
\NhOnBar d''\breve
e''1
}

{
c''2
\NhOnBar d''1
e''2
}


{
\time 4/8
c''4
\NhOnBar d''2
e''4
}

{
\time 4/16
c''8
\NhOnBar d''4
e''8
}


Aber Achtung, es ist keinerlei Sicherheitsnetz gespannt.
Dots und Accidentals, etc sind noch nicht erfasst.

Aber im Prinzip könnte man so ähnlich vorgehen ...


Gruß,
  Harm

ingmar

#3
Das ist ja großartig, herzlichen Dank an Euch beide! : - )

Ich werde allerdings erst am Wochenende dazu kommen, mir das im Detail anzuschauen.

ZitatHarm: Dots und Accidentals, etc sind noch nicht erfasst.

Wenn du mit Dots Verlängerungspunkte meinst: Die wurden ja bekanntlich hinter den Taktstrich geschrieben, das hattest du schon in einem alten Thread gelöst!

Ich möchte anschließend versuchen, diese beiden Lösungen zu verbinden, so dass bei punktierten Noten (etwa \xbar c1.) der Verlängerungspunkt hinter den Taktstrich kommt, bei nichtpunktierten (\xbar c1) die ganze Note auf diesen. So müsste man sich nur einen Befehl merken, ich nenn ihn hier mal \xbar.

(Anfangs hatte ich mir mal eine automatische Lösung vorgestellt, die das Überlappen über den Taktstrich automatisch erkennt, aber eigentlich führt das nur eine unnötige Komplexität ein. Heuete halte ich das eigentlich nicht für nötig.)

Also nochmal: Danke für Eure Hilfe!

--ingmar

Malte

Die Version mit extra-offset hat natürlich leider den Nachteil, daß das Spacing so tut, als wäre die Note noch an ihrem ursprünglichen Platz – deshalb gibts jeweils im ersten Takt von harms Notenbeispielen so ne große Lücke, besonders auffällig bei den ersten beiden Beispielen. Vielleicht kann man da noch was machen, daß die Note vor dem Taktstrich entsprechend skaliert wird und die Note auf dem Taktstrich vielleicht eine skalierte und verschobene Note im zweiten Takt ist?

ingmar

Malte - danke für deinen Einsatz! Aber ich brauche wohl wirklich eine Funktion, die die folgende Note zuverlässig verschiebt; es sind einfach zu viele Noten, um sie einzeln per Offset zu bearbeiten.

harm - Offenbar ist ein Fehler in deinem Code; LilyPond kritisiert die folgende Zeile mit der darunter angegebenen Meldung:
   (nh&bl (take (member grob nhs&bls) 2))
   Wrong type argument in position 1 (expecting pair): ()

Ich habe es unter 2.18.37 und 2.18.64 versucht und konnte es selber nicht reparieren.

-----

Ich habe das Problem soweit verstanden, dass der Taktstrich (bzw seine Position) offenbar dann, wenn die Note geschrieben wird, unbekannt oder unerreichbar ist. Aus dem weiteren Verlauf dieser Diskussion im englischsprachigen Forum entnehme ich, dass der Taktstrich kein Event ist und beginne, etwas von den Hintergründen zu ahnen.

Ich frage mich, wie Ganztaktpausen eigentlich gesetzt werden, denn um sie in die Mitte eines Takts zu kriegen, müsste doch der folgende Taktstrich schon bekannt sein...? Und Taktstriche können doch auch Fermaten tragen, lässt sich das nicht irgendwie ausnutzen?

Leider ist es mir trotz einiger heftiger Versuche nicht gelungen, so tief in das Thema Scheme einzusteigen, dass ich da auch nur ansatzweise weiterwüsste. Deshalb nochmal danke für Eure Unterstützung.


Gruß,
--ingmar

harm6

Zitat von: ingmar
harm - Offenbar ist ein Fehler in deinem Code; LilyPond kritisiert die folgende Zeile mit der darunter angegebenen Meldung:
   (nh&bl (take (member grob nhs&bls) 2))
   Wrong type argument in position 1 (expecting pair): ()

Nun, ich schrieb ja:
Zitat von: harmAchtung, es ist keinerlei Sicherheitsnetz gespannt.
Du hast jetzt offensichtlich einen Fall erwischt bei dem das "Sicherheitsnetz" nötig wäre.
In diesem Fall ist klar, daß man nur dann zwei Elemente aus einer Liste erhalten kann, wenn diese auch mindestens zwei hat ...

Schau mal, ob folgendes schon reicht (Du findest auch einen Versuch das bemängelte spacing zu verbessern):

NhOnBar =
  \once \override NoteHead.after-line-breaking =
#(lambda (grob)
  (let* ((sys (ly:grob-system grob))
         (all-elts (ly:grob-object sys 'all-elements))
         ;; get the stem of the note-head
         (stem (ly:grob-object grob 'stem))
         ;; get all note-heads and bar-lines from current system
         (nhs&bls
           (filter
             (lambda (elt)
               (or
                 (grob::has-interface elt 'note-head-interface)
                 (grob::has-interface elt 'bar-line-interface)))
             (ly:grob-array->list all-elts)))
         ;; get the sublist of nhs&bls starting with 'grob'
         (sub-list (member grob nhs&bls))
         ;; get a list of bar-lines from sub-list
         ;; the first bar-line should be next right to grob
         ;; TODO is this safe?
         (requested-bar-line
           (if (pair? sub-list)
               (filter
                 (lambda (e)
                   (grob::has-interface e 'bar-line-interface))
                 sub-list)
               '()))
         ;; crate a list of grob and related bar-line
         (nh&bl
           (if (pair? requested-bar-line)
               (list grob (car requested-bar-line))
               '()))
         ;; calculate their extents
         (exts
           (map
             (lambda (g) (ly:grob-extent g sys X))
             nh&bl)))
  ;; move note-head and stem on the bar-line via ly:grob-translate-axis!
  ;; the amount of the needed value is calculated as the difference between
  ;; the center of the note-head and the center of the stem in X-direction
  (if (pair? exts)
          (for-each
            (lambda (g)
              (if (ly:grob? g)
              (ly:grob-translate-axis!
                g
                (- (interval-center (cadr exts))
                   (interval-center (car exts)))
                X)))
        (list grob stem)))))
   

<<   
  {
  \time 4/2
  \repeat unfold 8 c''1
  }    
  {
  \time 4/2
  c''1
  \NhOnBar
  d''\breve
  e''1
  c''1
  \NhOnBar
  d''\breve
  e''1
  }   
  {
  \time 4/2
  \repeat unfold 4 c''1
  }
>>

{
c''2
\NhOnBar
\once \override NoteColumn.X-offset = #-2
d''1
e''2
}


Aber um den Code entscheidend zu verbessern bitte ich um ein Beispiel, wo und wie das Problem auftrat.

Gruß,
  Harm

ingmar

Hallo Harm,


vielen Dank für deine Arbeit!

Das im Post von gestern erwähnte Problem tritt für mich in deinem eigenen Beispiel auf - in dem Code, wie du ihn gepostet hast.

Hab mal unter Zeitdruck deinen neuen Code auf ein längeres Stück Musik losgelassen. Dort kommen Ganze, Halbe und Viertel vor. Es funktioniert grundsätzlich gut; ich bin aber auf folgende Problemen gestoßen:

  • Versetzungszeichen werden nicht mitgenommen (das hattest du, glaub ich, schon angedeutet)
  • Die Note scheint nicht ganz mittig zu sitzen; es sieht aus, als sitze sie ein Pixel zu weit links. Möglicherweise hat die Öffnung der "weißen" Noten eine ungerade Anzahl von Pixeln, so dass das ziemlich unvermeidlich ist, aber es fällt IMHO schon auf. Man würde sich wünschen, rechts noch ein Pixel zuzugeben.
  • Immer wieder werden einzelne Noten nicht vorwärts, zum nächsten Taktstrich, sondern zurück, zum vorhergehenden, geschoben. Eine Regel ist erstmal nicht erkennbar.
  • Einen Fall habe ich gefunden, wo die Note gar nicht verschoben wird, sondern an der alten Stelle bleibt.

Die beiden letzten Probleme sind die interessantesten, und ich habe gleich versucht, sie per Minimalbeispiel zu isolieren. Leider ist mir das aber nicht gelungen; im kleinen Beispiel verhalten sich die Snippets dann goldrichtig.

Ich werde erst in eine paar Tagen wieder dazu kommen und hoffe, dann Minimalbeispiele bauen zu können, die die Fehler deutlich exponieren. Bis dahin wollte ich dir aber einen Zwischenstand geben...

Ich habe ein paar Screenshots angehängt. Im letzten siehst du meinen Quellcode, zwei Takte vor dem im vorletzten Takt gezeigten Ausschnitt beginnen; die Funktion hab ich \HEADXBAR genannt. Es gibt auch \DOTXBAR, die den Verlängerungspunkt verschieben soll, hier aber als { } definiert ist. Der Quellcode soll beweisen, dass hier eigentlich schon verschoben werden müsste, was aber eben zweimal hintereinander nicht geschieht... Ich weiß, Quellcode per Screenshot ist wenig hilfreich - soll ja auch nur illustrieren.

Ich werde in ein paar Tagen versuchen, kurze Beispiele für die Issue 3. und Issue 4 zu finden - also zu isolieren, unter welchen Umständen der Code sich da nicht richtig verhält.

Nochmal: Danke für Deine Arbeit. Abgesehen von diesen Bugs sieht es, wie gesagt, sehr gut aus.


Gruß, : - )
--ingmar

harm6

Zitat
2.
Die Note scheint nicht ganz mittig zu sitzen; es sieht aus, als sitze sie ein Pixel zu weit links. Möglicherweise hat die Öffnung der "weißen" Noten eine ungerade Anzahl von Pixeln, so dass das ziemlich unvermeidlich ist, aber es fällt IMHO schon auf. Man würde sich wünschen, rechts noch ein Pixel zuzugeben.

Dein eigenes Issues1und2.tiff ist in der Auflösung nicht gut genug, um daraus irgendwas abzuleiten (schon die Dicke der Taktstriche variiert deutlich)
Im Anhang siehst Du einige pngs.
Erstellt mit
lilypond-git --png -dresolution=1000 atest-56.ly
und nachher mittels GIMP ausgeschnitten.
Denen zufolge sieht alles prächtig aus, imho.

Zitat
1.
Versetzungszeichen werden nicht mitgenommen (das hattest du, glaub ich, schon angedeutet)

Ist jetzt geregelt, aber es kann natürlich noch andere grobs geben, wie Script, TextScript, etc
Kommt das vor?

Zitat
3.
Immer wieder werden einzelne Noten nicht vorwärts, zum nächsten Taktstrich, sondern zurück, zum vorhergehenden, geschoben. Eine Regel ist erstmal nicht erkennbar.
4.
Einen Fall habe ich gefunden, wo die Note gar nicht verschoben wird, sondern an der alten Stelle bleibt.

Das liegt vor allem daran, daß die Selektion dessen was wohin verschoben wird sich darauf verließ, daß die entsprechende Liste nicht durcheinander kommt.
Da war ich offensichtlich zu optimistisch. Du hast ja gezeigt, daß es so nicht sicher ist.

Schau mal ob folgender deutlich überarbeiteter Code besser funktioniert.
Ich habe diverse Sicherheitsmechanismen eingebaut sowie die Selektion des gewünschten Taktstrichs verbessert.
Obs reicht ...?


NhOnBar =
  \once \override NoteHead.after-line-breaking =
    #(lambda (grob)
      (let* ((sys (ly:grob-system grob))
             (all-elts (ly:grob-object sys 'all-elements))
             ;; get the stem of the note-head
             (stem (ly:grob-object grob 'stem))
             ;; the parent-note-column
             (note-col (ly:grob-parent grob X))
             ;; note-column accidentals
             (acc (ly:note-column-accidentals note-col))
             ;; all bar-lines from 'all-elts' or '()
             (all-bar-lines
               (if (ly:grob-array? all-elts)
                   (filter
                     (lambda (e)
                       (grob::has-interface e 'bar-line-interface))
                     (ly:grob-array->list all-elts))
                   '()))
             ;; a list of bar-lines occurring after the note-head-grob or '()
             (bl-after-nh
               (if (pair? all-bar-lines)
                   (call-with-values
                     (lambda ()               
                      (partition
                       (lambda (x)
                         (> (car (grob::rhythmic-location x))
                            (car (grob::rhythmic-location grob))))
                        all-bar-lines))
                     (lambda (a b) a))
                   '()))
             ;; sort them after bar-number, better be paranoid
             (sorted-bl-after-nh
               (sort
                 bl-after-nh
                 (lambda (p q)
                   (< (car (grob::rhythmic-location p))
                      (car (grob::rhythmic-location q))))))
             ;; get the first bar-line from sorted-bl-after-nh, which is next
             ;; right to grob
             ;; it may not be the bar-line in the same context as grob, but
             ;; this does not makes a difference
             (requested-bar-line
               (if (pair? sorted-bl-after-nh)
                   (car sorted-bl-after-nh)
                   '()))
             ;; create a list of grob and related bar-line or put out '() if no
             ;; bar-line present
             (nh&bl
               (if (ly:grob? requested-bar-line)
                   (list grob requested-bar-line)
                   '()))
             ;; calculate their extents
             (exts
               (map-in-order
                 (lambda (g) (ly:grob-extent g sys X))
                 nh&bl)))
      ;; move note-head, stem and accidentals on the bar-line via
      ;; ly:grob-translate-axis!
      ;; the amount of the needed value is calculated as the difference between
      ;; the center of the note-head and the center of the stem in X-direction
      (if (pair? exts)
          (for-each
            (lambda (g)
              (if (ly:grob? g)
                  (ly:grob-translate-axis!
                    g
                    (- (interval-center (cadr exts))
                       (interval-center (car exts)))
                    X)))
            (list grob stem acc)))))
       

<<   
  {
    \time 4/2
    \repeat unfold 8 cis''1
  }
  {
    \time 4/2
    c''1
    \NhOnBar
    dis''\breve
    e''1
    c''1
    \NhOnBar
    d''\breve
    e''1
  }   
  {
    \time 4/2
    \repeat unfold 8 c''1
  }
>>

{
    c''2
    \NhOnBar
    \once \override NoteColumn.X-offset = #-2
    d''1
    e''2
}


{
    \time 4/8
    c''4
    \NhOnBar d''2
    e''4
}

{
    \time 4/16
    c''8
    \NhOnBar d''4
    e''8
}


Gruß,
  Harm

ingmar

#9
Hallo harm,


ich hatte nun Gelegenheit, die Sache mit einem längeren File auszuprobieren. Was soll ich sagen, es läuft alles, wie es soll! Ich konnte (bisher...) keinerlei Fehler entdecken.

Wegen der Auflösung der Files: Ich hab das betrachtet, was LilyPond also Default liefert, aber du hast sicher recht, da müsste man sich mal einen Ausdruck ansehen. ich nehme an, dass das dann auch in Ordnung kommt, kann es aber grad nicht prüfen.


Vielen herzlichen Dank für die prompte Hilfe,
--ingmar

harm6

ZitatWegen der Auflösung der Files: Ich hab das betrachtet, was LilyPond also Default liefert, aber du hast sicher recht, da müsste man sich mal einen Ausdruck ansehen. ich nehme an, dass das dann auch in Ordnung kommt, kann es aber grad nicht prüfen.

Ein png mit sehr hoher Auflösung zu erstellen, wie ich es gemacht hatte, ist tatsächlich nicht die beste Möglichkeit um Druckdetails zu prüfen, irgendeine Rasterung findet doch immer statt. (Es war aber die einfachste Möglichkeit die Ergebnisse hier zu posten.)
Auch ein Ausdruck hängt von der Druckerqualität und seiner Auflösung ab.
Am besten nimmt man einen pdf-Viewer mit höchster Auflösung. Für solche Zwecke habe ich früher Adobe Acrobat Reader genommen. Den gibts aber nicht mehr für Linux. So bin ich auf Foxit Reader gewechselt.
Beide schaffen bis 6400% Vergrößerung. Das reicht. ;)
Für die normale richtig/falsch-Kontrolle geht natürlich ein simpler pdf-viewer wie evince, xpdf oder dergleichen.

Gruß,
  Harm