Hi Lukas, that's great, and I hope you'll stick with me when I try to work that out into a generally usable (i.e. sufficiently flexible) solution to lobby with the GMTH ;-)
Am Freitag, den 26.06.2020, 10:07 +0200 schrieb Lukas-Fabian Moser: > Hi Urs, > > > Hey, that looks promising - I didn't know that. > > Now I'd only need a way to properly do the vertical alignment > > against > > the baseline of the lowest markup: > > > > \figures { > > <6 4 \markup { \circle \number 5 }> > > <3+ \markup { \circle \number 6 }> > > } > > This might be achieved using an inversed stacking direction. Great. > Harm's > figure reversal function can then be used to enable the user to > still > enter bass figures in the "usual" way. > > With a bit of syntactic sugar, this might look as below. I've started working on that, see below ... > > Problems so far: > > 1) The figures are still down-aligned. This might be remedied for > instance by defining a "maximal" number of figures for which to > leave > space and inserting placeholders. (This reminds me of one of my > long-time wishes, namely to have a dedicated "empty" figure for use > in > FiguredBass, which is sometimes needed to leave space for figures > that > are added later in a chord. I once patched my LilyPond to allow for > this, but this was an ugly hack involving transparent figures...) I have *not* started working on this yet. I don't find transparent markups that offensive. It's a commonplace approach also for achieving consistent baselines for strings without as/descenders. However, what we *actually* want is a common number of items *per system*, not for the whole score. What if we determine maximum number of entries of 3 and have many systems only use one - that would make for ugly whitespace all over the place. The only remedy I can see right now would be to deal with this in an after-line-breaking stencil. From there we could iterate over the whole system and check the maximum number of actually used layers. The result should be cached so this process is done only once per system. However, this won't always work because the context may not have been pushed sufficiently far down in situations like the attached example. > > 2) The "get-current-duration" function is simply awful: I didn't > know > how to find out which duration the parser would assign to a > duration-less note/bass figure, so I just let it create a note and > see > what its duration becomes. There must be a decent way to achieve > this. > (I need to give the bassStufe-function a duration because otherwise > it > does not work with figure \none.) I've made a number of improvements to your code: 1) reverseFigures creates a new music expression, from which you later retrieve the 'elements, which is unnecessary. (I renamed it to parse- signature 2) The figures do have their durations included, so we can retrieve them *here* and reuse that information later when generating the BassFigureEvent for the bass step markup. 2a) Instead of \none I rewrote \scaledeg to accept either a figure signature or a duration. This gives a nice interface where you can just specify a duration for an empty figure. Apart from that I've just added a few predicates to make the input more reliable and have typecheck warnings. I think this is going into a nice direction! Best Urs \version "2.20.0" \layout { \override BassFigureAlignment.stacking-dir = #UP } #(define (parse-signature sig) ; based on \reverseFigures by Harm (if ;; sig is either an EventChord (music) or a duration (ly:music? sig) (let* ((reversed (reverse (map (lambda (e) (cond ((and (eq? #t (ly:music-property e 'bracket-start)) (eq? #t (ly:music-property e 'bracket-stop))) '()) ((eq? #t (ly:music-property e 'bracket-start)) (begin (ly:music-set-property! e 'bracket-start '()) (ly:music-set-property! e 'bracket-stop #t))) ((eq? #t (ly:music-property e 'bracket-stop)) (begin (ly:music-set-property! e 'bracket-stop '()) (ly:music-set-property! e 'bracket-start #t)))) e) (ly:music-property sig 'elements)))) (duration (ly:music-property (first reversed) 'duration))) (cons duration reversed)) (cons sig '()) )) #(define (scale-degree degree duration) (let ((step-markup (if (eqv? degree 0) (markup #:null) (markup #:circle #:small #:number (number->string degree))))) (make-musics 'BassFigureEvent 'duration duration 'text (markup #:with-dimensions '(0 . 1) '(0 . 5) step-markup)) )) #(define (bass-degree? obj) "A bass degree is either a number between 1 and 7 or 0 (eqv. to nothing)!" (and (integer? obj) (> 8 obj) (< -1 obj))) #(define (figure-signature? obj) "A figure signature is either an EventChord music or a duration." (or (and (ly:music? obj) (music-is-of-type? obj 'event-chord)) (ly:duration? obj))) scaledeg = #(define-music-function (num signature) ((bass-degree? 0) figure- signature?) (let* ((props (parse-signature signature)) (duration (car props)) (used-signature (cdr props))) (make-music 'EventChord 'elements (cons (scale-degree num duration) used-signature)))) << \new Staff { \clef bass d2 e4 fis g2 a b cis' d' g2 fis1 } \figures { \scaledeg 1 <5 3>2 \scaledeg 2 <6>4 \scaledeg 3 <6>4 \scaledeg 4 <6 5>2 \scaledeg 5 2 \scaledeg 6 <6>2 \scaledeg 7 <6>4 \scaledeg <6 5> \scaledeg 1 <5 3>2 \scaledeg 4 <4 2> \scaledeg 3 <6>1 } >> > > Best > Lukas > > > > \version "2.20.0" > > \layout { > \override BassFigureAlignment.stacking-dir = #UP > } > > reverseFigures = % by Harm > #(define-music-function (mus)(ly:music?) > (music-map > (lambda (m) > (if (music-is-of-type? m 'event-chord) > (let ((ev-chrd-elts (ly:music-property m 'elements))) > (ly:music-set-property! m 'elements > (reverse > (map > (lambda (e) > (cond ((and (eq? #t > (ly:music-property e 'bracket-start)) > (eq? #t > (ly:music-property e 'bracket-stop))) > '()) > ((eq? #t > (ly:music-property e 'bracket-start)) > (begin > (ly:music-set-property! e 'bracket-start '()) > (ly:music-set-property! e 'bracket-stop #t))) > ((eq? #t > (ly:music-property e 'bracket-stop)) > (begin > (ly:music-set-property! e 'bracket-stop '()) > (ly:music-set-property! e 'bracket-start #t)))) > e) > ev-chrd-elts))) > m) > m)) > mus)) > > get-current-duration = > #(define-scheme-function () () > (let* ((tempmusic #{ {a} #}) > (els (ly:music-property tempmusic 'elements))) > (ly:music-property (car els) 'duration))) > > bassStufe = > #(define-music-function (num) (integer?) > (let ((scale-markup > (if (> num 0) > #{ \markup \circle \small \number #(number->string > num) #} > (markup #:null)))) > > (make-music > 'BassFigureEvent > 'duration > (get-current-duration) > 'text > #{ \markup > \with-dimensions #'(0 . 1) #'(0 . 5) > #scale-markup > #}))) > > scaledeg = > #(define-music-function (num signatur) ((integer? 0) ly:music?) > (let ((signatur-reversed (reverseFigures signatur))) > (make-music > 'EventChord > 'elements > (cons (bassStufe num) > (ly:music-property signatur-reversed 'elements))))) > > none = {} > > << > \new Staff { \clef bass d2 e4 fis g2 a b cis' d' g2 fis1 } > \figures { > \scaledeg 1 <5 3>2 > \scaledeg 2 <6>4 > \scaledeg 3 <6> > \scaledeg 4 <6 5>2 > \scaledeg 5 \none > \scaledeg 6 <6>2 > \scaledeg 7 <6>4 > \scaledeg <6 5> > \scaledeg 1 <5 3>2 > \scaledeg 4 <4 2> > \scaledeg 3 <6>1 > } > >> > > >