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);
105 The duration _until_ the next barline.
108 Completion_heads_engraver::next_barline_moment ()
110 Moment *e = unsmob_moment (get_property ("measurePosition"));
111 Moment *l = unsmob_moment (get_property ("measureLength"));
114 programming_error ("no timing props set?");
115 return Moment (1, 1);
122 Completion_heads_engraver::find_nearest_duration (Rational length)
129 this could surely be done more efficient. Left to the reader as an
131 while (d.get_length () > length && d.duration_log () < log_limit)
135 d = Duration (d.duration_log (), d.dot_count ()- 1);
139 d = Duration (d.duration_log () + 1, 2);
142 if (d.duration_log () >= log_limit)
145 d = Duration (d.duration_log (), 0);
148 d = d.compressed (length / d.get_length ());
155 Completion_heads_engraver::process_music ()
157 if (!is_first_ && !left_to_do_)
162 Moment now = now_mom ();
163 if (do_nothing_until_ > now.main_part_)
169 note_dur = find_nearest_duration (left_to_do_);
172 orig = unsmob_duration (note_events_[0]->get_property ("duration"));
175 Moment nb = next_barline_moment ();
176 if (nb < note_dur.get_length ())
178 note_dur = find_nearest_duration (nb.main_part_);
181 next.main_part_ += note_dur.get_length ();
183 get_global_context ()->add_moment_to_process (next);
184 do_nothing_until_ = next.main_part_;
188 left_to_do_ = orig->get_length ();
190 if (orig && note_dur.get_length () != orig->get_length ())
192 if (!scratch_note_events_.size ())
193 for (vsize i = 0; i < note_events_.size (); i++)
195 Music *m = note_events_[i]->clone ();
196 scratch_note_events_.push_back (m);
200 for (vsize i = 0; left_to_do_ && i < note_events_.size (); i++)
202 Music *event = note_events_[i];
203 if (scratch_note_events_.size ())
205 event = scratch_note_events_[i];
206 SCM pits = note_events_[i]->get_property ("pitch");
207 event->set_property ("pitch", pits);
210 event->set_property ("duration", note_dur.smobbed_copy ());
212 Item *note = make_item ("NoteHead", event->self_scm ());
213 note->set_property ("duration-log",
214 scm_from_int (note_dur.duration_log ()));
216 int dots = note_dur.dot_count ();
219 Item *d = make_item ("Dots", SCM_EOL);
220 Rhythmic_head::set_dots (note, d);
223 measly attempt to save an eeny-weenie bit of memory.
225 if (dots != scm_to_int (d->get_property ("dot-count")))
226 d->set_property ("dot-count", scm_from_int (dots));
228 d->set_parent (note, Y_AXIS);
232 Pitch *pit = unsmob_pitch (event->get_property ("pitch"));
234 int pos = pit->steps ();
235 SCM c0 = get_property ("middleCPosition");
236 if (scm_is_number (c0))
237 pos += scm_to_int (c0);
239 note->set_property ("staff-position", scm_from_int (pos));
240 notes_.push_back (note);
243 if (prev_notes_.size () == notes_.size ())
245 for (vsize i = 0; i < notes_.size (); i++)
247 Grob *p = make_spanner ("Tie", SCM_EOL);
248 Tie::set_head (p, LEFT, prev_notes_[i]);
249 Tie::set_head (p, RIGHT, notes_[i]);
255 left_to_do_ -= note_dur.get_length ();
258 don't do complicated arithmetic with grace notes.
261 && now_mom ().grace_part_)
262 left_to_do_ = Rational (0, 0);
266 Completion_heads_engraver::stop_translation_timestep ()
271 prev_notes_ = notes_;
276 for (vsize i = scratch_note_events_.size (); i--;)
277 scratch_note_events_[i]->unprotect ();
279 scratch_note_events_.clear ();
283 Completion_heads_engraver::start_translation_timestep ()
285 Moment now = now_mom ();
286 if (note_end_mom_.main_part_ <= now.main_part_)
288 note_events_.clear ();
289 prev_notes_.clear ();
293 Completion_heads_engraver::Completion_heads_engraver ()
297 #include "translator.icc"
299 ADD_TRANSLATOR (Completion_heads_engraver,
300 /* doc */ "This engraver replaces "
301 "@code{Note_heads_engraver}. It plays some trickery to "
302 "break long notes and automatically tie them into the next measure.",
303 /* create */ "NoteHead Dots Tie",
304 /* accept */ "note-event",
305 /* read */ "middleCPosition measurePosition measureLength",