[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Pedal gradual release
From: |
Aaron Hill |
Subject: |
Re: Pedal gradual release |
Date: |
Tue, 22 Jan 2019 05:23:34 -0800 |
User-agent: |
Roundcube Webmail/1.3.8 |
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
gradual-pedal.cropped.png
Description: PNG image