summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeith OHara <k-ohara5a5a@oco.net>2013-11-29 18:01:21 -0800
committerKeith OHara <k-ohara5a5a@oco.net>2013-12-06 22:56:42 -0800
commit9f909143a605a677787915e5dcce5dbc48f2211c (patch)
tree7fae7eaf404e1e3e66a90f62a0a7a4cdf0334d61
parentefe4591faf2f5c9353c742d9fe696476f042f2ef (diff)
Completion_*_engraver: add means to preserve scale factor; issue 3650
-rw-r--r--Documentation/notation/rhythms.itely43
-rw-r--r--input/regression/completion-heads-factor.ly16
-rw-r--r--input/regression/completion-rest.ly14
-rw-r--r--lily/completion-note-heads-engraver.cc18
-rw-r--r--lily/completion-rest-engraver.cc18
-rw-r--r--ly/engraver-init.ly2
-rw-r--r--scm/c++.scm5
-rw-r--r--scm/define-context-properties.scm23
-rw-r--r--scm/lily-library.scm10
-rw-r--r--scm/lily.scm1
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")