(process_acknowledged_grobs):
[lilypond.git] / lily / completion-note-heads-engraver.cc
blob641bb2358448f3f6459062b4282fd44bd7f262ae
1 /*
2 head-grav.cc -- part of GNU LilyPond
4 (c) 1997--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 */
7 #include <ctype.h>
9 #include "rhythmic-head.hh"
10 #include "paper-def.hh"
11 #include "event.hh"
12 #include "dots.hh"
13 #include "dot-column.hh"
14 #include "staff-symbol-referencer.hh"
15 #include "item.hh"
16 #include "score-engraver.hh"
17 #include "warn.hh"
18 #include "spanner.hh"
19 #include "tie.hh"
23 How does this work?
25 When we catch the note, we predict the end of the note. We keep the
26 events living until we reach the predicted end-time.
28 Every time process_music() is called and there are note events, we
29 figure out how long the note to typeset should be. It should be no
30 longer than what's specified, than what is left to do and it should
31 not cross barlines.
33 We copy the reqs into scratch note reqs, to make sure that we get
34 all durations exactly right.
37 class Completion_heads_engraver : public Engraver
39 Link_array<Item> notes_;
40 Link_array<Item> prev_notes_;
41 Link_array<Grob> ties_;
43 Link_array<Item> dots_;
44 Link_array<Music> note_reqs_;
45 Link_array<Music> scratch_note_reqs_;
47 Moment note_end_mom_;
48 bool first_b_;
49 Rational left_to_do_;
50 Rational do_nothing_until_;
52 Moment next_barline_moment ();
53 Duration find_nearest_duration (Rational length);
55 public:
56 TRANSLATOR_DECLARATIONS(Completion_heads_engraver);
58 protected:
59 virtual void initialize ();
60 virtual void start_translation_timestep ();
61 virtual bool try_music (Music *req) ;
62 virtual void process_music ();
63 virtual void stop_translation_timestep ();
66 void
67 Completion_heads_engraver::initialize ()
69 first_b_ = false;
72 bool
73 Completion_heads_engraver::try_music (Music *m)
75 if (m->is_mus_type ("note-event"))
77 note_reqs_.push (m);
79 first_b_ = true;
80 Moment musiclen = m->get_length ();
81 Moment now = now_mom();
83 if (now_mom ().grace_part_)
85 musiclen.grace_part_ = musiclen.main_part_ ;
86 musiclen.main_part_ = Rational (0,1);
88 note_end_mom_ = note_end_mom_ >? (now + musiclen);
89 do_nothing_until_ = Rational (0,0);
91 return true;
93 else if (m->is_mus_type ("busy-playing-event"))
95 return note_reqs_.size ();
98 return false;
103 The duration _until_ the next barline.
105 Moment
106 Completion_heads_engraver::next_barline_moment ( )
108 Moment *e = unsmob_moment (get_property ("measurePosition"));
109 Moment *l = unsmob_moment (get_property ("measureLength"));
110 if (!e || !l)
112 programming_error ("No timing props set?");
113 return Moment (1,1);
116 return (*l - *e);
119 Duration
120 Completion_heads_engraver::find_nearest_duration (Rational length)
122 int log_limit= 6;
124 Duration d(0,0);
127 this could surely be done more efficient. Left to the reader as an
128 excercise. */
129 while (d.get_length () > length && d.duration_log () < log_limit)
131 if (d.dot_count ())
133 d = Duration (d.duration_log (), d.dot_count ()- 1);
134 continue;
136 else
138 d = Duration (d.duration_log () + 1, 2);
142 if (d.duration_log () >= log_limit)
144 // junk the dots.
145 d = Duration (d.duration_log (), 0);
147 // scale up.
148 d = d.compressed (length / d.get_length ());
151 return d;
154 void
155 Completion_heads_engraver::process_music ()
157 if (!first_b_ && !left_to_do_)
158 return ;
160 first_b_ = false;
162 Moment now = now_mom ();
163 if (do_nothing_until_ > now.main_part_)
164 return ;
166 Duration note_dur;
167 Duration *orig = 0;
168 if (left_to_do_)
170 note_dur = find_nearest_duration (left_to_do_);
172 else
174 orig = unsmob_duration (note_reqs_[0]->get_mus_property ("duration"));
175 note_dur = *orig;
177 Moment nb = next_barline_moment ();
178 if (nb < note_dur.get_length ())
180 note_dur = find_nearest_duration (nb.main_part_);
182 Moment next = now;
183 next.main_part_ += note_dur.get_length ();
184 top_engraver ()->add_moment_to_process (next);
185 do_nothing_until_ = next.main_part_;
188 if (orig)
190 left_to_do_ = orig->get_length ();
193 if (orig && note_dur.get_length () != orig->get_length ())
195 if (!scratch_note_reqs_.size ())
196 for (int i = 0; i < note_reqs_.size (); i++)
198 Music * m = note_reqs_[i]->clone ();
199 scratch_note_reqs_.push (m);
204 for (int i = 0;
205 left_to_do_ && i < note_reqs_.size (); i++)
207 Item *note = new Item (get_property ("NoteHead"));
209 Music * req = note_reqs_[i];
210 if (scratch_note_reqs_.size())
212 req = scratch_note_reqs_[i];
213 SCM pits = note_reqs_[i]->get_mus_property ("pitch");
214 req->set_mus_property ("pitch",pits);
217 req->set_mus_property ("duration", note_dur.smobbed_copy ());
218 note->set_grob_property ("duration-log",
219 gh_int2scm (note_dur.duration_log ()));
221 int dots= note_dur.dot_count ();
222 if (dots)
224 Item * d = new Item (get_property ("Dots"));
225 Rhythmic_head::set_dots (note, d);
228 measly attempt to save an eeny-weenie bit of memory.
230 if (dots != gh_scm2int (d->get_grob_property ("dot-count")))
231 d->set_grob_property ("dot-count", gh_int2scm (dots));
233 d->set_parent (note, Y_AXIS);
234 announce_grob (d, SCM_EOL);
235 dots_.push (d);
238 Pitch *pit =unsmob_pitch (req->get_mus_property ("pitch"));
240 int pos = pit->steps ();
241 SCM c0 = get_property ("centralCPosition");
242 if (gh_number_p (c0))
243 pos += gh_scm2int (c0);
245 note->set_grob_property ("staff-position", gh_int2scm (pos));
246 announce_grob (note,req->self_scm ());
247 notes_.push (note);
250 if (prev_notes_.size() == notes_.size ())
252 for (int i= 0; i < notes_.size(); i++)
254 Grob * p = new Spanner (get_property ("Tie"));
255 Tie::set_interface (p); // cannot remove yet!
257 Tie::set_head (p, LEFT, prev_notes_[i]);
258 Tie::set_head (p, RIGHT, notes_[i]);
260 ties_.push (p);
261 announce_grob(p, SCM_EOL);
265 left_to_do_ -= note_dur.get_length ();
268 don't do complicated arithmetic with grace notes.
270 if (orig
271 && now_mom().grace_part_ )
273 left_to_do_ = Rational (0,0);
277 void
278 Completion_heads_engraver::stop_translation_timestep ()
280 for (int i = ties_.size (); i--;)
281 typeset_grob (ties_[i]);
282 ties_.clear();
284 for (int i=0; i < notes_.size (); i++)
286 typeset_grob (notes_[i]);
288 if (notes_.size())
289 prev_notes_ = notes_;
290 notes_.clear ();
292 for (int i=0; i < dots_.size (); i++)
294 typeset_grob (dots_[i]);
296 dots_.clear ();
298 for (int i = scratch_note_reqs_.size(); i--;)
300 scm_gc_unprotect_object (scratch_note_reqs_[i]->self_scm () );
304 scratch_note_reqs_.clear();
307 void
308 Completion_heads_engraver::start_translation_timestep ()
310 Moment now = now_mom ();
311 if (note_end_mom_.main_part_ <= now.main_part_)
313 note_reqs_.clear ();
314 prev_notes_.clear ();
318 Completion_heads_engraver::Completion_heads_engraver()
322 ENTER_DESCRIPTION(Completion_heads_engraver,
323 /* descr */ "This engraver replaces "
324 "@code{Note_heads_engraver}. It plays some trickery to "
325 "break long notes and automatically tie them into the next measure.",
326 /* creats*/ "NoteHead Dots Tie",
327 /* accepts */ "busy-playing-event note-event",
328 /* acks */ "",
329 /* reads */ "centralCPosition measurePosition measureLength",
330 /* write */ "");