diff options
author | Keith OHara <k-ohara5a5a@oco.net> | 2013-11-29 18:01:21 -0800 |
---|---|---|
committer | Keith OHara <k-ohara5a5a@oco.net> | 2013-12-06 22:56:42 -0800 |
commit | 9f909143a605a677787915e5dcce5dbc48f2211c (patch) | |
tree | 7fae7eaf404e1e3e66a90f62a0a7a4cdf0334d61 | |
parent | efe4591faf2f5c9353c742d9fe696476f042f2ef (diff) |
Completion_*_engraver: add means to preserve scale factor; issue 3650
-rw-r--r-- | Documentation/notation/rhythms.itely | 43 | ||||
-rw-r--r-- | input/regression/completion-heads-factor.ly | 16 | ||||
-rw-r--r-- | input/regression/completion-rest.ly | 14 | ||||
-rw-r--r-- | lily/completion-note-heads-engraver.cc | 18 | ||||
-rw-r--r-- | lily/completion-rest-engraver.cc | 18 | ||||
-rw-r--r-- | ly/engraver-init.ly | 2 | ||||
-rw-r--r-- | scm/c++.scm | 5 | ||||
-rw-r--r-- | scm/define-context-properties.scm | 23 | ||||
-rw-r--r-- | scm/lily-library.scm | 10 | ||||
-rw-r--r-- | scm/lily.scm | 1 |
10 files changed, 110 insertions, 40 deletions
diff --git a/Documentation/notation/rhythms.itely b/Documentation/notation/rhythms.itely index 2847d627c1..f935956922 100644 --- a/Documentation/notation/rhythms.itely +++ b/Documentation/notation/rhythms.itely @@ -1810,6 +1810,37 @@ inserts ties for notes. One of its uses is to debug complex scores: if the measures are not entirely filled, then the ties show exactly how much each measure is off. +The property @code{completionUnit} sets a preferred duration for +the split notes. + +@lilypond[quote,verbatim,relative=2] +\new Voice \with { + \remove "Note_heads_engraver" + \consists "Completion_heads_engraver" +} { + \time 9/8 g\breve. d4. \bar "||" + \set completionUnit = #(ly:make-moment 3 8) + g\breve. d4. +} +@end lilypond + +These engravers split notes with scaled duration, such as those in tuplets, +into notes with the same scale-factor as in the input note. + +@lilypond[quote,verbatim,relative=2] +\new Voice \with { + \remove "Note_heads_engraver" + \consists "Completion_heads_engraver" +} { + \time 2/4 r4 + \tuplet 3/2 {g4 a b} + \scaleDurations 2/3 {g a b} + g4*2/3 a b + \tuplet 3/2 {g4 a b} + r4 +} +@end lilypond + @seealso Music Glossary: @rglos{tie} @@ -1829,12 +1860,12 @@ Internals Reference: @rinternals{Forbid_line_break_engraver}. @knownissues -Not all durations (especially those containing tuplets) can be -represented exactly with normal notes and dots, but the -@code{Completion_heads_engraver} will not insert tuplets. - -The @code{Completion_heads_engraver} only affects notes; it does not -split rests. +For consistency with previous behavior, notes and rests with +duration longer than a measure, such as @code{c1*2}, are split into +notes without any scale factor, @code{@{ c1 c1 @}}. The property +@code{completionFactor} controls this behavior, and setting it to +@code{#f} cause split notes and rest to have the scale factor +of the input durations. @node Showing melody rhythms diff --git a/input/regression/completion-heads-factor.ly b/input/regression/completion-heads-factor.ly index 79510e1484..cc29f58d4e 100644 --- a/input/regression/completion-heads-factor.ly +++ b/input/regression/completion-heads-factor.ly @@ -1,12 +1,12 @@ -\version "2.16.0" +\version "2.19.0" \header{ texidoc=" If the @code{Note_heads_engraver} is replaced by the @code{Completion_heads_engraver}, -notes with a duration factor still keep their requested appearance. - -" +long notes, longer than @code{measureLength}, are split into un-scaled notes, +even if the original note used a scale-factor. +@code{completionFactor} controls this behavior." } \layout { ragged-right= ##t } @@ -20,5 +20,11 @@ notes with a duration factor still keep their requested appearance. c\breve | c1*2 | c2*4 | - c8*20 + c8*20 r2 \break + \tuplet 3/2 { d1 d d } + % \breve*2/3 is longer than a measure, but we want a tuplet, not repeats. + \set completionFactor = ##f + \tuplet 3/2 { e\breve e e } + \set completionFactor = #2/3 + \tuplet 3/2 { e\breve e e } } diff --git a/input/regression/completion-rest.ly b/input/regression/completion-rest.ly index e39e17aff8..365b7372e2 100644 --- a/input/regression/completion-rest.ly +++ b/input/regression/completion-rest.ly @@ -1,12 +1,12 @@ -\version "2.16.0" +\version "2.19.0" \header{ texidoc=" If the @code{Rest_engraver} is replaced by the @code{Completion_rest_engraver}, -rests with a duration factor still keep their requested appearance. - -" +long rests, longer than @code{measureLength}, are split into +un-scaled rests, even if the original duration used a scale-factor. +@code{completionFactor} controls this behavior." } \layout { ragged-right= ##t } @@ -20,5 +20,9 @@ rests with a duration factor still keep their requested appearance. r\breve | r1*2 | r2*4 | - r8*20 + r8*20 r2 \break + \bar "||" \time 2/4 + r\breve.*2/3 + \set completionFactor = #1/2 + r\breve.*2/3^"explicity request r1*1/2 rests" } diff --git a/lily/completion-note-heads-engraver.cc b/lily/completion-note-heads-engraver.cc index 52a7d6c0bd..bddca70abb 100644 --- a/lily/completion-note-heads-engraver.cc +++ b/lily/completion-note-heads-engraver.cc @@ -188,25 +188,24 @@ Completion_heads_engraver::process_music () note that note_dur may be strictly less than left_to_do_ (say, if left_to_do_ == 5/8) */ - if (factor_.denominator () == 1 && factor_ > Rational (1, 1)) - note_dur = Duration (left_to_do_, false); - else - note_dur = Duration (left_to_do_ / factor_, false).compressed (factor_); + note_dur = Duration (left_to_do_ / factor_, false).compressed (factor_); } else { orig = unsmob_duration (note_events_[0]->get_property ("duration")); note_dur = *orig; - factor_ = note_dur.factor (); + SCM factor = get_property ("completionFactor"); + if (ly_is_procedure (factor)) + factor = scm_call_2 (factor, + context ()->self_scm (), + note_dur.smobbed_copy ()); + factor_ = robust_scm2rational (factor, note_dur.factor ()); left_to_do_ = orig->get_length (); } Moment nb = next_moment (note_dur.get_length ()); if (nb.main_part_ && nb < note_dur.get_length ()) { - if (factor_.denominator () == 1 && factor_.numerator () > 1) - note_dur = Duration (nb.main_part_, false); - else - note_dur = Duration (nb.main_part_ / factor_, false).compressed (factor_); + note_dur = Duration (nb.main_part_ / factor_, false).compressed (factor_); } do_nothing_until_ = now.main_part_ + note_dur.get_length (); @@ -314,6 +313,7 @@ ADD_TRANSLATOR (Completion_heads_engraver, "TieColumn ", /* read */ + "completionFactor " "completionUnit " "measureLength " "measurePosition " diff --git a/lily/completion-rest-engraver.cc b/lily/completion-rest-engraver.cc index 61255226ea..aeb6673f37 100644 --- a/lily/completion-rest-engraver.cc +++ b/lily/completion-rest-engraver.cc @@ -184,25 +184,24 @@ Completion_rest_engraver::process_music () note that rest_dur may be strictly less than left_to_do_ (say, if left_to_do_ == 5/8) */ - if (factor_.denominator () == 1 && factor_ > Rational (1, 1)) - rest_dur = Duration (left_to_do_, false); - else - rest_dur = Duration (left_to_do_ / factor_, false).compressed (factor_); + rest_dur = Duration (left_to_do_ / factor_, false).compressed (factor_); } else { orig = unsmob_duration (rest_events_[0]->get_property ("duration")); rest_dur = *orig; - factor_ = rest_dur.factor (); + SCM factor = get_property ("completionFactor"); + if (ly_is_procedure (factor)) + factor = scm_call_2 (factor, + context ()->self_scm (), + rest_dur.smobbed_copy ()); + factor_ = robust_scm2rational (factor, rest_dur.factor()); left_to_do_ = orig->get_length (); } Moment nb = next_moment (rest_dur.get_length ()); if (nb.main_part_ && nb < rest_dur.get_length ()) { - if (factor_.denominator () == 1 && factor_.numerator () > 1) - rest_dur = Duration (nb.main_part_, false); - else - rest_dur = Duration (nb.main_part_ / factor_, false).compressed (factor_); + rest_dur = Duration (nb.main_part_ / factor_, false).compressed (factor_); } do_nothing_until_ = now.main_part_ + rest_dur.get_length (); @@ -268,6 +267,7 @@ ADD_TRANSLATOR (Completion_rest_engraver, "Rest ", /* read */ + "completionFactor " "completionUnit " "middleCPosition " "measurePosition " diff --git a/ly/engraver-init.ly b/ly/engraver-init.ly index ff777875c6..dfaa47e12f 100644 --- a/ly/engraver-init.ly +++ b/ly/engraver-init.ly @@ -634,6 +634,8 @@ automatically when an output definition (a @code{\\score} or autoBeaming = ##t autoBeamCheck = #default-auto-beam-check + completionFactor = #unity-if-multimeasure + scriptDefinitions = #default-script-alist pedalSustainStrings = #'("Ped." "*Ped." "*") diff --git a/scm/c++.scm b/scm/c++.scm index a131e7f342..ec54ed28c9 100644 --- a/scm/c++.scm +++ b/scm/c++.scm @@ -33,6 +33,11 @@ (and (pair? x) (index? (car x)) (index? (cdr x)))) +(define-public (rational-or-procedure? x) + (or + (and (rational? x) (exact? x)) + (procedure? x))) + (define-public (number-or-grob? x) (or (ly:grob? x) (number? x))) diff --git a/scm/define-context-properties.scm b/scm/define-context-properties.scm index 073c714097..69262b061d 100644 --- a/scm/define-context-properties.scm +++ b/scm/define-context-properties.scm @@ -218,6 +218,17 @@ and @samp{bracketed}.") symbol go, measured in half staff spaces from the center of the staff.") (completionBusy ,boolean? "Whether a completion-note head is playing.") + (completionFactor ,rational-or-procedure? +"When @code{Completion_heads_engraver} and +@code{Completion_rest_engraver} need to split a note or rest with a +scaled duration, such as @code{c2*3}, this specifies the scale factor +to use for the newly-split notes and rests created by the engraver. + +If @code{#f}, the completion engraver uses the scale-factor of +each duration being split. + +If set to a callback procedure, that procedure is called with the +context of the completion engraver, and the duration to be split.") (completionUnit ,ly:moment? "Sub-bar unit of completion.") (connectArpeggios ,boolean? "If set, connect arpeggios across piano staff.") @@ -447,12 +458,12 @@ associated with the current context. Ranges from@tie{}@w{-1} to@tie{}1, where the values@tie{}@w{-1} (@code{#LEFT}),@tie{}0 (@code{#CENTER}) and@tie{}1 (@code{#RIGHT}) correspond to hard left, center, and hard right, respectively.") - (midiReverbLevel ,number? "Reverb effect level for the MIDI channel -associated with the current context. Ranges from 0 to@tie{}1 -(0=off,@tie{}1=full effect).") - (midiChorusLevel ,number? "Chorus effect level for the MIDI channel -associated with the current context. Ranges from 0 to@tie{}1 -(0=off,@tie{}1=full effect).") + (midiReverbLevel ,number? "Reverb effect level for the MIDI +channel associated with the current context. Ranges from 0 +to@tie{}1 (0=off,@tie{}1=full effect).") + (midiChorusLevel ,number? "Chorus effect level for the MIDI +channel associated with the current context. Ranges from 0 +to@tie{}1 (0=off,@tie{}1=full effect).") (minimumFret ,number? "The tablature auto string-selecting mechanism selects the highest string with a fret at least @code{minimumFret}.") diff --git a/scm/lily-library.scm b/scm/lily-library.scm index 79134ca7d4..570c740775 100644 --- a/scm/lily-library.scm +++ b/scm/lily-library.scm @@ -121,6 +121,16 @@ non-visual scale factor 1." duration (base note length and dot count), as a number of whole notes." (duration-length (duration-visual dur))) +(define-public (unity-if-multimeasure context dur) + "Given a context and a duration, return @code{1} if the duration is +longer than the @code{measureLength} in that context, and @code{#f} otherwise. +This supports historic use of @code{Completion_heads_engraver} to split +@code{c1*3} into three whole notes." + (if (ly:moment<? (ly:context-property context 'measureLength) + (ly:duration-length dur)) + 1 + #f)) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; arithmetic (define-public (average x . lst) diff --git a/scm/lily.scm b/scm/lily.scm index 9e47fc0df7..1d69355e72 100644 --- a/scm/lily.scm +++ b/scm/lily.scm @@ -663,6 +663,7 @@ messages into errors.") (,number-or-string? . "number or string") (,number-pair? . "pair of numbers") (,number-pair-list? . "list of number pairs") + (,rational-or-procedure? . "an exact rational or procedure") (,rhythmic-location? . "rhythmic location") (,scheme? . "any type") (,string-or-pair? . "string or pair") |