Hi,

On Mon, Oct 13, 2014 at 1:18 PM, David Bellows <davebell...@gmail.com>
wrote:

> OK, I've now played around with it and it works very nicely and will be
> easy to integrate into my software. You've already put a lot of work into
> this so I hate to point out any of my peculiar preferences, but just in
> case you feel like putting even more time into it here they are:
>
> 1. Add the 15va/vb marking as well.
>

Done.


> 2. I'm not sure what standard practice is but you'll see in the score that
> my software produces it does not extend the ottava marking over rests. My
> music tends to have a lot of rests (a consequence of how it automatically
> changes staves) so it has a big effect on the overall look.
>

Fixed.

Standard practice is that an ottava should extend across rests if the note
afterwards takes the ottava, too.  If the rest is sufficiently long, the
ottava is broken.

I've incorporated the behavior you like, but I definitely believe that
there should be a choice for the user to extend across rests.  The function
ought to be able to detect whether the ottava can extend: there are rests
of sufficient length and the note after the rest needs the ottava.

Under no circumstances should an ottava end over a rest, like you see on
the version of your piece using \ottavate!



> 3. Change the text. In my score I just use "8" and "15" which given how
> crowded the score gets at times makes a big difference.
>

It turns out that \set Staff.ottavation = #"8" and the like have to be used
after \ottava #1, so I had to work it into the function.

In the future I could add an argument to the \ottavate to allow user
variations--like "8b," for example.  Right now, I hardcoded "8" and "15,"
as in your score.

You select the version--default or short names--with a boolean value--that
is ##t (short), ##f (default).


%%%
USAGE NOTES:

Take the following example:

