Am Do., 10. Okt. 2019 um 14:51 Uhr schrieb Leo Correia de Verdier
> Dear list!
> I'm soon going to start engraving a piece that contains very many glissandi 
> with timing marks (or actually, glissandi with broken bowing and 
> articulations during the way). The snippet in 
>  mentions the stems might need to be repositioned slightly, and I was 
> wondering if this would be possible to automate.
> I realize this will be complicated, since it (as I understand it at least) 
> will require changing stem lengths and notehead attachments after the 
> horizontal spacing is calculated (possibly changing pitches too, but the 
> solution I’m imagining would not do that). So I would need to access the 
> calculated positions of the stems and of the endpoints of the glissando. Is 
> that possible? Where and what do I need to read up to accomplish it? Or has 
> someone already done something similar?
> I have seen Piaras Hobans code in this thread: 
> , and 
> it’s interesting for me, but it relies on the stems being equally spaced, 
> which they shouldn’t be in my case.
> In the following example I’m looking for a way to calculate up and y-diff in 
> the glissNote function, instead of having to type them in by hand after 
> looking at the resulting score. (The gap between the stems and glissando-line 
> is intentional, the articulation positioning is just not done yet). I have 
> not done it minimal because I wanted to give a glimpse of the point of the 
> notation also.
> %%%%%%%%%
> \version "2.19.82"
> glissNote = #(define-music-function (up y-diff note) (boolean? number? 
> ly:music?)
>                #{ \tweak transparent ##t
>                   \tweak no-ledgers ##t
>                   \tweak stem-attachment #(cons (if up -1 1) y-diff)
>                   #note #} )
> glissRest = #(define-music-function (staff-position rest) (number? ly:music?)
>                #{ \tweak Y-offset #staff-position
>                  \tweak layer #-1
>                  \tweak whiteout #10
>                  \parenthesize
>                  #rest #} )
> glissSkip = #(define-music-function (music) (ly:music?)
>                #{ \override NoteColumn.glissando-skip = ##t
>                   #music
>                   \revert NoteColumn.glissando-skip #} )
> \relative c'' {
>   \time 6/8
>   \set glissandoMap = #'((1 . 1))
>   <a f'>16-> -\tweak layer #-2 \glissando
>   \glissSkip {
>     <a \glissNote ##f #.5 f'>8-. <a \glissNote ##f #0.3 f'>16-. <a \glissNote 
> ##f #.1 f'>8->
>     << {\glissRest #2.5 r8 s4} \\
>        {d,8_-_> <a' \glissNote ##f #-0.4 f'>^.  <a \glissNote ##f #-.6 f'>^.} 
> >>
>   }
>   \time 2/4 <a a'>2--
> }
>   %%%%%%%%%%
> Thanks a lot!
> /Leo

Hi Leo,

the topic of glissando-stems pops up from time to time.
See older discussions, p.e. here:

Some time ago I started to code something in this regard and took your
request to polish it a bit.
Though, there are still some TODOs:
- Scripts are not always positioned nicely.
- "Inner" beams 'positions can't be affected from Glissando.after-line-breaking.

Furthermore I'm not sure the whole approach fits your needs.
Nevertheless the code is attached, probably you'll find the
calculation for "gliss-stem-intersections" helpful.

\version "2.19.83"

#(define (make-cross-stencil coords)
  (let ((thick 0.1)
        (sz 0.2))
     (- (car coords) sz) 
     (- (cdr coords) sz)
     (+ (car coords) sz) 
     (+ (cdr coords) sz))
     (- (car coords) sz) 
     (+ (cdr coords) sz)
     (+ (car coords) sz) 
     (- (cdr coords) sz)))))

