Adding Ornamentations to Note Heads (Part 1)

While typesetting ancient music, it is not rare to face ornamentations like these pincés and cadences from Pièces de clavecin en concert by Rameau:

rameau

Notation like this is not built into LilyPond – but fortunately it is possible to add such (or any other) functionality to the program. With some advanced Scheme programming (using LilyPond’s extension language) it is possible to directly plug into its layout process.
In this series of articles, we will propose a way to typeset these ornamentations attached to note heads,

covering the following steps:

  1. define a new music event type for ornamentations, to add ornamentation events to notes;
  2. define an engraver which, for each note having an ornamentation event, will create a graphical object representing this ornamentation, attached to the note head;
  3. define this new graphical object type, and how to actually draw the ornamentations.

(Note: LilyPond does not seem to come with music type and grob definition utilities. We provide such utilities in the example file attached below: define-music-type, define-grob-property, define-grob-type.)

A first, simple, implementation

To keep things (somewhat) simple in this first post, we’ll define the base infrastructure to draw the pincé (small parenthesis) on the left edge of a note head, using the way parentheses are implemented in LilyPond as a reference.

Entering ornamentations

In order to draw a pincé, we have to find a way to attach some piece of information to the  music expression representing a note, so that an engraver (responsible for drawing the pincé) can detect and draw it next to the note. In LilyPond, articulation, text scripts, etc., are entered using postfix syntax:

\displayMusic c'-. ^"some text"
==>
(make-music 'NoteEvent
  'articulations     ;; articulations are placed in this property
  (list (make-music 'ArticulationEvent  ; the staccato event
          'articulation-type "staccato")
        (make-music 'TextScriptEvent    ; the text event
          'direction UP
          'text "some text"))
  'pitch (ly:make-pitch 0 0 0)
  'duration (ly:make-duration 2 0 1))

We’d like to enter our ornamentation the same way. Thus, we define a new music event type, akin to e.g. TextScriptEvent (defined in scm/define-music-types.scm).
The HeadOrnamentationEvent definition is:

%% a post script event for ornamentations attached to note heads
#(define-music-type HeadOrnamentationEvent (music-event)
   (description . "Print an ornamentation at a note head side")
   (types . (general-music post-event event head-ornamentation-event)))

Notice the post-event item in the types list: this is what will allow us to enter a pincé in postfix syntax:

pince=$(make-music 'HeadOrnamentationEvent)
\displayMusic c'\pince
==>
(make-music 'NoteEvent
  'articulations (list (make-music 'HeadOrnamentationEvent)) ;; here it is
  'pitch (ly:make-pitch 0 0 0)
  'duration (ly:make-duration 2 0 1))

Now that we have successfully attached the ornamentation information to the note, we have to make use of it and actually draw the ornamentation.

Engraving

The next step is to actually build the graphical object representing the ornamentation on a note edge whenever an ornamentation is found in a note event. This is done by an engraver, defined as follows (the HeadOrnamentation grob type being defined in the next section):

%% The head-ornamentation engraver, with its note-head acknowledger
%% (which adds HeadOrnamentation grobs to note heads)
#(define head-ornamentation-engraver
   (make-engraver
    (acknowledgers
     ((note-head-interface engraver note-grob source)
      ;; When the note head has ornamentation events among its articulations,
      ;; then create a HeadOrnamentation grob and attach it to the Head grob
      (let* ((note-event (ly:grob-property note-grob 'cause)))
        (for-each
         (lambda (articulation)
           (if (memq 'head-ornamentation-event
                     (ly:event-property articulation 'class))
               ;; this articulation is an ornementation => make the grob
               (let ((ornament-grob (ly:engraver-make-grob engraver
                                                           'HeadOrnamentation
                                                           note-grob)))
                 (ly:pointer-group-interface::add-grob ornament-grob
                                                       'elements
                                                       note-grob)
                 ;; the ornamentation is vertically aligned with the note head
                 (set! (ly:grob-parent ornament-grob Y) note-grob)
                 ;; the font size is relative to the note head's one
                 (set! (ly:grob-property ornament-grob 'font-size)
                       (+ (ly:grob-property ornament-grob 'font-size 0.0)
                          (ly:grob-property note-grob 'font-size 0.0))))))
         (ly:event-property note-event 'articulations)))))))

\layout {
  \context {
    \Voice
    \consists #head-ornamentation-engraver
  }
}

This code is mainly an adaptation of lily/parenthesis-engraver.cc.

What’s still missing here is the HeadOrnamentation grob definition, and how to draw the grobs.

Grob definition

Finally, the new graphical object type dedicated to note head ornamentation, HeadOrnamentation, has to be defined:

%% how to print a HeadOrnementation:
%% a first approximation is: (ly:text-interface::print me)
%% and then to move this stencil at the desired place
#(define (head-ornamentation::print me)
   "Prints a HeadOrnamentation grob (at a note head side)"
   (let* ((notes (ly:grob-object me 'elements))
          (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))
          (text (ly:text-interface::print me))
          (width (/ (interval-length (ly:stencil-extent text X)) 2.0))
          (x-coord (- (car x-ext) width)))
     (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))))))

%% HeadOrnementation grob definition:
%% a hardcoded text (the pincé markup) and the print function defined above
#(define-grob-type 'HeadOrnamentation
  `(;; hard coded pincé markup:
    (text . ,#{ \markup \fontsize #-4 \musicglyph #"accidentals.leftparen" #})
    (stencil . ,head-ornamentation::print)
    (meta . ((class . Item)
             (interfaces . (font-interface))))))

%% make the new grob definition visible to scores:
\layout {
  \context {
    \Global
    \grobdescriptions #all-grob-descriptions
  }
}

The hard part here is figuring out how to place the ornamentation grob relatively to the note head in the printing method. It is inspired by the function parentheses-item::print from scm/output-lib.scm.

All the infrastructure, from music entry to drawing, is now ready. The complete example file, simple-pince.ly, gives the following result:

simple-pince.preview

What’s next?

One would like to enter various kinds of ornamentations: pincés, cadences, etc., both on the left and the right side of note heads. That’s what we’ll figure out in the next post. Then, in a third chapter, we’ll address spacing and collision issues.

7 thoughts on “Adding Ornamentations to Note Heads (Part 1)

  1. Janek Warchoł

    Excellent tutorial, thank you! It will surely help me when i’ll have to do something similar. In fact, i may even change my mind about Scheme when such great resources appear – it doesn’t happen often that i understand a substantial piece of Scheme code at first reading 🙂

    Reply
  2. Jan-Peter Voigt

    This is very enlightening 🙂 thank you!
    Now it should be easy to make any ornament, one can think of!

    Reply
  3. Thomas Morley

    Great stuff!
    Commented coding is missing a lot in our source. Accompanied by your explanations it’s even better.

    Reply
  4. Pingback: Adding ornamentations to note heads (part 2) | Scores of Beauty

  5. Pingback: Adding Ornamentations to Note Heads (Part 3) | Scores of Beauty

  6. Simon

    When I try to run simple-pince.ly, I get the message:

    Parsing…
    warning: type check for `HeadOrnamentation’ failed; value `#’ must be of type `list’
    Interpreting music…
    Exited with exit status 1.

    Does someone know what’s going wrong?

    Reply
    1. Urs Liska

      Obviously this code isn’t compatible anymore with current LilyPond versions. Unfortunately I don’t have the time to look into it more closely (well, it’s not my code, and I have a hard time to understand it).

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *