2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1998--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
20 #include "engraver.hh"
23 #include "international.hh"
25 #include "note-head.hh"
26 #include "protected-scm.hh"
28 #include "staff-symbol-referencer.hh"
29 #include "stream-event.hh"
30 #include "tie-column.hh"
34 #include "translator.icc"
37 Manufacture ties. Acknowledge note heads, and put them into a
38 priority queue. If we have a TieEvent, connect the notes that finish
39 just at this time, and note that start at this time.
41 TODO: Remove the dependency on musical info. We should tie on the
42 basis of position and duration-log of the heads (not of the events).
45 struct Head_event_tuple
50 Stream_event *tie_stream_event_;
51 Stream_event *tie_event_;
52 // Indicate whether a tie from the same moment has been processed successfully
53 // This is needed for tied chords, e.g. <c e g>~ g, because otherwise the c
54 // and e will trigger a warning for an unterminated tie!
55 bool tie_from_chord_created;
60 tie_definition_ = SCM_EOL;
62 tie_stream_event_ = 0;
63 tie_from_chord_created = false;
67 class Tie_engraver : public Engraver
70 vector<Grob*> now_heads_;
71 vector<Head_event_tuple> heads_to_tie_;
77 void stop_translation_timestep ();
78 virtual void derived_mark () const;
79 void start_translation_timestep ();
80 DECLARE_ACKNOWLEDGER (note_head);
81 DECLARE_TRANSLATOR_LISTENER (tie);
82 void process_music ();
83 void typeset_tie (Grob *);
84 void report_unterminated_tie (Head_event_tuple const &);
85 bool has_autosplit_end (Stream_event *event);
87 TRANSLATOR_DECLARATIONS (Tie_engraver);
91 Tie_engraver::derived_mark () const
93 Engraver::derived_mark ();
94 for (vsize i = 0; i < heads_to_tie_.size (); i++)
95 scm_gc_mark (heads_to_tie_[i].tie_definition_);
98 Tie_engraver::Tie_engraver ()
104 IMPLEMENT_TRANSLATOR_LISTENER (Tie_engraver, tie);
106 Tie_engraver::listen_tie (Stream_event *ev)
108 ASSIGN_EVENT_ONCE (event_, ev);
111 void Tie_engraver::report_unterminated_tie (Head_event_tuple const &tie_start)
113 // If tie_from_chord_created is set, we have another note at the same
114 // moment that created a tie, so this is not necessarily an unterminated
115 // tie. Happens e.g. for <c e g>~ g
116 if (!tie_start.tie_from_chord_created)
117 tie_start.head_->warning (_("unterminated tie"));
121 Determines whether the end of an event was created by
122 a split in Completion_heads_engraver or by user input.
125 Tie_engraver::has_autosplit_end (Stream_event *event)
128 return to_boolean (event->get_property ("autosplit-end"));
133 Tie_engraver::process_music ()
136 for (vsize i = 0; !busy && i < heads_to_tie_.size (); i++)
137 busy |= (heads_to_tie_[i].tie_event_
138 || heads_to_tie_[i].tie_stream_event_);
141 context ()->set_property ("tieMelismaBusy", SCM_BOOL_T);
145 Tie_engraver::acknowledge_note_head (Grob_info i)
149 now_heads_.push_back (h);
150 for (vsize i = heads_to_tie_.size (); i--;)
152 Grob *th = heads_to_tie_[i].head_;
153 Stream_event *right_ev = unsmob_stream_event (h->get_property ("cause"));
154 Stream_event *left_ev = unsmob_stream_event (th->get_property ("cause"));
157 maybe should check positions too.
159 if (!right_ev || !left_ev)
163 Make a tie only if pitches are equal or if event end was not generated by
164 Completion_heads_engraver.
166 if (ly_is_equal (right_ev->get_property ("pitch"), left_ev->get_property ("pitch"))
167 && (!Tie_engraver::has_autosplit_end (left_ev)))
169 Grob *p = new Spanner (heads_to_tie_[i].tie_definition_);
170 Moment end = heads_to_tie_[i].end_moment_;
172 SCM cause = heads_to_tie_[i].tie_event_
173 ? heads_to_tie_[i].tie_event_->self_scm ()
174 : heads_to_tie_[i].tie_stream_event_->self_scm ();
176 announce_grob (p, cause);
177 Tie::set_head (p, LEFT, th);
178 Tie::set_head (p, RIGHT, h);
181 if (is_direction (unsmob_stream_event (cause)->get_property ("direction")))
183 Direction d = to_dir (unsmob_stream_event (cause)->get_property ("direction"));
184 p->set_property ("direction", scm_from_int (d));
188 heads_to_tie_.erase (heads_to_tie_.begin () + i);
191 Prevent all other tied notes ending at the same moment (assume
192 implicitly the notes have also started at the same moment!)
193 from triggering an "unterminated tie" warning. Needed e.g. for
196 for (vsize j = heads_to_tie_.size (); j--;)
198 if (heads_to_tie_[j].end_moment_ == end)
199 heads_to_tie_[j].tie_from_chord_created = true;
204 if (ties_.size () && ! tie_column_)
205 tie_column_ = make_spanner ("TieColumn", ties_[0]->self_scm ());
208 for (vsize i = ties_.size (); i--;)
209 Tie_column::add_tie (tie_column_, ties_[i]);
213 Tie_engraver::start_translation_timestep ()
215 if (heads_to_tie_.size () && !to_boolean (get_property ("tieWaitForNote")))
217 Moment now = now_mom ();
218 for (vsize i = heads_to_tie_.size (); i--; )
220 if (now > heads_to_tie_[i].end_moment_)
222 report_unterminated_tie (heads_to_tie_[i]);
223 heads_to_tie_.erase (heads_to_tie_.begin () + i);
228 context ()->set_property ("tieMelismaBusy",
229 ly_bool2scm (heads_to_tie_.size ()));
233 Tie_engraver::stop_translation_timestep ()
235 bool wait = to_boolean (get_property ("tieWaitForNote"));
240 vector<Head_event_tuple>::iterator it = heads_to_tie_.begin ();
241 for (; it < heads_to_tie_.end (); it++)
242 report_unterminated_tie (*it);
243 heads_to_tie_.clear ();
246 for (vsize i = 0; i < ties_.size (); i++)
247 typeset_tie (ties_[i]);
253 vector<Head_event_tuple> new_heads_to_tie;
256 Whether tie event has been processed and can be deleted or should
257 be kept for later portions of a split note.
259 bool event_processed = false;
261 for (vsize i = 0; i < now_heads_.size (); i++)
263 Grob *head = now_heads_[i];
264 Stream_event *left_ev
265 = unsmob_stream_event (head->get_property ("cause"));
269 // may happen for ambitus
273 // We only want real notes to cause ties, not e.g. pitched trills
274 if (!left_ev->in_event_class ("note-event"))
277 SCM left_articulations = left_ev->get_property ("articulations");
279 Stream_event *tie_event = 0;
280 Stream_event *tie_stream_event = event_;
281 for (SCM s = left_articulations;
282 !tie_event && !tie_stream_event && scm_is_pair (s);
285 Stream_event *ev = unsmob_stream_event (scm_car (s));
289 if (ev->in_event_class ("tie-event"))
293 if (left_ev && (tie_event || tie_stream_event)
294 && (!Tie_engraver::has_autosplit_end (left_ev)))
296 event_processed = true;
298 Head_event_tuple event_tup;
301 = updated_grob_properties (context (), ly_symbol2scm ("Tie"));
303 event_tup.head_ = head;
304 event_tup.tie_definition_ = start_definition;
305 event_tup.tie_event_ = tie_event;
306 event_tup.tie_stream_event_ = tie_stream_event;
308 Moment end = now_mom ();
311 end.grace_part_ += get_event_length (left_ev).main_part_;
315 end += get_event_length (left_ev);
317 event_tup.end_moment_ = end;
319 new_heads_to_tie.push_back (event_tup);
323 if (!wait && new_heads_to_tie.size ())
325 vector<Head_event_tuple>::iterator it=heads_to_tie_.begin ();
326 for (; it < heads_to_tie_.end (); it++)
327 report_unterminated_tie (*it);
328 heads_to_tie_.clear ();
331 // hmmm, how to do with copy () ?
332 for (vsize i = 0; i < new_heads_to_tie.size (); i++)
333 heads_to_tie_.push_back (new_heads_to_tie[i]);
336 Discard event only if it has been processed with at least one
346 Tie_engraver::typeset_tie (Grob *her)
348 if (! (Tie::head (her, LEFT) && Tie::head (her, RIGHT)))
349 warning (_ ("lonely tie"));
352 Drul_array<Grob *> new_head_drul;
353 new_head_drul[LEFT] = Tie::head (her, LEFT);
354 new_head_drul[RIGHT] = Tie::head (her, RIGHT);
357 if (!Tie::head (her, d))
358 new_head_drul[d] = Tie::head (her, (Direction) - d);
360 while (flip (&d) != LEFT);
362 Spanner *sp = dynamic_cast<Spanner*> (her);
363 sp->set_bound (LEFT, new_head_drul[LEFT]);
364 sp->set_bound (RIGHT, new_head_drul[RIGHT]);
367 ADD_ACKNOWLEDGER (Tie_engraver, note_head);
368 ADD_TRANSLATOR (Tie_engraver,
370 "Generate ties between note heads of equal pitch.",