musOne = \relative c''' {
  \clef treble
  g2 a b c d e f g a b c d e f g a b c d e f g
}

{
  \ottavate #'(4 . 7) #'(-3 . -6) ##t \musOne
}

##t selects the shortened names "8" and "15"

(4 . 7) specifies the range for "8": any note having 4-7 ledger lines
(inclusive) gets an "8."  Anything above this will get "15."

(-3 . -6) specifies the range for "15": here the range is 3-6 ledger
lines.  I like the negative numbers because they are visually more
expressive, though of course, there's no sense to negative numbers of
ledger lines....

Chords are assigned ottavas based on the average of the ledger-line count
of their constituent notes.  I'm sure there's a better algorithm.  If
you're more mathematically able than me, please feel free to suggest
something a bit more elegant :)

In the future, there ought to be some analysis of groups of notes.  So, for
example, an out-of-range note within a group of notes receiving an ottava
doesn't break the line.  But I'd like to make sure what I have now works
before I start enhancing it.


> Thanks again for the work you've put into this!
>

You're very welcome.

--David
\version "2.19.10"

#(define (ledger-line-no middle-C-pos p)
   "Returns the number of ledger-lines a pitch @var{p} will have with
middle C position @var{middle-C-pos} expressed as staff-steps from the
middle staff line."
   (let* ((ps (ly:pitch-steps p))
          (mid-staff-steps (- middle-C-pos))
          (top-line (+ mid-staff-steps 4))
          (bottom-line (- mid-staff-steps 4))
          (above? (> ps top-line))
          (below? (< ps bottom-line))
          (steps-outside-staff
           (cond
            (below? (- ps bottom-line))
            (above? (- ps top-line))
            (else 0))))
     (truncate (/ steps-outside-staff 2))))

#(define (find-clefMiddleCPosition mus)
   (let ((clef-pos -6)) ; treble is default
     (for-some-music
      (lambda (x)
        (let ((n (ly:music-property x 'symbol)))
          (and (eq? n 'middleCClefPosition)
               (set! clef-pos (ly:music-property x 'value)))))
      mus)
     clef-pos))

#(define (make-ottava-music arg)
   (list (make-music 'OttavaMusic 'ottava-number arg)))

ottavate =
#(define-music-function (parser location upper lower short-name? mus)
   (number-pair? number-pair? boolean? ly:music?)
   "Create ottavas for music based on numbers of ledger lines.  Both @var{upper}
and @var{lower} are pairs specifying a range of ledger lines: @var{upper}
determines @code{8va} and @code{15ma}, and @var{lower} determines @var{8vb} and
@var{15mb}.  Within this range (inclusive), an @code{8va} or @code{8ba} will
be created.  Notes with numbers of ledger lines exceeding these ranges will be
assigned @code{15ma} or @code{15mb}.

Numbers of ledger lines above the staff are specified in @var{upper} as
positive integers, while ledger lines below the staff are specified in @var{lower}
as negative numbers.

The numbers of ledger lines within chords are averaged.

The strings used for ottavas are selected by @var{short-name?}, which accepts
a boolean.  A setting of @code{#f} chooses the default strings, while @code{#t}
uses the shorter @code{8} and @code{15}.
"
   (let ((upper8 (car upper))
         (upper15 (cdr upper))
         (lower8 (car lower))
         (lower15 (cdr lower))
         (loco (make-ottava-music 0)))
     
     (define (select-ottava-music str)
       (let* ((options
               '(("up-an-octave" . 1)
                 ("down-an-octave" . -1)
                 ("up-two-octaves" . 2)
                 ("down-two-octaves" . -2)
                 ("loco" . 0))))
         (make-ottava-music (assoc-get str options))))
     
     (define (select-displacement-string ledger-count)
       (cond 
        ((> ledger-count upper15)
         "up-two-octaves")
        ((>= ledger-count upper8)
         "up-an-octave")
        ((< ledger-count lower15)
         "down-two-octaves")
        ((<= ledger-count lower8)
         "down-an-octave")
        (else "loco")))
     
     (define (calc-displacement clef-pos mus-expr)
       ; Return a string designating displacement.  "Loco" means "as written."
       ; Chords have the ledger-line count of their members averaged.
       ; Algorithm ought to be more sophisticated, and take context into consideration.
       ; We should not lose an ottava if one note in a passage dips below the
       ; threshold, and there should be a better way to handle chords.  Average
       ; their ledger-line counts?
       (cond
        ((music-is-of-type? mus-expr 'event-chord)
         (let* ((elts (ly:music-property mus-expr 'elements))
                (ledger-list
                 (map (lambda (e)
                        (ledger-line-no clef-pos (ly:music-property e 'pitch)))
                   elts))
                (avg (apply average ledger-list)))
           (select-displacement-string avg)))
        ((music-is-of-type? mus-expr 'note-event)
         (let* ((pitch (ly:music-property mus-expr 'pitch))
                (ledger-count (ledger-line-no clef-pos pitch)))
           (select-displacement-string ledger-count)))))
     
     (define (make-alternate-name name)
       (list (make-music
              'ContextSpeccedMusic
              'context-type
              'Staff
              'element
              (make-music
               'PropertySet
               'value name
               'symbol
               'ottavation))))
     
     (define (select-name displacement alternate?)
       (let* ((alternates
               '(("up-an-octave" . "8")
                 ("down-an-octave" . "8")
                 ("up-two-octaves" . "15")
                 ("down-two-octaves" . "15")
                 ("loco" . #f)))
              (selection (assoc-get displacement alternates)))
         (if alternate?
             (make-alternate-name selection)
             '())))

     (define (build-new-elts mus-expr new-expr prev clef-pos)
       (if (null? mus-expr)
           ;; ensure that ottava does not extend past a localized
           ;; use of \ottavate
           (append new-expr loco)
           
           (begin
            ;; find value for 'clefMiddleCPosition
            (if (eq? (ly:music-property (car mus-expr) 'name) 'ContextSpeccedMusic)
                (set! clef-pos (find-clefMiddleCPosition (car mus-expr))))
            
            (cond
             ;; We do not extend across rests for now.
             ((music-is-of-type? (car mus-expr) 'rest-event)
              (build-new-elts
               (cdr mus-expr)
               (append
                new-expr
                loco
                (list (car mus-expr)))
               "loco" clef-pos))
             
             ((or (music-is-of-type? (car mus-expr) 'event-chord)
                  (music-is-of-type? (car mus-expr) 'note-event))
              (let ((d (calc-displacement clef-pos (car mus-expr))))
                (cond
                 ((and d (not (string=? d prev)))
                  (build-new-elts
                   (cdr mus-expr)
                   (append
                    new-expr
                    (select-ottava-music d)
                    (select-name d short-name?)
                    (list (car mus-expr)))
                   d clef-pos))
                 (else
                  (build-new-elts
                   (cdr mus-expr)
                   (append new-expr (list (car mus-expr)))
                   prev clef-pos)))))
             ; ew.
             (else 
              (build-new-elts
               (cdr mus-expr)
               (append new-expr (list (car mus-expr)))
               prev clef-pos))))))
     
     (define (recurse music)
       (let ((elts (ly:music-property music 'elements))
             (e (ly:music-property music 'element)))
         
         (if (ly:music? e)
             (recurse e))
         (if (pair? elts)
             (if (or
                  (any (lambda (elt) (music-is-of-type? elt 'note-event)) elts)
                  (any (lambda (elt) (music-is-of-type? elt 'event-chord)) elts)
                  (any (lambda (elt) (music-is-of-type? elt 'rest-event)) elts))
                 (set! (ly:music-property music 'elements)
                       (build-new-elts elts '() "loco" -6))
                 (map recurse elts)))))
   
     (recurse mus)
     
     ;(display-scheme-music mus) ; for testing
   
     mus))

%%%%%%%%%%% EXAMPLE %%%%%%%%%%%%

musOne = \relative c''' {
  \clef treble
  g2 a b c d e f g a b c d e f g a b c d e f g
}

{
  \musOne
}

{
  \ottavate #'(4 . 7) #'(-3 . -6) ##t \musOne
}

musTwo = \relative c {
  \clef bass
  a2 g f e d c b a g f e d c b a g f e d c b a
}

{
  \musTwo
}

{
  \ottavate #'(4 . 7) #'(-3 . -5) ##f \musTwo
}

musThree = \relative c''' {
  \clef treble
  g2 r b r d r f r a r c r e r g r b r d r f r
}

{
  \ottavate #'(4 . 7) #'(-3 . -6) ##t \musThree
}


musFour = \relative c' {
  <c e g> <e g c> <g c e>
  <c e g> <e g c> <g c e>
  <c e g> <e g c> <g c e>
  <c e g> <e g c> <g c e>
  <c e g> <g c e> <e g c>
  <c e g> <g c e> <e g c>
  <c e g> <g c e> <e g c>
  <c e g> <g c e> <e g c>
}

{
  \musFour
}

{
  \ottavate #'(4 . 7) #'(-3 . -6) ##t \musFour
}
_______________________________________________
lilypond-user mailing list
lilypond-user@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-user

Reply via email to