/*
- 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--2006 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 "audio-item.hh"
#include "context.hh"
-#include "pqueue.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_;
- Stream_event *last_event_;
- vector<Audio_element_info> now_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;
- last_event_ = 0;
- ties_created_ = false;
}
-IMPLEMENT_TRANSLATOR_LISTENER (Tie_performer, tie);
void
Tie_performer::listen_tie (Stream_event *ev)
{
{
if (Audio_note *an = dynamic_cast<Audio_note *> (inf.elem_))
{
- now_heads_.push_back (inf);
- for (vsize i = heads_to_tie_.size (); i--;)
- {
- Stream_event *right_mus = inf.event_;
-
- Audio_note *th = dynamic_cast<Audio_note *> (heads_to_tie_[i].elem_);
- Stream_event *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 (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);
+ }
+ }
}
}
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 ();
+}
+
+void
+Tie_performer::boot ()
+{
+ ADD_LISTENER (Tie_performer, tie);
}
ADD_TRANSLATOR (Tie_performer,
- /* doc */ "Generate ties between noteheads of equal pitch.",
- /* create */ "",
- /* accept */ "tie-event",
- /* read */ "tieMelismaBusy",
- /* write */ "");
+ /* doc */
+ "Generate ties between note heads of equal pitch.",
+
+ /* create */
+ "",
+
+ /* read */
+ "tieWaitForNote",
+
+ /* write */
+ "tieMelismaBusy"
+ );