X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;ds=sidebyside;f=lily%2Fcompletion-note-heads-engraver.cc;h=1522dad8b3d4ada7bdb589003b6ae95dddd74ab4;hb=750a3ffcf9186636a0477358620bec74d12a7212;hp=c4afba07d6a336d1c0f33f00224f685403fc62c2;hpb=947454c5cc430a9627262de4f25274a64789d3c2;p=lilypond.git diff --git a/lily/completion-note-heads-engraver.cc b/lily/completion-note-heads-engraver.cc index c4afba07d6..1522dad8b3 100644 --- a/lily/completion-note-heads-engraver.cc +++ b/lily/completion-note-heads-engraver.cc @@ -1,26 +1,47 @@ /* - head-grav.cc -- part of GNU LilyPond + completion-note-heads-engraver.cc -- Completion_heads_engraver - (c) 1997--2004 Han-Wen Nienhuys + (c) 1997--2008 Han-Wen Nienhuys */ -#include +#include +using namespace std; -#include "rhythmic-head.hh" -#include "paper-def.hh" -#include "event.hh" -#include "dots.hh" #include "dot-column.hh" -#include "staff-symbol-referencer.hh" +#include "dots.hh" +#include "duration.hh" +#include "global-context.hh" #include "item.hh" +#include "output-def.hh" +#include "pitch.hh" +#include "pqueue.hh" +#include "rhythmic-head.hh" #include "score-engraver.hh" -#include "warn.hh" #include "spanner.hh" +#include "staff-symbol-referencer.hh" +#include "stream-event.hh" #include "tie.hh" -#include "global-context.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? When we catch the note, we predict the end of the note. We keep the @@ -30,302 +51,257 @@ figure out how long the note to typeset should be. It should be no longer than what's specified, than what is left to do and it should not cross barlines. - - We copy the reqs into scratch note reqs, to make sure that we get + + We copy the events into scratch note events, to make sure that we get all durations exactly right. */ class Completion_heads_engraver : public Engraver { - Link_array notes_; - Link_array prev_notes_; - Link_array ties_; - - Link_array dots_; - Link_array note_reqs_; - Link_array scratch_note_reqs_; + vector notes_; + vector prev_notes_; + + // Must remember notes for explicit ties. + vector tie_note_candidates_; + vector tie_note_candidate_events_; + vector ties_; + PQueue pending_ties_; + vector note_events_; + Stream_event *current_tie_event_; Moment note_end_mom_; - bool first_b_; + bool is_first_; Rational left_to_do_; Rational do_nothing_until_; - + Moment next_barline_moment (); - Duration find_nearest_duration (Rational length); - + Item *make_note_head (Stream_event*); + public: TRANSLATOR_DECLARATIONS (Completion_heads_engraver); protected: virtual void initialize (); - virtual void start_translation_timestep (); - virtual bool try_music (Music *req) ; - virtual void process_music (); - virtual void stop_translation_timestep (); + void make_tie (Grob *, Grob *); + void start_translation_timestep (); + void process_music (); + void stop_translation_timestep (); + DECLARE_TRANSLATOR_LISTENER (note); + DECLARE_TRANSLATOR_LISTENER (tie); }; void Completion_heads_engraver::initialize () { - first_b_ = false; + is_first_ = false; + current_tie_event_ = 0; } -bool -Completion_heads_engraver::try_music (Music *m) +IMPLEMENT_TRANSLATOR_LISTENER (Completion_heads_engraver, note); +void +Completion_heads_engraver::listen_note (Stream_event *ev) { - if (m->is_mus_type ("note-event")) - { - note_reqs_.push (m); - - first_b_ = true; - Moment musiclen = m->get_length (); - Moment now = now_mom (); - - if (now_mom ().grace_part_) - { - musiclen.grace_part_ = musiclen.main_part_ ; - musiclen.main_part_ = Rational (0,1); - } - note_end_mom_ = note_end_mom_ >? (now + musiclen); - do_nothing_until_ = Rational (0,0); - - return true; - } - else if (m->is_mus_type ("busy-playing-event")) - { - return note_reqs_.size (); - } - - return false; + note_events_.push_back (ev); + is_first_ = true; + Moment now = now_mom (); + Moment musiclen = get_event_length (ev, now); + + note_end_mom_ = max (note_end_mom_, (now + musiclen)); + 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. - */ +*/ Moment -Completion_heads_engraver::next_barline_moment ( ) +Completion_heads_engraver::next_barline_moment () { Moment *e = unsmob_moment (get_property ("measurePosition")); Moment *l = unsmob_moment (get_property ("measureLength")); - if (!e || !l) + if (!e || !l || !to_boolean (get_property ("timing"))) { - programming_error ("No timing props set?"); - return Moment (1,1); + return Moment (0, 0); } return (*l - *e); } -Duration -Completion_heads_engraver::find_nearest_duration (Rational length) +Item* +Completion_heads_engraver::make_note_head (Stream_event *ev) { - int log_limit= 6; + Item *note = make_item ("NoteHead", ev->self_scm ()); + Pitch *pit = unsmob_pitch (ev->get_property ("pitch")); - Duration d (0,0); + int pos = pit->steps (); + SCM c0 = get_property ("middleCPosition"); + if (scm_is_number (c0)) + pos += scm_to_int (c0); - /* - this could surely be done more efficient. Left to the reader as an - excercise. */ - while (d.get_length () > length && d.duration_log () < log_limit) - { - if (d.dot_count ()) - { - d = Duration (d.duration_log (), d.dot_count ()- 1); - continue; - } - else - { - d = Duration (d.duration_log () + 1, 2); - } - } + note->set_property ("staff-position", scm_from_int (pos)); - if (d.duration_log () >= log_limit) - { - // junk the dots. - d = Duration (d.duration_log (), 0); - - // scale up. - d = d.compressed (length / d.get_length ()); - } - - return d; + return note; } void Completion_heads_engraver::process_music () { - if (!first_b_ && !left_to_do_) - return ; + 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); + } - first_b_ = false; + is_first_ = false; - Moment now = now_mom (); + Moment now = now_mom (); if (do_nothing_until_ > now.main_part_) - return ; - + return; + Duration note_dur; Duration *orig = 0; if (left_to_do_) - { - note_dur = find_nearest_duration (left_to_do_); - } + note_dur = Duration (left_to_do_, false); else { - orig = unsmob_duration (note_reqs_[0]->get_property ("duration")); + orig = unsmob_duration (note_events_[0]->get_property ("duration")); note_dur = *orig; } Moment nb = next_barline_moment (); - if (nb < note_dur.get_length ()) + if (nb.main_part_ && nb < note_dur.get_length ()) { - note_dur = find_nearest_duration (nb.main_part_); + note_dur = Duration (nb.main_part_, false); - Moment next = now; - next.main_part_ += note_dur.get_length (); - - get_global_context ()->add_moment_to_process (next); - do_nothing_until_ = next.main_part_; + do_nothing_until_ = now.main_part_ + note_dur.get_length (); } if (orig) + left_to_do_ = orig->get_length (); + + for (vsize i = 0; left_to_do_ && i < note_events_.size (); i++) { - left_to_do_ = orig->get_length (); - } + bool need_clone = !orig || *orig != note_dur; + Stream_event *event = note_events_[i]; - if (orig && note_dur.get_length () != orig->get_length ()) + if (need_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 ())); + + 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()) { - if (!scratch_note_reqs_.size ()) - for (int i = 0; i < note_reqs_.size (); i++) + for (vsize i = 0; i < tie_note_candidate_events_.size(); i++) + for (vsize j = 0; j < note_events_.size(); j++) { - Music * m = note_reqs_[i]->clone (); - scratch_note_reqs_.push (m); + 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]); } } - - - for (int i = 0; - left_to_do_ && i < note_reqs_.size (); i++) - { - Item *note = make_item ("NoteHead"); - - Music * req = note_reqs_[i]; - if (scratch_note_reqs_.size ()) - { - req = scratch_note_reqs_[i]; - SCM pits = note_reqs_[i]->get_property ("pitch"); - req->set_property ("pitch",pits); - } - req->set_property ("duration", note_dur.smobbed_copy ()); - note->set_property ("duration-log", - gh_int2scm (note_dur.duration_log ())); - - int dots= note_dur.dot_count (); - if (dots) - { - Item * d = make_item ("Dots"); - Rhythmic_head::set_dots (note, d); - - /* - measly attempt to save an eeny-weenie bit of memory. - */ - if (dots != gh_scm2int (d->get_property ("dot-count"))) - d->set_property ("dot-count", gh_int2scm (dots)); - - d->set_parent (note, Y_AXIS); - announce_grob (d, SCM_EOL); - dots_.push (d); - } - - Pitch *pit =unsmob_pitch (req->get_property ("pitch")); - - int pos = pit->steps (); - SCM c0 = get_property ("centralCPosition"); - if (gh_number_p (c0)) - pos += gh_scm2int (c0); - - note->set_property ("staff-position", gh_int2scm (pos)); - announce_grob (note,req->self_scm ()); - notes_.push (note); - } - if (prev_notes_.size () == notes_.size ()) { - for (int i= 0; i < notes_.size (); i++) - { - Grob * p = make_spanner ("Tie"); - Tie::set_interface (p); // cannot remove yet! - - Tie::set_head (p, LEFT, prev_notes_[i]); - Tie::set_head (p, RIGHT, notes_[i]); - - ties_.push (p); - announce_grob (p, SCM_EOL); - } + for (vsize i = 0; i < notes_.size (); i++) + make_tie (prev_notes_[i], notes_[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()); /* don't do complicated arithmetic with grace notes. - */ - if (orig - && now_mom ().grace_part_ ) - { - left_to_do_ = Rational (0,0); - } + */ + if (orig && now_mom ().grace_part_) + left_to_do_ = Rational (0, 0); +} + +void +Completion_heads_engraver::make_tie (Grob *left, Grob *right) +{ + Grob *p = make_spanner ("Tie", SCM_EOL); + Tie::set_head (p, LEFT, left); + Tie::set_head (p, RIGHT, right); + ties_.push_back (p); } - + void Completion_heads_engraver::stop_translation_timestep () { - for (int i = ties_.size (); i--;) - typeset_grob (ties_[i]); ties_.clear (); - - for (int i=0; i < notes_.size (); i++) - { - typeset_grob (notes_[i]); - } + if (notes_.size ()) prev_notes_ = notes_; notes_.clear (); - - for (int i=0; i < dots_.size (); i++) - { - typeset_grob (dots_[i]); - } - dots_.clear (); - - for (int i = scratch_note_reqs_.size (); i--;) - { - scm_gc_unprotect_object (scratch_note_reqs_[i]->self_scm () ); - } - - scratch_note_reqs_.clear (); } void 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_) { - note_reqs_.clear (); + tie_note_candidate_events_ = note_events_; + tie_note_candidates_ = prev_notes_; + + note_events_.clear (); prev_notes_.clear (); } + context ()->set_property ("completionBusy", + ly_bool2scm (note_events_.size ())); } Completion_heads_engraver::Completion_heads_engraver () { } -ENTER_DESCRIPTION (Completion_heads_engraver, -/* descr */ "This engraver replaces " -"@code{Note_heads_engraver}. It plays some trickery to " -"break long notes and automatically tie them into the next measure.", -/* creats*/ "NoteHead Dots Tie", -/* accepts */ "busy-playing-event note-event", -/* acks */ "", -/* reads */ "centralCPosition measurePosition measureLength", -/* write */ ""); +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 " + );