2 completion-note-heads-engraver.cc -- Completion_heads_engraver
4 (c) 1997--2009 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"
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.
34 Stream_event
* tie_event_
;
35 Pending_tie() { tie_event_
= 0; }
38 int compare(Pending_tie
const &a
, Pending_tie
const &b
)
40 return compare(a
.when_
, b
.when_
);
47 When we catch the note, we predict the end of the note. We keep the
48 events living until we reach the predicted end-time.
50 Every time process_music () is called and there are note events, we
51 figure out how long the note to typeset should be. It should be no
52 longer than what's specified, than what is left to do and it should
55 We copy the events into scratch note events, to make sure that we get
56 all durations exactly right.
59 class Completion_heads_engraver
: public Engraver
62 vector
<Item
*> prev_notes_
;
64 // Must remember notes for explicit ties.
65 vector
<Item
*> tie_note_candidates_
;
66 vector
<Stream_event
*> tie_note_candidate_events_
;
68 PQueue
<Pending_tie
> pending_ties_
;
69 vector
<Stream_event
*> note_events_
;
71 Stream_event
*current_tie_event_
;
75 Rational do_nothing_until_
;
77 Moment
next_barline_moment ();
78 Item
*make_note_head (Stream_event
*);
81 TRANSLATOR_DECLARATIONS (Completion_heads_engraver
);
84 virtual void initialize ();
85 void make_tie (Grob
*, Grob
*);
86 void start_translation_timestep ();
87 void process_music ();
88 void stop_translation_timestep ();
89 DECLARE_TRANSLATOR_LISTENER (note
);
90 DECLARE_TRANSLATOR_LISTENER (tie
);
94 Completion_heads_engraver::initialize ()
97 current_tie_event_
= 0;
100 IMPLEMENT_TRANSLATOR_LISTENER (Completion_heads_engraver
, note
);
102 Completion_heads_engraver::listen_note (Stream_event
*ev
)
104 note_events_
.push_back (ev
);
107 Moment now
= now_mom ();
108 Moment musiclen
= get_event_length (ev
, now
);
110 note_end_mom_
= max (note_end_mom_
, (now
+ musiclen
));
111 do_nothing_until_
= Rational (0, 0);
114 IMPLEMENT_TRANSLATOR_LISTENER (Completion_heads_engraver
, tie
);
116 Completion_heads_engraver::listen_tie (Stream_event
*ev
)
119 current_tie_event_
= ev
;
123 The duration _until_ the next barline.
126 Completion_heads_engraver::next_barline_moment ()
128 Moment
*e
= unsmob_moment (get_property ("measurePosition"));
129 Moment
*l
= unsmob_moment (get_property ("measureLength"));
130 if (!e
|| !l
|| !to_boolean (get_property ("timing")))
132 return Moment (0, 0);
139 Completion_heads_engraver::make_note_head (Stream_event
*ev
)
141 Item
*note
= make_item ("NoteHead", ev
->self_scm ());
142 Pitch
*pit
= unsmob_pitch (ev
->get_property ("pitch"));
144 int pos
= pit
->steps ();
145 SCM c0
= get_property ("middleCPosition");
146 if (scm_is_number (c0
))
147 pos
+= scm_to_int (c0
);
149 note
->set_property ("staff-position", scm_from_int (pos
));
155 Completion_heads_engraver::process_music ()
157 if (!is_first_
&& !left_to_do_
)
160 if (current_tie_event_
)
163 pending
.when_
= note_end_mom_
;
164 pending
.tie_event_
= current_tie_event_
;
165 pending_ties_
.insert (pending
);
170 Moment now
= now_mom ();
171 if (do_nothing_until_
> now
.main_part_
)
177 note_dur
= Duration (left_to_do_
, false);
180 orig
= unsmob_duration (note_events_
[0]->get_property ("duration"));
183 Moment nb
= next_barline_moment ();
184 if (nb
.main_part_
&& nb
< note_dur
.get_length ())
186 note_dur
= Duration (nb
.main_part_
, false);
188 do_nothing_until_
= now
.main_part_
+ note_dur
.get_length ();
192 left_to_do_
= orig
->get_length ();
194 for (vsize i
= 0; left_to_do_
&& i
< note_events_
.size (); i
++)
196 bool need_clone
= !orig
|| *orig
!= note_dur
;
197 Stream_event
*event
= note_events_
[i
];
200 event
= event
->clone ();
202 SCM pits
= note_events_
[i
]->get_property ("pitch");
204 event
->set_property ("pitch", pits
);
205 event
->set_property ("duration", note_dur
.smobbed_copy ());
206 event
->set_property ("length", Moment (note_dur
.get_length ()).smobbed_copy ());
207 event
->set_property ("duration-log", scm_from_int (note_dur
.duration_log ()));
209 Item
*note
= make_note_head (event
);
212 notes_
.push_back (note
);
215 if (pending_ties_
.size ()
216 && pending_ties_
.front().when_
== now_mom())
218 for (vsize i
= 0; i
< tie_note_candidate_events_
.size(); i
++)
219 for (vsize j
= 0; j
< note_events_
.size(); j
++)
221 Pitch
*p
= unsmob_pitch (note_events_
[j
]->get_property ("pitch"));
223 = unsmob_pitch (tie_note_candidate_events_
[j
]->get_property ("pitch"));
224 if (p
&& p_last
&& *p
== *p_last
)
225 make_tie (tie_note_candidates_
[i
], notes_
[j
]);
229 if (prev_notes_
.size () == notes_
.size ())
231 for (vsize i
= 0; i
< notes_
.size (); i
++)
232 make_tie (prev_notes_
[i
], notes_
[i
]);
235 left_to_do_
-= note_dur
.get_length ();
237 get_global_context ()->add_moment_to_process (now
.main_part_
+ note_dur
.get_length());
239 don't do complicated arithmetic with grace notes.
241 if (orig
&& now_mom ().grace_part_
)
242 left_to_do_
= Rational (0, 0);
246 Completion_heads_engraver::make_tie (Grob
*left
, Grob
*right
)
248 Grob
*p
= make_spanner ("Tie", SCM_EOL
);
249 Tie::set_head (p
, LEFT
, left
);
250 Tie::set_head (p
, RIGHT
, right
);
255 Completion_heads_engraver::stop_translation_timestep ()
260 prev_notes_
= notes_
;
265 Completion_heads_engraver::start_translation_timestep ()
267 Moment now
= now_mom ();
268 while (pending_ties_
.size() && pending_ties_
.front().when_
< now
)
270 pending_ties_
.delmin();
272 current_tie_event_
= 0;
273 if (note_end_mom_
.main_part_
<= now
.main_part_
)
275 tie_note_candidate_events_
= note_events_
;
276 tie_note_candidates_
= prev_notes_
;
278 note_events_
.clear ();
279 prev_notes_
.clear ();
281 context ()->set_property ("completionBusy",
282 ly_bool2scm (note_events_
.size ()));
285 Completion_heads_engraver::Completion_heads_engraver ()
289 ADD_TRANSLATOR (Completion_heads_engraver
,
291 "This engraver replaces @code{Note_heads_engraver}. It plays"
292 " some trickery to break long notes and automatically tie them"
293 " into the next measure.",