]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/tie-performer.cc
Issue 5167/6: Changes: show \markup xxx = ... \etc assignments
[lilypond.git] / lily / tie-performer.cc
index 35d52ce0d4ed19ed9d9e842c7b501953fd1d6342..1f95023eb6ab0962146dd13b54ce07f08b665717 100644 (file)
@@ -1,9 +1,20 @@
 /*
-  tie-performer.cc -- implement Tie_performer
+  This file is part of LilyPond, the GNU music typesetter.
 
-  source file of the GNU LilyPond music typesetter
+  Copyright (C) 1998--2015 Han-Wen Nienhuys <hanwen@xs4all.nl>
 
-  (c) 1998--2009 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
+  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 "performer.hh"
 #include "context.hh"
 #include "stream-event.hh"
 #include "translator.icc"
+#include <list>
+
+struct Head_audio_event_tuple
+{
+  Audio_element_info head_;
+  // 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;
+    end_moment_ = m;
+  }
+};
 
 class Tie_performer : public Performer
 {
   Stream_event *event_;
-  vector<Audio_element_info> now_heads_;
-  vector<Audio_element_info> now_tied_heads_;
-  vector<Audio_element_info> heads_to_tie_;
-
-  bool ties_created_;
+  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;
-  ties_created_ = false;
 }
 
-IMPLEMENT_TRANSLATOR_LISTENER (Tie_performer, tie);
 void
 Tie_performer::listen_tie (Stream_event *ev)
 {
@@ -57,33 +80,64 @@ Tie_performer::acknowledge_audio_element (Audio_element_info inf)
 {
   if (Audio_note *an = dynamic_cast<Audio_note *> (inf.elem_))
     {
+      // 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);
+        now_tied_heads_.push_back (inf_mom);
       else
-        now_heads_.push_back (inf);
+        now_heads_.push_back (inf_mom);
 
-      // 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
-      vector<Audio_element_info>::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;
-         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;
-             an->tie_to (th);
-             ties_created_ = true;
-             // 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);
+            }
+        }
     }
 }
 
@@ -91,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 ();
-      ties_created_ = false;
+      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_)
     {
-      heads_to_tie_ = now_heads_;
+      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"
+               );