]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/completion-note-heads-engraver.cc
Run `make grand-replace'.
[lilypond.git] / lily / completion-note-heads-engraver.cc
index 66e31aaf52da8b541c31160f86930a0f25154dc8..1522dad8b3d4ada7bdb589003b6ae95dddd74ab4 100644 (file)
@@ -1,7 +1,7 @@
 /*
   completion-note-heads-engraver.cc -- Completion_heads_engraver
 
-  (c) 1997--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
+  (c) 1997--2008 Han-Wen Nienhuys <hanwen@xs4all.nl>
 */
 
 #include <cctype>
@@ -14,6 +14,7 @@ using namespace std;
 #include "item.hh"
 #include "output-def.hh"
 #include "pitch.hh"
+#include "pqueue.hh"
 #include "rhythmic-head.hh"
 #include "score-engraver.hh"
 #include "spanner.hh"
@@ -27,6 +28,18 @@ using namespace std;
 /*
   TODO: make matching rest engraver.
 */
+struct Pending_tie
+{
+  Moment when_;
+  Stream_event* tie_event_;
+  Pending_tie() { tie_event_ = 0; }
+};
+
+int compare(Pending_tie const &a, Pending_tie const &b)
+{
+  return compare(a.when_, b.when_);
+}
+
 
 /*
   How does this work?
@@ -47,35 +60,41 @@ class Completion_heads_engraver : public Engraver
 {
   vector<Item*> notes_;
   vector<Item*> prev_notes_;
-  vector<Grob*> ties_;
 
-  vector<Item*> dots_;
+  // 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_;
-  vector<Stream_event*> scratch_note_events_;
 
+  Stream_event *current_tie_event_;
   Moment note_end_mom_;
   bool is_first_;
   Rational left_to_do_;
   Rational do_nothing_until_;
 
   Moment next_barline_moment ();
-  Duration find_nearest_duration (Rational length);
+  Item *make_note_head (Stream_event*);
 
 public:
   TRANSLATOR_DECLARATIONS (Completion_heads_engraver);
 
 protected:
   virtual void initialize ();
+  void make_tie (Grob *, Grob *);
   void start_translation_timestep ();
   void process_music ();
   void stop_translation_timestep ();
   DECLARE_TRANSLATOR_LISTENER (note);
+  DECLARE_TRANSLATOR_LISTENER (tie);
 };
 
 void
 Completion_heads_engraver::initialize ()
 {
   is_first_ = false;
+  current_tie_event_ = 0;
 }
 
 IMPLEMENT_TRANSLATOR_LISTENER (Completion_heads_engraver, note);
@@ -85,19 +104,21 @@ Completion_heads_engraver::listen_note (Stream_event *ev)
   note_events_.push_back (ev);
   
   is_first_ = true;
-  Moment musiclen = get_event_length (ev);
   Moment now = now_mom ();
-
-  if (now_mom ().grace_part_)
-    {
-      musiclen.grace_part_ = musiclen.main_part_;
-      musiclen.main_part_ = Rational (0, 1);
-    }
+  Moment musiclen = get_event_length (ev, now);
 
   note_end_mom_ = max (note_end_mom_, (now + musiclen));
   do_nothing_until_ = Rational (0, 0);
 }
 
+IMPLEMENT_TRANSLATOR_LISTENER (Completion_heads_engraver, tie);
+void
+Completion_heads_engraver::listen_tie (Stream_event *ev)
+{
+  is_first_ = true;
+  current_tie_event_ = ev;
+}
+
 /*
   The duration _until_ the next barline.
 */
@@ -106,46 +127,28 @@ Completion_heads_engraver::next_barline_moment ()
 {
   Moment *e = unsmob_moment (get_property ("measurePosition"));
   Moment *l = unsmob_moment (get_property ("measureLength"));
-  if (!e || !l)
+  if (!e || !l || !to_boolean (get_property ("timing")))
     {
-      programming_error ("no timing props set?");
-      return Moment (1, 1);
+      return Moment (0, 0);
     }
 
   return (*l - *e);
 }
 
-Duration
-Completion_heads_engraver::find_nearest_duration (Rational length)
+Item*
+Completion_heads_engraver::make_note_head (Stream_event *ev)
 {
-  int log_limit = 6;
+  Item *note = make_item ("NoteHead", ev->self_scm ());
+  Pitch *pit = unsmob_pitch (ev->get_property ("pitch"));
 
-  Duration d (0, 0);
+  int pos = pit->steps ();
+  SCM c0 = get_property ("middleCPosition");
+  if (scm_is_number (c0))
+    pos += scm_to_int (c0);
 
-  /*
-    this could surely be done more efficient. Left to the reader as an
-    excercise.  */
-  while (d.get_length () > length && d.duration_log () < log_limit)
-    {
-      if (d.dot_count ())
-       {
-         d = Duration (d.duration_log (), d.dot_count ()- 1);
-         continue;
-       }
-      else
-       d = Duration (d.duration_log () + 1, 2);
-    }
-
-  if (d.duration_log () >= log_limit)
-    {
-      // junk the dots.
-      d = Duration (d.duration_log (), 0);
-
-      // scale up.
-      d = d.compressed (length / d.get_length ());
-    }
+  note->set_property ("staff-position", scm_from_int (pos));
 
-  return d;
+  return note;
 }
 
 void
@@ -154,6 +157,14 @@ Completion_heads_engraver::process_music ()
   if (!is_first_ && !left_to_do_)
     return;
 
+  if (current_tie_event_)
+    {
+      Pending_tie pending;
+      pending.when_ = note_end_mom_;
+      pending.tie_event_ = current_tie_event_;
+      pending_ties_.insert (pending);
+    }
+  
   is_first_ = false;
 
   Moment now = now_mom ();
@@ -163,106 +174,83 @@ Completion_heads_engraver::process_music ()
   Duration note_dur;
   Duration *orig = 0;
   if (left_to_do_)
-    note_dur = find_nearest_duration (left_to_do_);
+    note_dur = Duration (left_to_do_, false);
   else
     {
       orig = unsmob_duration (note_events_[0]->get_property ("duration"));
       note_dur = *orig;
     }
   Moment nb = next_barline_moment ();
-  if (nb < note_dur.get_length ())
+  if (nb.main_part_ && nb < note_dur.get_length ())
     {
-      note_dur = find_nearest_duration (nb.main_part_);
+      note_dur = Duration (nb.main_part_, false);
 
-      Moment next = now;
-      next.main_part_ += note_dur.get_length ();
-
-      get_global_context ()->add_moment_to_process (next);
-      do_nothing_until_ = next.main_part_;
+      do_nothing_until_ = now.main_part_ + note_dur.get_length ();
     }
 
   if (orig)
     left_to_do_ = orig->get_length ();
 
-  if (orig && note_dur.get_length () != orig->get_length ())
-    {
-      if (!scratch_note_events_.size ())
-       for (vsize i = 0; i < note_events_.size (); i++)
-         {
-           Stream_event *m = note_events_[i]->clone ();
-           scratch_note_events_.push_back (m);
-         }
-    }
-  else
-    {
-      for (vsize i = 0; i < note_events_.size (); i++)
-       {
-         Stream_event *c =  note_events_[i]->clone ();
-         scratch_note_events_.push_back (c);
-       }
-    }
-  
   for (vsize i = 0; left_to_do_ && i < note_events_.size (); i++)
     {
+      bool need_clone = !orig || *orig != note_dur;
       Stream_event *event = note_events_[i];
-      if (scratch_note_events_.size ())
-       {
-         event = scratch_note_events_[i];
-         SCM pits = note_events_[i]->get_property ("pitch");
-         event->set_property ("pitch", pits);
-       }
-
-      event->set_property ("duration", note_dur.smobbed_copy ());
-
-      Item *note = make_item ("NoteHead", event->self_scm ());
-      note->set_property ("duration-log",
-                         scm_from_int (note_dur.duration_log ()));
-
-      int dots = note_dur.dot_count ();
-      if (dots)
-       {
-         Item *d = make_item ("Dots", SCM_EOL);
-         Rhythmic_head::set_dots (note, d);
 
-         d->set_property ("dot-count", scm_from_int (dots));
+      if (need_clone)
+       event = event->clone ();
 
-         d->set_parent (note, Y_AXIS);
-         dots_.push_back (d);
-       }
+      SCM pits = note_events_[i]->get_property ("pitch");
 
-      Pitch *pit = unsmob_pitch (event->get_property ("pitch"));
-
-      int pos = pit->steps ();
-      SCM c0 = get_property ("middleCPosition");
-      if (scm_is_number (c0))
-       pos += scm_to_int (c0);
+      event->set_property ("pitch", pits);
+      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 ()));
 
-      note->set_property ("staff-position", scm_from_int (pos));
+      Item *note = make_note_head (event);
+      if (need_clone)
+       event->unprotect ();
       notes_.push_back (note);
     }
-
+  
+  if (pending_ties_.size ()
+      && pending_ties_.front().when_ == now_mom())
+    {
+      for (vsize i = 0; i < tie_note_candidate_events_.size(); i++)
+       for (vsize j = 0; j < note_events_.size(); j++)
+         {
+           Pitch *p =  unsmob_pitch (note_events_[j]->get_property ("pitch"));
+           Pitch *p_last
+             = unsmob_pitch (tie_note_candidate_events_[j]->get_property ("pitch"));
+           if (p && p_last && *p == *p_last)
+             make_tie (tie_note_candidates_[i], notes_[j]);
+         }
+    }
+      
   if (prev_notes_.size () == notes_.size ())
     {
       for (vsize i = 0; i < notes_.size (); i++)
-       {
-         Grob *p = make_spanner ("Tie", SCM_EOL);
-         Tie::set_head (p, LEFT, prev_notes_[i]);
-         Tie::set_head (p, RIGHT, notes_[i]);
-
-         ties_.push_back (p);
-       }
+       make_tie (prev_notes_[i], notes_[i]);
     }
 
   left_to_do_ -= note_dur.get_length ();
-
+  if (left_to_do_)
+    get_global_context ()->add_moment_to_process (now.main_part_ + note_dur.get_length());
   /*
     don't do complicated arithmetic with grace notes.
   */
-  if (orig
-      && now_mom ().grace_part_)
+  if (orig && now_mom ().grace_part_)
     left_to_do_ = Rational (0, 0);
 }
 
+void
+Completion_heads_engraver::make_tie (Grob *left, Grob *right)
+{
+  Grob *p = make_spanner ("Tie", SCM_EOL);
+  Tie::set_head (p, LEFT, left);
+  Tie::set_head (p, RIGHT, right);
+  ties_.push_back (p);
+}
+                                    
 void
 Completion_heads_engraver::stop_translation_timestep ()
 {
@@ -271,24 +259,27 @@ Completion_heads_engraver::stop_translation_timestep ()
   if (notes_.size ())
     prev_notes_ = notes_;
   notes_.clear ();
-
-  dots_.clear ();
-
-  for (vsize i = scratch_note_events_.size (); i--;)
-    scratch_note_events_[i]->unprotect ();
-
-  scratch_note_events_.clear ();
 }
 
 void
 Completion_heads_engraver::start_translation_timestep ()
 {
   Moment now = now_mom ();
+  while (pending_ties_.size() && pending_ties_.front().when_ < now)
+    {
+      pending_ties_.delmin();
+    }
+  current_tie_event_ = 0;
   if (note_end_mom_.main_part_ <= now.main_part_)
     {
+      tie_note_candidate_events_ = note_events_;
+      tie_note_candidates_ = prev_notes_;
+
       note_events_.clear ();
       prev_notes_.clear ();
     }
+  context ()->set_property ("completionBusy",
+                           ly_bool2scm (note_events_.size ()));
 }
 
 Completion_heads_engraver::Completion_heads_engraver ()
@@ -296,16 +287,21 @@ Completion_heads_engraver::Completion_heads_engraver ()
 }
 
 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.",
+               /* doc */
+               "This engraver replaces @code{Note_heads_engraver}.  It plays"
+               " some trickery to break long notes and automatically tie them"
+               " into the next measure.",
+
                /* create */
                "NoteHead "
                "Dots "
-               "Tie",
+               "Tie ",
+
                /* read */
                "middleCPosition "
                "measurePosition "
-               "measureLength",
+               "measureLength ",
 
-               /* write */ "");
+               /* write */
+               "completionBusy "
+               );