Thank you very much!

Since I’m not very experienced with either lilypond or programming in general 
this will be a lot for me to break up, understand and work with, but it 
contains most of what I need. Now I’ll go and do my homework for some days (or 
even longer). In some time I’ll be back with results, whether they’ll be usable 
or not remains to be seen. The beams are needed for it to be really useful for 
me, but I’ll give script positions, slur positions and ability to handle broken 
glissandi at least some thought too. 

When you write that "inner beams positions can’t be affected from 
Glissando.after-line-breaking", do you mean it’s impossible, that they would 
need to be recreated rather than repositioned or just that the actual code 
can’t handle them yet?

Cheers

/Leo
> 13 okt. 2019 kl. 19:23 skrev Thomas Morley <thomasmorle...@gmail.com>:
> 
> Am Do., 10. Okt. 2019 um 14:51 Uhr schrieb Leo Correia de Verdier
> <leo.correia.de.verd...@gmail.com>:
>> 
>> 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 
>> http://lilypond.org/doc/v2.19/Documentation/notation/expressive-marks-as-lines#glissando
>>  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: 
>> https://lists.gnu.org/archive/html/lilypond-user/2014-03/msg00717.html , 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:
> https://codereview.appspot.com/4661061/#ps1
> 
> 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.
> etc
> 
> 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.
> 
> Cheers,
>  Harm
\version "2.19.83"


#(define (make-cross-stencil coords)
  (let ((thick 0.1)
        (sz 0.2))
 (ly:stencil-add
   (make-line-stencil 
     thick
     (- (car coords) sz) 
     (- (cdr coords) sz)
     (+ (car coords) sz) 
     (+ (cdr coords) sz))
   (make-line-stencil 
     thick
     (- (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))
           (staff-symbol-line-positions 
             (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))
           (ncs 
             (filter
               (lambda (elt)
                 (let (;; Going for `ly:grob-relative-coordinate´ disturbs
                       ;; vertical spacing, thus we sort/filter using
                       ;; `grob::when´
                       (elt-when (grob::when elt)))
                   (and
                     (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
           (stems
             (map
               (lambda (nc) (ly:grob-object nc 'stem))
               ncs))
           (stem-begin-positions
             (map
               (lambda (stem)
                 (ly:grob-property stem 'stem-begin-position))
               stems))
           (stems-x-coords
             (map
               (lambda (nc)
                 (ly:grob-relative-coordinate 
                   (ly:grob-object nc 'stem) sys X))
               ncs))
           (gliss-stem-intersections
             (map
               (lambda (stem-x-coord)
                 (cons
                   ;; TODO do we need the x-value at all?
                   (+
                      (- stem-x-coord X-left)
                      (- (car stil-x-ext) left-padding)
                      half-line-thick)
                   (+
                      (*
                         gliss-gradient
                         (+
                            (- stem-x-coord X-left)
                            (- (car stil-x-ext) left-padding)
                            half-line-thick
                            (- (+ (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)))))
               stems-x-coords)))
               
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
      ;;
      ;; 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
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      ;for-each
      ; (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
      ;;
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      
      (for-each
        (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))
                 (scripts
                   (filter
                     (lambda (elt)
                       (grob::has-interface elt 'script-interface))
                     (ly:grob-array->list pap-col-elts-array)))
                 (beam (ly:grob-object stem 'beam))
                 (new-stem-y-ext
                   (ordered-cons
                     (+ (cdr gsi) (* stem-dir pad-y))
                     (if (ly:grob? beam)
                         (begin
                           ;(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))
                 (flag-stil
                   (if (and (ly:grob? flag) 
                            (grob::has-interface flag 'flag-interface))
                       (ly:grob-property flag 'stencil)
                       #f)))
      
            ;;;;;;;;;;;
            ;; move scripts according to new Stem.stencil below
            ;;;;;;;;;;;
            
            (if (pair? scripts) (begin 
                (for-each
                  (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:stencil-translate-axis
                          (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))
                          Y))))
                  (iota (length scripts) 1 1)
                  scripts)))
                
            ;;;;;;;;;;;
            ;; 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)))
                      (new-stem-end
                        (if (positive? stem-dir)
                            (cdr new-stem-y-ext)
                            (car new-stem-y-ext))))
                  (ly:grob-set-property! flag 'stencil 
                    (ly:stencil-translate-axis
                      flag-stil
                      (- new-stem-end default-stem-end)
                      Y))))
            
            ;;;;;;;;;;;
            ;; recreate a new Stem.stencil
            ;;;;;;;;;;;
            
            (ly:grob-set-property! stem 'stencil
              (ly:round-filled-box 
                (ly:grob-extent stem stem X) 
                new-stem-y-ext
                blot))))
        gliss-stem-intersections
        stems))))
      
      
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% EXAMPLES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Not essential, only to ease testings
multipleTransposes =
#(define-music-function (parser location m music)(ly:music? ly:music?)
   (music-clone m
    'elements
    (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 NoteHead.no-ledgers = ##t
  \temporary \override Accidental.stencil = ##f
  \temporary \override Stem.no-stem-extend = ##t
}

glissOff = {
  \revert NoteColumn.glissando-skip
  \revert NoteHead.stem-attachment
  %\revert NoteHead.stencil
  \revert NoteHead.transparent
  \revert NoteHead.no-ledgers
  \revert Accidental.stencil
  \revert Stem.no-stem-extend
}

mus = {
  c''4\glissando
  \glissOn
  b'8-.
  \noBeam
  b'->
  bes'-.
  \noBeam
  bes'-.
  a'[-_
  aes']-.---\prall
  \glissOff
  g'1
}
	
\multipleTransposes { 
	c, 
	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)
  \mus
}
> 

_______________________________________________
lilypond-user mailing list
lilypond-user@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-user

Reply via email to