Workaround for broken MusicXML files (percussion clef in MuseScore)
[lilypond.git] / lily / completion-note-heads-engraver.cc
blobb4c4afb0877a7a945e2c385d2eb6cb0fbee262be
1 /*
2 completion-note-heads-engraver.cc -- Completion_heads_engraver
4 (c) 1997--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
5 */
7 #include <cctype>
8 using namespace std;
10 #include "dot-column.hh"
11 #include "dots.hh"
12 #include "duration.hh"
13 #include "global-context.hh"
14 #include "item.hh"
15 #include "output-def.hh"
16 #include "pitch.hh"
17 #include "pqueue.hh"
18 #include "rhythmic-head.hh"
19 #include "score-engraver.hh"
20 #include "spanner.hh"
21 #include "staff-symbol-referencer.hh"
22 #include "stream-event.hh"
23 #include "tie.hh"
24 #include "warn.hh"
26 #include "translator.icc"
29 TODO: make matching rest engraver.
31 struct Pending_tie
33 Moment when_;
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_);
45 How does this work?
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
53 not cross barlines.
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
61 vector<Item*> notes_;
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_;
67 vector<Grob*> ties_;
68 PQueue<Pending_tie> pending_ties_;
69 vector<Stream_event*> note_events_;
71 Stream_event *current_tie_event_;
72 Moment note_end_mom_;
73 bool is_first_;
74 Rational left_to_do_;
75 Rational do_nothing_until_;
77 Moment next_barline_moment ();
78 Item *make_note_head (Stream_event*);
80 public:
81 TRANSLATOR_DECLARATIONS (Completion_heads_engraver);
83 protected:
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);
93 void
94 Completion_heads_engraver::initialize ()
96 is_first_ = false;
97 current_tie_event_ = 0;
100 IMPLEMENT_TRANSLATOR_LISTENER (Completion_heads_engraver, note);
101 void
102 Completion_heads_engraver::listen_note (Stream_event *ev)
104 note_events_.push_back (ev);
106 is_first_ = true;
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);
115 void
116 Completion_heads_engraver::listen_tie (Stream_event *ev)
118 is_first_ = true;
119 current_tie_event_ = ev;
123 The duration _until_ the next barline.
125 Moment
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);
135 return (*l - *e);
138 Item*
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));
151 return note;
154 void
155 Completion_heads_engraver::process_music ()
157 if (!is_first_ && !left_to_do_)
158 return;
160 if (current_tie_event_)
162 Pending_tie pending;
163 pending.when_ = note_end_mom_;
164 pending.tie_event_ = current_tie_event_;
165 pending_ties_.insert (pending);
168 is_first_ = false;
170 Moment now = now_mom ();
171 if (do_nothing_until_ > now.main_part_)
172 return;
174 Duration note_dur;
175 Duration *orig = 0;
176 if (left_to_do_)
177 note_dur = Duration (left_to_do_, false);
178 else
180 orig = unsmob_duration (note_events_[0]->get_property ("duration"));
181 note_dur = *orig;
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 ();
191 if (orig)
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];
199 if (need_clone)
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);
210 if (need_clone)
211 event->unprotect ();
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"));
222 Pitch *p_last
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 ();
236 if (left_to_do_)
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);
245 void
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);
251 ties_.push_back (p);
254 void
255 Completion_heads_engraver::stop_translation_timestep ()
257 ties_.clear ();
259 if (notes_.size ())
260 prev_notes_ = notes_;
261 notes_.clear ();
264 void
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,
290 /* doc */
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.",
295 /* create */
296 "NoteHead "
297 "Dots "
298 "Tie ",
300 /* read */
301 "middleCPosition "
302 "measurePosition "
303 "measureLength ",
305 /* write */
306 "completionBusy "