2 completion-note-heads-engraver.cc -- Completion_heads_engraver
4 (c) 1997--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
10 #include "rhythmic-head.hh"
11 #include "output-def.hh"
14 #include "dot-column.hh"
15 #include "staff-symbol-referencer.hh"
17 #include "score-engraver.hh"
21 #include "global-context.hh"
22 #include "duration.hh"
26 TODO: make matching rest engraver.
32 When we catch the note, we predict the end of the note. We keep the
33 events living until we reach the predicted end-time.
35 Every time process_music () is called and there are note events, we
36 figure out how long the note to typeset should be. It should be no
37 longer than what's specified, than what is left to do and it should
40 We copy the events into scratch note events, to make sure that we get
41 all durations exactly right.
44 class Completion_heads_engraver : public Engraver
47 vector<Item*> prev_notes_;
51 vector<Music*> note_events_;
52 vector<Music*> scratch_note_events_;
57 Rational do_nothing_until_;
59 Moment next_barline_moment ();
60 Duration find_nearest_duration (Rational length);
63 TRANSLATOR_DECLARATIONS (Completion_heads_engraver);
66 virtual void initialize ();
67 void start_translation_timestep ();
68 virtual bool try_music (Music *event);
69 void process_music ();
70 void stop_translation_timestep ();
74 Completion_heads_engraver::initialize ()
80 Completion_heads_engraver::try_music (Music *m)
82 if (m->is_mus_type ("note-event"))
84 note_events_.push_back (m);
87 Moment musiclen = m->get_length ();
88 Moment now = now_mom ();
90 if (now_mom ().grace_part_)
92 musiclen.grace_part_ = musiclen.main_part_;
93 musiclen.main_part_ = Rational (0, 1);
95 note_end_mom_ = max (note_end_mom_, (now + musiclen));
96 do_nothing_until_ = Rational (0, 0);
100 else if (m->is_mus_type ("busy-playing-event"))
101 return note_events_.size () && is_first_;
107 The duration _until_ the next barline.
110 Completion_heads_engraver::next_barline_moment ()
112 Moment *e = unsmob_moment (get_property ("measurePosition"));
113 Moment *l = unsmob_moment (get_property ("measureLength"));
116 programming_error ("no timing props set?");
117 return Moment (1, 1);
124 Completion_heads_engraver::find_nearest_duration (Rational length)
131 this could surely be done more efficient. Left to the reader as an
133 while (d.get_length () > length && d.duration_log () < log_limit)
137 d = Duration (d.duration_log (), d.dot_count ()- 1);
141 d = Duration (d.duration_log () + 1, 2);
144 if (d.duration_log () >= log_limit)
147 d = Duration (d.duration_log (), 0);
150 d = d.compressed (length / d.get_length ());
157 Completion_heads_engraver::process_music ()
159 if (!is_first_ && !left_to_do_)
164 Moment now = now_mom ();
165 if (do_nothing_until_ > now.main_part_)
171 note_dur = find_nearest_duration (left_to_do_);
174 orig = unsmob_duration (note_events_[0]->get_property ("duration"));
177 Moment nb = next_barline_moment ();
178 if (nb < note_dur.get_length ())
180 note_dur = find_nearest_duration (nb.main_part_);
183 next.main_part_ += note_dur.get_length ();
185 get_global_context ()->add_moment_to_process (next);
186 do_nothing_until_ = next.main_part_;
190 left_to_do_ = orig->get_length ();
192 if (orig && note_dur.get_length () != orig->get_length ())
194 if (!scratch_note_events_.size ())
195 for (vsize i = 0; i < note_events_.size (); i++)
197 Music *m = note_events_[i]->clone ();
198 scratch_note_events_.push_back (m);
202 for (vsize i = 0; left_to_do_ && i < note_events_.size (); i++)
204 Music *event = note_events_[i];
205 if (scratch_note_events_.size ())
207 event = scratch_note_events_[i];
208 SCM pits = note_events_[i]->get_property ("pitch");
209 event->set_property ("pitch", pits);
212 event->set_property ("duration", note_dur.smobbed_copy ());
214 Item *note = make_item ("NoteHead", event->self_scm ());
215 note->set_property ("duration-log",
216 scm_from_int (note_dur.duration_log ()));
218 int dots = note_dur.dot_count ();
221 Item *d = make_item ("Dots", SCM_EOL);
222 Rhythmic_head::set_dots (note, d);
225 measly attempt to save an eeny-weenie bit of memory.
227 if (dots != scm_to_int (d->get_property ("dot-count")))
228 d->set_property ("dot-count", scm_from_int (dots));
230 d->set_parent (note, Y_AXIS);
234 Pitch *pit = unsmob_pitch (event->get_property ("pitch"));
236 int pos = pit->steps ();
237 SCM c0 = get_property ("middleCPosition");
238 if (scm_is_number (c0))
239 pos += scm_to_int (c0);
241 note->set_property ("staff-position", scm_from_int (pos));
242 notes_.push_back (note);
245 if (prev_notes_.size () == notes_.size ())
247 for (vsize i = 0; i < notes_.size (); i++)
249 Grob *p = make_spanner ("Tie", SCM_EOL);
250 Tie::set_head (p, LEFT, prev_notes_[i]);
251 Tie::set_head (p, RIGHT, notes_[i]);
257 left_to_do_ -= note_dur.get_length ();
260 don't do complicated arithmetic with grace notes.
263 && now_mom ().grace_part_)
264 left_to_do_ = Rational (0, 0);
268 Completion_heads_engraver::stop_translation_timestep ()
273 prev_notes_ = notes_;
278 for (vsize i = scratch_note_events_.size (); i--;)
279 scratch_note_events_[i]->unprotect ();
281 scratch_note_events_.clear ();
285 Completion_heads_engraver::start_translation_timestep ()
287 Moment now = now_mom ();
288 if (note_end_mom_.main_part_ <= now.main_part_)
290 note_events_.clear ();
291 prev_notes_.clear ();
295 Completion_heads_engraver::Completion_heads_engraver ()
299 #include "translator.icc"
301 ADD_TRANSLATOR (Completion_heads_engraver,
302 /* doc */ "This engraver replaces "
303 "@code{Note_heads_engraver}. It plays some trickery to "
304 "break long notes and automatically tie them into the next measure.",
305 /* create */ "NoteHead Dots Tie",
306 /* accept */ "busy-playing-event note-event",
307 /* read */ "middleCPosition measurePosition measureLength",