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"
27 TODO: make matching rest engraver.
33 When we catch the note, we predict the end of the note. We keep the
34 events living until we reach the predicted end-time.
36 Every time process_music () is called and there are note events, we
37 figure out how long the note to typeset should be. It should be no
38 longer than what's specified, than what is left to do and it should
41 We copy the reqs into scratch note reqs, to make sure that we get
42 all durations exactly right.
45 class Completion_heads_engraver : public Engraver
47 Link_array<Item> notes_;
48 Link_array<Item> prev_notes_;
49 Link_array<Grob> ties_;
51 Link_array<Item> dots_;
52 Link_array<Music> note_reqs_;
53 Link_array<Music> scratch_note_reqs_;
58 Rational do_nothing_until_;
60 Moment next_barline_moment ();
61 Duration find_nearest_duration (Rational length);
64 TRANSLATOR_DECLARATIONS (Completion_heads_engraver);
67 virtual void initialize ();
68 virtual void start_translation_timestep ();
69 virtual bool try_music (Music *req);
70 virtual void process_music ();
71 virtual void stop_translation_timestep ();
75 Completion_heads_engraver::initialize ()
81 Completion_heads_engraver::try_music (Music *m)
83 if (m->is_mus_type ("note-event"))
88 Moment musiclen = m->get_length ();
89 Moment now = now_mom ();
91 if (now_mom ().grace_part_)
93 musiclen.grace_part_ = musiclen.main_part_;
94 musiclen.main_part_ = Rational (0, 1);
96 note_end_mom_ = max (note_end_mom_, (now + musiclen));
97 do_nothing_until_ = Rational (0, 0);
101 else if (m->is_mus_type ("busy-playing-event"))
103 return note_reqs_.size () && is_first_;
110 The duration _until_ the next barline.
113 Completion_heads_engraver::next_barline_moment ()
115 Moment *e = unsmob_moment (get_property ("measurePosition"));
116 Moment *l = unsmob_moment (get_property ("measureLength"));
119 programming_error ("no timing props set?");
120 return Moment (1, 1);
127 Completion_heads_engraver::find_nearest_duration (Rational length)
134 this could surely be done more efficient. Left to the reader as an
136 while (d.get_length () > length && d.duration_log () < log_limit)
140 d = Duration (d.duration_log (), d.dot_count ()- 1);
145 d = Duration (d.duration_log () + 1, 2);
149 if (d.duration_log () >= log_limit)
152 d = Duration (d.duration_log (), 0);
155 d = d.compressed (length / d.get_length ());
162 Completion_heads_engraver::process_music ()
164 if (!is_first_ && !left_to_do_)
169 Moment now = now_mom ();
170 if (do_nothing_until_ > now.main_part_)
177 note_dur = find_nearest_duration (left_to_do_);
181 orig = unsmob_duration (note_reqs_[0]->get_property ("duration"));
184 Moment nb = next_barline_moment ();
185 if (nb < note_dur.get_length ())
187 note_dur = find_nearest_duration (nb.main_part_);
190 next.main_part_ += note_dur.get_length ();
192 get_global_context ()->add_moment_to_process (next);
193 do_nothing_until_ = next.main_part_;
198 left_to_do_ = orig->get_length ();
201 if (orig && note_dur.get_length () != orig->get_length ())
203 if (!scratch_note_reqs_.size ())
204 for (int i = 0; i < note_reqs_.size (); i++)
206 Music *m = note_reqs_[i]->clone ();
207 scratch_note_reqs_.push (m);
212 left_to_do_ && i < note_reqs_.size (); i++)
214 Music *req = note_reqs_[i];
215 if (scratch_note_reqs_.size ())
217 req = scratch_note_reqs_[i];
218 SCM pits = note_reqs_[i]->get_property ("pitch");
219 req->set_property ("pitch", pits);
222 req->set_property ("duration", note_dur.smobbed_copy ());
224 Item *note = make_item ("NoteHead", req->self_scm ());
225 note->set_property ("duration-log",
226 scm_int2num (note_dur.duration_log ()));
228 int dots = note_dur.dot_count ();
231 Item *d = make_item ("Dots", SCM_EOL);
232 Rhythmic_head::set_dots (note, d);
235 measly attempt to save an eeny-weenie bit of memory.
237 if (dots != scm_to_int (d->get_property ("dot-count")))
238 d->set_property ("dot-count", scm_int2num (dots));
240 d->set_parent (note, Y_AXIS);
244 Pitch *pit = unsmob_pitch (req->get_property ("pitch"));
246 int pos = pit->steps ();
247 SCM c0 = get_property ("middleCPosition");
248 if (scm_is_number (c0))
249 pos += scm_to_int (c0);
251 note->set_property ("staff-position", scm_int2num (pos));
255 if (prev_notes_.size () == notes_.size ())
257 for (int i = 0; i < notes_.size (); i++)
259 Grob *p = make_spanner ("Tie", SCM_EOL);
260 Tie::set_interface (p); // cannot remove yet!
262 Tie::set_head (p, LEFT, prev_notes_[i]);
263 Tie::set_head (p, RIGHT, notes_[i]);
269 left_to_do_ -= note_dur.get_length ();
272 don't do complicated arithmetic with grace notes.
275 && now_mom ().grace_part_)
277 left_to_do_ = Rational (0, 0);
282 Completion_heads_engraver::stop_translation_timestep ()
287 prev_notes_ = notes_;
292 for (int i = scratch_note_reqs_.size (); i--;)
294 scm_gc_unprotect_object (scratch_note_reqs_[i]->self_scm ());
297 scratch_note_reqs_.clear ();
301 Completion_heads_engraver::start_translation_timestep ()
303 Moment now = now_mom ();
304 if (note_end_mom_.main_part_ <= now.main_part_)
307 prev_notes_.clear ();
311 Completion_heads_engraver::Completion_heads_engraver ()
315 ADD_TRANSLATOR (Completion_heads_engraver,
316 /* descr */ "This engraver replaces "
317 "@code{Note_heads_engraver}. It plays some trickery to "
318 "break long notes and automatically tie them into the next measure.",
319 /* creats*/ "NoteHead Dots Tie",
320 /* accepts */ "busy-playing-event note-event",
322 /* reads */ "middleCPosition measurePosition measureLength",