]> git.donarmstrong.com Git - lilypond.git/commitdiff
Completion_*_engraver: add means to preserve scale factor; issue 3650
authorKeith OHara <k-ohara5a5a@oco.net>
Sat, 30 Nov 2013 02:01:21 +0000 (18:01 -0800)
committerKeith OHara <k-ohara5a5a@oco.net>
Sat, 7 Dec 2013 06:56:42 +0000 (22:56 -0800)
Documentation/notation/rhythms.itely
input/regression/completion-heads-factor.ly
input/regression/completion-rest.ly
lily/completion-note-heads-engraver.cc
lily/completion-rest-engraver.cc
ly/engraver-init.ly
scm/c++.scm
scm/define-context-properties.scm
scm/lily-library.scm
scm/lily.scm

index 2847d627c1c7dfa30d05ef2542d9a2868b1d0f37..f935956922b947c7623b89a2d5a2001104146400 100644 (file)
@@ -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
index 79510e1484513a4c2e03097277aa8056cb9de885..cc29f58d4eb1a96f1249907fe6188d66c69ef25a 100644 (file)
@@ -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 }
 }
index e39e17aff898419e01a3bd6726af65430e79b3db..365b7372e2a6a1ae1f86367664fc96e0625bdd0f 100644 (file)
@@ -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"
 }
index 52a7d6c0bd4d0a64db0fb6610a889c313f9c0781..bddca70abbd3faa3175969eaa769363e067c6ec5 100644 (file)
@@ -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 "
index 61255226eac4eb53b385f1eadbde873507199bdd..aeb6673f3725eb9f5522ae460718bc581a181e94 100644 (file)
@@ -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 "
index ff777875c646962d020341d35778db291dda23e1..dfaa47e12faf136a7a5e8ac63e345e3b9858141c 100644 (file)
@@ -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." "*")
index a131e7f3428d95870bcc3ec05b1643fe7d899552..ec54ed28c9ebb9c57542154356958d14acd30fce 100644 (file)
   (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)))
 
index 073c71409743cc7737c27cf440a5f8619663ccc4..69262b061dbb165b8307ec089f9d5ee203899005 100644 (file)
@@ -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}.")
index 79134ca7d46caf1f16c80478755aa5fa97f71472..570c7407750e75d16d693628950d127b2f465158 100644 (file)
@@ -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)
index 9e47fc0df7548dcdc55d049f7afbfa7997a882c0..1d69355e72a567cdf9d48cd58da2ffc081e94445 100644 (file)
@@ -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")