]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/tie-performer.cc
Release: bump Welcome versions.
[lilypond.git] / lily / tie-performer.cc
index e6cb44de8f317a8fbe56a7e24e95d98354bceaa4..1f95023eb6ab0962146dd13b54ce07f08b665717 100644 (file)
@@ -1,7 +1,7 @@
 /*
   This file is part of LilyPond, the GNU music typesetter.
 
-  Copyright (C) 1998--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
+  Copyright (C) 1998--2015 Han-Wen Nienhuys <hanwen@xs4all.nl>
 
   LilyPond is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 #include "context.hh"
 #include "stream-event.hh"
 #include "translator.icc"
-// #include "international.hh"
-#include <deque>
+#include <list>
 
-struct Head_event_tuple
+struct Head_audio_event_tuple
 {
   Audio_element_info head_;
-  Moment moment_;
-  Head_event_tuple () { }
-  Head_event_tuple (Audio_element_info h, Moment m)
+  // The end moment of the note, so we can calculate a skip and check whether
+  // the note still goes on
+  Moment end_moment_;
+  Head_audio_event_tuple () {}
+  Head_audio_event_tuple (Audio_element_info h, Moment m)
   {
     head_ = h;
-    moment_ = m;
+    end_moment_ = m;
   }
 };
 
-
 class Tie_performer : public Performer
 {
   Stream_event *event_;
-  // We don't really need a deque here. A vector would suffice. However,
-  // for some strange reason, using vectors always leads to memory 
-  // corruption in the STL templates! (i.e. after the first
-  // now_heads_.push_back (inf_mom), the now_heads_.size() will be 
-  // something like 3303820998 :(
-  deque<Head_event_tuple> now_heads_;
-  deque<Head_event_tuple> now_tied_heads_;
-  deque<Head_event_tuple> heads_to_tie_;
+  list<Head_audio_event_tuple> now_heads_;
+  list<Head_audio_event_tuple> now_tied_heads_; // new tied notes
+  list<Head_audio_event_tuple> heads_to_tie_; // heads waiting for closing tie
 
 protected:
   void stop_translation_timestep ();
   void start_translation_timestep ();
   virtual void acknowledge_audio_element (Audio_element_info);
   void process_music ();
-  DECLARE_TRANSLATOR_LISTENER (tie);
+  void listen_tie (Stream_event *);
 public:
   TRANSLATOR_DECLARATIONS (Tie_performer);
 };
 
-Tie_performer::Tie_performer ()
+Tie_performer::Tie_performer (Context *c)
+  : Performer (c)
 {
   event_ = 0;
 }
 
-IMPLEMENT_TRANSLATOR_LISTENER (Tie_performer, tie);
 void
 Tie_performer::listen_tie (Stream_event *ev)
 {
@@ -85,36 +80,64 @@ Tie_performer::acknowledge_audio_element (Audio_element_info inf)
 {
   if (Audio_note *an = dynamic_cast<Audio_note *> (inf.elem_))
     {
-//       message (_f ("acknowledge_audio_element, Size of now_heads_=%d", now_heads_.size ()));
-      Head_event_tuple inf_mom (inf, now_mom ());
+      // for each tied note, store the info and its end moment, so we can
+      // later on check whether (1) the note is still ongoing and (2) how
+      // long the skip is with tieWaitForNote
+      Head_audio_event_tuple inf_mom (inf, now_mom () + an->length_mom_);
       if (an->tie_event_)
         now_tied_heads_.push_back (inf_mom);
       else
         now_heads_.push_back (inf_mom);
 
-//       message (_f ("acknowledge_audio_element, added, Size of now_heads_=%d", now_heads_.size ()));
-      // Find a previous note that ties to the current note. If it exists, 
+      // Find a previous note that ties to the current note. If it exists,
       // remove it from the heads_to_tie vector and create the tie
-      deque<Head_event_tuple>::iterator it;
+      list<Head_audio_event_tuple>::iterator it;
       bool found = false;
       Stream_event *right_mus = inf.event_;
-      for ( it = heads_to_tie_.begin() ; (!found) && (it < heads_to_tie_.end()); it++ )
+      for (it = heads_to_tie_.begin ();
+           !found && (it != heads_to_tie_.end ());
+           it++)
+        {
+          Audio_element_info et = (*it).head_;
+          Audio_note *th = dynamic_cast<Audio_note *> (et.elem_);
+          Stream_event *left_mus = et.event_;
+
+          if (th && right_mus && left_mus
+              && ly_is_equal (right_mus->get_property ("pitch"),
+                              left_mus->get_property ("pitch")))
+            {
+              found = true;
+              // (*it).moment_ already stores the end of the tied note!
+              Moment skip = now_mom () - (*it).end_moment_;
+              an->tie_to (th, skip);
+              it = heads_to_tie_.erase (it);
+            }
+        }
+      if (found)
+        return;
+      for (it = heads_to_tie_.begin ();
+           !found && (it != heads_to_tie_.end ());
+           it++)
         {
-         Audio_element_info et = (*it).head_;
-         Audio_note *th = dynamic_cast<Audio_note *> (et.elem_);
-         Stream_event *left_mus = et.event_;
-
-         if (th && right_mus && left_mus
-             && ly_is_equal (right_mus->get_property ("pitch"),
-                             left_mus->get_property ("pitch")))
-           {
-             found = true;
-             Moment skip = now_mom() - (*it).moment_ - th->length_mom_;
-             an->tie_to (th, skip);
-             // this invalidates the iterator, we are leaving the loop anyway
-             heads_to_tie_.erase (it);
-           }
-       }
+          Audio_element_info et = (*it).head_;
+          Audio_note *th = dynamic_cast<Audio_note *> (et.elem_);
+          Stream_event *left_mus = et.event_;
+
+          if (!(th && right_mus && left_mus))
+            continue;
+
+          SCM p1 = left_mus->get_property ("pitch");
+          SCM p2 = right_mus->get_property ("pitch");
+          if (unsmob<Pitch> (p1) && unsmob<Pitch> (p2)
+              && unsmob<Pitch> (p1)->tone_pitch () == unsmob<Pitch> (p2)->tone_pitch ())
+            {
+              found = true;
+              // (*it).moment_ already stores the end of the tied note!
+              Moment skip = now_mom () - (*it).end_moment_;
+              an->tie_to (th, skip);
+              it = heads_to_tie_.erase (it);
+            }
+        }
     }
 }
 
@@ -122,43 +145,61 @@ void
 Tie_performer::start_translation_timestep ()
 {
   context ()->set_property ("tieMelismaBusy",
-                           ly_bool2scm (heads_to_tie_.size ()));
+                            ly_bool2scm (heads_to_tie_.size ()));
 }
 
+// a predicate implemented as a class, used to delete all tied notes with end
+// moment in the past:
+class end_moment_passed
+{
+protected:
+  Moment now;
+public:
+  end_moment_passed (Moment mom) : now (mom) {}
+  bool operator () (const Head_audio_event_tuple &value)
+  {
+    return (value.end_moment_ <= now);
+  }
+};
+
 void
 Tie_performer::stop_translation_timestep ()
 {
-  // We might have dangling open ties like c~ d. Close them, unless we have
-  // tieWaitForNote set...
+  // We might have dangling open ties like c~ d. Close them, unless the first
+  // note is still ongoing or we have we have tieWaitForNote set...
   if (!to_boolean (get_property ("tieWaitForNote")))
     {
-      heads_to_tie_.clear ();
+      heads_to_tie_.remove_if (end_moment_passed (now_mom ()));
     }
 
+  // Append now_heads_ and now_tied_heads to heads_to_tie_ for the next time step
   if (event_)
     {
-      for (vsize i = now_heads_.size (); i--;)
-        heads_to_tie_.push_back (now_heads_[i]);
+      heads_to_tie_.splice (heads_to_tie_.end (), now_heads_);
     }
-
-  for (vsize i = now_tied_heads_.size (); i--;)
-    heads_to_tie_.push_back (now_tied_heads_[i]);
+  heads_to_tie_.splice (heads_to_tie_.end (), now_tied_heads_);
 
   event_ = 0;
   now_heads_.clear ();
   now_tied_heads_.clear ();
 }
 
+void
+Tie_performer::boot ()
+{
+  ADD_LISTENER (Tie_performer, tie);
+}
+
 ADD_TRANSLATOR (Tie_performer,
-               /* doc */
-               "Generate ties between note heads of equal pitch.",
+                /* doc */
+                "Generate ties between note heads of equal pitch.",
 
-               /* create */
-               "",
+                /* create */
+                "",
 
-               /* read */
-               "tieWaitForNote",
+                /* read */
+                "tieWaitForNote",
 
-               /* write */
-               "tieMelismaBusy"
-               );
+                /* write */
+                "tieMelismaBusy"
+               );