2 completion-note-heads-engraver.cc -- Completion_heads_engraver
4 (c) 1997--2005 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
46 Link_array<Item> notes_;
47 Link_array<Item> prev_notes_;
48 Link_array<Grob> ties_;
50 Link_array<Item> dots_;
51 Link_array<Music> note_events_;
52 Link_array<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 (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 (int i = 0; i < note_events_.size (); i++)
197 Music *m = note_events_[i]->clone ();
198 scratch_note_events_.push (m);
203 left_to_do_ && i < note_events_.size (); i++)
205 Music *event = note_events_[i];
206 if (scratch_note_events_.size ())
208 event = scratch_note_events_[i];
209 SCM pits = note_events_[i]->get_property ("pitch");
210 event->set_property ("pitch", pits);
213 event->set_property ("duration", note_dur.smobbed_copy ());
215 Item *note = make_item ("NoteHead", event->self_scm ());
216 note->set_property ("duration-log",
217 scm_from_int (note_dur.duration_log ()));
219 int dots = note_dur.dot_count ();
222 Item *d = make_item ("Dots", SCM_EOL);
223 Rhythmic_head::set_dots (note, d);
226 measly attempt to save an eeny-weenie bit of memory.
228 if (dots != scm_to_int (d->get_property ("dot-count")))
229 d->set_property ("dot-count", scm_from_int (dots));
231 d->set_parent (note, Y_AXIS);
235 Pitch *pit = unsmob_pitch (event->get_property ("pitch"));
237 int pos = pit->steps ();
238 SCM c0 = get_property ("middleCPosition");
239 if (scm_is_number (c0))
240 pos += scm_to_int (c0);
242 note->set_property ("staff-position", scm_from_int (pos));
246 if (prev_notes_.size () == notes_.size ())
248 for (int i = 0; i < notes_.size (); i++)
250 Grob *p = make_spanner ("Tie", SCM_EOL);
251 Tie::set_head (p, LEFT, prev_notes_[i]);
252 Tie::set_head (p, RIGHT, notes_[i]);
258 left_to_do_ -= note_dur.get_length ();
261 don't do complicated arithmetic with grace notes.
264 && now_mom ().grace_part_)
265 left_to_do_ = Rational (0, 0);
269 Completion_heads_engraver::stop_translation_timestep ()
274 prev_notes_ = notes_;
279 for (int i = scratch_note_events_.size (); i--;)
280 scratch_note_events_[i]->unprotect ();
282 scratch_note_events_.clear ();
286 Completion_heads_engraver::start_translation_timestep ()
288 Moment now = now_mom ();
289 if (note_end_mom_.main_part_ <= now.main_part_)
291 note_events_.clear ();
292 prev_notes_.clear ();
296 Completion_heads_engraver::Completion_heads_engraver ()
300 #include "translator.icc"
302 ADD_TRANSLATOR (Completion_heads_engraver,
303 /* doc */ "This engraver replaces "
304 "@code{Note_heads_engraver}. It plays some trickery to "
305 "break long notes and automatically tie them into the next measure.",
306 /* create */ "NoteHead Dots Tie",
307 /* accept */ "busy-playing-event note-event",
308 /* read */ "middleCPosition measurePosition measureLength",