[tablatures] bend.ly - please test and comment

[ Thread Index | Date Index | More lilynet.net/tablatures Archives ]


Hello tablature users group,

here is my first attempt to bends.
I have made some changes, so I can cover most of the cases David has
sent to us some time ago.

When version 2.13.4 will be released, everything will be fine; in the meantime you'll probably have to \include "tablature.ly" - otherwise there will be spurious
stems and stuff in the tablature.

What's important: please test the commands, fiddle around with the values
defined in the first few lines - I hope the variable names are self-explanatory;
if not, call me.

The syntax "problem" is not relevant at this early stage, but I would like to
simplify the structure; for example, the callback should recognize two
consecutive calls automagically, as in

\bendOn c4 ( d )( e )( c ) \bendOff

the same holds for ties

\bendOn c2 ( d ) ~ d ( c ) \bendOff

would definitely be better than

 \bendOn c2 ( \holdBend ) ~ d ( c ) \bendOff

And if there is a possibility to check whether a slur points to a grace note or not, I could get rid of the distinction between \preBend and \preBendOnly (silly name, you
can guess which commmand I'd like to drop :-)

Ok, so much for now, have fun!

Marc
%
% bend.ly
%
% preliminary tests for drawing bends
% 2009-08-13
% 
% Marc Hohl
%
% TODO:
% - draw dashed line for \holdBend
% - enable consecutive bend ups
% - simplify \preBend and \holdBend usage
% - ...

#(display "\n\nbend.ly â?? 2009-08-13\n\n")

\version "2.13.3"

\paper {
   indent = 0
   ragged-right = ##f }

%%% sizes and values (to be changed/adapted):

% the line thickness of bends and stuff:
#(define bend-line-thickness 0.1)

% the height of pointed slurs is fixed regardless of its width
% (TODO: should this be changed?):
#(define pointed-slur-height 2.0)

% the linear amount of the bend arrow (TODO: find a better name?)
#(define bend-ratio 0.35)

% the distance between the topmost tablature line and the arrow head
#(define bend-dy 2.75)

% the height of the bend arrow head:
#(define bend-arrow-height 1.25)

% the width of the arrow head base:
#(define bend-arrow-width 0.8)

% the distance between the tablature line where a bend starts
% and the starting point in vertical direction:
#(define bend-y-offset 0.35)

%%% internal commands
#(define-public (quarterdiff->string quarterdiff)
  (let ((wholesteps (floor (/ quarterdiff 4))))

       (string-append (case wholesteps
                            ((0) "")
                            (else (number->string wholesteps)))
                      (case (modulo quarterdiff 4)
                            ((1) "¼")
                            ((2) "½")
                            ((3) "¾")
                            (else "")))))

%%% markup commands
#(define-markup-command (pointedSlur layout props thickness bx by mx my ex ey)
  (number? number? number? number? number? number? number?)
  (interpret-markup layout props
    (markup #:postscript
            (ly:format "~f setlinewidth
                        ~f ~f moveto
                        ~f ~f lineto
                        ~f ~f lineto stroke" thickness bx by mx my ex ey))))

#(define-markup-command (drawBendArrow layout props
  thickness begin-x middle-x end-x begin-y end-y arrow-lx arrow-rx arrow-y outstring)
  (number? number? number? number? number? number? number? number? number? string?)
  (interpret-markup layout props
    (markup #:postscript
            (ly:format "~f setlinewidth
                        ~f ~f moveto
                        ~f ~f lineto
                        ~f ~f ~f ~f ~f ~f curveto
                        stroke
                        ~f ~f moveto
                        ~f ~f lineto
                        ~f ~f lineto
                        closepath fill"
                        thickness
                        begin-x begin-y
                        middle-x begin-y
                        middle-x begin-y end-x begin-y end-x arrow-y
                        arrow-lx arrow-y
                        end-x end-y
                        arrow-rx arrow-y)
            #:hspace 0
            #:translate (cons (- end-x 1.2) (+ end-y 0.5))
            #:fontsize -2
            #:bold #:center-column (outstring))))

#(define-markup-command (drawHoldBend layout props
  thickness begin-x end-x line-y)
  (number? number? number? number?)
  (interpret-markup layout props
    (markup #:postscript
            (ly:format "~f setlinewidth
                        ~f ~f moveto
                        ~f ~f lineto
                        stroke" thickness begin-x line-y end-x line-y))))

#(define-markup-command (drawHoldBendWithArrow layout props
  thickness begin-x begin-y end-x end-y arrow-lx arrow-rx arrow-y outstring)
  (number? number? number? number? number? number? number? number? string?)
  (interpret-markup layout props
    (markup #:postscript
            (ly:format "~f setlinewidth
                        ~f ~f moveto
                        ~f ~f lineto
                        stroke
                        ~f ~f moveto
                        ~f ~f lineto
                        ~f ~f lineto
                        closepath fill
                        ~f ~f moveto
                        ~f ~f lineto
                        stroke"
                        thickness
                        begin-x begin-y
                        begin-x arrow-y
                        arrow-lx arrow-y
                        begin-x end-y
                        arrow-rx arrow-y
                        begin-x end-y
                        end-x end-y)
            #:hspace 0
            #:translate (cons (- begin-x 1.2) (+ end-y 0.5))
            #:fontsize -2
            #:bold #:center-column (outstring))))

#(define-markup-command (drawHoldBendArrowOnly layout props
  thickness begin-x begin-y end-x end-y arrow-lx arrow-rx arrow-y outstring)
  (number? number? number? number? number? number? number? number? string?)
  (interpret-markup layout props
    (markup #:postscript
            (ly:format "~f setlinewidth
                        ~f ~f moveto
                        ~f ~f lineto
                        stroke
                        ~f ~f moveto
                        ~f ~f lineto
                        ~f ~f lineto
                        closepath fill"
                        thickness
                        begin-x begin-y
                        begin-x arrow-y
                        arrow-lx arrow-y
                        begin-x end-y
                        arrow-rx arrow-y)
            #:hspace 0
            #:translate (cons (- begin-x 1.2) (+ end-y 0.5))
            #:fontsize -2
            #:bold #:center-column (outstring))))

#(define-markup-command (drawDashedLine layout props
  thickness begin-x end-x line-y)
  (number? number? number? number?)
  ;; TODO: draws a full line instead of a dashed line
  (interpret-markup layout props
    (markup #:postscript
            (ly:format "~f setlinewidth
                        ~f ~f moveto
                        ~f ~f lineto
                        stroke"
                        thickness begin-x line-y end-x line-y))))

%%% callbacks

#(define-public (slur::draw-pointed-slur grob)
  (let* (;;(pointed-slur-height 2.0)
         ;;(bend-line-thickness 0.1)
         (control-points (ly:grob-property grob 'control-points))
         (direction (ly:grob-property grob 'direction))
         (left-point (car control-points))
         (right-point (cadddr control-points))
         (begin-x (car left-point))
         (begin-y (cdr left-point))
         (end-x (car right-point))
         (end-y (cdr right-point))
         (middle-x (/ (+ begin-x end-x) 2))
         (middle-y (/ (+ begin-y end-y) 2))
         (normal-x (* direction (- begin-y end-y)))
         (normal-y (* direction (- end-x begin-x)))
         (normal-length (sqrt (+ (* normal-x normal-x)
                                 (* normal-y normal-y))))
         (point-x (+ middle-x (* pointed-slur-height
                                 (/ normal-x normal-length))))
         (point-y (+ middle-y (* pointed-slur-height
                                 (/ normal-y normal-length)))))

         (grob-interpret-markup grob
                                (make-pointedSlur-markup bend-line-thickness
                                  begin-x begin-y point-x point-y end-x end-y))))

#(define-public (slur::draw-bend-arrow grob)
  (let* (;;(bend-line-thickness 0.1)
         ;;(bend-ratio 0.35)
         ;;(bend-dy 2.75)
         ;;(bend-arrow-height 1.25)
         ;;(bend-arrow-width 0.8)
         ;;(bend-y-offset 0.35)
         (staff-symbol (ly:grob-object grob 'staff-symbol))
         (line-count (ly:grob-property staff-symbol 'line-count))
         (staff-space (ly:grob-property staff-symbol 'staff-space))
         (left-bound (ly:spanner-bound grob LEFT))
         (right-bound (ly:spanner-bound grob RIGHT))
         (left-tab-note-head (ly:grob-property left-bound 'cause))
         (right-tab-note-head (ly:grob-property right-bound 'cause))
         (control-points (ly:grob-property grob 'control-points))
         (left-point (car control-points))
         (right-point (cadddr control-points))
         (left-pitch  (ly:event-property (event-cause left-bound) 'pitch))
         (right-pitch (ly:event-property (event-cause right-bound) 'pitch))
         (quarterdiff (- (ly:pitch-quartertones right-pitch)
                         (ly:pitch-quartertones left-pitch)))
         (begin-x (car left-point))
         (begin-y (+ (* (/ (ly:grob-property left-tab-note-head 'staff-position) 2)
                        staff-space)
                     bend-y-offset))
         ;; cdr left-point doesn't work, because invisible stems are included
         (end-x (car right-point))
         (end-y (+ (* (/ (- line-count 1) 2) staff-space) bend-dy))
         (arrow-lx (- end-x (/ bend-arrow-width 2)))
         (arrow-rx (+ end-x (/ bend-arrow-width 2)))
         (arrow-y (- end-y bend-arrow-height))
         (middle-x (+ begin-x (* bend-ratio (- end-x begin-x))))
         (bend-amount (quarterdiff->string quarterdiff)))

        (if (< quarterdiff 0)
            ;; bend down
            (let* ((y-offset (cdr (ly:grob-extent left-tab-note-head left-tab-note-head Y)))
                   (temp begin-y))

                  (set! begin-y end-y) ;; swap begin-y/end-y
                  (set! end-y (+ temp y-offset))
                  (set! arrow-y (+ end-y bend-arrow-height))
                  (set! bend-amount "")
                  (ly:grob-set-property! right-tab-note-head 'stencil
                                         (lambda (grob) (parenthesize-tab-note-head grob))))
            ;; bend up
            (let* ((x-offset (/ (cdr (ly:grob-extent left-tab-note-head left-tab-note-head X))
                                2)))

                  (set! begin-x (+ begin-x x-offset))
                  (ly:grob-set-property! right-tab-note-head 'transparent #t)))
        ;; draw resulting bend arrow
        (grob-interpret-markup grob
                               (make-drawBendArrow-markup
                                 bend-line-thickness
                                 begin-x middle-x end-x begin-y end-y
                                 arrow-lx arrow-rx arrow-y
                                 bend-amount))))

#(define-public (slur::draw-pre-bend-hold grob)
  (let* (;;(bend-line-thickness 0.1)
         ;;(bend-dy 2.75)
         ;;(bend-arrow-height 1.25)
         ;;(bend-arrow-width 0.8)
         ;;(bend-y-offset 0.35)
         (staff-symbol (ly:grob-object grob 'staff-symbol))
         (line-count (ly:grob-property staff-symbol 'line-count))
         (staff-space (ly:grob-property staff-symbol 'staff-space))
         (left-bound (ly:spanner-bound grob LEFT))
         (right-bound (ly:spanner-bound grob RIGHT))
         (left-tab-note-head (ly:grob-property left-bound 'cause))
         (right-tab-note-head (ly:grob-property right-bound 'cause))
         (control-points (ly:grob-property grob 'control-points))
         (left-point (car control-points))
         (right-point (cadddr control-points))
         (left-pitch  (ly:event-property (event-cause left-bound) 'pitch))
         (right-pitch (ly:event-property (event-cause right-bound) 'pitch))
         (quarterdiff (- (ly:pitch-quartertones right-pitch)
                         (ly:pitch-quartertones left-pitch)))
         (begin-x (car left-point))
         (y-offset (cdr (ly:grob-extent left-tab-note-head left-tab-note-head Y)))
         (begin-y (+ (* (/ (ly:grob-property left-tab-note-head 'staff-position)
                           2)
                        staff-space)
                     y-offset))
         ;; cdr left-point doesn't work, because invisible stems are included
         (end-x (car right-point))
         (end-y (+ (* (/ (- line-count 1) 2) staff-space) bend-dy))
         (arrow-lx (- begin-x (/ bend-arrow-width 2)))
         (arrow-rx (+ begin-x (/ bend-arrow-width 2)))
         (arrow-y (- end-y bend-arrow-height))
         (bend-amount (quarterdiff->string quarterdiff)))

        (ly:grob-set-property! right-tab-note-head 'transparent #t)
        ;; draw resulting bend arrow
        (grob-interpret-markup grob
                               (make-drawHoldBendWithArrow-markup
                                 bend-line-thickness
                                 begin-x begin-y
                                 end-x end-y
                                 arrow-lx arrow-rx arrow-y
                                 bend-amount))))

#(define-public (slur::draw-pre-bend-only grob)
  (let* (;;(bend-line-thickness 0.1)
         ;;(bend-dy 2.75)
         ;;(bend-arrow-height 1.25)
         ;;(bend-arrow-width 0.8)
         (staff-symbol (ly:grob-object grob 'staff-symbol))
         (line-count (ly:grob-property staff-symbol 'line-count))
         (staff-space (ly:grob-property staff-symbol 'staff-space))
         (left-bound (ly:spanner-bound grob LEFT))
         (right-bound (ly:spanner-bound grob RIGHT))
         (left-tab-note-head (ly:grob-property left-bound 'cause))
         (right-tab-note-head (ly:grob-property right-bound 'cause))
         (control-points (ly:grob-property grob 'control-points))
         (left-point (car control-points))
         (right-point (cadddr control-points))
         (left-pitch  (ly:event-property (event-cause left-bound) 'pitch))
         (right-pitch (ly:event-property (event-cause right-bound) 'pitch))
         (quarterdiff (- (ly:pitch-quartertones right-pitch)
                         (ly:pitch-quartertones left-pitch)))
         (begin-x (car left-point))
         (y-offset (cdr (ly:grob-extent left-tab-note-head left-tab-note-head Y)))
         (begin-y (+ (* (/ (ly:grob-property left-tab-note-head 'staff-position)
                           2)
                        staff-space)
                     y-offset))
         ;; cdr left-point doesn't work, because invisible stems are included
         (end-x (car right-point))
         (end-y (+ (* (/ (- line-count 1) 2) staff-space) bend-dy))
         (arrow-lx (- begin-x (/ bend-arrow-width 2)))
         (arrow-rx (+ begin-x (/ bend-arrow-width 2)))
         (arrow-y (- end-y bend-arrow-height))
         (bend-amount (quarterdiff->string quarterdiff)))

        (ly:grob-set-property! right-tab-note-head 'transparent #t)
        ;; draw resulting bend arrow
        (grob-interpret-markup grob
                               (make-drawHoldBendArrowOnly-markup
                                 bend-line-thickness
                                 begin-x begin-y
                                 end-x end-y
                                 arrow-lx arrow-rx arrow-y
                                 bend-amount))))

#(define-public (tie::draw-hold-bend grob)
  (let* (;;(bend-line-thickness 0.1)
         ;;(bend-dy 2.75)
         ;;(bend-arrow-height 1.25)
         ;;(bend-arrow-width 0.8)
         (staff-symbol (ly:grob-object grob 'staff-symbol))
         (line-count (ly:grob-property staff-symbol 'line-count))
         (staff-space (ly:grob-property staff-symbol 'staff-space))
         (left-tab-note-head (ly:spanner-bound grob LEFT))
         (right-tab-note-head (ly:spanner-bound grob RIGHT))
         (control-points (ly:grob-property grob 'control-points))
         (left-point (car control-points))
         (right-point (cadddr control-points))
         (begin-x (car left-point))
         (end-x (car right-point))
         (line-y (+ (* (/ (- line-count 1) 2) staff-space) bend-dy)))

        (ly:grob-set-property! right-tab-note-head 'transparent #t)
        (grob-interpret-markup grob
                               (make-drawDashedLine-markup
                                 bend-line-thickness
                                 begin-x end-x line-y))))

%%% music functions

bendOn = {
  \override Voice.Slur #'stencil = #slur::draw-pointed-slur
  \override TabVoice.Slur #'stencil = #slur::draw-bend-arrow
}

bendOff = {
  \revert Voice.Slur #'stencil
  \override TabVoice.Slur #'stencil = ##f
  %\override TabVoice.Slur #'direction = #UP
}

bendGrace = #(define-music-function (parser location note) (ly:music?)
  #{
    \once \override Voice.Stem #'stencil = ##f
    \once \override Voice.Stem #'direction = #DOWN
    \once \override Voice.Slur #'direction = #UP
    \grace $note
  #})

preBendOnly = #(define-music-function (parser location note) (ly:music?)
  #{
    \once \override TabVoice.Slur #'stencil = #slur::draw-pre-bend-only
    \once \override TabStaff.ParenthesesItem #'transparent = ##t
    \parenthesize $note
  #})

preBend = #(define-music-function (parser location note) (ly:music?)
  #{
    \once \override TabVoice.Slur #'stencil = #slur::draw-pre-bend-hold
    \once \override TabStaff.ParenthesesItem #'transparent = ##t
    \parenthesize $note
  #})

holdBend = #(define-music-function (parser location) ()
  #{
    \once \override TabVoice.Tie #'stencil = #tie::draw-hold-bend
  #})

%% the test (finally!)

test = \relative c'' {
  \bendOn
  % First, some bends to see if they work from the topmost to the lowest string
  c4 ( d )( c2 )
  c4\2 ( d\2 )( c2\2 )
  c4\3 ( des\3 )( c2\3 ) \break
  c,4\4 ( d\4 )( c2\4 )
  c4\5 ( d\5 )( c2\5 )
  c4\6 ( d\6 )( c2\6 ) \break
  % is the bend amount displayed correctly? (should be ½ in both cases)
  c4 ( cis) d ( es )
  % grace notes
  \bendGrace c8(  d4 )( c4 ) r2
  % the distinction between \preBendOnly and \preBend is not very
  % elegant here, I hope that there will be a better solution...
  \bendGrace { \preBendOnly c8( } d2)  r2
  \bendGrace { \preBend c8( d)( } c2)  r2
  c4 ( es) e\2 ( gis\2 ) \break
  % quarter tone bends are not yet supported as they should be, but
  % the bend amount is calculated correctly ;-)
  c,4 ( cih ) c4 ( cisih )
  % I hope that in future releases the tie will recognize automagically
  % that he ties to a note which is bent, but I don't know how (yet).
  \bendGrace c'8 ( \holdBend d2 ) ~ d2 ( c1 ) 
  \bendOff
  % switching bends off works apparently
  c,4 ( e ) c4 ( f )
  c'4 ( b ) c4 ( a )
}

\score {
  <<
    \new Staff {
      \new Voice {
        \clef "G_8"
        \test
      }
    }
    \new TabStaff {
      \new TabVoice {
        \clef "tab"
        \set TabStaff.stringTunings = #guitar-seven-string-tuning
        \test
      }
    }
  >>
}


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/