2 head-grav.cc -- part of GNU LilyPond
4 (c) 1997--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
9 #include "rhythmic-head.hh"
10 #include "paper-def.hh"
13 #include "dot-column.hh"
14 #include "staff-symbol-referencer.hh"
16 #include "score-engraver.hh"
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
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_
;
50 Rational do_nothing_until_
;
52 Moment
next_barline_moment ();
53 Duration
find_nearest_duration (Rational length
);
56 TRANSLATOR_DECLARATIONS(Completion_heads_engraver
);
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 ();
67 Completion_heads_engraver::initialize ()
73 Completion_heads_engraver::try_music (Music
*m
)
75 if (m
->is_mus_type ("note-event"))
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);
93 else if (m
->is_mus_type ("busy-playing-event"))
95 return note_reqs_
.size ();
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?");
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);
138 d
= Duration (d
.duration_log () + 1, 2);
142 if (d
.duration_log () >= log_limit
)
145 d
= Duration (d
.duration_log (), 0);
148 d
= d
.compressed (length
/ d
.get_length ());
155 Completion_heads_engraver::process_music ()
157 if (!first_b_
&& !left_to_do_
)
162 Moment now
= now_mom ();
163 if (do_nothing_until_
> now
.main_part_
)
170 note_dur
= find_nearest_duration (left_to_do_
);
174 orig
= unsmob_duration (note_reqs_
[0]->get_mus_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 ();
184 top_engraver ()->add_moment_to_process (next
);
185 do_nothing_until_
= next
.main_part_
;
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
);
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 ();
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
);
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 ());
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
]);
261 announce_grob(p
, SCM_EOL
);
265 left_to_do_
-= note_dur
.get_length ();
268 don't do complicated arithmetic with grace notes.
271 && now_mom().grace_part_
)
273 left_to_do_
= Rational (0,0);
278 Completion_heads_engraver::stop_translation_timestep ()
280 for (int i
= ties_
.size (); i
--;)
281 typeset_grob (ties_
[i
]);
284 for (int i
=0; i
< notes_
.size (); i
++)
286 typeset_grob (notes_
[i
]);
289 prev_notes_
= notes_
;
292 for (int i
=0; i
< dots_
.size (); i
++)
294 typeset_grob (dots_
[i
]);
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();
308 Completion_heads_engraver::start_translation_timestep ()
310 Moment now
= now_mom ();
311 if (note_end_mom_
.main_part_
<= now
.main_part_
)
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",
329 /* reads */ "centralCPosition measurePosition measureLength",