>From 37acebbdfb8876ca5ef94d473f4cfc234c4e8bd2 Mon Sep 17 00:00:00 2001 From: Knut Petersen Date: Sun, 1 Jan 2017 16:16:24 +0100 Subject: [PATCH] Automatic LyricExtenders Version 2017-01-01 Signed-off-by: Knut Petersen --- Documentation/learning/common-notation.itely | 22 +-- Documentation/notation/vocal.itely | 266 ++++++++++++--------------- lily/extender-engraver.cc | 6 - lily/lyric-extender.cc | 65 ++++--- lily/parser.yy | 4 +- lily/self-alignment-interface.cc | 9 +- ly/music-functions-init.ly | 54 +++++- scm/define-grob-properties.scm | 12 ++ scm/define-grobs.scm | 5 +- scm/music-functions.scm | 12 +- 10 files changed, 254 insertions(+), 201 deletions(-) diff --git a/Documentation/learning/common-notation.itely b/Documentation/learning/common-notation.itely index b51766c0a2..2845ead743 100644 --- a/Documentation/learning/common-notation.itely +++ b/Documentation/learning/common-notation.itely @@ -597,7 +597,7 @@ Music Glossary: @rglos{note value}, @rglos{triplet}. @notation{Tuplets} are made with the @code{\tuplet} keyword. It takes two arguments: a fraction and a piece of music. The -fraction is the number of tuplet notes over the number +fraction is the number of tuplet notes over the number of notes normally filling the same duration. For triplets, there are three notes instead of two, so @notation{triplets} have 3/2 as their fraction. @@ -1134,12 +1134,11 @@ that should be included in the melisma: >> @end lilypond -If a syllable extends over several notes or a single very long -note an @notation{extender line} is usually drawn from the -syllable extending under all the notes for that syllable. It is -entered as two underscores @code{__}. Here is an example from the -first three bars of @notation{Dido's Lament}, from Purcell's address@hidden and Æneas}: +If a syllable extends over several notes or a single very long note an address@hidden line} is usually drawn from the syllable extending under all +the notes for that syllable. Lilypond adds extender lines automatically at +appropriate places. Here is an example from the first three bars of address@hidden's Lament}, from Purcell's @notation{Dido and Æneas}: @lilypond[verbatim,quote] << @@ -1151,7 +1150,7 @@ first three bars of @notation{Dido's Lament}, from Purcell's } \addlyrics { When I am laid, - am laid __ in earth, + am laid in earth, } >> @end lilypond @@ -1175,8 +1174,8 @@ far about aligning lyrics to notes. d4 d e | c2 } \addlyrics { - A -- way in a __ man -- ger, - no __ crib for a bed, + A -- way in a man -- ger, + no crib for a bed, } >> @end lilypond @@ -1230,7 +1229,7 @@ example from Handel's @notation{Judas Maccabæus}: c''8 | c8([ bes]) a a([ g]) f | f'4. b, | c4.~ 4 } \addlyrics { - Let flee -- cy flocks the hills a -- dorn, __ + Let flee -- cy flocks the hills a -- dorn, } \relative { \key f \major @@ -1520,4 +1519,3 @@ of manuals}. There is a lot of information about LilyPond, so newcomers often do not know where they should look for help. If you spend five minutes reading that section carefully, you might save yourself hours of frustration looking in the wrong places! - diff --git a/Documentation/notation/vocal.itely b/Documentation/notation/vocal.itely index 118a58e482..1f5901fdac 100644 --- a/Documentation/notation/vocal.itely +++ b/Documentation/notation/vocal.itely @@ -261,7 +261,7 @@ For more details, see @ref{Automatic syllable durations}. % takes durations and alignment from notes in "one" \new Lyrics \lyricsto "one" { - Life is __ _ love, live __ life. + Life is _ love, live life. } % takes durations and alignment from notes in "one" initially @@ -634,10 +634,10 @@ word, that syllable is usually joined to the following one with a hyphenated line. This is indicated by placing a double hyphen, @address@hidden, immediately after the syllable. -Alternatively, when a melisma occurs on the last or only syllable in -a word an extender line is usually drawn from the end of the syllable -to the last note of the melisma. This is indicated by placing a -double underscore, @code{__}, immediately after the word. +Alternatively, when a melisma occurs on the last or only syllable +in a word an extender line is usually drawn from the end of the +syllable to the last note of the melisma. Lilypond detects places +where extender lines should be used and adds them automatically. There are five ways in which melismata can be indicated: @@ -656,7 +656,7 @@ together: 8 } \new Lyrics \lyricsto "melody" { - Ky -- ri -- e __ + Ky -- ri -- e } >> @end lilypond @@ -674,7 +674,7 @@ entering lyrics: e8 ( d e2 ) } \new Lyrics \lyricsto "melody" { - Ky -- ri -- e __ + Ky -- ri -- e } >> @end lilypond @@ -736,7 +736,7 @@ to be added to the melisma. e8 d e2 } \new Lyrics \lyricsto "melody" { - Ky -- ri -- _ _ _ e __ _ _ + Ky -- ri -- _ _ _ e _ _ } >> @end lilypond @@ -784,7 +784,7 @@ should be included in the melisma: g8 [ f ] ~ 4 ~ f } \new Lyrics \lyricsto "melody" { - Ky -- ri -- _ e __ _ _ _ + Ky -- ri -- _ e _ _ _ } >> @end lilypond @@ -813,32 +813,9 @@ Notation Reference: Internals Reference: @rinternals{Tunable context properties}. address@hidden -Extender lines under melismata are not created automatically; they -must be inserted manually with a double underscore. - - @node Extenders and hyphens @unnumberedsubsubsec Extenders and hyphens address@hidden melisma address@hidden extender - address@hidden TODO cf Multiple notes to one syllable; should this be merged in? - address@hidden leave this as samp. -gp -In the last syllable of a word, melismata are sometimes indicated with -a long horizontal line starting in the melisma syllable, and ending in -the next one. Such a line is called an extender line, and it is -entered as @samp{ __ } (note the spaces before and after the two -underscore characters). - address@hidden are indicated in the score with extender lines, -which are entered as one double underscore; but short melismata can -also be entered by skipping individual notes, which are entered as -single underscore characters; these do not make an extender line to be -typeset by default.} - @cindex hyphens @c leave this as samp. -gp @@ -854,6 +831,49 @@ distance between two syllables) and the @code{minimum-length} (threshold below which hyphens are removed) properties of @code{LyricHyphen}. address@hidden melisma address@hidden extender + +In the last syllable of a word, melismata are normally indicated with a long +horizontal line starting in the melisma syllable, and ending in the next one. +Such a line is called an extender line, and it is added automatically. + address@hidden previous versions of lilypond it was necessary to use a space and +two underscore characters after a syllable to induce generation of an extender +line. The current version of lilypond completely ignores the extender token address@hidden + +Sometimes it is necessary to skip some notes. Most of the time you will use +simply @samp{ "" }, but it is also possible to use @samp{ \markup\null }. + address@hidden + address@hidden instructs lilypond to generate lyric extenders +automatically. + address@hidden instructs lilypond not to generate lyric extenders +automatically. + address@hidden @var{num}} sets the lower length limit for lyric +extenders to num staffspaces. No lyric extenders shorter than this will be +automatically generated. If currently inhibited, automatic generation of +lyric extenders is also enabled. + address@hidden @var{num}} generates a lyric extender that starts num +staffspaces left from the associated note instead of a lyrics syllable. + address@hidden forces a lyric extender where none would be generated +otherwise. + address@hidden @var{num}} forces a lyric extenders of length num +staffspaces where none would be generated otherwise. + address@hidden suppresses a lyric extender that would be automatically +generated. + address@hidden @var{num}} tells lilypond to stop the next lyric +extender num staffspaces left from the normal endpoint. + @seealso Internals Reference: @rinternals{LyricExtender}, @@ -1265,11 +1285,8 @@ correctly nested in parallel sections: \new Lyrics \lyricsto "melody" { Not re -- peat -- ed. << - { The first time words. } - \new Lyrics { - \set associatedVoice = "melody" - Sec -- ond time words. - } + { The first time words. } + \new Lyrics { Sec -- ond time words. } >> } >> @@ -1284,24 +1301,18 @@ More verses may be added in a similar way: \new Staff { \new Voice = "singleVoice" { \relative { - a'4 a a a - \repeat volta 3 { b4 b b b } + a'4 a a a + \repeat volta 3 { b4 b b b } c4 c c c - } + } } } \new Lyrics \lyricsto "singleVoice" { Not re -- peat -- ed. << { The first time words. } - \new Lyrics { - \set associatedVoice = "singleVoice" - Sec -- ond time words. - } - \new Lyrics { - \set associatedVoice = "singleVoice" - The third time words. - } + \new Lyrics { Sec -- ond time words. } + \new Lyrics { The third time words. } >> The end sec -- tion. } @@ -1324,26 +1335,22 @@ To position them correctly use @code{alignBelowContext}: \new Staff { \new Voice = "melody" { \relative { - a'4 a a a - \repeat volta 3 { b4 b b b } + a'4 a a a + \repeat volta 3 { b4 b b b } c4 c c c - } + } } } \new Lyrics = "firstVerse" \lyricsto "melody" { Not re -- peat -- ed. << { The first time words. } - \new Lyrics = "secondVerse" - \with { alignBelowContext = #"firstVerse" } { - \set associatedVoice = "melody" - Sec -- ond time words. - } - \new Lyrics = "thirdVerse" - \with { alignBelowContext = #"secondVerse" } { - \set associatedVoice = "melody" - The third time words. - } + \new Lyrics = "secondVerse" + \with { alignBelowContext = #"firstVerse" } + { Sec -- ond time words. } + \new Lyrics = "thirdVerse" + \with { alignBelowContext = #"secondVerse" } + { The third time words. } >> The end sec -- tion. } @@ -1396,25 +1403,17 @@ lyrics correctly. } @end lilypond address@hidden \skip @cindex skipping notes in lyrics @cindex lyrics, skipping notes -But when the repeated section has different words, or when one -of the @code{\alternative} blocks starts with a rest, a repeat -construct cannot be used around the words and @code{\skip} commands -have to be inserted manually to skip over the notes in the -alternative sections which do not apply. +But when the repeated section has different words, or when one of the address@hidden blocks starts with a rest, a repeat construct cannot be +used around the words and @samp{ "" } have to be inserted manually to skip +over the notes in the alternative sections which do not apply. -Note: do not use an underscore, @code{_}, to skip notes -- an -underscore indicates a melisma, causing the preceding syllable -to be left-aligned. - address@hidden @address@hidden command must be followed by a number, -but this number is ignored in lyrics which derive their durations -from the notes in an associated melody through @code{\addlyrics} or address@hidden Each @address@hidden skips a single note of any -value, irrespective of the value of the following number.} +Note: do not use an underscore, @code{_}, to skip notes -- an underscore +indicates a melisma, causing the preceding syllable to be left-aligned and an +extender to be generated. @lilypond[verbatim,quote,ragged-right] \score { @@ -1431,15 +1430,12 @@ value, irrespective of the value of the following number.} } \new Lyrics { \lyricsto "melody" { - The first time words. - \repeat unfold 2 { \skip 1 } - End here. + The first time words. "" "" End here. } } \new Lyrics { \lyricsto "melody" { - Sec -- ond - \repeat unfold 2 { \skip 1 } + Sec -- ond "" "" time words. } } @@ -1449,21 +1445,28 @@ value, irrespective of the value of the following number.} @cindex lyrics and tied notes @funindex \repeatTie - -When a note is tied over into two or more alternative endings a -tie is used to carry the note into the first alternative ending and -a @code{\repeatTie} is used in the second and subsequent endings. -This structure causes difficult alignment problems when lyrics are -involved and increasing the length of the alternative sections so -the tied notes are contained wholly within them may give a more -acceptable result. - -The tie creates a melisma into the first alternative, but not into -the second and subsequent alternatives, so to align the lyrics -correctly it is necessary to disable the automatic creation of -melismata over the volta section and insert manual skips. address@hidden \earlyExtender address@hidden extender, early address@hidden \shortenExtender address@hidden extender, shorten + +When a note is tied over into two or more alternative endings a tie is used to +carry the note into the first alternative ending and a @code{\repeatTie} is +used in the second and subsequent endings. This structure causes difficult +alignment problems when lyrics are involved and increasing the length of the +alternative sections so the tied notes are contained wholly within them may +give a more acceptable result. + +The tie creates a melisma into the first alternative, but not into the second +and subsequent alternatives, so to align the lyrics correctly it is necessary +to disable the automatic creation of melismata over the volta section and +insert manual skips. Note the use of @code{\earlyExtender}. @lilypond[quote,verbatim] +\paper { + ragged-right = ##f +} + \score { << \new Staff { @@ -1480,10 +1483,10 @@ melismata over the volta section and insert manual skips. } \new Lyrics { \lyricsto "melody" { - \repeat volta 2 { Here's a __ } + \repeat volta 2 { Here's a } \alternative { - { \skip 1 verse } - { \skip 1 sec } + { _ verse } + { \earlyExtender #-4 sec -- } } ond one. } @@ -1492,48 +1495,22 @@ melismata over the volta section and insert manual skips. } @end lilypond -Note that if @code{\unfoldRepeats} is used around a section -containing @code{\repeatTie}, the @code{\repeatTie} should be -removed to avoid both types of tie being printed. +Note that if @code{\unfoldRepeats} is used around a section containing address@hidden, the @code{\repeatTie} should be removed to avoid both types +of tie being printed. + +When the repeated section has different words a @code{\repeat} cannot be used +around the lyrics and skip commands coded as @code{""} need to be inserted +manually, as before. -When the repeated section has different words a @code{\repeat} -cannot be used around the lyrics and @code{\skip} commands need to -be inserted manually, as before. address@hidden and @code{\earlyExtender} can be used to give an +optimal solution to the problem of lyric extenders in such situations: @lilypond[quote,verbatim] -\score { - << - \new Staff { - \time 2/4 - \new Voice = "melody" { - \relative { - \repeat volta 2 { b'4 b ~} - \alternative { { b b } { b \repeatTie c } } - c4 c - } - } - } - \new Lyrics { - \lyricsto "melody" { - Here's a __ verse. - \repeat unfold 2 { \skip 1 } - } - } - \new Lyrics { - \lyricsto "melody" { - Here's one - \repeat unfold 2 { \skip 1 } - more to sing. - } - } - >> +\paper { + ragged-right = ##f } address@hidden lilypond -If you wish to show extenders and hyphens into and out of -alternative sections these must be inserted manually. - address@hidden,verbatim] \score { << \new Staff { @@ -1548,15 +1525,12 @@ alternative sections these must be inserted manually. } \new Lyrics { \lyricsto "melody" { - Here's a __ verse. - \repeat unfold 2 { \skip 1 } + Here's a verse. "" "" } } \new Lyrics { \lyricsto "melody" { - Here's "a_" - \skip 1 - "_" sec -- ond one. + Here's \shortenExtender #4 one "" \earlyExtender #-4 more to sing. } } >> @@ -1667,7 +1641,7 @@ too short, since the lyrics are aligned only to the top voice: @lilypond[quote,verbatim] soprano = \relative { b'8( c d c) d2 } alto = \relative { g'2 b8( a g a) } -words = \lyricmode { la __ la __ } +words = \lyricmode { la la } \new Staff << \new Voice = "sopranoVoice" { \voiceOne \soprano } @@ -1686,7 +1660,7 @@ appropriately: soprano = \relative { b'8( c d c) d2 } alto = \relative { g'2 b8( a g a) } aligner = \relative { b'8( c d c) b( a g a) } -words = \lyricmode { la __ la __ } +words = \lyricmode { la la } \new Staff << \new Voice { \voiceOne \soprano } @@ -1703,7 +1677,7 @@ function, which does not allow lyrics on its own: soprano = \relative { b'8( c d c) d2 } alto = \relative { g'2 b8( a g a) } aligner = \relative { b'8( c d c) b( a g a) } -words = \lyricmode { la __ la __ } +words = \lyricmode { la la } \new Staff << \new Voice \partcombine \soprano \alto @@ -1728,7 +1702,7 @@ soprano = \relative { b'8( c d c) d2 } altoOne = \relative { g'2 b8( a b4) } altoTwo = \relative { d'2 g4( fis8 g) } aligner = \relative { b'8( c d c) d( d d d) } -words = \lyricmode { la __ la __ } +words = \lyricmode { la la } \new ChoirStaff \with {\accepts NullVoice } << \new Staff \soprano @@ -1944,7 +1918,7 @@ block: } \new Lyrics \with { includeGraceNotes = ##t } \lyricsto melody { - Ah __ fa + Ah fa } >> @end lilypond @@ -3364,7 +3338,3 @@ Ancient vocal music is supported, as explained in @seealso Notation Reference: @ref{Ancient notation}. - - - - diff --git a/lily/extender-engraver.cc b/lily/extender-engraver.cc index 39fa7788de..c5b9587022 100644 --- a/lily/extender-engraver.cc +++ b/lily/extender-engraver.cc @@ -162,18 +162,12 @@ Extender_engraver::finalize () if (extender_) { completize_extender (extender_); - - if (!extender_->get_bound (RIGHT)) - extender_->warning (_ ("unterminated extender")); extender_ = 0; } if (pending_extender_) { completize_extender (pending_extender_); - - if (!pending_extender_->get_bound (RIGHT)) - pending_extender_->warning (_ ("unterminated extender")); pending_extender_ = 0; } } diff --git a/lily/lyric-extender.cc b/lily/lyric-extender.cc index 8afe2c5569..452fe45edd 100644 --- a/lily/lyric-extender.cc +++ b/lily/lyric-extender.cc @@ -45,51 +45,64 @@ Lyric_extender::print (SCM smob) common = common->common_refpoint (me->get_system (), X_AXIS); Real sl = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness")); + bool start_of_part_b = + !left_edge->internal_has_interface ( + ly_symbol2scm ("lyric-syllable-interface")); + bool end_of_part_a = me->get_bound (RIGHT)->break_status_dir (); + bool force_extender = to_boolean (me->get_property ("force-extender")); + bool no_extender = to_boolean (me->get_property ("no-extender")); + + if (!force_extender && no_extender) + return SCM_EOL; extract_grob_set (me, "heads", heads); - - if (!heads.size ()) + if (!heads.size () + || (!force_extender + && !start_of_part_b && !end_of_part_a && heads.size () < 2)) return SCM_EOL; common = common_refpoint_of_array (heads, common, X_AXIS); Real left_point = 0.0; - if (left_edge->internal_has_interface (ly_symbol2scm ("lyric-syllable-interface"))) + if (!start_of_part_b) left_point = left_edge->extent (common, X_AXIS)[RIGHT]; - else if (heads.size ()) - left_point = heads[0]->extent (common, X_AXIS)[LEFT]; else - left_point = left_edge->extent (common, X_AXIS)[RIGHT]; + left_point = heads[0]->extent (common, X_AXIS)[LEFT]; if (isinf (left_point)) return SCM_EOL; - /* It seems that short extenders are even lengthened to go past the - note head, but haven't found a pattern in it yet. --hwn 1/1/04 */ - SCM minlen = me->get_property ("minimum-length"); - Real right_point - = left_point + (robust_scm2double (minlen, 0)); - - right_point = min (right_point, me->get_system ()->get_bound (RIGHT)->relative_coordinate (common, X_AXIS)); + Real h = sl * robust_scm2double (me->get_property ("thickness"), 0); + Drul_array paddings ( + robust_scm2double (me->get_property ("left-padding"), h), + robust_scm2double (me->get_property ("right-padding"), h)); + Real force_len = robust_scm2double (me->get_property ("force-length"), 0); + Real collapse_len = robust_scm2double (me->get_property ("collapse-length"), + 0); - if (heads.size ()) - right_point = max (right_point, heads.back ()->extent (common, X_AXIS)[RIGHT]); + Real right_point = heads.back ()->extent (common, X_AXIS)[RIGHT]; - Real h = sl * robust_scm2double (me->get_property ("thickness"), 0); - Drul_array paddings (robust_scm2double (me->get_property ("left-padding"), h), - robust_scm2double (me->get_property ("right-padding"), h)); + if (force_extender) + right_point = max (heads.back ()->extent (common, X_AXIS)[RIGHT], + left_point + force_len); if (right_text) - right_point = min (right_point, (robust_relative_extent (right_text, common, X_AXIS)[LEFT] - paddings[RIGHT])); + right_point = min (right_point, + (robust_relative_extent (right_text, common, + X_AXIS)[LEFT])); + + if (end_of_part_a) + right_point = robust_relative_extent (me->get_bound (RIGHT), + common, X_AXIS)[LEFT]; + + right_point -= paddings[RIGHT]; - /* run to end of line. */ - if (me->get_bound (RIGHT)->break_status_dir ()) - right_point = max (right_point, (robust_relative_extent (me->get_bound (RIGHT), common, X_AXIS)[LEFT] - paddings[RIGHT])); + if (!start_of_part_b) + left_point += paddings[LEFT]; - left_point += paddings[LEFT]; Real w = right_point - left_point; - if (w < 1.5 * h) + if (w < collapse_len && !force_extender && !end_of_part_a && !start_of_part_b) return SCM_EOL; Stencil mol (Lookup::round_filled_box (Box (Interval (0, w), @@ -106,9 +119,13 @@ ADD_INTERFACE (Lyric_extender, " note).", /* properties */ + "collapse-length " + "force-extender " + "force-length " "heads " "left-padding " "next " + "no-extender " "right-padding " "thickness " ); diff --git a/lily/parser.yy b/lily/parser.yy index 065713de1a..b446806a8b 100644 --- a/lily/parser.yy +++ b/lily/parser.yy @@ -3101,9 +3101,7 @@ post_event_nofinger: $$ = MY_MAKE_MUSIC ("HyphenEvent", @$)->unprotect (); } | EXTENDER { - if (!parser->lexer_->is_lyric_state ()) - parser->parser_error (@1, _ ("have to be in Lyric mode for lyrics")); - $$ = MY_MAKE_MUSIC ("ExtenderEvent", @$)->unprotect (); + $$ = SCM_UNSPECIFIED; } | script_dir direction_reqd_event { if (!SCM_UNBNDP ($1)) diff --git a/lily/self-alignment-interface.cc b/lily/self-alignment-interface.cc index 512f970e0e..f6737e6bd2 100644 --- a/lily/self-alignment-interface.cc +++ b/lily/self-alignment-interface.cc @@ -59,7 +59,8 @@ Self_alignment_interface::aligned_on_self (Grob *me, Axis a, bool pure, int star // However, empty extent and non-empty stencil would be suspicious. if (!ext.is_empty ()) return scm_from_double (- ext.linear_combination (scm_to_double (align))); - else if (me->get_stencil ()) + else if (me->get_stencil () && !(me->internal_has_interface ( + ly_symbol2scm ("lyric-syllable-interface")))) warning (me->name () + " has empty extent and non-empty stencil."); } return scm_from_double (0.0); @@ -141,7 +142,8 @@ Self_alignment_interface::aligned_on_parent (Grob *me, Axis a) // However, empty extent and non-empty stencil would be suspicious. if (!ext.is_empty ()) x -= ext.linear_combination (scm_to_double (self_align)); - else if (me->get_stencil ()) + else if (me->get_stencil () && !(me->internal_has_interface ( + ly_symbol2scm ("lyric-syllable-interface")))) warning (me->name () + " has empty extent and non-empty stencil."); } @@ -150,7 +152,8 @@ Self_alignment_interface::aligned_on_parent (Grob *me, Axis a) // See comment above. if (!he.is_empty ()) x += he.linear_combination (scm_to_double (par_align)); - else if (him->get_stencil ()) + else if (him->get_stencil () && !(him->internal_has_interface ( + ly_symbol2scm ("lyric-syllable-interface")))) warning (him->name () + " has empty extent and non-empty stencil."); } diff --git a/ly/music-functions-init.ly b/ly/music-functions-init.ly index 1f94319bce..babfaed3e2 100644 --- a/ly/music-functions-init.ly +++ b/ly/music-functions-init.ly @@ -202,6 +202,29 @@ staves.") (context-spec-music (make-skip-music skip) 'Staff "down" clef-2))))) +autoExtenderLimit = +#(define-music-function (num) (number?) + (_i "Set the lower length limit for lyric extenders +to @var{num} staffspaces. No lyric extenders shorter than +this will be automatically generated. If currently inhibited, +automatic generation of lyric extenders is also enabled.") + #{ \autoExtendersOn + \override Lyrics.LyricExtender.collapse-length = #num #}) + +autoExtendersOff = +#(define-music-function () () + (_i "Instructs lilypond not to generate lyric extenders +automatically.") + #{ \override LyricExtender.no-extender = ##t + \override LyricExtender.force-extender = ##f #}) + +autoExtendersOn = +#(define-music-function () () + (_i "Instructs lilypond to generate lyric extenders +automatically.") + #{ \override LyricExtender.no-extender = ##f + \override LyricExtender.force-extender = ##f #}) + balloonGrobText = #(define-music-function (grob-name offset text) (symbol? number-pair? markup?) @@ -394,7 +417,13 @@ displayScheme = (display-scheme-music expr port)) expr) - +earlyExtender = +#(define-music-function (num) (number?) + (_i "Generate a lyric extender that starts @var{num} +staffspaces left from the associated note instead of +a lyrics syllable.") + #{ \forceExtender + \lyricmode { \tweak self-alignment-X #num \markup\null } #}) endSpanners = #(define-music-function (music) (ly:music?) @@ -495,6 +524,17 @@ to the preceding note or rest as a post-event with @code{-}.") 'footnote-text footnote))) (once (propertyTweak 'footnote-music mus item)))) +forceExtender = +#(define-music-function () () + (_i "Force a lyric extender once.") + #{ \once \override LyricExtender.force-extender = ##t #}) + +forceExtenderTo = +#(define-music-function (num) (number?) + (_i "Force a lyric extender of length @var{num} staffspaces.") + #{ \forceExtender + \once \override Lyrics.LyricExtender.force-length = #num #}) + grace = #(def-grace-function startGraceMusic stopGraceMusic (_i "Insert @var{music} as grace notes.")) @@ -879,6 +919,12 @@ musicMap = (_i "Apply @var{proc} to @var{mus} and all of the music it contains.") (music-map proc mus)) +noExtender = +#(define-music-function () () + (_i "Suppress a lyric extender that would be automatically +generated.") + #{ \once \override LyricExtender.no-extender = ##t #}) + %% noPageBreak and noPageTurn are music functions (not music indentifiers), %% because music identifiers are not allowed at top-level. noPageBreak = @@ -1680,6 +1726,12 @@ shiftDurations = (shift-duration-log arg dur dots)) +shortenExtender = +#(define-music-function (num) (number?) + (_i "Stop the next lyric extender @var{num} staffspaces +left from the normal endpoint.") + #{ \once \override LyricExtender.right-padding = #num #}) + single = #(define-music-function (overrides music) (ly:music? ly:music?) diff --git a/scm/define-grob-properties.scm b/scm/define-grob-properties.scm index c234792dd2..41b6139313 100644 --- a/scm/define-grob-properties.scm +++ b/scm/define-grob-properties.scm @@ -185,6 +185,9 @@ that should be used for clef modifiers with various clefs") edges of beams?") (collapse-height ,ly:dimension? "Minimum height of system start delimiter. If equal or smaller, the bracket/@/brace/@/line is removed.") + (collapse-length ,ly:dimension? "An automatically generated +lyric extender is suppressed if it would be shorter than +this length.") (collision-interfaces ,list? "A list of interfaces for which automatic beam-collision resolution is run.") (collision-voice-only ,boolean? "Does automatic beam collsion apply @@ -330,9 +333,16 @@ allowed.") (footnote ,boolean? "Should this be a footnote or in-note?") (footnote-music ,ly:music? "Music creating a footnote.") (footnote-text ,markup? "A footnote for the grob.") + (force-extender ,boolean? "Force a lyric extender to be generated +if none would be generated otherwise and/or force it to be at least as +wide as indicated by property @code{length-limit-or-forced-length} unless +it would collide with the next syllable. Overrides property address@hidden") (force-hshift ,number? "This specifies a manual shift for notes in collisions. The unit is the note head width of the first voice note. This is used by @rinternals{note-collision-interface}.") + (force-length ,ly:dimension? "A forced lyric extender +is given this length if possible.") (forced-spacing ,number? "Spacing forced between grobs, used in various ligature engravers.") (fraction ,fraction? "Numerator and denominator of a time @@ -663,6 +673,8 @@ syllable following an extender).") (no-alignment ,boolean? "If set, don't place this grob in a @code{VerticalAlignment}; rather, place it using its own @code{Y-offset} callback.") + (no-extender ,boolean? "Inhibits generation of lyric extenders. +Can be overridden by property @code{force-extender}.") (no-ledgers ,boolean? "If set, don't draw ledger lines on this object.") (no-stem-extend ,boolean? "If set, notes with ledger lines do not diff --git a/scm/define-grobs.scm b/scm/define-grobs.scm index e36ea12bf2..714aff5b1a 100644 --- a/scm/define-grobs.scm +++ b/scm/define-grobs.scm @@ -1361,7 +1361,10 @@ (LyricExtender . ( - (minimum-length . 1.5) + (collapse-length . 1.5) + (force-extender . #f) + (force-length . 1.5) + (no-extender . #f) (stencil . ,ly:lyric-extender::print) (thickness . 0.8) ; line-thickness (Y-extent . (0 . 0)) diff --git a/scm/music-functions.scm b/scm/music-functions.scm index b1dc2f9c61..ce9198eb20 100644 --- a/scm/music-functions.scm +++ b/scm/music-functions.scm @@ -728,10 +728,16 @@ making it possible to @code{\\revert} to any previous value afterwards." 'articulation-type name properties)) +(define (add-extender! event) + (ly:music-set-property! event 'articulations + (append (ly:music-property event 'articulations) + (list (make-music 'ExtenderEvent)))) + event) + (define-public (make-lyric-event string duration) - (make-music 'LyricEvent - 'duration duration - 'text string)) + (if (and (string? string)(string=? " " string)) + (make-music 'LyricEvent 'duration duration 'text string) + (add-extender! (make-music 'LyricEvent 'duration duration 'text string)))) (define-safe-public (make-span-event type span-dir) (make-music type -- 2.11.0