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_
;
52 vector
<Stream_event
*> note_events_
;
57 Rational do_nothing_until_
;
59 Moment
next_barline_moment ();
60 Item
*make_note_head (Stream_event
*);
63 TRANSLATOR_DECLARATIONS (Completion_heads_engraver
);
66 virtual void initialize ();
67 void start_translation_timestep ();
68 void process_music ();
69 void stop_translation_timestep ();
70 DECLARE_TRANSLATOR_LISTENER (note
);
74 Completion_heads_engraver::initialize ()
79 IMPLEMENT_TRANSLATOR_LISTENER (Completion_heads_engraver
, note
);
81 Completion_heads_engraver::listen_note (Stream_event
*ev
)
83 note_events_
.push_back (ev
);
86 Moment now
= now_mom ();
87 Moment musiclen
= get_event_length (ev
, now
);
89 note_end_mom_
= max (note_end_mom_
, (now
+ musiclen
));
90 do_nothing_until_
= Rational (0, 0);
94 The duration _until_ the next barline.
97 Completion_heads_engraver::next_barline_moment ()
99 Moment
*e
= unsmob_moment (get_property ("measurePosition"));
100 Moment
*l
= unsmob_moment (get_property ("measureLength"));
101 if (!e
|| !l
|| !to_boolean (get_property ("timing")))
103 return Moment (0, 0);
110 Completion_heads_engraver::make_note_head (Stream_event
*ev
)
112 Item
*note
= make_item ("NoteHead", ev
->self_scm ());
113 Pitch
*pit
= unsmob_pitch (ev
->get_property ("pitch"));
115 int pos
= pit
->steps ();
116 SCM c0
= get_property ("middleCPosition");
117 if (scm_is_number (c0
))
118 pos
+= scm_to_int (c0
);
120 note
->set_property ("staff-position", scm_from_int (pos
));
126 Completion_heads_engraver::process_music ()
128 if (!is_first_
&& !left_to_do_
)
133 Moment now
= now_mom ();
134 if (do_nothing_until_
> now
.main_part_
)
140 note_dur
= Duration (left_to_do_
, false);
143 orig
= unsmob_duration (note_events_
[0]->get_property ("duration"));
146 Moment nb
= next_barline_moment ();
147 if (nb
.main_part_
&& nb
< note_dur
.get_length ())
149 note_dur
= Duration (nb
.main_part_
, false);
151 do_nothing_until_
= now
.main_part_
+ note_dur
.get_length ();
155 left_to_do_
= orig
->get_length ();
157 for (vsize i
= 0; left_to_do_
&& i
< note_events_
.size (); i
++)
159 bool need_clone
= !orig
|| *orig
!= note_dur
;
160 Stream_event
*event
= note_events_
[i
];
163 event
= event
->clone ();
165 SCM pits
= note_events_
[i
]->get_property ("pitch");
167 event
->set_property ("pitch", pits
);
168 event
->set_property ("duration", note_dur
.smobbed_copy ());
169 event
->set_property ("length", Moment (note_dur
.get_length ()).smobbed_copy ());
170 event
->set_property ("duration-log", scm_from_int (note_dur
.duration_log ()));
172 Item
*note
= make_note_head (event
);
175 notes_
.push_back (note
);
178 if (prev_notes_
.size () == notes_
.size ())
180 for (vsize i
= 0; i
< notes_
.size (); i
++)
182 Grob
*p
= make_spanner ("Tie", SCM_EOL
);
183 Tie::set_head (p
, LEFT
, prev_notes_
[i
]);
184 Tie::set_head (p
, RIGHT
, notes_
[i
]);
190 left_to_do_
-= note_dur
.get_length ();
192 get_global_context ()->add_moment_to_process (now
.main_part_
+ note_dur
.get_length());
194 don't do complicated arithmetic with grace notes.
197 && now_mom ().grace_part_
)
198 left_to_do_
= Rational (0, 0);
202 Completion_heads_engraver::stop_translation_timestep ()
207 prev_notes_
= notes_
;
212 Completion_heads_engraver::start_translation_timestep ()
214 Moment now
= now_mom ();
215 if (note_end_mom_
.main_part_
<= now
.main_part_
)
217 note_events_
.clear ();
218 prev_notes_
.clear ();
220 context ()->set_property ("completionBusy",
221 ly_bool2scm (note_events_
.size ()));
224 Completion_heads_engraver::Completion_heads_engraver ()
228 ADD_TRANSLATOR (Completion_heads_engraver
,
230 "This engraver replaces @code{Note_heads_engraver}. It plays"
231 " some trickery to break long notes and automatically tie them"
232 " into the next measure.",