From b50e8ca24526b40d3ad2c382649679fe309132b7 Mon Sep 17 00:00:00 2001
From: Neil Puttock
Date: Sat, 14 Nov 2009 13:50:56 +0000
Subject: [PATCH] Add Completion_rests_engraver.
---
lily/completion-rests-engraver.cc | 282 +++++++++++++++++++++++++++++++++++++
1 files changed, 282 insertions(+), 0 deletions(-)
create mode 100644 lily/completion-rests-engraver.cc
diff --git a/lily/completion-rests-engraver.cc b/lily/completion-rests-engraver.cc
new file mode 100644
index 0000000..45a78d6
--- /dev/null
+++ b/lily/completion-rests-engraver.cc
@@ -0,0 +1,282 @@
+/*
+ completion-rests-engraver.cc -- Completion_rests_engraver
+
+ (c) 2009 Han-Wen Nienhuys
+*/
+
+#include "duration.hh"
+#include "global-context.hh"
+#include "output-def.hh"
+#include "paper-column.hh"
+#include "pitch.hh"
+#include "rhythmic-head.hh"
+#include "score-engraver.hh"
+#include "spanner.hh"
+#include "staff-symbol-referencer.hh"
+#include "stream-event.hh"
+#include "warn.hh"
+
+#include
+using namespace std;
+
+#include "translator.icc"
+
+/*
+ How does this work?
+
+ When we catch the note, we predict the end of the note. We keep the
+ events living until we reach the predicted end-time.
+
+ Every time process_music () is called and there are note events, we
+ figure out how long the note 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 note events, to make sure that we get
+ all durations exactly right.
+*/
+
+class Completion_rests_engraver : public Engraver
+{
+ vector rests_;
+ vector prev_rests_;
+
+ vector rest_events_;
+
+ Moment rest_end_mom_;
+ bool is_first_;
+ Rational left_to_do_;
+ Rational do_nothing_until_;
+
+ Moment next_barline_moment ();
+ Grob *make_rest (Stream_event *);
+
+ int start_measure_;
+
+
+public:
+ TRANSLATOR_DECLARATIONS (Completion_rests_engraver);
+
+protected:
+ virtual void initialize ();
+ void start_translation_timestep ();
+ void process_music ();
+ void stop_translation_timestep ();
+ DECLARE_TRANSLATOR_LISTENER (rest);
+};
+
+void
+Completion_rests_engraver::initialize ()
+{
+ is_first_ = false;
+ start_measure_ = 0;
+}
+
+IMPLEMENT_TRANSLATOR_LISTENER (Completion_rests_engraver, rest);
+void
+Completion_rests_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 bar line.
+*/
+Moment
+Completion_rests_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);
+ else
+ return (*l - *e);
+}
+
+Grob *
+Completion_rests_engraver::make_rest (Stream_event *ev)
+{
+ Grob *rest = 0;
+ if (*unsmob_moment (ev->get_property ("length"))
+ % *unsmob_moment (get_property ("measureLength")) == Moment (0, 0))
+ {
+ rest = make_spanner ("MultiMeasureRest", ev->self_scm ());
+ start_measure_ = scm_to_int (get_property ("internalBarNumber"));
+ }
+ else
+ {
+ rest = make_item ("Rest", ev->self_scm ());
+ Pitch *pit = unsmob_pitch (ev->get_property ("pitch"));
+
+ if (pit)
+ {
+ int pos = pit->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_rests_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 *orig = 0;
+ if (left_to_do_)
+ {
+ rest_dur = Duration (left_to_do_, false);
+ }
+ else
+ {
+ orig = unsmob_duration (rest_events_[0]->get_property ("duration"));
+ rest_dur = *orig;
+ }
+ Moment nb = next_barline_moment ();
+
+ if (*unsmob_moment (get_property ("measurePosition")) == Moment (0, 0)
+ && to_boolean (get_property ("skipBars"))
+ && (rest_dur.get_length () %
+ *unsmob_moment (get_property ("measureLength")) == Moment (0, 0)))
+ {
+ cout << "\nrest_dur " << rest_dur.to_string ();
+ cout << "\nfull-bar";
+ do_nothing_until_ = now.main_part_ + rest_dur.get_length ();
+ }
+ else if (nb.main_part_ && nb < rest_dur.get_length ())
+ {
+ rest_dur = Duration (nb.main_part_, false);
+
+ do_nothing_until_ = now.main_part_ + rest_dur.get_length ();
+ }
+
+ if (orig)
+ left_to_do_ = orig->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");
+
+ if (unsmob_pitch (pits))
+ event->set_property ("pitch", pits);
+ event->set_property ("duration", rest_dur.smobbed_copy ());
+ event->set_property ("length",
+ Moment (rest_dur.get_length ()).smobbed_copy ());
+
+ Grob *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 notes.
+ */
+ if (orig && now_mom ().grace_part_)
+ left_to_do_ = Rational (0, 0);
+}
+
+void
+Completion_rests_engraver::stop_translation_timestep ()
+{
+ if (prev_rests_.size ())
+ {
+ Grob *cmc = unsmob_grob (get_property ("currentCommandColumn"));
+ for (vsize i = 0; i < prev_rests_.size (); i++)
+ {
+ if (dynamic_cast (prev_rests_[i]))
+ add_bound_item ((Spanner *) prev_rests_[i], cmc);
+
+ }
+ prev_rests_.clear ();
+ }
+
+ if (rests_.size ())
+ {
+ Grob *cmc = unsmob_grob (get_property ("currentCommandColumn"));
+ for (vsize i = 0; i < rests_.size (); i++)
+ {
+ if (dynamic_cast (rests_[i]))
+ add_bound_item ((Spanner *) rests_[i], cmc);
+
+ }
+
+ prev_rests_ = rests_;
+ rests_.clear ();
+ }
+}
+
+void
+Completion_rests_engraver::start_translation_timestep ()
+{
+
+ Moment now = now_mom ();
+ if (rest_end_mom_.main_part_ <= now.main_part_)
+ rest_events_.clear ();
+
+ if (prev_rests_.size ())
+ {
+ for (vsize i = 0; i < prev_rests_.size (); i++)
+ {
+ if (dynamic_cast (prev_rests_[i]))
+ {
+ int cur = scm_to_int (get_property ("internalBarNumber"));
+ int num = cur - start_measure_;
+ prev_rests_[i]->set_property ("measure-count", scm_from_int (num));
+ }
+
+ }
+ }
+ context ()->set_property ("completionBusy",
+ ly_bool2scm (rest_events_.size ()));
+}
+
+Completion_rests_engraver::Completion_rests_engraver ()
+{
+}
+
+ADD_TRANSLATOR (Completion_rests_engraver,
+ /* doc */
+ "This engraver replaces @code{Rest_engraver}. It plays"
+ " some trickery to break long rests at bar lines.",
+
+ /* create */
+ "MultiMeasureRest "
+ "Rest ",
+
+ /* read */
+ "middleCPosition "
+ "measurePosition "
+ "measureLength ",
+
+ /* write */
+ "completionBusy "
+ );
--
1.6.3.3