Hi Alasdair,

You might start from the attached file provided by Nicolas Sceaux. I
did not test recently but with a previous version I had to play with
the code to add space when a mordent just follows a bar line. I used it
to edit the gamba part of Morel's chaconne a few years ago. I am not
currently at home and I don't have a sample code at hand.

Hope this helps,
Jean

Le dimanche 23 août 2020 à 17:31 +1000, Alasdair McAndrew a écrit :
> I have created a small markup command for an ornament, which appears
> in engraved music of its time as a small "x", meaning a lower
> mordent.  Another common ornament would be written as a sort of large
> comma, and this meant a trill, starting on the upper note, and
> increasing in speed.  This is for late 17th and early 18th century
> French music for the viola da gamba.
> 
> The point is that these ornaments should go next to the notes to
> which they refer, and not necessarily above or below the staff. 
>  This little example of a few bars indicate some of the difficulties,
> taken from the 4th book of "Pièces de Violes" by Marin Marais,
> published in Paris in 1717:
> 
> 
> 
> You'll see various bowing marks (actually there's only one used here:
> "e", which stands for the French "enfler": "swelling"; that is, a
> bow-stroke without the customary "attack"), x's and commas for
> ornaments, numbers with dots indicating fingers and which string, and
> next to the double-stopped d and f-sharp in the third and final bars
> what looks like a broken cross; this is a "barre", indicating a
> single finger laid over two or more strings at once.  
> 
> What I'm trying to do is to retain as much as possible of this
> notation in Lilypond.  I could probably bodge up quite a lot of this
> if there was some way of placing expressions next to the notes
> themselves - but I can't find out how to do that.  
> 
> Any advice would be gratefully received!
> 
> Thank you very much,
> Alasdair
> 
\version "2.20.0"

%%%
%%% Utilities for defining new grobs, grob properties and music event types
%%% (there should be built-in commands to do that in LilyPond)
%%%
#(define (define-grob-definition grob-name grob-entry)
   "Define a new grob and add it to `all-grob-definitions', after
scm/define-grobs.scm fashion.
After grob definitions are added, use:
\\layout {
  \\context {
    \\Global
    \\grobdescriptions #all-grob-descriptions
  }
}
to register them."
   (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))))

#(define-public (define-grob-property symbol type? description)
   "Define a new grob property.
`symbol': the property name
`type?': the type predicate for this property
`description': the type documentation"
  (set-object-property! symbol 'backend-type? type?)
  (set-object-property! symbol 'backend-doc description)
  symbol)

#(define-public (define-music-type type-name properties)
   "Add a new music type description to `music-descriptions'
and `music-name-to-property-table'."
   (set-object-property! type-name
                         'music-description
                         (cdr (assq 'description properties)))
   (let ((properties (list-copy properties)))
     (set! properties (assoc-set! properties 'name type-name))
     (set! properties (assq-remove! properties 'description))
     (hashq-set! music-name-to-property-table type-name properties)
     (set! music-descriptions
           (cons (cons type-name properties)
                 music-descriptions))))

%%%
%%% HeadOrnementation grob type
%%%
#(define (head-ornementation::print me)
   "Prints a HeadOrnementation grob (at a note head side)"
   (let* ((notes (ly:grob-object me 'elements))
          (staff-pos (ly:grob-staff-position (ly:grob-array-ref notes 0)))
          (y-ref (ly:grob-common-refpoint-of-array me notes Y))
          (x-ref (ly:grob-common-refpoint-of-array me notes X))
          (x-ext (ly:relative-group-extent notes x-ref X))
          (y-ext (ly:relative-group-extent notes y-ref Y))
          (y-coord (+ (interval-center y-ext)
                      (if (and (eq? (ly:grob-property me 'shift-when-on-line) #t)
                               (memq staff-pos '(-2 0 2)))
                          0.5
                          0)))
          (padding (ly:grob-property me 'padding 0.1))
          (direction (ly:grob-property me 'direction LEFT))
          (text (ly:text-interface::print me))
          (width (/ (interval-length (ly:stencil-extent text X)) 2.0))
          (x-coord (if (= direction LEFT)
                       (- (car x-ext) width padding)
                       (+ (cdr x-ext) width padding))))
     (ly:stencil-translate
      text
      (cons
       (- x-coord (ly:grob-relative-coordinate me x-ref X))
       (- y-coord (ly:grob-relative-coordinate me y-ref Y))))))

%% a new grob property (used to shift an ornementation when the
%% note head is on a staff line)
#(define-grob-property 'shift-when-on-line boolean?
   "If true, then the ornementation is vertically shifted when
the note head is on a staff line.")

%% HeadOrnemenation grob definition:
%% a piece of text attached to a note head side.
#(define-grob-definition
  'HeadOrnementation
  `((font-size . 0)
    (padding . 0.1)
    (shift-when-on-line . #f)
    (stencil . ,head-ornementation::print)
    (meta . ((class . Item)
             (interfaces . (font-interface))))))

\layout {
  \context {
    \Global
    \grobdescriptions #all-grob-descriptions
  }
}

%%% Head-ornementation Engraver
%%%
#(define (make-head-ornementation
          engraver note-grob markp direction is-inside shift-on-line)
   "Creates a HeadOrnementation grob attached to a note head.
`note-grob': the note head the ornementation is attached to
`markp': the ornementation markup
`direction': where the ornementation should be printed (LEFT or RIGHT of the note head)
`is-inside': if true, then the ornemenation is printed between accidental
   or dots and the note head (in this case the accidental or dots are shifted
   to the outside); otherwise it is printed outside dots or accidentals.
