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"
23 TODO: make matching rest engraver.
29 When we catch the note, we predict the end of the note. We keep the
30 events living until we reach the predicted end-time.
32 Every time process_music () is called and there are note events, we
33 figure out how long the note to typeset should be. It should be no
34 longer than what's specified, than what is left to do and it should
37 We copy the reqs into scratch note reqs, to make sure that we get
38 all durations exactly right.
41 class Completion_heads_engraver : public Engraver
43 Link_array<Item> notes_;
44 Link_array<Item> prev_notes_;
45 Link_array<Grob> ties_;
47 Link_array<Item> dots_;
48 Link_array<Music> note_reqs_;
49 Link_array<Music> scratch_note_reqs_;
54 Rational do_nothing_until_;
56 Moment next_barline_moment ();
57 Duration find_nearest_duration (Rational length);
60 TRANSLATOR_DECLARATIONS (Completion_heads_engraver);
63 virtual void initialize ();
64 virtual void start_translation_timestep ();
65 virtual bool try_music (Music *req);
66 virtual void process_music ();
67 virtual void stop_translation_timestep ();
71 Completion_heads_engraver::initialize ()
77 Completion_heads_engraver::try_music (Music *m)
79 if (m->is_mus_type ("note-event"))
84 Moment musiclen = m->get_length ();
85 Moment now = now_mom ();
87 if (now_mom ().grace_part_)
89 musiclen.grace_part_ = musiclen.main_part_;
90 musiclen.main_part_ = Rational (0, 1);
92 note_end_mom_ = note_end_mom_ >? (now + musiclen);
93 do_nothing_until_ = Rational (0, 0);
97 else if (m->is_mus_type ("busy-playing-event"))
99 return note_reqs_.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);
141 d = Duration (d.duration_log () + 1, 2);
145 if (d.duration_log () >= log_limit)
148 d = Duration (d.duration_log (), 0);
151 d = d.compressed (length / d.get_length ());
158 Completion_heads_engraver::process_music ()
160 if (!is_first_ && !left_to_do_)
165 Moment now = now_mom ();
166 if (do_nothing_until_ > now.main_part_)
173 note_dur = find_nearest_duration (left_to_do_);
177 orig = unsmob_duration (note_reqs_[0]->get_property ("duration"));
180 Moment nb = next_barline_moment ();
181 if (nb < note_dur.get_length ())
183 note_dur = find_nearest_duration (nb.main_part_);
186 next.main_part_ += note_dur.get_length ();
188 get_global_context ()->add_moment_to_process (next);
189 do_nothing_until_ = next.main_part_;
194 left_to_do_ = orig->get_length ();
197 if (orig && note_dur.get_length () != orig->get_length ())
199 if (!scratch_note_reqs_.size ())
200 for (int i = 0; i < note_reqs_.size (); i++)
202 Music *m = note_reqs_[i]->clone ();
203 scratch_note_reqs_.push (m);
208 left_to_do_ && i < note_reqs_.size (); i++)
210 Music *req = note_reqs_[i];
211 if (scratch_note_reqs_.size ())
213 req = scratch_note_reqs_[i];
214 SCM pits = note_reqs_[i]->get_property ("pitch");
215 req->set_property ("pitch", pits);
218 req->set_property ("duration", note_dur.smobbed_copy ());
220 Item *note = make_item ("NoteHead", req->self_scm ());
221 note->set_property ("duration-log",
222 scm_int2num (note_dur.duration_log ()));
224 int dots = note_dur.dot_count ();
227 Item *d = make_item ("Dots", SCM_EOL);
228 Rhythmic_head::set_dots (note, d);
231 measly attempt to save an eeny-weenie bit of memory.
233 if (dots != scm_to_int (d->get_property ("dot-count")))
234 d->set_property ("dot-count", scm_int2num (dots));
236 d->set_parent (note, Y_AXIS);
240 Pitch *pit = unsmob_pitch (req->get_property ("pitch"));
242 int pos = pit->steps ();
243 SCM c0 = get_property ("middleCPosition");
244 if (scm_is_number (c0))
245 pos += scm_to_int (c0);
247 note->set_property ("staff-position", scm_int2num (pos));
251 if (prev_notes_.size () == notes_.size ())
253 for (int i = 0; i < notes_.size (); i++)
255 Grob *p = make_spanner ("Tie", SCM_EOL);
256 Tie::set_interface (p); // cannot remove yet!
258 Tie::set_head (p, LEFT, prev_notes_[i]);
259 Tie::set_head (p, RIGHT, notes_[i]);
265 left_to_do_ -= note_dur.get_length ();
268 don't do complicated arithmetic with grace notes.
271 && now_mom ().grace_part_)
273 left_to_do_ = Rational (0, 0);
278 Completion_heads_engraver::stop_translation_timestep ()
283 prev_notes_ = notes_;
288 for (int i = scratch_note_reqs_.size (); i--;)
290 scm_gc_unprotect_object (scratch_note_reqs_[i]->self_scm ());
293 scratch_note_reqs_.clear ();
297 Completion_heads_engraver::start_translation_timestep ()
299 Moment now = now_mom ();
300 if (note_end_mom_.main_part_ <= now.main_part_)
303 prev_notes_.clear ();
307 Completion_heads_engraver::Completion_heads_engraver ()
311 ADD_TRANSLATOR (Completion_heads_engraver,
312 /* descr */ "This engraver replaces "
313 "@code{Note_heads_engraver}. It plays some trickery to "
314 "break long notes and automatically tie them into the next measure.",
315 /* creats*/ "NoteHead Dots Tie",
316 /* accepts */ "busy-playing-event note-event",
318 /* reads */ "middleCPosition measurePosition measureLength",