/*
- completion-note-heads-engraver.cc -- Completion_heads_engraver
+ This file is part of LilyPond, the GNU music typesetter.
- (c) 1997--2008 Han-Wen Nienhuys <hanwen@xs4all.nl>
+ Copyright (C) 1997--2011 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 <cctype>
#include "item.hh"
#include "output-def.hh"
#include "pitch.hh"
-#include "pqueue.hh"
#include "rhythmic-head.hh"
#include "score-engraver.hh"
#include "spanner.hh"
#include "staff-symbol-referencer.hh"
#include "stream-event.hh"
#include "tie.hh"
+#include "tie-column.hh"
#include "warn.hh"
#include "translator.icc"
-/*
- TODO: make matching rest engraver.
-*/
-struct Pending_tie
-{
- Moment when_;
- Stream_event* tie_event_;
- Pending_tie() { tie_event_ = 0; }
-};
-
-int compare(Pending_tie const &a, Pending_tie const &b)
-{
- return compare(a.when_, b.when_);
-}
-
-
/*
How does this work?
class Completion_heads_engraver : public Engraver
{
- vector<Item*> notes_;
- vector<Item*> prev_notes_;
-
+ vector<Item *> notes_;
+ vector<Item *> prev_notes_;
// Must remember notes for explicit ties.
- 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_;
+ vector<Item *> tie_note_candidates_;
+ vector<Stream_event *> tie_note_candidate_events_;
+ vector<Grob *> ties_;
+ vector<Stream_event *> note_events_;
+ Spanner *tie_column_;
Moment note_end_mom_;
bool is_first_;
Rational left_to_do_;
Rational do_nothing_until_;
+ Rational factor_;
Moment next_barline_moment ();
- Item *make_note_head (Stream_event*);
+ Item *make_note_head (Stream_event *);
public:
TRANSLATOR_DECLARATIONS (Completion_heads_engraver);
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);
Completion_heads_engraver::listen_note (Stream_event *ev)
{
note_events_.push_back (ev);
-
+
is_first_ = true;
Moment now = now_mom ();
Moment musiclen = get_event_length (ev, now);
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.
+ The duration _until_ the next bar line.
*/
Moment
Completion_heads_engraver::next_barline_moment ()
return (*l - *e);
}
-Item*
+Item *
Completion_heads_engraver::make_note_head (Stream_event *ev)
{
Item *note = make_item ("NoteHead", ev->self_scm ());
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;
Moment now = now_mom ();
Duration note_dur;
Duration *orig = 0;
if (left_to_do_)
- note_dur = Duration (left_to_do_, false);
+ {
+ /*
+ note that note_dur may be strictly less than left_to_do_
+ (say, if left_to_do_ == 5/8)
+ */
+ if (factor_.denominator () == 1 && factor_ > Rational (1, 1))
+ note_dur = Duration (left_to_do_, false);
+ else
+ note_dur = Duration (left_to_do_ / factor_, false).compressed (factor_);
+ }
else
{
orig = unsmob_duration (note_events_[0]->get_property ("duration"));
note_dur = *orig;
+ factor_ = note_dur.factor ();
+ left_to_do_ = orig->get_length ();
}
Moment nb = next_barline_moment ();
if (nb.main_part_ && nb < note_dur.get_length ())
{
- note_dur = Duration (nb.main_part_, false);
-
- do_nothing_until_ = now.main_part_ + note_dur.get_length ();
+ if (factor_.denominator () == 1 && factor_ > Rational (1, 1))
+ note_dur = Duration (nb.main_part_, false);
+ else
+ note_dur = Duration (nb.main_part_ / factor_, false).compressed (factor_);
}
- if (orig)
- left_to_do_ = orig->get_length ();
+ do_nothing_until_ = now.main_part_ + note_dur.get_length ();
for (vsize i = 0; left_to_do_ && i < note_events_.size (); i++)
{
Stream_event *event = note_events_[i];
if (need_clone)
- event = event->clone ();
+ event = event->clone ();
SCM pits = note_events_[i]->get_property ("pitch");
-
event->set_property ("pitch", pits);
event->set_property ("duration", note_dur.smobbed_copy ());
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 ();
+ 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_[j]->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++)
- make_tie (prev_notes_[i], notes_[i]);
+ make_tie (prev_notes_[i], notes_[i]);
}
+ if (ties_.size () && !tie_column_)
+ tie_column_ = make_spanner ("TieColumn", ties_[0]->self_scm ());
+
+ if (tie_column_)
+ for (vsize i = ties_.size (); i--;)
+ Tie_column::add_tie (tie_column_, ties_[i]);
+
left_to_do_ -= note_dur.get_length ();
if (left_to_do_)
- get_global_context ()->add_moment_to_process (now.main_part_ + note_dur.get_length());
+ get_global_context ()->add_moment_to_process (now.main_part_ + note_dur.get_length ());
/*
don't do complicated arithmetic with grace notes.
*/
Tie::set_head (p, RIGHT, right);
ties_.push_back (p);
}
-
+
void
Completion_heads_engraver::stop_translation_timestep ()
{
ties_.clear ();
+ tie_column_ = 0;
if (notes_.size ())
prev_notes_ = notes_;
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_;
prev_notes_.clear ();
}
context ()->set_property ("completionBusy",
- ly_bool2scm (note_events_.size ()));
+ ly_bool2scm (note_events_.size ()));
}
Completion_heads_engraver::Completion_heads_engraver ()
{
+ tie_column_ = 0;
}
ADD_TRANSLATOR (Completion_heads_engraver,
- /* doc */
- "This engraver replaces @code{Note_heads_engraver}. It plays"
- " some trickery to break long notes and automatically tie them"
- " into the next measure.",
-
- /* create */
- "NoteHead "
- "Dots "
- "Tie ",
-
- /* read */
- "middleCPosition "
- "measurePosition "
- "measureLength ",
-
- /* write */
- "completionBusy "
- );
+ /* doc */
+ "This engraver replaces @code{Note_heads_engraver}. It plays"
+ " some trickery to break long notes and automatically tie them"
+ " into the next measure.",
+
+ /* create */
+ "NoteHead "
+ "Tie "
+ "TieColumn ",
+
+ /* read */
+ "measureLength "
+ "measurePosition "
+ "middleCPosition "
+ "timing ",
+
+ /* write */
+ "completionBusy "
+ );