summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Nieuwenhuizen <janneke@gnu.org>2011-03-04 21:06:18 +0100
committerJan Nieuwenhuizen <janneke@gnu.org>2011-03-04 21:30:17 +0100
commitb02ea152073d11899bb17eecabe4d47a6009756d (patch)
tree3e42b88cc4a9a5f2e11bd8533ebf0176e6435c1c
parent8bac2a503958d9b34e9e061357139d13a7137f7c (diff)
Completion_rest_engraver: new engraver.
-rw-r--r--input/regression/completion-heads-factor.ly1
-rw-r--r--input/regression/completion-rest.ly26
-rw-r--r--lily/completion-note-heads-engraver.cc44
-rw-r--r--lily/completion-rest-engraver.cc250
4 files changed, 300 insertions, 21 deletions
diff --git a/input/regression/completion-heads-factor.ly b/input/regression/completion-heads-factor.ly
index a065a37e0b..f4955f5a3a 100644
--- a/input/regression/completion-heads-factor.ly
+++ b/input/regression/completion-heads-factor.ly
@@ -22,4 +22,5 @@ notes with a duration factor still keep their requested appearance.
c\breve |
c1*2 |
c2*4 |
+ c8*20
}
diff --git a/input/regression/completion-rest.ly b/input/regression/completion-rest.ly
new file mode 100644
index 0000000000..7ac0ac8bb5
--- /dev/null
+++ b/input/regression/completion-rest.ly
@@ -0,0 +1,26 @@
+\version "2.13.53"
+
+\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.
+
+"
+}
+
+#(set-paper-size "a6")
+
+\layout { ragged-right= ##t }
+
+
+\new Voice \with {
+ \remove "Rest_engraver"
+ \consists "Completion_rest_engraver"
+} \relative c'{
+
+ r\breve |
+ r1*2 |
+ r2*4 |
+ r8*20
+}
diff --git a/lily/completion-note-heads-engraver.cc b/lily/completion-note-heads-engraver.cc
index 59b47ab525..08bbcb47e8 100644
--- a/lily/completion-note-heads-engraver.cc
+++ b/lily/completion-note-heads-engraver.cc
@@ -45,12 +45,13 @@ struct Pending_tie
{
Moment when_;
Stream_event* tie_event_;
- Pending_tie() { tie_event_ = 0; }
+ Pending_tie () : tie_event_ (0) {}
};
-int compare(Pending_tie const &a, Pending_tie const &b)
+static int
+compare (Pending_tie const &a, Pending_tie const &b)
{
- return compare(a.when_, b.when_);
+ return compare (a.when_, b.when_);
}
@@ -73,20 +74,17 @@ class Completion_heads_engraver : public Engraver
{
vector<Item*> notes_;
vector<Item*> prev_notes_;
-
// Must remember notes for explicit ties.
vector<Item*> tie_note_candidates_;
vector<Stream_event*> tie_note_candidate_events_;
vector<Grob*> ties_;
PQueue<Pending_tie> pending_ties_;
vector<Stream_event*> note_events_;
-
Stream_event *current_tie_event_;
Moment note_end_mom_;
bool is_first_;
Rational left_to_do_;
Rational do_nothing_until_;
-
Rational factor_;
Moment next_barline_moment ();
@@ -194,7 +192,10 @@ Completion_heads_engraver::process_music ()
note that note_dur may be strictly less than left_to_do_
(say, if left_to_do_ == 5/8)
*/
- note_dur = Duration (left_to_do_ / factor_, false).compressed (factor_);
+ 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_);
}
else
{
@@ -205,7 +206,12 @@ Completion_heads_engraver::process_music ()
}
Moment nb = next_barline_moment ();
if (nb.main_part_ && nb < note_dur.get_length ())
- note_dur = Duration (nb.main_part_ / factor_, false).compressed (factor_);
+ {
+ if (factor_.denominator () == 1 && factor_ > Rational (1, 1))
+ note_dur = Duration (nb.main_part_, false);
+ else
+ note_dur = Duration (nb.main_part_ / factor_, false).compressed (factor_);
+ }
do_nothing_until_ = now.main_part_ + note_dur.get_length ();
@@ -218,14 +224,10 @@ Completion_heads_engraver::process_music ()
event = event->clone ();
SCM pits = note_events_[i]->get_property ("pitch");
- Duration appearance = note_dur;
- if (factor_.denominator () == 1 && factor_ > Rational (1, 1))
- appearance = Duration (note_dur.get_length (), false);
-
event->set_property ("pitch", pits);
- event->set_property ("duration", appearance.smobbed_copy ());
- event->set_property ("length", Moment (appearance.get_length ()).smobbed_copy ());
- event->set_property ("duration-log", scm_from_int (appearance.duration_log ()));
+ event->set_property ("duration", note_dur.smobbed_copy ());
+ event->set_property ("length", Moment (note_dur.get_length ()).smobbed_copy ());
+ event->set_property ("duration-log", scm_from_int (note_dur.duration_log ()));
Item *note = make_note_head (event);
if (need_clone)
@@ -311,18 +313,18 @@ ADD_TRANSLATOR (Completion_heads_engraver,
/* doc */
"This engraver replaces @code{Note_heads_engraver}. It plays"
" some trickery to break long notes and automatically tie them"
- " into the next measure.",
-
+ " into the next measure."
+ ,
/* create */
"NoteHead "
"Dots "
- "Tie ",
-
+ "Tie "
+ ,
/* read */
"middleCPosition "
"measurePosition "
- "measureLength ",
-
+ "measureLength "
+ ,
/* write */
"completionBusy "
);
diff --git a/lily/completion-rest-engraver.cc b/lily/completion-rest-engraver.cc
new file mode 100644
index 0000000000..d1eca99e1c
--- /dev/null
+++ b/lily/completion-rest-engraver.cc
@@ -0,0 +1,250 @@
+/*
+ This file is part of LilyPond, the GNU music typesetter.
+
+ Copyright (C) 1997--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
+ Jan Nieuwenhuizen <janneke@gnu.org>
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <cctype>
+using namespace std;
+
+#include "dot-column.hh"
+#include "dots.hh"
+#include "duration.hh"
+#include "global-context.hh"
+#include "item.hh"
+#include "output-def.hh"
+#include "pitch.hh"
+#include "pqueue.hh"
+#include "rhythmic-head.hh"
+#include "score-engraver.hh"
+#include "spanner.hh"
+#include "staff-symbol-referencer.hh"
+#include "stream-event.hh"
+#include "tie.hh"
+#include "warn.hh"
+
+#include "translator.icc"
+
+/*
+ How does this work?
+
+ When we catch the rest, we predict the end of the rest. We keep the
+ events living until we reach the predicted end-time.
+
+ Every time process_music () is called and there are rest events, we
+ figure out how long the rest to typeset should be. It should be no
+ longer than what's specified, than what is left to do and it should
+ not cross barlines.
+
+ We copy the events into scratch rest events, to make sure that we get
+ all durations exactly right.
+*/
+
+class Completion_rest_engraver : public Engraver
+{
+ vector<Item*> rests_;
+ vector<Item*> prev_rests_;
+ vector<Stream_event*> rest_events_;
+ Moment rest_end_mom_;
+ bool is_first_;
+ Rational left_to_do_;
+ Rational do_nothing_until_;
+ Rational factor_;
+
+ Moment next_barline_moment ();
+ Item *make_rest (Stream_event*);
+
+public:
+ TRANSLATOR_DECLARATIONS (Completion_rest_engraver);
+
+protected:
+ virtual void initialize ();
+ void start_translation_timestep ();
+ void process_music ();
+ void stop_translation_timestep ();
+ DECLARE_TRANSLATOR_LISTENER (rest);
+};
+
+void
+Completion_rest_engraver::initialize ()
+{
+ is_first_ = false;
+}
+
+IMPLEMENT_TRANSLATOR_LISTENER (Completion_rest_engraver, rest);
+void
+Completion_rest_engraver::listen_rest (Stream_event *ev)
+{
+ rest_events_.push_back (ev);
+
+ is_first_ = true;
+ Moment now = now_mom ();
+ Moment musiclen = get_event_length (ev, now);
+
+ rest_end_mom_ = max (rest_end_mom_, (now + musiclen));
+ do_nothing_until_ = Rational (0, 0);
+}
+
+/*
+ The duration _until_ the next barline.
+*/
+Moment
+Completion_rest_engraver::next_barline_moment ()
+{
+ Moment *e = unsmob_moment (get_property ("measurePosition"));
+ Moment *l = unsmob_moment (get_property ("measureLength"));
+ if (!e || !l || !to_boolean (get_property ("timing")))
+ {
+ return Moment (0, 0);
+ }
+
+ return (*l - *e);
+}
+
+Item*
+Completion_rest_engraver::make_rest (Stream_event *ev)
+{
+ Item *rest = make_item ("Rest", ev->self_scm ());
+ if (Pitch *p = unsmob_pitch (ev->get_property ("pitch")))
+ {
+ int pos = p->steps ();
+ SCM c0 = get_property ("middleCPosition");
+ if (scm_is_number (c0))
+ pos += scm_to_int (c0);
+ rest->set_property ("staff-position", scm_from_int (pos));
+ }
+
+ return rest;
+}
+
+void
+Completion_rest_engraver::process_music ()
+{
+ if (!is_first_ && !left_to_do_)
+ return;
+
+ is_first_ = false;
+
+ Moment now = now_mom ();
+ if (do_nothing_until_ > now.main_part_)
+ return;
+
+ Duration rest_dur;
+ Duration appearance;
+ Duration *orig = 0;
+ if (left_to_do_)
+ {
+ /*
+ rest 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_);
+ appearance = Duration (left_to_do_, false);
+ }
+ else
+ {
+ orig = unsmob_duration (rest_events_[0]->get_property ("duration"));
+ rest_dur = *orig;
+ factor_ = rest_dur.factor ();
+ left_to_do_ = orig->get_length ();
+ }
+ Moment nb = next_barline_moment ();
+ if (nb.main_part_ && nb < rest_dur.get_length ())
+ {
+ if (factor_.denominator () == 1 && factor_ > Rational (1, 1))
+ rest_dur = Duration (nb.main_part_, false);
+ else
+ rest_dur = Duration (nb.main_part_ / factor_, false).compressed (factor_);
+ }
+
+ do_nothing_until_ = now.main_part_ + rest_dur.get_length ();
+
+ for (vsize i = 0; left_to_do_ && i < rest_events_.size (); i++)
+ {
+ bool need_clone = !orig || *orig != rest_dur;
+ Stream_event *event = rest_events_[i];
+
+ if (need_clone)
+ event = event->clone ();
+
+ SCM pits = rest_events_[i]->get_property ("pitch");
+ event->set_property ("pitch", pits);
+ event->set_property ("duration", rest_dur.smobbed_copy ());
+ event->set_property ("length", Moment (rest_dur.get_length ()).smobbed_copy ());
+ event->set_property ("duration-log", scm_from_int (rest_dur.duration_log ()));
+
+ Item *rest = make_rest (event);
+ if (need_clone)
+ event->unprotect ();
+ rests_.push_back (rest);
+ }
+
+ left_to_do_ -= rest_dur.get_length ();
+ if (left_to_do_)
+ get_global_context ()->add_moment_to_process (now.main_part_ + rest_dur.get_length());
+ /*
+ don't do complicated arithmetic with grace rests.
+ */
+ if (orig && now_mom ().grace_part_)
+ left_to_do_ = Rational (0, 0);
+}
+
+void
+Completion_rest_engraver::stop_translation_timestep ()
+{
+ if (rests_.size ())
+ prev_rests_ = rests_;
+ rests_.clear ();
+}
+
+void
+Completion_rest_engraver::start_translation_timestep ()
+{
+ Moment now = now_mom ();
+ if (rest_end_mom_.main_part_ <= now.main_part_)
+ {
+ rest_events_.clear ();
+ prev_rests_.clear ();
+ }
+ context ()->set_property ("restCompletionBusy",
+ ly_bool2scm (rest_events_.size ()));
+}
+
+Completion_rest_engraver::Completion_rest_engraver ()
+{
+}
+
+ADD_TRANSLATOR (Completion_rest_engraver,
+ /* doc */
+ "This engraver replaces @code{Rest_engraver}. It plays"
+ " some trickery to break long rests into the next measure."
+ ,
+ /* create */
+ "Rest "
+ "Dots "
+ ,
+ /* read */
+ "middleCPosition "
+ "measurePosition "
+ "measureLength "
+ ,
+ /* write */
+ "restCompletionBusy "
+ );