On 2019-01-20 5:16 am, Andrew Bernard wrote:

> Hi Aaron,

>

> What we really want is a lovely curved bezier spline! I appreciate your

> suggestion, but I personally would rather code PostScript than hack

> hairpins to be pedal dynamics. I went through all that when I tried to

> do

> this by hijacking text spanners, and it was never satisfactory.

>

> The desire for nice gradual release curves was what led to a PostScript

> concept.

I have been meaning to find a non-trivial project to allow me to dig

into LilyPond and Scheme more than I usually do. Your use case of

gradual piano pedaling seemed a good fit. As such, do not feel

compelled at all to make use of this. Certainly, it would be nice if it

were actually used; but this has been a good learning exercise all the

same.

And I should definitely note this is still a work-in-progress:

%%%%

\version "2.19.82"

#(define ((gradual-piano-pedal-bracket shape) grob)

(define (make-dashed-curve-stencil thick left right knots step)

(define (curve param)

(let ((param (/ (- param left) (- right left)))

(degree (- (length knots) 1))

(points (list-copy knots)))

(for-each (lambda (outer) (for-each (lambda (inner)

(list-set! points inner (+ (* (- 1 param) (list-ref points

(- inner 1)))

(* param (list-ref points

inner)))))

(iota (+ (- degree outer) 1) degree -1))) (iota degree 1))

(list-ref points degree)))

(define (nearest-even num) (* 2 (inexact->exact (floor (/ num 2)))))

(let* ((steps (nearest-even (/ (- right left) step)))

(step (/ (- right left) (- steps 1)))

(x (iota steps left step))

(y (map curve x)))

(apply ly:stencil-add (map (lambda (n)

(make-line-stencil thick (list-ref x n) (list-ref y n)

(list-ref x (+ 1 n)) (list-ref y (+ 1 n)))) (iota (/ steps 2)

0 2)))))

(let* ((stencil (ly:piano-pedal-bracket::print grob))

(width (interval-length (ly:stencil-extent stencil X)))

(height (ly:grob-property grob 'edge-height '(1 . 1)))

(layout (ly:grob-layout grob))

(thick (ly:output-def-lookup layout 'line-thickness))

(x-scale (- width thick))

(shape-left (map (lambda (arg) (* (first arg) x-scale)) shape))

(shape-right (append (drop shape-left 1) (list x-scale)))

(shape-knots (map (lambda (arg) (drop arg 1)) shape)))

(if (grob::is-live? grob) (ly:stencil-add stencil

(apply ly:stencil-add

(filter-map (lambda (left right knots)

(and (< 0 (length knots))

(make-dashed-curve-stencil thick left right knots

0.25)))

shape-left shape-right shape-knots)))

'())))

#(define (number-list-list? arg) (and (list? arg) (every number-list?

arg)))

gradualSustain = #(define-event-function (shape) (number-list-list?)

#{ -\tweak stencil #(gradual-piano-pedal-bracket shape) \sustainOn #})

<<

\new PianoStaff

<<

\new Staff { \clef treble \repeat unfold 8 c''4 }

\new Staff { \clef bass \repeat unfold 8 c4 }

>>

\new Dynamics

\with {

pedalSustainStyle = #'bracket

\override PianoPedalBracket.edge-height = #'(2 . 3)

}

{ s4 s4*6\gradualSustain #'((0 2 2 0)(0.3)(0.4 0 5 -3 3))

s4\sustainOff }

>>

%%%%

\gradualSustain replaces \sustainOn, although a \sustainOff is still

needed to terminate the bracket. The parameter to \gradualSustain

indicates the desired shape as a list of curve segments. The first

value in each segment indicates the left edge of the segment, specified

as a fraction of the total bracket width. The right edge is determined

by the next segment in the list.

When included, the remaining values in each segment list indicate the

desired height of the curve along the segment using an appropriately

ordered Bezier. There is no limit on the number values you can provide,

as the code above computes Beziers of arbitrary order.

Breaking down the example above we have first a quadratic Bezier going

from 0% to 30% along the horizontal that begins at two staff spaces

above the base and ends at the bracket base. The second entry in the

list omits height values which is shorthand for no curve but it serves

to terminate the first curve. Finally, the last curve starts at 40% and

continues to the end using a cubic Bezier.

----

There are still several improvements I plan on making:

- Broken pedal brackets are not supported, as it tries to apply the same

curve to both parts.

- Curve segments are not connected visually if there is a discontinuity.

I presume a solid vertical line would be sufficient to join the broken

ends, similar to how the ends meet the base of the pedal bracket.

However, it might make sense for the vertical to be dashed as well.

- The dash width is hard-coded, but ideally should inherit the

'dash-fraction and 'dash-period properties or otherwise mimic existing

behavior. Also, I did not account for line thickness, so the gaps are

too short.

- The dashes are independently computed per segment, so there is

non-uniformity as the algorithm attempts to have a perfect number of

dashes. I should probably average the ideal step sizes to ensure

consistency throughout the whole shape, although I suspect I will have

to fudge a little when curves meet. In that situation, the end points

of the touching curves should probably only have each one half of a

dash, so the two halves will appear as one.

- The dashes are computed based only on horizontal distance, which means

vertical movement stretches the dashes. The actual arc length should be

factored in when computed the number of steps to ensure consistency

through each arc.

-- Aaron Hill

_______________________________________________

lilypond-user mailing list

[hidden email]
https://lists.gnu.org/mailman/listinfo/lilypond-user