2 completion-note-heads-engraver.cc -- Completion_heads_engraver
4 (c) 1997--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
10 #include "dot-column.hh"
12 #include "duration.hh"
13 #include "global-context.hh"
16 #include "output-def.hh"
18 #include "rhythmic-head.hh"
19 #include "score-engraver.hh"
21 #include "staff-symbol-referencer.hh"
22 #include "stream-event.hh"
26 #include "translator.icc"
29 TODO: make matching rest engraver.
35 When we catch the note, we predict the end of the note. We keep the
36 events living until we reach the predicted end-time.
38 Every time process_music () is called and there are note events, we
39 figure out how long the note to typeset should be. It should be no
40 longer than what's specified, than what is left to do and it should
43 We copy the events into scratch note events, to make sure that we get
44 all durations exactly right.
47 class Completion_heads_engraver : public Engraver
50 vector<Item*> prev_notes_;
54 vector<Stream_event*> note_events_;
55 vector<Stream_event*> scratch_note_events_;
60 Rational do_nothing_until_;
62 Moment next_barline_moment ();
63 Duration find_nearest_duration (Rational length);
66 TRANSLATOR_DECLARATIONS (Completion_heads_engraver);
69 virtual void initialize ();
70 void start_translation_timestep ();
71 void process_music ();
72 void stop_translation_timestep ();
73 DECLARE_TRANSLATOR_LISTENER (note);
77 Completion_heads_engraver::initialize ()
82 IMPLEMENT_TRANSLATOR_LISTENER (Completion_heads_engraver, note);
84 Completion_heads_engraver::listen_note (Stream_event *ev)
86 note_events_.push_back (ev);
89 Moment musiclen = get_event_length (ev);
90 Moment now = now_mom ();
92 if (now_mom ().grace_part_)
94 musiclen.grace_part_ = musiclen.main_part_;
95 musiclen.main_part_ = Rational (0, 1);
98 note_end_mom_ = max (note_end_mom_, (now + musiclen));
99 do_nothing_until_ = Rational (0, 0);
103 The duration _until_ the next barline.
106 Completion_heads_engraver::next_barline_moment ()
108 Moment *e = unsmob_moment (get_property ("measurePosition"));
109 Moment *l = unsmob_moment (get_property ("measureLength"));
112 programming_error ("no timing props set?");
113 return Moment (1, 1);
120 Completion_heads_engraver::find_nearest_duration (Rational length)
127 this could surely be done more efficient. Left to the reader as an
129 while (d.get_length () > length && d.duration_log () < log_limit)
133 d = Duration (d.duration_log (), d.dot_count ()- 1);
137 d = Duration (d.duration_log () + 1, 2);
140 if (d.duration_log () >= log_limit)
143 d = Duration (d.duration_log (), 0);
146 d = d.compressed (length / d.get_length ());
153 Completion_heads_engraver::process_music ()
155 if (!is_first_ && !left_to_do_)
160 Moment now = now_mom ();
161 if (do_nothing_until_ > now.main_part_)
167 note_dur = find_nearest_duration (left_to_do_);
170 orig = unsmob_duration (note_events_[0]->get_property ("duration"));
173 Moment nb = next_barline_moment ();
174 if (nb < note_dur.get_length ())
176 note_dur = find_nearest_duration (nb.main_part_);
179 next.main_part_ += note_dur.get_length ();
181 get_global_context ()->add_moment_to_process (next);
182 do_nothing_until_ = next.main_part_;
186 left_to_do_ = orig->get_length ();
188 if (orig && note_dur.get_length () != orig->get_length ())
190 if (!scratch_note_events_.size ())
191 for (vsize i = 0; i < note_events_.size (); i++)
193 Stream_event *m = note_events_[i]->clone ();
194 scratch_note_events_.push_back (m);
198 for (vsize i = 0; left_to_do_ && i < note_events_.size (); i++)
200 Stream_event *event = note_events_[i];
201 if (scratch_note_events_.size ())
203 event = scratch_note_events_[i];
204 SCM pits = note_events_[i]->get_property ("pitch");
205 event->set_property ("pitch", pits);
208 event->set_property ("duration", note_dur.smobbed_copy ());
210 Item *note = make_item ("NoteHead", event->self_scm ());
211 note->set_property ("duration-log",
212 scm_from_int (note_dur.duration_log ()));
214 int dots = note_dur.dot_count ();
217 Item *d = make_item ("Dots", SCM_EOL);
218 Rhythmic_head::set_dots (note, d);
221 measly attempt to save an eeny-weenie bit of memory.
223 if (dots != scm_to_int (d->get_property ("dot-count")))
224 d->set_property ("dot-count", scm_from_int (dots));
226 d->set_parent (note, Y_AXIS);
230 Pitch *pit = unsmob_pitch (event->get_property ("pitch"));
232 int pos = pit->steps ();
233 SCM c0 = get_property ("middleCPosition");
234 if (scm_is_number (c0))
235 pos += scm_to_int (c0);
237 note->set_property ("staff-position", scm_from_int (pos));
238 notes_.push_back (note);
241 if (prev_notes_.size () == notes_.size ())
243 for (vsize i = 0; i < notes_.size (); i++)
245 Grob *p = make_spanner ("Tie", SCM_EOL);
246 Tie::set_head (p, LEFT, prev_notes_[i]);
247 Tie::set_head (p, RIGHT, notes_[i]);
253 left_to_do_ -= note_dur.get_length ();
256 don't do complicated arithmetic with grace notes.
259 && now_mom ().grace_part_)
260 left_to_do_ = Rational (0, 0);
264 Completion_heads_engraver::stop_translation_timestep ()
269 prev_notes_ = notes_;
274 for (vsize i = scratch_note_events_.size (); i--;)
275 scratch_note_events_[i]->unprotect ();
277 scratch_note_events_.clear ();
281 Completion_heads_engraver::start_translation_timestep ()
283 Moment now = now_mom ();
284 if (note_end_mom_.main_part_ <= now.main_part_)
286 note_events_.clear ();
287 prev_notes_.clear ();
291 Completion_heads_engraver::Completion_heads_engraver ()
295 ADD_TRANSLATOR (Completion_heads_engraver,
296 /* doc */ "This engraver replaces "
297 "@code{Note_heads_engraver}. It plays some trickery to "
298 "break long notes and automatically tie them into the next measure.",
299 /* create */ "NoteHead Dots Tie",
300 /* accept */ "note-event",
301 /* read */ "middleCPosition measurePosition measureLength",