From 8dd920d2143611a60bdaf82831613aae6342d2aa Mon Sep 17 00:00:00 2001 From: Neil Puttock Date: Tue, 2 Mar 2010 20:20:39 +0000 Subject: [PATCH] Fix #189: Episema over single neume. * add Episema_engraver (based on Text_spanner_engraver) which listens to EpisemaEvent and creates Episema spanner * set NoteHeads as 'side-support-elements so spanner can be quantized over neume/melisma * ensure episema doesn't extend past bound items by setting NoteColumns as spanner bounds * add regression test --- Documentation/notation/ancient.itely | 23 ++-- input/regression/display-lily-tests.ly | 1 + input/regression/episema.ly | 16 +++ lily/episema-engraver.cc | 179 +++++++++++++++++++++++++ ly/engraver-init.ly | 24 +--- ly/gregorian.ly | 4 +- ly/spanners-init.ly | 4 + scm/define-event-classes.scm | 2 +- scm/define-grob-interfaces.scm | 5 + scm/define-grobs.scm | 27 ++++ scm/define-music-display-methods.scm | 2 + scm/define-music-types.scm | 5 + 12 files changed, 258 insertions(+), 34 deletions(-) create mode 100644 input/regression/episema.ly create mode 100644 lily/episema-engraver.cc diff --git a/Documentation/notation/ancient.itely b/Documentation/notation/ancient.itely index adec460f29..96671041ed 100644 --- a/Documentation/notation/ancient.itely +++ b/Documentation/notation/ancient.itely @@ -1302,18 +1302,26 @@ Vaticana} style are provided. \override TextScript #'font-family = #'typewriter \override TextScript #'font-shape = #'upright \override Script #'padding = #-0.1 - a\ictus_"ictus " \break - a\circulus_"circulus " \break - a\semicirculus_"semicirculus " \break - a\accentus_"accentus " \break + a\ictus_"ictus " \bar "" \break + a\circulus_"circulus " \bar "" \break + a\semicirculus_"semicirculus " \bar "" \break + a\accentus_"accentus " \bar "" \break \[ a_"episema" \episemInitium \pes b \flexa a b \episemFinis \flexa a \] } } @end lilypond -@c @seealso -@c TODO: nothing here yet ... +Snippets: +@rlsr{Ancient notation}. + +Internals Reference: +@rinternals{Episema}, +@rinternals{EpisemaEvent}, +@rinternals{Episema_engraver}, +@rinternals{Script}, +@rinternals{ScriptEvent}, +@rinternals{Script_engraver}. @knownissues @@ -1321,9 +1329,6 @@ Vaticana} style are provided. Some articulations are vertically placed too closely to the corresponding note heads. -The episema line is not displayed in many cases. If it is displayed, -the right end of the episema line is often too far to the right. - @c {{{2Augmentum dots (@emph{morae}) @node Augmentum dots (@emph{morae}) diff --git a/input/regression/display-lily-tests.ly b/input/regression/display-lily-tests.ly index 7d81a696af..5c128d1654 100644 --- a/input/regression/display-lily-tests.ly +++ b/input/regression/display-lily-tests.ly @@ -132,6 +132,7 @@ stderr of this run." \test "" ##[ { c( c) c^( c^) c_( c_) } #] % SlurEvent \test "" ##[ { c\< c\! c^\< c^\! c_\< c_\! } #] % CrescendoEvent \test "" ##[ { c\> c\! c^\> c^\! c_\> c_\! } #] % DecrescendoEvent +\test "" ##[ { c\episemInitium c\episemFinis } #] % EpisemaEvent \test "" ##[ { c\( c\) c^\( c^\) c_\( c_\) } #] % PhrasingSlurEvent \test "" ##[ { c\sustainOn c\sustainOff } #] % SustainEvent \test "" ##[ { c\sostenutoOn c\sostenutoOff } #] % SostenutoEvent diff --git a/input/regression/episema.ly b/input/regression/episema.ly new file mode 100644 index 0000000000..44ccae21e1 --- /dev/null +++ b/input/regression/episema.ly @@ -0,0 +1,16 @@ +\version "2.13.15" + +\header { + texidoc = "An episema can be typeset over a single neume or a +melisma. Its position is quantized between staff lines." +} + +#(set-global-staff-size 26) +\include "gregorian.ly" + +\new VaticanaVoice { + \revert Score.SpacingSpanner #'packed-spacing + a\episemInitium\episemFinis + \[ a\episemInitium \pes b \flexa a\episemFinis \] + \[ a\episemInitium \pes b \flexa a b\episemFinis \flexa a \] +} diff --git a/lily/episema-engraver.cc b/lily/episema-engraver.cc new file mode 100644 index 0000000000..a24640f8b9 --- /dev/null +++ b/lily/episema-engraver.cc @@ -0,0 +1,179 @@ +/* + This file is part of LilyPond, the GNU music typesetter. + + Copyright (C) 2010 Neil Puttock + + LilyPond is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + LilyPond is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with LilyPond. If not, see . +*/ + +#include "engraver.hh" +#include "international.hh" +#include "note-column.hh" +#include "pointer-group-interface.hh" +#include "side-position-interface.hh" +#include "spanner.hh" +#include "stream-event.hh" + +#include "translator.icc" + +class Episema_engraver : public Engraver +{ +public: + TRANSLATOR_DECLARATIONS (Episema_engraver); +protected: + virtual void finalize (); + DECLARE_TRANSLATOR_LISTENER (episema); + DECLARE_ACKNOWLEDGER (note_column); + DECLARE_ACKNOWLEDGER (note_head); + void stop_translation_timestep (); + void process_music (); + +private: + Spanner *span_; + Spanner *finished_; + Stream_event *current_event_; + Drul_array event_drul_; + vector note_columns_; + void typeset_all (); +}; + +Episema_engraver::Episema_engraver () +{ + finished_ = 0; + current_event_ = 0; + span_ = 0; + event_drul_.set (0, 0); +} + +IMPLEMENT_TRANSLATOR_LISTENER (Episema_engraver, episema); +void +Episema_engraver::listen_episema (Stream_event *ev) +{ + Direction d = to_dir (ev->get_property ("span-direction")); + // Must not ASSIGN_EVENT_ONCE here, since episema + // can be typeset over a single neume + event_drul_[d] = ev; +} + +void +Episema_engraver::process_music () +{ + if (event_drul_[START]) + { + if (current_event_) + event_drul_[START]->origin ()->warning (_ ("already have an episema")); + else + { + current_event_ = event_drul_[START]; + span_ = make_spanner ("Episema", event_drul_[START]->self_scm ()); + + event_drul_[START] = 0; + } + } + if (event_drul_[STOP]) + { + if (!span_) + event_drul_[STOP] + ->origin ()->warning (_ ("cannot find start of episema")); + else + { + finished_ = span_; + announce_end_grob (finished_, SCM_EOL); + span_ = 0; + current_event_ = 0; + note_columns_.clear (); + } + } +} + +void +Episema_engraver::typeset_all () +{ + if (finished_) + { + if (!finished_->get_bound (RIGHT)) + { + Grob *col = (note_columns_.size () + ? note_columns_.back () + : unsmob_grob (get_property ("currentMusicalColumn"))); + finished_->set_bound (RIGHT, col); + } + finished_ = 0; + } +} + +void +Episema_engraver::stop_translation_timestep () +{ + if (span_ && !span_->get_bound (LEFT)) + { + Grob *col = (note_columns_.size () + ? note_columns_.front () + : unsmob_grob (get_property ("currentMusicalColumn"))); + span_->set_bound (LEFT, col); + } + + typeset_all (); + event_drul_.set (0, 0); +} + +void +Episema_engraver::finalize () +{ + typeset_all (); + if (span_) + { + current_event_->origin ()->warning (_ ("unterminated episema")); + span_->suicide (); + span_ = 0; + } +} + +void +Episema_engraver::acknowledge_note_column (Grob_info info) +{ + note_columns_.push_back (info.grob ()); +} + +void +Episema_engraver::acknowledge_note_head (Grob_info info) +{ + if (span_) + { + Side_position_interface::add_support (span_, info.grob ()); + add_bound_item (span_, info.grob ()); + } + else if (finished_) + { + Side_position_interface::add_support (finished_, info.grob ()); + add_bound_item (finished_, info.grob ()); + } +} + +ADD_ACKNOWLEDGER (Episema_engraver, note_column); +ADD_ACKNOWLEDGER (Episema_engraver, note_head); + +ADD_TRANSLATOR (Episema_engraver, + /* doc */ + "Create an @emph{Editio Vaticana}-style episema line.", + + /* create */ + "Episema ", + + /* read */ + "", + + /* write */ + "" + ); diff --git a/ly/engraver-init.ly b/ly/engraver-init.ly index b4f023e500..6d4b656dd7 100644 --- a/ly/engraver-init.ly +++ b/ly/engraver-init.ly @@ -825,6 +825,8 @@ of Editio Vaticana." \remove "Stem_engraver" \remove "Ligature_bracket_engraver" \consists "Vaticana_ligature_engraver" + \remove "Text_spanner_engraver" + \consists "Episema_engraver" %% Set default head for notes outside of \[ \]. \override NoteHead #'style = #'vaticana.punctum @@ -835,15 +837,6 @@ of Editio Vaticana." %% There are no beams in Gregorian Chant notation. autoBeaming = ##f - - %% Prepare TextSpanner for \episem{Initium|Finis} use. - %% - %% FIXME: The line @code{\override TextSpanner #'padding = #-0.1} is - %% required to force the articulation signs being placed vertically - %% tightly to the correpsonding note heads. - %% - \override TextSpanner #'style = #'line - \override TextSpanner #'padding = #-0.1 } \context { @@ -892,6 +885,7 @@ of Editio Vaticana." \Voice \name "GregorianTranscriptionVoice" \alias "Voice" + \consists "Episema_engraver" %% Removing ligature bracket engraver without replacing it by some %% other ligature engraver would cause a "Junking event: `LigatureEvent'" @@ -906,18 +900,6 @@ of Editio Vaticana." %% There are no beams in Gregorian Chant notation. autoBeaming = ##f - %% Prepare TextSpanner for \episem{Initium|Finis} use. - %% - %% N.B.: dash-fraction MUST be unset; otherwise, TextSpanner will - %% always produce dashed lines, regardless of the style property. - %% - %% FIXME: The line @code{\override TextSpanner #'padding = #-0.1} is - %% required to force the articulation signs being placed vertically - %% tightly to the correpsonding note heads. - %% - \override TextSpanner #'dash-fraction = #'() - \override TextSpanner #'style = #'line - \override TextSpanner #'padding = #-0.1 } \context { diff --git a/ly/gregorian.ly b/ly/gregorian.ly index d8fc1aac7b..367dbbf16b 100644 --- a/ly/gregorian.ly +++ b/ly/gregorian.ly @@ -149,8 +149,6 @@ accentus = #(make-articulation "accentus") ictus = #(make-articulation "ictus") semicirculus = #(make-articulation "semicirculus") circulus = #(make-articulation "circulus") -episemInitium = \startTextSpan -episemFinis = \stopTextSpan % % \augmentum increases the dot-count value of all note heads to which @@ -224,7 +222,7 @@ ligature = #(define-music-function \layout { indent = 0.0 - %%% TODO: should raggedright be the default? + %%% TODO: should ragged-right be the default? %ragged-right = ##t ragged-last = ##t diff --git a/ly/spanners-init.ly b/ly/spanners-init.ly index 7b26094e49..afcfaf445f 100644 --- a/ly/spanners-init.ly +++ b/ly/spanners-init.ly @@ -18,6 +18,10 @@ startTrillSpan = #(make-span-event 'TrillSpanEvent START) stopTrillSpan = #(make-span-event 'TrillSpanEvent STOP) +episemInitium = #(make-span-event 'EpisemaEvent START) +episemFinis = #(make-span-event 'EpisemaEvent STOP) + + % STOP: junkme! cresc = { #(ly:export (make-event-chord (list cr))) diff --git a/scm/define-event-classes.scm b/scm/define-event-classes.scm index eb69eaf8e2..70387c8c26 100644 --- a/scm/define-event-classes.scm +++ b/scm/define-event-classes.scm @@ -41,7 +41,7 @@ (part-combine-event . (solo-one-event solo-two-event unisono-event)) (break-event . (line-break-event page-break-event page-turn-event)) (dynamic-event . (absolute-dynamic-event)) - (span-event . (span-dynamic-event beam-event ligature-event + (span-event . (span-dynamic-event beam-event episema-event ligature-event pedal-event phrasing-slur-event slur-event staff-span-event text-span-event trill-span-event tremolo-span-event tuplet-span-event)) diff --git a/scm/define-grob-interfaces.scm b/scm/define-grob-interfaces.scm index 96d3bd7d97..b73506790d 100644 --- a/scm/define-grob-interfaces.scm +++ b/scm/define-grob-interfaces.scm @@ -66,6 +66,11 @@ note)." "Dynamic text spanner." '(text)) +(ly:add-interface + 'episema-interface + "An episema line." + '()) + (ly:add-interface 'finger-interface "A fingering instruction." diff --git a/scm/define-grobs.scm b/scm/define-grobs.scm index b1cfb63bec..efd310c1da 100644 --- a/scm/define-grobs.scm +++ b/scm/define-grobs.scm @@ -722,6 +722,33 @@ text-interface)))))) + (Episema + . ( + (bound-details . ((left . ((Y . 0) + (padding . 0) + (attach-dir . ,LEFT) + )) + (right . ((Y . 0) + (padding . 0) + (attach-dir . ,RIGHT) + )) + )) + (direction . ,UP) + (left-bound-info . ,ly:line-spanner::calc-left-bound-info) + (quantize-position . #t) + (right-bound-info . ,ly:line-spanner::calc-right-bound-info) + (side-axis . ,Y) + (stencil . ,ly:line-spanner::print) + (style . line) + (Y-offset . ,ly:side-position-interface::y-aligned-side) + (meta . ((class . Spanner) + (interfaces . (episema-interface + font-interface + line-interface + line-spanner-interface + side-position-interface)))))) + + (Fingering . ( diff --git a/scm/define-music-display-methods.scm b/scm/define-music-display-methods.scm index 3254a90469..469c65408a 100644 --- a/scm/define-music-display-methods.scm +++ b/scm/define-music-display-methods.scm @@ -143,6 +143,7 @@ 'BeamForbidEvent 'CrescendoEvent 'DecrescendoEvent + 'EpisemaEvent 'ExtenderEvent 'FingeringEvent 'GlissandoEvent @@ -234,6 +235,7 @@ (define-span-event-display-method SlurEvent (event parser) #f "(" ")") (define-span-event-display-method CrescendoEvent (event parser) #f "\\<" "\\!") (define-span-event-display-method DecrescendoEvent (event parser) #f "\\>" "\\!") +(define-span-event-display-method EpisemaEvent (event parser) #f "\\episemInitium" "\\episemFinis") (define-span-event-display-method PhrasingSlurEvent (event parser) #f "\\(" "\\)") (define-span-event-display-method SustainEvent (event parser) #f "\\sustainOn" "\\sustainOff") (define-span-event-display-method SostenutoEvent (event parser) #f "\\sostenutoOn" "\\sostenutoOff") diff --git a/scm/define-music-types.scm b/scm/define-music-types.scm index 973e59a403..356ba898a9 100644 --- a/scm/define-music-types.scm +++ b/scm/define-music-types.scm @@ -177,6 +177,11 @@ An alternative syntax is @var{note}@code{\\decr} @dots{} event)) )) + (EpisemaEvent + . ((description . "Begin or end an episema.") + (types . (general-music span-event event episema-event)) + )) + (Event . ((description . "Atomic music event.") (types . (general-music event)) -- 2.39.2