%% Glissando has no pointer to the covered NoteColumns, because in most 
%% traditional music NoteColumns are *not* skipped.
%% Thus reading those NoteColumns is inconvenient.
%% Correcting Beam.positions is impossible here either. For this pupose an 
%% additional override is needed.
#(define (glissando-and-stems pad-y)
  (lambda (grob)
    (let* ((layout (ly:grob-layout grob))
           (blot (ly:output-def-lookup layout 'blot-diameter))
           (staff-symbol (ly:grob-object grob 'staff-symbol))
             (ly:grob-property staff-symbol 'line-positions '(-4 -2 0 2 4)))
           (top-staff-line (apply max staff-symbol-line-positions))
           (bottom-staff-line (apply min staff-symbol-line-positions))
           (staff-space (ly:staff-symbol-staff-space grob))
           (half-line-thick (/ (ly:staff-symbol-line-thickness grob) 2))
           (original (ly:grob-original grob))
           (left-bound (ly:spanner-bound original LEFT))
           (right-bound (ly:spanner-bound original RIGHT))
           (left-bound-when (grob::when left-bound))
           (right-bound-when (grob::when right-bound))
           ;(stil (ly:grob-property grob 'stencil))
           (stil (ly:line-spanner::print grob))
           (stil-x-ext (ly:stencil-extent stil X))
           (stil-y-ext (ly:stencil-extent stil Y))
           (left-bound-info (ly:grob-property grob 'left-bound-info))
           (X-left (assoc-get 'X left-bound-info))
           (Y-left (assoc-get 'Y left-bound-info))
           (left-padding (assoc-get 'padding left-bound-info))
           (right-bound-info (ly:grob-property grob 'right-bound-info))
           (X-right (assoc-get 'X right-bound-info))
           (Y-right (assoc-get 'Y right-bound-info))
           (gliss-gradient (/ (- Y-right Y-left) (- X-right X-left)))
           (sys (ly:grob-system grob))
           (sys-elts-array (ly:grob-object sys 'all-elements))
               (lambda (elt)
                 (let (;; Going for `ly:grob-relative-coordinate´ disturbs
                       ;; vertical spacing, thus we sort/filter using
                       ;; `grob::when´
                       (elt-when (grob::when elt)))
                     (grob::has-interface elt 'note-column-interface)
                     (ly:grob-property elt 'glissando-skip #f)
                     (ly:grob-array? (ly:grob-object elt 'note-heads))
                     (ly:moment<? left-bound-when elt-when)
                     (not (ly:moment<? right-bound-when elt-when)))))
               (ly:grob-array->list sys-elts-array)))
           ;; Stems from all NoteColumns covered by the Glissando
               (lambda (nc) (ly:grob-object nc 'stem))
               (lambda (stem)
                 (ly:grob-property stem 'stem-begin-position))
               (lambda (nc)
                   (ly:grob-object nc 'stem) sys X))
               (lambda (stem-x-coord)
                   ;; TODO do we need the x-value at all?
                      (- stem-x-coord X-left)
                      (- (car stil-x-ext) left-padding)
                            (- stem-x-coord X-left)
                            (- (car stil-x-ext) left-padding)
                            (- (+ (car stil-x-ext) half-line-thick))))
                      (if (negative? gliss-gradient)
                          (- (cdr stil-y-ext) half-line-thick)
                          (+ (car stil-y-ext) half-line-thick)))))
      ;; For conveniance/debugging
      ;; Color left/right bound
      ;(ly:grob-set-property! left-bound 'color red)
      ;(ly:grob-set-property! right-bound 'color green)
      ;; Color passed note-heads
      ; (lambda (nh)
      ;   (ly:grob-set-property! nh 'stencil (ly:note-head::print nh))
      ;   (ly:grob-set-property! nh 'color cyan))
      ; (append-map
      ;   (lambda (nc)
      ;     (ly:grob-array->list (ly:grob-object nc 'note-heads)))
      ;   ncs))
      ;; Add cross-stencils where Stem and Glissando intersect
      ;(ly:grob-set-property! grob 'stencil
      ;  (apply ly:stencil-add
      ;    (ly:grob-property grob 'stencil)
      ;      (map make-cross-stencil gliss-stem-intersections)))
      ;;  Recreate Stem.stencil to match the glissando 
      ;;  Move Flag
      ;;  Move Script
        (lambda (gsi stem)
          (let* ((stem-length (ly:grob-property stem 'length))
                 (stem-y-ext (ly:grob-extent stem stem Y))
                 (stem-y-length (- (cdr stem-y-ext) (car stem-y-ext)))
                 (stem-dir (ly:grob-property stem 'direction))
                 (pap-col (ly:item-get-column stem))
                 (pap-col-elts-array (ly:grob-object pap-col 'elements))
                     (lambda (elt)
                       (grob::has-interface elt 'script-interface))
                     (ly:grob-array->list pap-col-elts-array)))
                 (beam (ly:grob-object stem 'beam))
                     (+ (cdr gsi) (* stem-dir pad-y))
                     (if (ly:grob? beam)
                           ;(pretty-print stem-length)
                           (if (negative? stem-dir)
                               (car stem-y-ext)
                               (cdr stem-y-ext))
                         (+ (* stem-dir stem-y-length)
                            (cdr gsi) 
                            (* stem-dir pad-y)))))
                 (flag (ly:grob-object stem 'flag))
                   (if (and (ly:grob? flag) 
                            (grob::has-interface flag 'flag-interface))
                       (ly:grob-property flag 'stencil)
            ;; move scripts according to new Stem.stencil below
            (if (pair? scripts) (begin 
                  (lambda (i script)
                    (let* ((script-stil (ly:grob-property script 'stencil))
                           (script-y-off (ly:grob-property script 'Y-offset))
                           (script-padding (ly:grob-property script 'padding)))
                      ;; TODO Scripts should avoid staff-lines!
                      ;;      Special-case some scripts?
                      (ly:grob-set-property! script 'stencil
                          (ly:grob-property script 'stencil)
                             ;; move script to zero-line
                             (- script-y-off) 
                             ;; move script to glissando-line
                             (cdr gsi)
                             ;; Apply one staff-space padding for each script.
                             ;; There are probably multiple ones per 
                             ;; NoteColumn
                             (* i staff-space stem-dir -1)
                             (* script-padding stem-dir -1))
                  (iota (length scripts) 1 1)
            ;; move Flag.stencil according to new Stem.stencil below
            (if flag-stil
                (let ((default-stem-end
                        (if (positive? stem-dir)
                            (cdr stem-y-ext)
                            (car stem-y-ext)))
                        (if (positive? stem-dir)
                            (cdr new-stem-y-ext)
                            (car new-stem-y-ext))))
                  (ly:grob-set-property! flag 'stencil 
                      (- new-stem-end default-stem-end)
            ;; recreate a new Stem.stencil
            (ly:grob-set-property! stem 'stencil
                (ly:grob-extent stem stem X) 

%% Not essential, only to ease testings
multipleTransposes =
#(define-music-function (parser location m music)(ly:music? ly:music?)
   (music-clone m
    (map (lambda (pitch)
	  (ly:music-property #{ \transpose c $pitch $music #} 'element))
         (event-chord-pitches m))))

glissOn = {
  \temporary \override NoteColumn.glissando-skip = ##t
  \temporary \override NoteHead.stem-attachment = #'(0 . 0)
  %\temporary \override NoteHead.stencil = #point-stencil
  \temporary \override NoteHead.transparent = ##t
  \temporary \override = ##t
  \temporary \override Accidental.stencil = ##f
  \temporary \override = ##t

glissOff = {
  \revert NoteColumn.glissando-skip
  \revert NoteHead.stem-attachment
  %\revert NoteHead.stencil
  \revert NoteHead.transparent
  \revert Accidental.stencil

mus = {
\multipleTransposes { 
	d, e, f, g, a, b, 
	c d e f 
	g a b 
  \override NoteHead.layer = -1000
  \override Glissando.after-line-breaking = #(glissando-and-stems 0.3)
lilypond-user mailing list

Reply via email to