]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/tie-performer.cc
Run grand replace for 2015.
[lilypond.git] / lily / tie-performer.cc
index bcbac27567cdfa089fa2fd6b53d6328114dacd16..ba7954e995724678d1cf62e55053bc7aac9dead0 100644 (file)
@@ -1,34 +1,57 @@
 /*
-  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--2005 Han-Wen Nienhuys <hanwen@cs.uu.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 "music.hh"
-#include "context.hh"
 #include "audio-item.hh"
-#include "pqueue.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
 {
-  Music *event_;
-  Music *last_event_;
-  Array<Audio_element_info> now_heads_;
-  Array<Audio_element_info> heads_to_tie_;
-
-  bool ties_created_;
+  Stream_event *event_;
+  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);
-  virtual bool try_music (Music *);
   void process_music ();
+  DECLARE_TRANSLATOR_LISTENER (tie);
 public:
   TRANSLATOR_DECLARATIONS (Tie_performer);
 };
@@ -36,19 +59,13 @@ public:
 Tie_performer::Tie_performer ()
 {
   event_ = 0;
-  last_event_ = 0;
-  ties_created_ = false;
 }
 
-bool
-Tie_performer::try_music (Music *mus)
+IMPLEMENT_TRANSLATOR_LISTENER (Tie_performer, tie);
+void
+Tie_performer::listen_tie (Stream_event *ev)
 {
-  if (mus->is_mus_type ("tie-event"))
-    {
-      event_ = mus;
-    }
-
-  return true;
+  event_ = ev;
 }
 
 void
@@ -63,22 +80,64 @@ Tie_performer::acknowledge_audio_element (Audio_element_info inf)
 {
   if (Audio_note *an = dynamic_cast<Audio_note *> (inf.elem_))
     {
-      now_heads_.push (inf);
-      for (int i = heads_to_tie_.size (); i--;)
-       {
-         Music *right_mus = inf.event_;
-
-         Audio_note *th = dynamic_cast<Audio_note *> (heads_to_tie_[i].elem_);
-         Music *left_mus = heads_to_tie_[i].event_;
-
-         if (right_mus && left_mus
-             && ly_is_equal (right_mus->get_property ("pitch"),
-                              left_mus->get_property ("pitch")))
-           {
-             an->tie_to (th);
-             ties_created_ = true;
-           }
-       }
+      // 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);
+
+      // 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
+      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++)
+        {
+          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))
+            continue;
+
+          SCM p1 = left_mus->get_property ("pitch");
+          SCM p2 = right_mus->get_property ("pitch");
+          if (Pitch::is_smob (p1) && Pitch::is_smob (p2)
+              && Pitch::unsmob (p1)->tone_pitch () == Pitch::unsmob (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);
+            }
+        }
     }
 }
 
@@ -86,33 +145,55 @@ 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 ()
 {
-  if (ties_created_)
+  // 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 ();
-      last_event_ = 0;
-      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_;
-      last_event_ = event_;
+      heads_to_tie_.splice (heads_to_tie_.end (), now_heads_);
     }
+  heads_to_tie_.splice (heads_to_tie_.end (), now_tied_heads_);
+
   event_ = 0;
   now_heads_.clear ();
+  now_tied_heads_.clear ();
 }
 
-#include "translator.icc"
-
 ADD_TRANSLATOR (Tie_performer,
-               /* descr */ "Generate ties between noteheads of equal pitch.",
-               /* creats*/ "",
-               /* accepts */ "tie-event",
-               /* reads */ "tieMelismaBusy",
-               /* write */ "");
+                /* doc */
+                "Generate ties between note heads of equal pitch.",
+
+                /* create */
+                "",
+
+                /* read */
+                "tieWaitForNote",
+
+                /* write */
+                "tieMelismaBusy"
+               );