2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2012 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 "directional-element-interface.hh"
24 #include "international.hh"
25 #include "note-column.hh"
28 #include "stream-event.hh"
31 #include "translator.icc"
38 This is largely similar to Slur_engraver. Check if fixes
41 (on principle, engravers don't use inheritance for code sharing)
43 For info on SlurStubs, check out slur-engraver.cc.
48 It is possible that a slur starts and ends on the same note. At
49 least, it is for phrasing slurs: a note can be both beginning and
52 class Phrasing_slur_engraver : public Engraver
54 vector<Stream_event *> start_events_;
55 vector<Stream_event *> stop_events_;
56 vector<Slur_info> slur_infos_;
57 vector<Slur_info> end_slur_infos_;
58 vector<Grob_info> objects_to_acknowledge_;
61 DECLARE_TRANSLATOR_LISTENER (phrasing_slur);
62 DECLARE_ACKNOWLEDGER (inline_accidental);
63 DECLARE_ACKNOWLEDGER (fingering);
64 DECLARE_ACKNOWLEDGER (note_column);
65 DECLARE_ACKNOWLEDGER (slur);
66 DECLARE_ACKNOWLEDGER (script);
67 DECLARE_ACKNOWLEDGER (dots);
68 DECLARE_ACKNOWLEDGER (text_script);
69 DECLARE_END_ACKNOWLEDGER (tie);
70 DECLARE_ACKNOWLEDGER (tuplet_number);
72 void acknowledge_extra_object (Grob_info);
73 void stop_translation_timestep ();
74 void process_music ();
76 virtual void finalize ();
77 virtual void derived_mark () const;
80 TRANSLATOR_DECLARATIONS (Phrasing_slur_engraver);
83 Phrasing_slur_engraver::Phrasing_slur_engraver ()
88 Phrasing_slur_engraver::derived_mark () const
90 for (vsize i = start_events_.size (); i--;)
91 scm_gc_mark (start_events_[i]->self_scm ());
92 for (vsize i = stop_events_.size (); i--;)
93 scm_gc_mark (stop_events_[i]->self_scm ());
96 IMPLEMENT_TRANSLATOR_LISTENER (Phrasing_slur_engraver, phrasing_slur);
98 Phrasing_slur_engraver::listen_phrasing_slur (Stream_event *ev)
100 Direction d = to_dir (ev->get_property ("span-direction"));
102 start_events_.push_back (ev);
104 stop_events_.push_back (ev);
105 else ev->origin ()->warning (_f ("direction of %s invalid: %d",
106 "phrasing-slur-event", int (d)));
110 Phrasing_slur_engraver::acknowledge_note_column (Grob_info info)
112 Grob *e = info.grob ();
113 for (vsize i = slur_infos_.size (); i--;)
115 Slur::add_column (slur_infos_[i].slur_, e);
116 Grob *stub = make_spanner ("SlurStub", info.grob ()->self_scm ());
117 slur_infos_[i].stubs_.push_back (stub);
119 for (vsize i = end_slur_infos_.size (); i--;)
121 Slur::add_column (end_slur_infos_[i].slur_, e);
122 Grob *stub = make_spanner ("SlurStub", info.grob ()->self_scm ());
123 end_slur_infos_[i].stubs_.push_back (stub);
128 Phrasing_slur_engraver::acknowledge_extra_object (Grob_info info)
130 objects_to_acknowledge_.push_back (info);
134 Phrasing_slur_engraver::acknowledge_inline_accidental (Grob_info info)
136 acknowledge_extra_object (info);
140 Phrasing_slur_engraver::acknowledge_dots (Grob_info info)
142 acknowledge_extra_object (info);
146 Phrasing_slur_engraver::acknowledge_fingering (Grob_info info)
148 acknowledge_extra_object (info);
152 Phrasing_slur_engraver::acknowledge_tuplet_number (Grob_info info)
154 acknowledge_extra_object (info);
158 Phrasing_slur_engraver::acknowledge_script (Grob_info info)
160 if (!info.grob ()->internal_has_interface (ly_symbol2scm ("dynamic-interface")))
162 acknowledge_extra_object (info);
166 Phrasing_slur_engraver::acknowledge_text_script (Grob_info info)
168 acknowledge_extra_object (info);
172 Phrasing_slur_engraver::acknowledge_end_tie (Grob_info info)
174 acknowledge_extra_object (info);
178 Phrasing_slur_engraver::acknowledge_slur (Grob_info info)
180 if (!info.grob ()->internal_has_interface (ly_symbol2scm ("cross-staff-stub-interface")))
181 acknowledge_extra_object (info);
185 Phrasing_slur_engraver::finalize ()
187 for (vsize i = 0; i < slur_infos_.size (); i++)
189 slur_infos_[i].slur_->warning (_ ("unterminated phrasing slur"));
190 slur_infos_[i].slur_->suicide ();
191 for (vsize j = 0; j < slur_infos_[i].stubs_.size (); j++)
192 slur_infos_[i].stubs_[j]->suicide ();
194 slur_infos_.clear ();
198 Phrasing_slur_engraver::process_music ()
200 for (vsize i = 0; i < stop_events_.size (); i++)
202 Stream_event *ev = stop_events_[i];
203 string id = robust_scm2string (ev->get_property ("spanner-id"), "");
205 // Find the slurs that are ended with this event (by checking the spanner-id)
207 for (vsize j = slur_infos_.size (); j--;)
209 if (id == robust_scm2string (slur_infos_[j].slur_->get_property ("spanner-id"), ""))
212 end_slur_infos_.push_back (slur_infos_[j].slur_);
213 slur_infos_.erase (slur_infos_.begin () + j);
218 // Ignore redundant stop events for this id
219 for (vsize j = stop_events_.size (); --j > i;)
221 if (id == robust_scm2string (stop_events_[j]->get_property ("spanner-id"), ""))
222 stop_events_.erase (stop_events_.begin () + j);
226 ev->origin ()->warning (_ ("cannot end phrasing slur"));
229 vsize old_slurs = slur_infos_.size ();
230 for (vsize i = start_events_.size (); i--;)
232 Stream_event *ev = start_events_[i];
233 string id = robust_scm2string (ev->get_property ("spanner-id"), "");
234 Direction updown = to_dir (ev->get_property ("direction"));
237 for (vsize j = slur_infos_.size (); !(completed = (j-- == 0));)
239 // Check if we already have a slur with the same spanner-id.
240 if (id == robust_scm2string (slur_infos_[j].slur_->get_property ("spanner-id"), ""))
244 // We already have an old slur, so give a warning
245 // and completely ignore the new slur.
246 ev->origin ()->warning (_ ("already have phrasing slur"));
247 start_events_.erase (start_events_.begin () + i);
251 // If this slur event has no direction, it will not
252 // contribute anything new to the existing slur(s), so
258 Stream_event *c = unsmob_stream_event (slur_infos_[j].slur_->get_property ("cause"));
262 slur_infos_[j].slur_->programming_error ("phrasing slur without a cause");
266 Direction slur_dir = to_dir (c->get_property ("direction"));
268 // If the existing slur does not have a direction yet,
269 // we'd rather take the new one.
273 slur_infos_[j].slur_->suicide ();
274 for (vsize k = 0; k < slur_infos_[i].stubs_.size (); k++)
275 slur_infos_[j].stubs_[k]->suicide ();
276 slur_infos_.erase (slur_infos_.begin () + j);
280 // If the existing slur has the same direction as ours, drop ours
282 if (slur_dir == updown)
286 // If the loop completed, our slur is new
289 Grob *slur = make_spanner ("PhrasingSlur", ev->self_scm ());
290 slur->set_property ("spanner-id", ly_string2scm (id));
292 set_grob_direction (slur, updown);
293 slur_infos_.push_back (slur);
299 Phrasing_slur_engraver::stop_translation_timestep ()
301 if (Grob *g = unsmob_grob (get_property ("currentCommandColumn")))
303 for (vsize i = 0; i < end_slur_infos_.size (); i++)
304 Slur::add_extra_encompass (end_slur_infos_[i].slur_, g);
306 if (!start_events_.size ())
307 for (vsize i = 0; i < slur_infos_.size (); i++)
308 Slur::add_extra_encompass (slur_infos_[i].slur_, g);
311 for (vsize i = 0; i < end_slur_infos_.size (); i++)
313 Spanner *s = dynamic_cast<Spanner *> (end_slur_infos_[i].slur_);
314 if (!s->get_bound (RIGHT))
315 s->set_bound (RIGHT, unsmob_grob (get_property ("currentMusicalColumn")));
316 announce_end_grob (s, SCM_EOL);
319 for (vsize i = 0; i < objects_to_acknowledge_.size (); i++)
320 Slur::auxiliary_acknowledge_extra_object (objects_to_acknowledge_[i], slur_infos_, end_slur_infos_);
322 for (vsize i = 0; i < end_slur_infos_.size (); i++)
324 // There are likely SlurStubs we don't need. Get rid of them.
326 vector<Grob *> stubs;
327 for (vsize j = 0; j < end_slur_infos_[i].stubs_.size (); j++)
329 Grob *stub = end_slur_infos_[i].stubs_[j];
330 Grob *vag = Grob::get_vertical_axis_group (stub);
333 vector<Grob *>::const_iterator it =
334 find (vags.begin (), vags.end (), vag);
335 if (it != vags.end ())
339 vags.push_back (vag);
340 stubs.push_back (stub);
345 end_slur_infos_[i].slur_->programming_error ("Cannot find vertical axis group for NoteColumn.");
349 for (vsize j = 0; j < stubs.size (); j++)
350 Slur::main_to_stub (end_slur_infos_[i].slur_, stubs[j]);
353 objects_to_acknowledge_.clear ();
354 end_slur_infos_.clear ();
355 start_events_.clear ();
356 stop_events_.clear ();
359 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, inline_accidental);
360 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, fingering)
361 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, note_column);
362 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, slur);
363 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, script);
364 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, dots);
365 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, text_script);
366 ADD_END_ACKNOWLEDGER (Phrasing_slur_engraver, tie);
367 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, tuplet_number);
369 ADD_TRANSLATOR (Phrasing_slur_engraver,
371 "Print phrasing slurs. Similar to @ref{Slur_engraver}.",