2 completion-note-heads-engraver.cc -- Completion_heads_engraver
4 (c) 1997--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
9 #include "rhythmic-head.hh"
10 #include "output-def.hh"
13 #include "dot-column.hh"
14 #include "staff-symbol-referencer.hh"
16 #include "score-engraver.hh"
20 #include "global-context.hh"
21 #include "duration.hh"
25 TODO: make matching rest engraver.
31 When we catch the note, we predict the end of the note. We keep the
32 events living until we reach the predicted end-time.
34 Every time process_music () is called and there are note events, we
35 figure out how long the note to typeset should be. It should be no
36 longer than what's specified, than what is left to do and it should
39 We copy the events into scratch note events, to make sure that we get
40 all durations exactly right.
43 class Completion_heads_engraver : public Engraver
45 Link_array<Item> notes_;
46 Link_array<Item> prev_notes_;
47 Link_array<Grob> ties_;
49 Link_array<Item> dots_;
50 Link_array<Music> note_events_;
51 Link_array<Music> scratch_note_events_;
56 Rational do_nothing_until_;
58 Moment next_barline_moment ();
59 Duration find_nearest_duration (Rational length);
62 TRANSLATOR_DECLARATIONS (Completion_heads_engraver);
65 virtual void initialize ();
66 void start_translation_timestep ();
67 virtual bool try_music (Music *event);
68 void process_music ();
69 void stop_translation_timestep ();
73 Completion_heads_engraver::initialize ()
79 Completion_heads_engraver::try_music (Music *m)
81 if (m->is_mus_type ("note-event"))
83 note_events_.push (m);
86 Moment musiclen = m->get_length ();
87 Moment now = now_mom ();
89 if (now_mom ().grace_part_)
91 musiclen.grace_part_ = musiclen.main_part_;
92 musiclen.main_part_ = Rational (0, 1);
94 note_end_mom_ = max (note_end_mom_, (now + musiclen));
95 do_nothing_until_ = Rational (0, 0);
99 else if (m->is_mus_type ("busy-playing-event"))
100 return note_events_.size () && is_first_;
106 The duration _until_ the next barline.
109 Completion_heads_engraver::next_barline_moment ()
111 Moment *e = unsmob_moment (get_property ("measurePosition"));
112 Moment *l = unsmob_moment (get_property ("measureLength"));
115 programming_error ("no timing props set?");
116 return Moment (1, 1);
123 Completion_heads_engraver::find_nearest_duration (Rational length)
130 this could surely be done more efficient. Left to the reader as an
132 while (d.get_length () > length && d.duration_log () < log_limit)
136 d = Duration (d.duration_log (), d.dot_count ()- 1);
140 d = Duration (d.duration_log () + 1, 2);
143 if (d.duration_log () >= log_limit)
146 d = Duration (d.duration_log (), 0);
149 d = d.compressed (length / d.get_length ());
156 Completion_heads_engraver::process_music ()
158 if (!is_first_ && !left_to_do_)
163 Moment now = now_mom ();
164 if (do_nothing_until_ > now.main_part_)
170 note_dur = find_nearest_duration (left_to_do_);
173 orig = unsmob_duration (note_events_[0]->get_property ("duration"));
176 Moment nb = next_barline_moment ();
177 if (nb < note_dur.get_length ())
179 note_dur = find_nearest_duration (nb.main_part_);
182 next.main_part_ += note_dur.get_length ();
184 get_global_context ()->add_moment_to_process (next);
185 do_nothing_until_ = next.main_part_;
189 left_to_do_ = orig->get_length ();
191 if (orig && note_dur.get_length () != orig->get_length ())
193 if (!scratch_note_events_.size ())
194 for (int i = 0; i < note_events_.size (); i++)
196 Music *m = note_events_[i]->clone ();
197 scratch_note_events_.push (m);
202 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));
245 if (prev_notes_.size () == notes_.size ())
247 for (int 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 (int 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",