Hi Stefano,
On Mon, Feb 5, 2018 at 11:53 PM, Stefano Troncaro
<stefanotronc...@gmail.com> wrote:
> David and Harm, I'm really impressed by the level of expertise you both have
> showed in this thread. The function works wonderfully, and I'm really
> grateful for your help!
>
> I feel kind of bad for asking, but I'm stuck after trying to do what I
> thought would be a minor tweak. I wanted to make it so that the two lines
> that form the hairpin would end in the same vertical line, since when the
> hairpin is rotated the end-points of the two lines are displaced. I tried
> achieve this by drawing a white box that overlaps with the line that
> overextends, therefore "deleting" the excess.
>
> While I could not always place the box correctly, due to how
> ly:stencil-stack works (I don't explain this further because of the
> following), the real problem I found is that even when the box is properly
> placed, the shortened line looks off. This is because Lilypond naturally
> makes line endings smooth, so the "cut the excess with a box" approach
> creates a hairpin with one line ending smoothly and the other ending
> harshly. This is less evident for thinner lines but is easy to see with
> thicker ones. Another flaw of this approach is that the white box reserves
> unused space.
>
> So, with that in mind, I wonder: is there a way to smoothen the line after
> "cutting" it (which I doubt) or, lacking that, is there a way to access only
> one line of the hairpin to shorten it by the necessary amount? The later I
> imagine like a Hairpin.shorten-pair that affects only one of the two lines.
> Alternatively, is it more sensible to just draw the two lines and stack them
> into a stencil? I have not yet tried this but the more I think about it the
> more it looks like the most viable option. I tried to search the definition
> of ly:hairpin::print to see how Lilypond does this, but I couldn't find it.
At this point I think you would get the best results by rewriting
ly:hairpin::print from scratch so that it has the existing
functionality with your enhancements worked in.
At some point I translated the function from C++ into Scheme for some
experimentation. It's fairly direct. I found a version of this where
I left the original C++ code inlined as comments
(add-shorten-pair.ly).
I used this as a preliminary to adding 'shorten-pair directly into the
C++ code, and I don't remember if I made improvements to the codebase
along the way...
Hopefully, you can make use of it!
I also located a file which shows what you can do from scratch: here
adjusting the size of the circle in the circled tip (not with
shorten-pair here...) FWIW.
Hope this helps...
David
\version "2.19.23"
#(define broken-right-neighbor
(lambda (grob)
(let* ((pieces (ly:spanner-broken-into (ly:grob-original grob)))
(me-list (member grob pieces)))
(if (> (length me-list) 1)
(cadr me-list)
'()))))
#(define (interval-dir-set i val dir)
(cond ((= dir LEFT) (set-car! i val))
((= dir RIGHT) (set-cdr! i val))
(else (ly:error "dir must be LEFT or RIGHT"))))
#(define (other-dir dir) (- dir))
%{
;; C++ is from lily/hairpin.cc
;;
;; MAKE_SCHEME_CALLBACK (Hairpin, print, 1);
;; SCM
;; Hairpin::print (SCM smob)
;; {
;; Spanner *me = unsmob<Spanner> (smob);
%}
#(define hairpin::print-scheme
(lambda (grob)
;; SCM s = me->get_property ("grow-direction");
(let ((grow-dir (ly:grob-property grob 'grow-direction)))
;; if (!is_direction (s))
;; {
;; me->suicide ();
;; return SCM_EOL;
;; }
(if (not (ly:dir? grow-dir))
(begin
(ly:grob-suicide! grob)
'())
;; Direction grow_dir = to_dir (s);
;; Real padding = robust_scm2double (me->get_property ("bound-padding"), 0.5);
;;
;; Drul_array<bool> broken;
;; Drul_array<Item *> bounds;
;; for (LEFT_and_RIGHT (d))
;; {
;; bounds[d] = me->get_bound (d);
;; broken[d] = bounds[d]->break_status_dir () != CENTER;
;; }
(let* ((padding (ly:grob-property grob 'bound-padding 0.5))
(bounds (cons (ly:spanner-bound grob LEFT)
(ly:spanner-bound grob RIGHT)))
(broken (cons
(not (= (ly:item-break-dir (car bounds)) CENTER))
(not (= (ly:item-break-dir (cdr bounds)) CENTER)))))
;; if (broken[RIGHT])
;; {
;; Spanner *next = me->broken_neighbor (RIGHT);
;; // Hairpin-parts suicide in after-line-breaking if they need not be drawn
;; if (next)
;; {
;; (void) next->get_property ("after-line-breaking");
;; broken[RIGHT] = next->is_live ();
;; }
;; else
;; broken[RIGHT] = false;
;; }
(if (cdr broken)
(let ((next (broken-right-neighbor grob)))
(if (ly:spanner? next)
(begin
(ly:grob-property next 'after-line-breaking)
(set-cdr! broken (grob::is-live? next)))
(set-cdr! broken #f))))
;; Grob *common = bounds[LEFT]->common_refpoint (bounds[RIGHT], X_AXIS);
;; Drul_array<Real> x_points;
;;
;; /*
;; Use the height and thickness of the hairpin when making a circled tip
;; */
;; bool circled_tip = ly_scm2bool (me->get_property ("circled-tip"));
;; Real height = robust_scm2double (me->get_property ("height"), 0.2)
;; * Staff_symbol_referencer::staff_space (me);
;; /*
;; FIXME: 0.525 is still just a guess...
;; */
;; Real rad = height * 0.525;
;; Real thick = 1.0;
;; if (circled_tip)
;; thick = robust_scm2double (me->get_property ("thickness"), 1.0)
;; * Staff_symbol_referencer::line_thickness (me);
(let* ((common
(ly:grob-common-refpoint (car bounds) (cdr bounds) X))
(x-points (cons 0 0))
(circled-tip (ly:grob-property grob 'circled-tip))
(height (* (ly:grob-property grob 'height 0.2)
(ly:staff-symbol-staff-space grob)))
(rad (* 0.525 height))
; commented out is directly from C++, but it leads to thick lines when
; no circled tip!
;(thick 1.0)
;(thick (if circled-tip
;(* (ly:grob-property grob 'thickness 1.0)
;(ly:staff-symbol-line-thickness grob))
;thick))
(thick (* (ly:grob-property grob 'thickness 1.0)
(ly:staff-symbol-line-thickness grob))))
;; for (LEFT_and_RIGHT (d))
;; {
;; Item *b = bounds[d];
;; Interval e = Axis_group_interface::generic_bound_extent (b, common, X_AXIS);
;;
;; x_points[d] = b->relative_coordinate (common, X_AXIS);
(define (inner dir)
(let* ((b (interval-bound bounds dir))
(e (ly:generic-bound-extent b common))) ; X-AXIS assumed
(interval-dir-set
x-points (ly:grob-relative-coordinate b common X) dir)
;; if (broken [d])
;; {
;; if (d == LEFT)
;; x_points[d] = e[-d];
(if (interval-bound broken dir)
(if (= dir LEFT)
(interval-dir-set
x-points (interval-bound e (other-dir dir)) dir)
;; else
;; {
;; Real broken_bound_padding
;; = robust_scm2double (me->get_property ("broken-bound-padding"), 0.0);
;; extract_grob_set (me, "concurrent-hairpins", chp);
(let* ((broken-bound-padding
(ly:grob-property grob 'broken-bound-padding 0.0))
(chp (ly:grob-object grob 'concurrent-hairpins)))
;; for (vsize i = 0; i < chp.size (); i++)
;; {
;; Spanner *span_elt = dynamic_cast<Spanner *> (chp[i]);
;; if (span_elt->get_bound (RIGHT)->break_status_dir () == LEFT)
;; broken_bound_padding = max (broken_bound_padding,
;; robust_scm2double (span_elt->get_property ("broken-bound-padding"), 0.0));
;; }
(let loop ((i 0))
(if (and (ly:grob-array? chp) ; hmm...why no test in C++ needed?
(< i (ly:grob-array-length chp)))
(let ((span-elt (ly:grob-array-ref chp i)))
(if (= (ly:item-break-dir (ly:spanner-bound span-elt RIGHT))
LEFT)
(set! broken-bound-padding
(max broken-bound-padding
(ly:grob-property span-elt 'broken-bound-padding 0.0))))
(loop (1+ i)))))
;; x_points[d] -= d * broken_bound_padding;
;; }
;; }
(interval-dir-set
x-points
(- (interval-bound x-points dir)
(* dir broken-bound-padding))
dir)))
;; else
;; {
;; if (Text_interface::has_interface (b))
;; {
;; if (!e.is_empty ())
;; x_points[d] = e[-d] - d * padding;
;; }
(if (grob::has-interface b 'text-interface)
(if (not (interval-empty? e))
(interval-dir-set x-points
(- (interval-bound e (other-dir dir))
(* dir padding))
dir))
;; else
;; {
;; bool neighbor_found = false;
;; Spanner *adjacent = NULL;
;; extract_grob_set (me, "adjacent-spanners", neighbors);
(let* ((neighbor-found #f)
(adjacent '()) ; spanner
(neighbors (ly:grob-object grob 'adjacent-spanners))
(neighbors-len (if (ly:grob-array? neighbors)
(ly:grob-array-length neighbors)
0))) ; this shouldn't be necessary -- see comment above
;; for (vsize i = 0; i < neighbors.size (); i++)
;; {
;; /*
;; FIXME: this will f*ck up in case of polyphonic
;; notes in other voices. Need to look at note-columns
;; in the current staff/voice.
;; */
;; adjacent = dynamic_cast<Spanner *> (neighbors[i]);
;; if (adjacent
;; && (adjacent->get_bound (-d)->get_column ()
;; == b->get_column ()))
;; {
;; neighbor_found = true;
;; break;
;; }
;; }
(let inner-two ((i 0))
(if (and (< i neighbors-len)
(not neighbor-found))
(begin
(set! adjacent (ly:grob-array-ref neighbors i))
(if (and (ly:spanner? adjacent)
(eq? (ly:item-get-column (ly:spanner-bound adjacent (other-dir dir)))
(ly:item-get-column b)))
(set! neighbor-found #t))
(inner-two (1+ i)))))
;; if (neighbor_found)
;; {
;; if (Hairpin::has_interface (adjacent))
;; {
;; /*
;; Handle back-to-back hairpins with a circle in the middle
;; */
;; if (circled_tip && (grow_dir != d))
;; x_points[d] = e.center () + d * (rad - thick / 2.0);
;; /*
;; If we're hung on a paper column, that means we're not
;; adjacent to a text-dynamic, and we may move closer. We
;; make the padding a little smaller, here.
;; */
;; else
;; x_points[d] = e.center () - d * padding / 3;
;; }
;; // Our neighbor is a dynamic text spanner.
;; // If we end on the text, pad as for text dynamics
;; else if (d == RIGHT)
;; x_points[d] = e[-d] - d * padding;
;; }
(if neighbor-found
(if (grob::has-interface adjacent 'hairpin-interface)
(if (and circled-tip (not (eq? grow-dir dir)))
(interval-dir-set x-points
(+ (interval-center e)
(* dir
(- rad (/ thick 2.0))))
dir)
(interval-dir-set x-points
(- (interval-center e)
(/ (* dir padding) 3.0))
dir))
(if (= dir RIGHT)
(interval-dir-set x-points
(- (interval-bound e (other-dir dir))
(* dir padding))
dir)))
;; else
;; {
;; if (d == RIGHT // end at the left edge of a rest
;; && Note_column::has_interface (b)
;; && Note_column::has_rests (b))
;; x_points[d] = e[-d];
;; else
;; x_points[d] = e[d];
(begin
(if (and (= dir RIGHT)
(grob::has-interface b 'note-column-interface)
(ly:grob-array? (ly:grob-object b 'rest)))
(interval-dir-set x-points
(interval-bound e (other-dir dir))
dir)
(interval-dir-set x-points
(interval-bound e dir)
dir))
;; if (Item::is_non_musical (b))
;; x_points[d] -= d * padding;
;; }
;; }
;; }
;; }
(if (eq? (ly:grob-property b 'non-musical) #t)
(interval-dir-set x-points
(- (interval-bound x-points dir)
(* dir padding))
dir)))))))))
(inner LEFT)
(inner RIGHT)
;; Real width = x_points[RIGHT] - x_points[LEFT];
;;
;; if (width < 0)
;; {
;; me->warning (_ ((grow_dir < 0) ? "decrescendo too small"
;; : "crescendo too small"));
;; width = 0;
;; }
(let* ((width (- (interval-bound x-points RIGHT)
(interval-bound x-points LEFT)))
(width (if (< width 0)
(begin
(ly:warning
(if (< grow-dir 0)
"decrescendo too small"
"crescendo too small"))
0)
width))
;; bool continued = broken[Direction (-grow_dir)];
;; bool continuing = broken[Direction (grow_dir)];
(continued (interval-bound broken (other-dir grow-dir)))
(continuing (interval-bound broken grow-dir))
;; Real starth = 0;
;; Real endh = 0;
;; if (grow_dir < 0)
;; {
;; starth = continuing ? 2 * height / 3 : height;
;; endh = continued ? height / 3 : 0.0;
;; }
;; else
;; {
;; starth = continued ? height / 3 : 0.0;
;; endh = continuing ? 2 * height / 3 : height;
;; }
(starth (if (< grow-dir 0)
(if continuing
(* 2 (/ height 3))
height)
(if continued
(/ height 3)
0.0)))
(endh (if (< grow-dir 0)
(if continued
(/ height 3)
0.0)
(if continuing
(* 2 (/ height 3))
height)))
;; /*
;; should do relative to staff-symbol staff-space?
;; */
;; Stencil mol;
;; Real x = 0.0;
;;
;; /*
;; Compensate for size of circle
;; */
;; Direction tip_dir = -grow_dir;
(mol empty-stencil)
(x 0.0)
(tip-dir (other-dir grow-dir)))
;; if (circled_tip && !broken[tip_dir])
;; {
;; if (grow_dir > 0)
;; x = rad * 2.0;
;; else if (grow_dir < 0)
;; width -= rad * 2.0;
;; }
(if (and circled-tip
(not (interval-bound broken tip-dir)))
(if (> grow-dir 0)
(set! x (* rad 2.0))
(if (< grow-dir 0)
(set! width (- width (* rad 2.0))))))
;; mol = Line_interface::line (me, Offset (x, starth), Offset (width, endh));
;; mol.add_stencil (Line_interface::line (me,
;; Offset (x, -starth),
;; Offset (width, -endh)));
(set! mol (make-line-stencil thick x starth width endh))
(set! mol
(ly:stencil-add
mol
(make-line-stencil thick x (- starth) width (- endh))))
;; /*
;; Support al/del niente notation by putting a circle at the
;; tip of the (de)crescendo.
;; */
;; if (circled_tip)
;; {
;; Box extent (Interval (-rad, rad), Interval (-rad, rad));
;;
;; /* Hmmm, perhaps we should have a Lookup::circle () method? */
;; Stencil circle (extent,
;; scm_list_4 (ly_symbol2scm ("circle"),
;; scm_from_double (rad),
;; scm_from_double (thick),
;; SCM_BOOL_F));
(if circled-tip
(let ((circle (make-circle-stencil rad thick #f)))
;; /*
;; don't add another circle if the hairpin is broken
;; */
;; if (!broken[tip_dir])
;; mol.add_at_edge (X_AXIS, tip_dir, Stencil (circle), 0);
;; }
(if (not (interval-bound broken tip-dir))
(set! mol
(ly:stencil-combine-at-edge mol X tip-dir circle 0)))))
;; mol.translate_axis (x_points[LEFT]
;; - bounds[LEFT]->relative_coordinate (common, X_AXIS),
;; X_AXIS);
;; return mol.smobbed_copy ();
;; }
(set! mol
(ly:stencil-translate-axis mol
(- (interval-bound x-points LEFT)
(ly:grob-relative-coordinate (interval-bound bounds LEFT) common X))
X))
mol)))))))
music =
{
c'1\<
c'2\! c'2~\>
c'2~ c'2\!
c'2\> c'2\< c'1
c''1\<
c''4 a' c''\< a'
c''4 a' c''\! a'\<
c''4 a' c'' a'\!
c''1~\<
c''1~
\break
c''1\!
c'1\!\<
\break
c'1
\break
c'2 c'2\!
c''1\<
c''4 a' c''\mf a'
c''1\<
c''4 a' c''\ffff a'
c''4\< c''\! d''\> e''\!
<<
f''1
{ s4 s4\< s4\> s4\! }
>>
\override Hairpin.to-barline = ##f
c''1\<
c''1\!
}
\markup \huge \bold "DEFAULT"
{
\music
\override Hairpin.circled-tip = ##t
\music
}
\markup \huge \bold "SCHEME REWRITE"
{
\override Hairpin.stencil = #hairpin::print-scheme
\music
\override Hairpin.circled-tip = ##t
\music
}
\layout {
ragged-right = ##t
}
\version "2.19.23"
#(define (define-grob-property symbol type? description)
(if (not (equal? (object-property symbol 'backend-doc) #f))
(ly:error (_ "symbol ~S redefined") symbol))
(set-object-property! symbol 'backend-type? type?)
(set-object-property! symbol 'backend-doc description)
symbol)
#(map
(lambda (x)
(apply define-grob-property x))
`(
(circled-tip-radius ,number? "Radius for hairpin circled tip")
; add more properties here
))
#(define broken-neighbor
(lambda (grob)
(let* ((pieces (ly:spanner-broken-into (ly:grob-original grob)))
(me-list (member grob pieces)))
(if (> (length me-list) 1)
(cadr me-list)
'()))))
#(define (interval-dir-set i val dir)
(cond ((= dir LEFT) (set-car! i val))
((= dir RIGHT) (set-cdr! i val))
(else (ly:error "dir must be LEFT or RIGHT"))))
#(define (other-dir dir) (- dir))
#(define hairpin::print-scheme
(lambda (grob)
(let ((grow-dir (ly:grob-property grob 'grow-direction)))
(if (not (ly:dir? grow-dir))
(begin
(ly:grob-suicide! grob)
'()))
(let* ((padding (ly:grob-property grob 'bound-padding 0.5))
(bounds (cons (ly:spanner-bound grob LEFT)
(ly:spanner-bound grob RIGHT)))
(broken (cons
(not (= (ly:item-break-dir (car bounds)) CENTER))
(not (= (ly:item-break-dir (cdr bounds)) CENTER)))))
(if (cdr broken)
(let ((next (broken-neighbor grob)))
(if (ly:spanner? next)
(begin
(ly:grob-property next 'after-line-breaking)
(set-cdr! broken (grob::is-live? next)))
(set-cdr! broken #f))))
(let* ((common
(ly:grob-common-refpoint (car bounds) (cdr bounds) X))
(x-points (cons 0 0))
(circled-tip (ly:grob-property grob 'circled-tip))
(height (* (ly:grob-property grob 'height 0.2)
(ly:staff-symbol-staff-space grob)))
;(rad (* 0.525 height))
(rad (ly:grob-property grob 'circled-tip-radius (* 0.525 height)))
; commented out is directly from C++, but it leads to think lines when
; no circled tip!
;(thick 1.0)
;(thick (if circled-tip
;(* (ly:grob-property grob 'thickness 1.0)
;(ly:staff-symbol-line-thickness grob))
;thick))
(thick (* (ly:grob-property grob 'thickness 1.0)
(ly:staff-symbol-line-thickness grob))))
(define (inner dir)
(let* ((b (interval-bound bounds dir))
(e (ly:generic-bound-extent b common))) ; X-AXIS assumed
(interval-dir-set
x-points (ly:grob-relative-coordinate b common X) dir)
(if (interval-bound broken dir)
(if (= dir LEFT)
(interval-dir-set
x-points (interval-bound e (other-dir dir)) dir)
(let* ((broken-bound-padding
(ly:grob-property grob 'broken-bound-padding 0.0))
(chp (ly:grob-object grob 'concurrent-hairpins)))
(let loop ((i 0))
(if (and (ly:grob-array? chp) ; hmm...why no test in C++ needed?
(< i (ly:grob-array-length chp)))
(let ((span-elt (ly:grob-array-ref chp i)))
(if (= (ly:item-break-dir (ly:spanner-bound span-elt RIGHT))
LEFT)
(set! broken-bound-padding
(max broken-bound-padding
(ly:grob-property span-elt 'broken-bound-padding 0.0))))
(loop (1+ i)))))
(interval-dir-set
x-points
(- (interval-bound x-points dir)
(* dir broken-bound-padding))
dir)))
(if (grob::has-interface b 'text-interface)
(if (not (interval-empty? e))
(interval-dir-set x-points
(- (interval-bound e (other-dir dir))
(* dir padding))
dir))
(let* ((neighbor-found #f)
(adjacent '()) ; spanner
(neighbors (ly:grob-object grob 'adjacent-spanners))
(neighbors-len (if (ly:grob-array? neighbors)
(ly:grob-array-length neighbors)
0))) ; this shouldn't be necessary -- see comment above
(let inner-two ((i 0))
(if (and (< i neighbors-len)
(not neighbor-found))
(begin
(set! adjacent (ly:grob-array-ref neighbors i))
(if (and (ly:spanner? adjacent)
(eq? (ly:item-get-column (ly:spanner-bound adjacent (other-dir dir)))
(ly:item-get-column b)))
(set! neighbor-found #t))
(inner-two (1+ i)))))
(if neighbor-found
(if (grob::has-interface adjacent 'hairpin-interface)
(if (and circled-tip (not (eq? grow-dir dir)))
(interval-dir-set x-points
(+ (interval-center e)
(* dir
(- rad (/ thick 2.0))))
dir)
(interval-dir-set x-points
(- (interval-center e)
(/ (* dir padding) 3.0))
dir))
(if (= dir RIGHT)
(interval-dir-set x-points
(- (interval-bound e (other-dir dir))
(* dir padding))
dir)))
(begin
(if (and (= dir RIGHT)
(grob::has-interface b 'note-column-interface)
(ly:grob-array? (ly:grob-object b 'rest)))
(interval-dir-set x-points
(interval-bound e (other-dir dir))
dir)
(interval-dir-set x-points
(interval-bound e dir)
dir))
(if (eq? (ly:grob-property b 'non-musical) #t)
(interval-dir-set x-points
(- (interval-bound x-points dir)
(* dir padding))
dir)))))))))
(inner LEFT)
(inner RIGHT)
(let* ((width (- (interval-bound x-points RIGHT)
(interval-bound x-points LEFT)))
(width (if (< width 0)
(begin
(ly:warning
(if (< grow-dir 0)
"decrescendo too small"
"crescendo too small"))
0)
width))
(continued (interval-bound broken (other-dir grow-dir)))
(continuing (interval-bound broken grow-dir))
(starth (if (< grow-dir 0)
(if continuing
(* 2 (/ height 3))
height)
(if continued
(/ height 3)
0.0)))
(endh (if (< grow-dir 0)
(if continued
(/ height 3)
0.0)
(if continuing
(* 2 (/ height 3))
height)))
(mol empty-stencil)
(x 0.0)
(tip-dir (other-dir grow-dir)))
(if (and circled-tip
(not (interval-bound broken tip-dir)))
(if (> grow-dir 0)
(set! x (* rad 2.0))
(if (< grow-dir 0)
(set! width (- width (* rad 2.0))))))
(set! mol (make-line-stencil thick x starth width endh))
(set! mol
(ly:stencil-add
mol
(make-line-stencil thick x (- starth) width (- endh))))
(if circled-tip
(let ((circle (make-circle-stencil rad thick #f)))
(if (not (interval-bound broken tip-dir))
(set! mol
(ly:stencil-combine-at-edge mol X tip-dir circle 0)))))
(set! mol
(ly:stencil-translate-axis mol
(- (interval-bound x-points LEFT)
(ly:grob-relative-coordinate (interval-bound bounds LEFT) common X))
X))
mol))))))
music =
{
c'1\<
c'2\! c'2~\>
c'2~ c'2\!
c'2\> c'2\< c'1
c''1\<
c''4 a' c''\< a'
c''4 a' c''\! a'\<
c''4 a' c'' a'\!
c''1~\<
c''1~
\break
c''1\!
c'1\!\<
\break
c'1
\break
c'2 c'2\!
c''1\<
c''4 a' c''\mf a'
c''1\<
c''4 a' c''\ffff a'
c''4\< c''\! d''\> e''\!
<<
f''1
{ s4 s4\< s4\> s4\! }
>>
\once \override Hairpin.to-barline = ##f
c''1\<
c''1\!
c''4\< c''\! d''\> e''\!
<< f''1 { s4 s\< s\> s\! } >>
\override Hairpin.minimum-length = #5
<< f''1 { s4 s\< s\> s\! } >>
\revert Hairpin.minimum-length
}
\markup \huge \bold "DEFAULT"
{
\music
\override Hairpin.circled-tip = ##t
\music
}
\markup \huge \bold "SCHEME REWRITE"
{
\override Hairpin.stencil = #hairpin::print-scheme
\music
\override Hairpin.circled-tip = ##t
\override Hairpin.circled-tip-radius = 2
\music
}
\layout {
ragged-right = ##t
}
_______________________________________________
lilypond-user mailing list
lilypond-user@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-user