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