`shift-on-line': if true, and when the note head is on a staff line, then the
   ornementation is vertically shifted."
   (let ((ornementation (ly:engraver-make-grob engraver
                                               'HeadOrnementation
                                               note-grob)))
     (set! (ly:grob-property ornementation 'direction) direction)
     (set! (ly:grob-property ornementation 'text) markp)
     (set! (ly:grob-property ornementation 'shift-when-on-line) shift-on-line)
     (ly:pointer-group-interface::add-grob ornementation 'elements note-grob)
     (set! (ly:grob-parent ornementation Y) note-grob)
     (set! (ly:grob-property ornementation 'font-size)
           (+ (ly:grob-property ornementation 'font-size 0.0)
           (ly:grob-property note-grob 'font-size 0.0)))
     (let* ((orn-stencil (ly:text-interface::print ornementation))
            (orn-width (interval-length (ly:stencil-extent orn-stencil X)))
            (note-column (ly:grob-object note-grob 'axis-group-parent-X))
            (accidentals (ly:note-column-accidentals note-column))
            (dot-column (ly:note-column-dot-column note-column)))
       (cond ((and (= direction LEFT) (ly:grob? accidentals) is-inside)
              ;; if ornementation on the left side of the note is "inside",
              ;; then shift the accidental to the left to make room for
              ;; the ornementation
              (set! (ly:grob-property accidentals 'padding)
                    (+ orn-width (* 2 (ly:grob-property ornementation 'padding)))))
             ((and (= direction RIGHT) (ly:grob? dot-column) is-inside)
              ;; if ornementation on the right side of the note is "inside",
              ;; then shift the dots to the right to make room for
              ;; the ornementation
              (set! (ly:grob-property dot-column 'positioning-done)
                    (lambda (grob)
                      (ly:dot-column::calc-positioning-done grob)
                      (ly:grob-translate-axis! grob orn-width X))))))))
   
#(define (head-ornementation-engraver-acknowledge-note-head
          engraver note-grob source-engraver)
   "Note head acknowledge method for the head ornementation engraver.
When the note head event attached to the note head grob has ornementation
events among its articulations, then create a HeadOrnementation grob"
   (let* ((note-event (ly:grob-property note-grob 'cause)))
     (for-each (lambda (articulation)
                 (if (memq 'head-ornementation-event
                            (ly:event-property articulation 'class))
                     (begin
                       (if (markup? (ly:event-property articulation 'text-left))
                           (make-head-ornementation
                            engraver
                            note-grob
                            (ly:event-property articulation 'text-left)
                            LEFT
                            (ly:event-property articulation 'is-inside)
                            (ly:event-property articulation 'shift-when-on-line)))
                       (if (markup? (ly:event-property articulation 'text-right))
                           (make-head-ornementation
                            engraver
                            note-grob
                            (ly:event-property articulation 'text-right)
                            RIGHT
                            (ly:event-property articulation 'is-inside)
                            (ly:event-property articulation 'shift-when-on-line))))))
               (ly:event-property note-event 'articulations))))

%% The head-ornementation engraver, with its note-head acknowledger
%% (which creates the HeadOrnementation grobs)
#(define head-ornementation-engraver
   `((acknowledgers
      (note-head-interface
       . ,head-ornementation-engraver-acknowledge-note-head))))

\layout {
  \context {
    \Score
    \consists #head-ornementation-engraver
  }
}

%%%
%%% HeadOrnementationEvent definition
%%%

#(define-event-class 'head-ornementation-event 'music-event)
%% a post script event for ornementations attached to note heads
#(define-music-type 'HeadOrnementationEvent
   '((description . "Print an ornementation at a note head side")
     (types . (general-music post-event event head-ornementation-event))))

%%%
%%% Head ornementation music functions
%%%

%% Helper music function for defining head-ornementation events
#(define (make-head-ornementation-event text-left text-right is-inside shift-on-line)
   "Makes a head ornementation"
   (make-music 'HeadOrnementationEvent
               'text-left text-left
               'text-right text-right
               'is-inside is-inside
               'shift-when-on-line shift-on-line))

#(define (make-left-head-ornementation-event text is-inside shift-on-line)
   "Makes a head ornementation"
   (make-head-ornementation-event text #f is-inside shift-on-line))

#(define (make-right-head-ornementation-event text is-inside shift-on-line)
   "Makes a head ornementation"
   (make-head-ornementation-event #f text is-inside shift-on-line))

%%%
%%% Ornementation definitions
%%%

%% Parenthesis before note head
parb = #(make-left-head-ornementation-event
         (markup #:fontsize -4 #:musicglyph "accidentals.leftparen")
         #t #f)

%% Parenthesis after note head
para = #(make-right-head-ornementation-event
         (markup #:fontsize -4 #:musicglyph "accidentals.rightparen")
         #t #f)

%% Parenthesis before and after note head
parc = #(make-head-ornementation-event
         (markup #:fontsize -4 #:musicglyph "accidentals.leftparen")
         (markup #:fontsize -4 #:musicglyph "accidentals.rightparen")
         #t #f)

%% Prall after note head
pralla = #(make-right-head-ornementation-event
           (markup #:concat (#:hspace 0.2 #:musicglyph "scripts.prall"))
           #t #t)

%% Prall before note head
prallb = #(make-left-head-ornementation-event
           (markup #:concat (#:musicglyph "scripts.prall" #:hspace 0.2))
           #t #t)

%% ^ sign after note head
circA = #(make-right-head-ornementation-event
          (markup #:concat (#:hspace 0.1 #:raise 1 #:musicglyph "scripts.umarcato"))
          #f #f)
          

Reply via email to