#include "item.hh"
#include "output-def.hh"
#include "pitch.hh"
-#include "pqueue.hh"
#include "rhythmic-head.hh"
#include "score-engraver.hh"
#include "spanner.hh"
/*
TODO: make matching rest engraver.
*/
-struct Pending_tie
-{
- Moment when_;
- Stream_event* tie_event_;
- Pending_tie () : tie_event_ (0) {}
-};
-
-static int
-compare (Pending_tie const &a, Pending_tie const &b)
-{
- return compare (a.when_, b.when_);
-}
-
/*
How does this work?
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_;
- Stream_event *current_tie_event_;
Moment note_end_mom_;
bool is_first_;
Rational left_to_do_;
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);
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.
*/
{
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;
event->set_property ("length", Moment (note_dur.get_length ()).smobbed_copy ());
event->set_property ("duration-log", scm_from_int (note_dur.duration_log ()));
+ /*
+ The Completion_heads_engraver splits an event into a group of consecutive events.
+ For each event in the group, the property "autosplit-end" denotes whether the current event
+ was truncated during splitting. Based on "autosplit-end", the Tie_engraver decides whether a
+ tie event should be processed.
+ */
+ event->set_property ("autosplit-end",
+ ly_bool2scm (left_to_do_ - note_dur.get_length () > Rational (0)));
+
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_[i]->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++)
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_;
void process_music ();
void typeset_tie (Grob *);
void report_unterminated_tie (Head_event_tuple const &);
+ bool has_autosplit_end (Stream_event *event);
public:
TRANSLATOR_DECLARATIONS (Tie_engraver);
};
tie_start.head_->warning (_("unterminated tie"));
}
+/*
+ Determines whether the end of an event was created by
+ a split in Completion_heads_engraver or by user input.
+*/
+bool
+Tie_engraver::has_autosplit_end (Stream_event *event)
+{
+ if (event)
+ return to_boolean (event->get_property ("autosplit-end"));
+ return false;
+}
+
void
Tie_engraver::process_music ()
{
if (!right_ev || !left_ev)
continue;
- if (ly_is_equal (right_ev->get_property ("pitch"),
- left_ev->get_property ("pitch")))
+ /*
+ Make a tie only if pitches are equal or if event end was not generated by
+ Completion_heads_engraver.
+ */
+ if (ly_is_equal (right_ev->get_property ("pitch"), left_ev->get_property ("pitch"))
+ && (!Tie_engraver::has_autosplit_end (left_ev)))
{
Grob *p = new Spanner (heads_to_tie_[i].tie_definition_);
Moment end = heads_to_tie_[i].end_moment_;
ties_.push_back (p);
heads_to_tie_.erase (heads_to_tie_.begin () + i);
- // Prevent all other tied notes ending at the same moment (assume
- // implicitly the notes have also started at the same moment!)
- // from triggering an "unterminated tie" warning. Needed e.g. for
- // <c e g>~ g
+ /*
+ Prevent all other tied notes ending at the same moment (assume
+ implicitly the notes have also started at the same moment!)
+ from triggering an "unterminated tie" warning. Needed e.g. for
+ <c e g>~ g
+ */
for (vsize j = heads_to_tie_.size (); j--;)
{
if (heads_to_tie_[j].end_moment_ == end)
vector<Head_event_tuple> new_heads_to_tie;
+ /*
+ Whether tie event has been processed and can be deleted or should
+ be kept for later portions of a split note.
+ */
+ bool event_processed = false;
+
for (vsize i = 0; i < now_heads_.size (); i++)
{
Grob *head = now_heads_[i];
tie_event = ev;
}
- if (left_ev && (tie_event || tie_stream_event))
+ if (left_ev && (tie_event || tie_stream_event)
+ && (!Tie_engraver::has_autosplit_end (left_ev)))
{
+ event_processed = true;
+
Head_event_tuple event_tup;
SCM start_definition
for (vsize i = 0; i < new_heads_to_tie.size (); i++)
heads_to_tie_.push_back (new_heads_to_tie[i]);
- event_ = 0;
+ /*
+ Discard event only if it has been processed with at least one
+ appropriate note.
+ */
+ if (event_processed)
+ event_ = 0;
+
now_heads_.clear ();
}