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)