2 completion-note-heads-engraver.cc -- Completion_heads_engraver
4 (c) 1997--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
10 #include "dot-column.hh"
12 #include "duration.hh"
13 #include "global-context.hh"
15 #include "output-def.hh"
17 #include "rhythmic-head.hh"
18 #include "score-engraver.hh"
20 #include "staff-symbol-referencer.hh"
21 #include "stream-event.hh"
25 #include "translator.icc"
28 TODO: make matching rest engraver.
34 When we catch the note, we predict the end of the note. We keep the
35 events living until we reach the predicted end-time.
37 Every time process_music () is called and there are note events, we
38 figure out how long the note to typeset should be. It should be no
39 longer than what's specified, than what is left to do and it should
42 We copy the events into scratch note events, to make sure that we get
43 all durations exactly right.
46 class Completion_heads_engraver : public Engraver
49 vector<Item*> prev_notes_;
53 vector<Stream_event*> note_events_;
54 vector<Stream_event*> scratch_note_events_;
59 Rational do_nothing_until_;
61 Moment next_barline_moment ();
62 Duration find_nearest_duration (Rational length);
65 TRANSLATOR_DECLARATIONS (Completion_heads_engraver);
68 virtual void initialize ();
69 void start_translation_timestep ();
70 void process_music ();
71 void stop_translation_timestep ();
72 DECLARE_TRANSLATOR_LISTENER (note);
76 Completion_heads_engraver::initialize ()
81 IMPLEMENT_TRANSLATOR_LISTENER (Completion_heads_engraver, note);
83 Completion_heads_engraver::listen_note (Stream_event *ev)
85 note_events_.push_back (ev);
88 Moment musiclen = get_event_length (ev);
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);
97 note_end_mom_ = max (note_end_mom_, (now + musiclen));
98 do_nothing_until_ = Rational (0, 0);
102 The duration _until_ the next barline.
105 Completion_heads_engraver::next_barline_moment ()
107 Moment *e = unsmob_moment (get_property ("measurePosition"));
108 Moment *l = unsmob_moment (get_property ("measureLength"));
111 programming_error ("no timing props set?");
112 return Moment (1, 1);
119 Completion_heads_engraver::find_nearest_duration (Rational length)
126 this could surely be done more efficient. Left to the reader as an
128 while (d.get_length () > length && d.duration_log () < log_limit)
132 d = Duration (d.duration_log (), d.dot_count ()- 1);
136 d = Duration (d.duration_log () + 1, 2);
139 if (d.duration_log () >= log_limit)
142 d = Duration (d.duration_log (), 0);
145 d = d.compressed (length / d.get_length ());
152 Completion_heads_engraver::process_music ()
154 if (!is_first_ && !left_to_do_)
159 Moment now = now_mom ();
160 if (do_nothing_until_ > now.main_part_)
166 note_dur = find_nearest_duration (left_to_do_);
169 orig = unsmob_duration (note_events_[0]->get_property ("duration"));
172 Moment nb = next_barline_moment ();
173 if (nb < note_dur.get_length ())
175 note_dur = find_nearest_duration (nb.main_part_);
178 next.main_part_ += note_dur.get_length ();
180 get_global_context ()->add_moment_to_process (next);
181 do_nothing_until_ = next.main_part_;
185 left_to_do_ = orig->get_length ();
187 if (orig && note_dur.get_length () != orig->get_length ())
189 if (!scratch_note_events_.size ())
190 for (vsize i = 0; i < note_events_.size (); i++)
192 Stream_event *m = note_events_[i]->clone ();
193 scratch_note_events_.push_back (m);
198 for (vsize i = 0; i < note_events_.size (); i++)
200 Stream_event *c = note_events_[i]->clone ();
201 scratch_note_events_.push_back (c);
205 for (vsize i = 0; left_to_do_ && i < note_events_.size (); i++)
207 Stream_event *event = note_events_[i];
208 if (scratch_note_events_.size ())
210 event = scratch_note_events_[i];
211 SCM pits = note_events_[i]->get_property ("pitch");
212 event->set_property ("pitch", pits);
215 event->set_property ("duration", note_dur.smobbed_copy ());
217 Item *note = make_item ("NoteHead", event->self_scm ());
218 note->set_property ("duration-log",
219 scm_from_int (note_dur.duration_log ()));
221 int dots = note_dur.dot_count ();
224 Item *d = make_item ("Dots", SCM_EOL);
225 Rhythmic_head::set_dots (note, d);
227 d->set_property ("dot-count", scm_from_int (dots));
229 d->set_parent (note, Y_AXIS);
233 Pitch *pit = unsmob_pitch (event->get_property ("pitch"));
235 int pos = pit->steps ();
236 SCM c0 = get_property ("middleCPosition");
237 if (scm_is_number (c0))
238 pos += scm_to_int (c0);
240 note->set_property ("staff-position", scm_from_int (pos));
241 notes_.push_back (note);
244 if (prev_notes_.size () == notes_.size ())
246 for (vsize i = 0; i < notes_.size (); i++)
248 Grob *p = make_spanner ("Tie", SCM_EOL);
249 Tie::set_head (p, LEFT, prev_notes_[i]);
250 Tie::set_head (p, RIGHT, notes_[i]);
256 left_to_do_ -= note_dur.get_length ();
259 don't do complicated arithmetic with grace notes.
262 && now_mom ().grace_part_)
263 left_to_do_ = Rational (0, 0);
267 Completion_heads_engraver::stop_translation_timestep ()
272 prev_notes_ = notes_;
277 for (vsize i = scratch_note_events_.size (); i--;)
278 scratch_note_events_[i]->unprotect ();
280 scratch_note_events_.clear ();
284 Completion_heads_engraver::start_translation_timestep ()
286 Moment now = now_mom ();
287 if (note_end_mom_.main_part_ <= now.main_part_)
289 note_events_.clear ();
290 prev_notes_.clear ();
294 Completion_heads_engraver::Completion_heads_engraver ()
298 ADD_TRANSLATOR (Completion_heads_engraver,
299 /* doc */ "This engraver replaces "
300 "@code{Note_heads_engraver}. It plays some trickery to "
301 "break long notes and automatically tie them into the next measure.",