Hi Mojca,
On Thu, Jul 14, 2016 at 4:41 AM, Mojca Miklavec
<[email protected]> wrote:
> Dear David,
>
> On 14 July 2016 at 00:01, David Nalesnik wrote:
>> On Wed, Jul 13, 2016 at 1:58 AM, Mojca Miklavec wrote:
>>>
>>> I would like to have:
>>>
>>> - a thick horizontal line for "push" and nothing at all on "pull", no
>>> vertical lines
>>>
>>> - it should start and stop at the vertical bar (if the push/pull stops
>>> at '|', otherwise "cover the appropriate durations" below the pitches)
>>>
>>> - probably something like \startPush to start the thick line, then
>>> perhaps specify the duration with something like "s1 s1", followed by
>>> \startPull that would disable
>>> (It would be soooooo much better if \startPush and \startPull could be
>>> placed in the first staff with melody, between the pitches, so that
>>> one wouldn't have to think twice about the proper duration of that
>>> thick line.)
>>>
>>> - the vertical position should be somewhere below the lyrics
>>
>> This is a situation which calls for a new Grob. Unfortunately,
>> there's no user interface for doing this, but it is possible to mess
>> with internals and create the following file. (Which I don't
>> guarantee :) )
>
> Wow! That's a piece of code!
There's a bit of duplication of non-public functions involved in grob
and event creation here. If you were going to make a grob the
accepted way -- i.e, in the code base --, you would thankfully need a
bit less.
I commented out some stuff that I don't believe is needed here,
leftovers from the code for drawing text spanners that I adapted.
(I also renamed the engraver.)
>
> Based on your comment
> % Change vertical position of spanner. Sorry, not automatic.
>
> I just wanted to add that my intention wasn't to put the horizontal
> line into "arithmetic centre" of the whitespace between the staves.
> Actually it makes a lot more sense to keep the height of the line
> pretty much constant within a single line. So it looks better to me
> when I *remove*
> \override Staff.AccordionPushSpanner.staff-padding = 2
> from the middle of the first line.
>
> Bottomline: what you thought to be a problem doesn't seem to be a
> problem at all.
Ah, OK. Good. I assumed that the push applied to both staves and so should be
centered like many piano dynamics. But as I say I know nothing about
accordion notation!
>
>> As far as position relative to lyrics, it's helpful to know that you
>> can override the direction property of the spanner.
>>
>> This is rough, but hopefully it will get you started.
>
> Wonderful. I will need some time to fully digest the code.
>
> One minor "problems" I noticed:
>
> In the basses I can use
> f1-4^\markup {"f"}
> and the finger position is OK, but \markup{"f"} is placed above the
> horizontal line.
>
> This is not critical (yet?).
This isn't so hard to fix. I added the outside-staff-interface to the
new Grob. This handles
the stacking of objects above or below the staff. You'd override the
property outside-staff-priority to get different
results.
>
>
> A few more questions (before I dive into the code):
>
> - Is there any rule or a guideline to decide whether to put repeats
> (\repeat volta 2 ...) in the upper or in the bottom stave?
No, put it in one or the other. You can put it in both if you like..
>
> - The way you implemented these lines, is there a way to access the
> state (pushing or pulling) in the upper stave? (I would eventually
> want to do more complex things with it, but for simplicity let's say
> that I would want all the pitches in the upper stave to be red while
> pushing, or if I would additionally want to add \upbow or \downbow
> symbols in the upper line.) I'm not asking for the code to do the
> colouring just yet, I just want to know whether lilypond has access to
> the variable that would let me do it at some point.
Yes, this is the sort of thing you'd do with a music function. The
coloring is simple; I've added a function to do that.
The function adds the push/pull events and simply introduces a
NoteHead override. You'll see that the coloring function affects only
the objects in the Staff that it is applied to. (You could
changes the overrides in the function to Score.NoteHead to get both
staves, but I take it that's not what you want.)
As far as downbows, that's doable as well. It's a little more
Scheme-ish, though, and I'm not really awake yet :) There are almost
certainly a number of snippets in the Lilypond Snippet Repository
(LSR) that do this -- automatic articulations with Scheme, something
like that.
%%%%%%%%%%%%%%
One thing I'm not understanding is if you envision the push/pull
belonging exclusively to the upper staff, affecting both staves
together, or being placeable in either staff.
And also how lyrics relate. My reason for putting the push/pull in
the lower staff in the original example is because that was the only
way I could get them to be above the horizontal line as you requested.
You can see that that is not the case in the current example (second
score) where the push/pull is in the upper staff.
I don't know how to control the placement of the Lyric context any
better than this.
Possibly the push/pull engraver should be in a Dynamics context. but
then we'd need to find another solution for getting colors and/or
downbows into the upper staff.
>
> Many many thanks,
> Mojca
You're welcome!
David
\version "2.19.30"
#(define-event-class 'accordion-push-span-event 'span-event)
#(define ly:accordion-push-spanner::print
(lambda (grob)
(let* ((bound-L (ly:spanner-bound grob LEFT))
(bound-R (ly:spanner-bound grob RIGHT))
;; (common-X (ly:grob-system grob)) probably does the same
;; as the next three lines...
(common-X (ly:grob-common-refpoint grob bound-L X))
(common-X (ly:grob-common-refpoint bound-L bound-R X))
(common-X (ly:grob-common-refpoint grob common-X X))
(extent-L (ly:grob-robust-relative-extent bound-L common-X X))
(left-X (if (= 1 (ly:item-break-dir bound-L))
(cdr extent-L)
(car extent-L)))
(right-X (cdr (ly:grob-robust-relative-extent bound-R common-X X)))
(off (ly:grob-relative-coordinate grob common-X X))
(th (ly:grob-property grob 'thickness 1))
(stil (ly:round-filled-box (cons left-X right-X) (cons 0 th) 0.0)))
(ly:stencil-translate-axis stil (- off) X))))
#(define (add-grob-definition grob-name grob-entry)
(let* ((meta-entry (assoc-get 'meta grob-entry))
(class (assoc-get 'class meta-entry))
(ifaces-entry (assoc-get 'interfaces meta-entry)))
(set-object-property! grob-name 'translation-type? ly:grob-properties?)
(set-object-property! grob-name 'is-grob? #t)
(set! ifaces-entry (append (case class
((Item) '(item-interface))
((Spanner) '(spanner-interface))
((Paper_column) '((item-interface
paper-column-interface)))
((System) '((system-interface
spanner-interface)))
(else '(unknown-interface)))
ifaces-entry))
(set! ifaces-entry (uniq-list (sort ifaces-entry symbol<?)))
(set! ifaces-entry (cons 'grob-interface ifaces-entry))
(set! meta-entry (assoc-set! meta-entry 'name grob-name))
(set! meta-entry (assoc-set! meta-entry 'interfaces
ifaces-entry))
(set! grob-entry (assoc-set! grob-entry 'meta meta-entry))
(set! all-grob-descriptions
(cons (cons grob-name grob-entry)
all-grob-descriptions))))
#(add-grob-definition
'AccordionPushSpanner
`(
(direction . ,UP)
(staff-padding . 0.8)
(thickness . 1)
(outside-staff-priority . 1000)
(stencil . ,ly:accordion-push-spanner::print)
(staff-padding . 0.8)
(meta . ((class . Spanner)
(interfaces . (font-interface
line-interface
line-spanner-interface
axis-group-interface
outside-staff-interface
side-position-interface))))))
#(define scheme-event-spanner-types
'(
(AccordionPushSpanEvent
. ((description . "Used to signal where accordion push lines
start and stop.")
(types . (accordion-push-span-event span-event event))
))
))
#(set!
scheme-event-spanner-types
(map (lambda (x)
(set-object-property! (car x)
'music-description
(cdr (assq 'description (cdr x))))
(let ((lst (cdr x)))
(set! lst (assoc-set! lst 'name (car x)))
(set! lst (assq-remove! lst 'description))
(hashq-set! music-name-to-property-table (car x) lst)
(cons (car x) lst)))
scheme-event-spanner-types))
#(set! music-descriptions
(append scheme-event-spanner-types music-descriptions))
#(set! music-descriptions
(sort music-descriptions alist<?))
#(define (add-bound-item spanner item)
(if (null? (ly:spanner-bound spanner LEFT))
(ly:spanner-set-bound! spanner LEFT item)
(ly:spanner-set-bound! spanner RIGHT item)))
%{
#(define (axis-offset-symbol axis)
(if (eq? axis X) 'X-offset 'Y-offset))
#(define (set-axis! grob axis)
(if (not (number? (ly:grob-property grob 'side-axis)))
(begin
(set! (ly:grob-property grob 'side-axis) axis)
(ly:grob-chain-callback
grob
(if (eq? axis X)
ly:side-position-interface::x-aligned-side
side-position-interface::y-aligned-side)
(axis-offset-symbol axis)))))
%}
accordionPushSpannerEngraver =
#(lambda (context)
(let ((span '())
(finished '())
(event-start '())
(event-stop '()))
(make-engraver
(listeners ((accordion-push-span-event engraver event)
(if (= START (ly:event-property event 'span-direction))
(set! event-start event)
(set! event-stop event))))
((process-music trans)
(if (ly:stream-event? event-stop)
(if (null? span)
(ly:warning "You're trying to end a accordion push spanner but you haven't started one.")
(begin (set! finished span)
(ly:engraver-announce-end-grob trans finished event-start)
(set! span '())
(set! event-stop '()))))
(if (ly:stream-event? event-start)
(begin (set! span (ly:engraver-make-grob trans 'AccordionPushSpanner event-start))
; (set-axis! span Y)
(set! event-start '()))))
((stop-translation-timestep trans)
(if (and (ly:spanner? span)
(null? (ly:spanner-bound span LEFT)))
(ly:spanner-set-bound! span LEFT
(ly:context-property context 'currentCommandColumn)))
(if (ly:spanner? finished)
(begin
(if (null? (ly:spanner-bound finished RIGHT))
(ly:spanner-set-bound! finished RIGHT
(ly:context-property context 'currentCommandColumn)))
(set! finished '())
(set! event-start '())
(set! event-stop '()))))
((finalize trans)
(if (ly:spanner? finished)
(begin
(if (null? (ly:spanner-bound finished RIGHT))
(ly:spanner-set-bound! finished RIGHT
(ly:context-property context 'currentMusicalColumn)))
(set! finished '())))
(if (ly:spanner? span)
(begin
(ly:warning "I think there's a dangling accordion push spanner :-(")
(ly:grob-suicide! span)
(set! span '())))))))
startPush =
#(make-span-event 'AccordionPushSpanEvent START)
startPull =
#(make-span-event 'AccordionPushSpanEvent STOP)
redPush =
#(define-music-function (music) (ly:music?)
#{
\override NoteHead.color = #red
\startPush
#music
\startPull
\revert NoteHead.color
#})
%%%%%%%%%%%%%%%%%%%%%%%%%%%%% EXAMPLE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\new PianoStaff <<
\new Staff {
f'1
f'1
f'1
f'1
f'1
f'1
f'1
f'1
f'1
f'4 f'2.
f'4 f'2.
f'1
f'1
f'4 f'2.
}
\new Lyrics \lyricmode {
foo1 foo foo foo
}
\new Staff {
\clef bass
\override Staff.AccordionPushSpanner.direction = #UP
f1
f1
\redPush {
f1
f1
f1-4^\markup {"f"}
}
f1
f1
\repeat volta 2 {
\startPush f1
f1
f4 \startPull f2.
f4 \startPush f2.
f1
\break
f1
f1 \startPull
}
}
>>
\new PianoStaff <<
\new Staff {
\override Staff.AccordionPushSpanner.direction = #DOWN
f'1
f'1
\redPush {
f'1
f'1
f'1_4_\markup {"f"}
}
f'1
f'1
\repeat volta 2 {
\startPush f'1\f
f'1
f'4 \startPull f'2.
f'4 \startPush f'2.
f'1
\break
f'1
f'1 \startPull
}
}
\new Lyrics \lyricmode { foo1 foo foo foo }
\new Staff {
\clef bass
f1
f1
f1
f1
f1
f1
f1
f1
f1
f4 f2.
f4 f2.
f1
f1
f4 f2.
}
>>
\layout {
ragged-last = ##t
\context {
\Global
\grobdescriptions #all-grob-descriptions
}
\context {
\Staff
\consists \accordionPushSpannerEngraver
}
}
_______________________________________________
lilypond-user mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/lilypond-user