LEFT | RIGHT |
1 /* | 1 /* |
2 This file is part of LilyPond, the GNU music typesetter. | 2 This file is part of LilyPond, the GNU music typesetter. |
3 | 3 |
4 Copyright (C) 1997--2012 Han-Wen Nienhuys <hanwen@xs4all.nl> | 4 Copyright (C) 1997--2012 Han-Wen Nienhuys <hanwen@xs4all.nl> |
5 | 5 |
6 LilyPond is free software: you can redistribute it and/or modify | 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 | 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 | 8 the Free Software Foundation, either version 3 of the License, or |
9 (at your option) any later version. | 9 (at your option) any later version. |
10 | 10 |
11 LilyPond is distributed in the hope that it will be useful, | 11 LilyPond is distributed in the hope that it will be useful, |
12 but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 GNU General Public License for more details. | 14 GNU General Public License for more details. |
15 | 15 |
16 You should have received a copy of the GNU General Public License | 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/>. | 17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>. |
18 */ | 18 */ |
19 | 19 |
20 #include "engraver.hh" | 20 #include "engraver.hh" |
21 | 21 |
22 #include "context.hh" | 22 #include "context.hh" |
23 #include "directional-element-interface.hh" | 23 #include "directional-element-interface.hh" |
24 #include "international.hh" | 24 #include "international.hh" |
25 #include "note-column.hh" | 25 #include "note-column.hh" |
26 #include "slur.hh" | 26 #include "slur.hh" |
| 27 #include "slur-proto-engraver.hh" |
27 #include "spanner.hh" | 28 #include "spanner.hh" |
28 #include "stream-event.hh" | 29 #include "stream-event.hh" |
29 #include "warn.hh" | 30 #include "warn.hh" |
30 | 31 |
31 #include "translator.icc" | 32 #include "translator.icc" |
32 | 33 |
33 /* | 34 class Phrasing_slur_engraver : public Slur_proto_engraver |
34 NOTE NOTE NOTE | |
35 | |
36 This is largely similar to Slur_engraver. Check if fixes | |
37 apply there too. | |
38 | |
39 (on principle, engravers don't use inheritance for code sharing) | |
40 | |
41 */ | |
42 | |
43 /* | |
44 It is possible that a slur starts and ends on the same note. At | |
45 least, it is for phrasing slurs: a note can be both beginning and | |
46 ending of a phrase. | |
47 */ | |
48 class Phrasing_slur_engraver : public Engraver | |
49 { | 35 { |
50 vector<Stream_event *> start_events_; | |
51 vector<Stream_event *> stop_events_; | |
52 vector<Grob *> slurs_; | |
53 vector<Grob *> end_slurs_; | |
54 vector<Grob_info> objects_to_acknowledge_; | |
55 Stream_event *break_slur_; | |
56 | |
57 protected: | 36 protected: |
58 DECLARE_TRANSLATOR_LISTENER (phrasing_slur); | 37 DECLARE_TRANSLATOR_LISTENER (phrasing_slur); |
59 DECLARE_TRANSLATOR_LISTENER (break_phrasing_slur); | 38 DECLARE_TRANSLATOR_LISTENER (break_phrasing_slur); |
60 DECLARE_ACKNOWLEDGER (inline_accidental); | |
61 DECLARE_ACKNOWLEDGER (fingering); | |
62 DECLARE_ACKNOWLEDGER (note_column); | |
63 DECLARE_ACKNOWLEDGER (slur); | 39 DECLARE_ACKNOWLEDGER (slur); |
64 DECLARE_ACKNOWLEDGER (script); | |
65 DECLARE_ACKNOWLEDGER (dots); | |
66 DECLARE_ACKNOWLEDGER (text_script); | |
67 DECLARE_END_ACKNOWLEDGER (tie); | |
68 DECLARE_ACKNOWLEDGER (tuplet_number); | |
69 | |
70 void acknowledge_extra_object (Grob_info); | |
71 void stop_translation_timestep (); | |
72 void process_music (); | |
73 | |
74 virtual void finalize (); | |
75 virtual void derived_mark () const; | |
76 | 40 |
77 public: | 41 public: |
78 TRANSLATOR_DECLARATIONS (Phrasing_slur_engraver); | 42 TRANSLATOR_DECLARATIONS (Phrasing_slur_engraver); |
79 }; | 43 }; |
80 | 44 |
81 Phrasing_slur_engraver::Phrasing_slur_engraver () | 45 Phrasing_slur_engraver::Phrasing_slur_engraver () : |
| 46 Slur_proto_engraver (0, "PhrasingSlur", "phrasing slur", "phrasing-slur-event"
) |
82 { | 47 { |
83 } | 48 break_slur_ = 0; |
84 | |
85 void | |
86 Phrasing_slur_engraver::derived_mark () const | |
87 { | |
88 for (vsize i = start_events_.size (); i--;) | |
89 scm_gc_mark (start_events_[i]->self_scm ()); | |
90 for (vsize i = stop_events_.size (); i--;) | |
91 scm_gc_mark (stop_events_[i]->self_scm ()); | |
92 } | 49 } |
93 | 50 |
94 IMPLEMENT_TRANSLATOR_LISTENER (Phrasing_slur_engraver, phrasing_slur); | 51 IMPLEMENT_TRANSLATOR_LISTENER (Phrasing_slur_engraver, phrasing_slur); |
95 void | 52 void |
96 Phrasing_slur_engraver::listen_phrasing_slur (Stream_event *ev) | 53 Phrasing_slur_engraver::listen_phrasing_slur (Stream_event *ev) |
97 { | 54 { |
98 Direction d = to_dir (ev->get_property ("span-direction")); | 55 internal_listen_slur (ev); |
99 if (d == START) | |
100 start_events_.push_back (ev); | |
101 else if (d == STOP) | |
102 stop_events_.push_back (ev); | |
103 else ev->origin ()->warning (_f ("direction of %s invalid: %d", | |
104 "phrasing-slur-event", int (d))); | |
105 } | 56 } |
106 | 57 |
107 IMPLEMENT_TRANSLATOR_LISTENER (Phrasing_slur_engraver, break_phrasing_slur); | 58 IMPLEMENT_TRANSLATOR_LISTENER (Phrasing_slur_engraver, break_phrasing_slur); |
108 void | 59 void |
109 Phrasing_slur_engraver::listen_break_phrasing_slur (Stream_event *ev) | 60 Phrasing_slur_engraver::listen_break_phrasing_slur (Stream_event *ev) |
110 { | 61 { |
111 ASSIGN_EVENT_ONCE (break_slur_, ev); | 62 internal_listen_break_slur (ev); |
112 } | |
113 | |
114 void | |
115 Phrasing_slur_engraver::acknowledge_note_column (Grob_info info) | |
116 { | |
117 Grob *e = info.grob (); | |
118 for (vsize i = slurs_.size (); i--;) | |
119 Slur::add_column (slurs_[i], e); | |
120 for (vsize i = end_slurs_.size (); i--;) | |
121 Slur::add_column (end_slurs_[i], e); | |
122 } | |
123 | |
124 void | |
125 Phrasing_slur_engraver::acknowledge_extra_object (Grob_info info) | |
126 { | |
127 objects_to_acknowledge_.push_back (info); | |
128 } | |
129 | |
130 void | |
131 Phrasing_slur_engraver::acknowledge_inline_accidental (Grob_info info) | |
132 { | |
133 acknowledge_extra_object (info); | |
134 } | |
135 | |
136 void | |
137 Phrasing_slur_engraver::acknowledge_dots (Grob_info info) | |
138 { | |
139 acknowledge_extra_object (info); | |
140 } | |
141 | |
142 void | |
143 Phrasing_slur_engraver::acknowledge_fingering (Grob_info info) | |
144 { | |
145 acknowledge_extra_object (info); | |
146 } | |
147 | |
148 void | |
149 Phrasing_slur_engraver::acknowledge_tuplet_number (Grob_info info) | |
150 { | |
151 acknowledge_extra_object (info); | |
152 } | |
153 | |
154 void | |
155 Phrasing_slur_engraver::acknowledge_script (Grob_info info) | |
156 { | |
157 if (!info.grob ()->internal_has_interface (ly_symbol2scm ("dynamic-interface")
)) | |
158 acknowledge_extra_object (info); | |
159 } | |
160 | |
161 void | |
162 Phrasing_slur_engraver::acknowledge_text_script (Grob_info info) | |
163 { | |
164 acknowledge_extra_object (info); | |
165 } | |
166 | |
167 void | |
168 Phrasing_slur_engraver::acknowledge_end_tie (Grob_info info) | |
169 { | |
170 acknowledge_extra_object (info); | |
171 } | 63 } |
172 | 64 |
173 void | 65 void |
174 Phrasing_slur_engraver::acknowledge_slur (Grob_info info) | 66 Phrasing_slur_engraver::acknowledge_slur (Grob_info info) |
175 { | 67 { |
176 acknowledge_extra_object (info); | 68 acknowledge_extra_object (info); |
177 } | |
178 | |
179 void | |
180 Phrasing_slur_engraver::finalize () | |
181 { | |
182 for (vsize i = 0; i < slurs_.size (); i++) | |
183 { | |
184 slurs_[i]->warning (_ ("unterminated phrasing slur")); | |
185 slurs_[i]->suicide (); | |
186 } | |
187 slurs_.clear (); | |
188 } | |
189 | |
190 void | |
191 Phrasing_slur_engraver::process_music () | |
192 { | |
193 if (break_slur_ | |
194 && unsmob_grob (get_property ("currentCommandColumn"))) | |
195 { | |
196 for (vsize i = slurs_.size (); i--;) | |
197 { | |
198 Grob *ccc = unsmob_grob (get_property ("currentCommandColumn")); | |
199 Spanner *s = dynamic_cast<Spanner *> (slurs_[i]); | |
200 s->set_bound (RIGHT, ccc); | |
201 announce_end_grob (s, SCM_EOL); | |
202 slurs_.erase (slurs_.begin () + i); | |
203 if (robust_scm2dir (break_slur_->get_property ("side-to-junk"), CENTER
) == LEFT) | |
204 s->suicide (); | |
205 | |
206 if (robust_scm2dir (break_slur_->get_property ("side-to-junk"), CENTER
) != RIGHT) | |
207 { | |
208 SCM spanner_id = s->get_property ("spanner-id"); | |
209 Spanner *slur = make_spanner ("Slur", s->self_scm ()); | |
210 slur->set_property ("spanner-id", spanner_id); | |
211 if (scm_is_number (s->get_property_data ("direction"))) | |
212 set_grob_direction (slur, robust_scm2dir (s->get_property ("dire
ction"), UP)); | |
213 slur->set_bound (LEFT, ccc); | |
214 slurs_.push_back (slur); | |
215 } | |
216 } | |
217 } | |
218 else if (to_boolean (get_property ("breakSlurHere"))) | |
219 warning ("no current command column to break slur"); | |
220 | |
221 for (vsize i = 0; i < stop_events_.size (); i++) | |
222 { | |
223 Stream_event *ev = stop_events_[i]; | |
224 string id = robust_scm2string (ev->get_property ("spanner-id"), ""); | |
225 | |
226 // Find the slurs that are ended with this event (by checking the spanner-
id) | |
227 bool ended = false; | |
228 for (vsize j = slurs_.size (); j--;) | |
229 { | |
230 if (id == robust_scm2string (slurs_[j]->get_property ("spanner-id"), "
")) | |
231 { | |
232 ended = true; | |
233 end_slurs_.push_back (slurs_[j]); | |
234 slurs_.erase (slurs_.begin () + j); | |
235 } | |
236 } | |
237 if (ended) | |
238 { | |
239 // Ignore redundant stop events for this id | |
240 for (vsize j = stop_events_.size (); --j > i;) | |
241 { | |
242 if (id == robust_scm2string (stop_events_[j]->get_property ("spann
er-id"), "")) | |
243 stop_events_.erase (stop_events_.begin () + j); | |
244 } | |
245 } | |
246 else | |
247 ev->origin ()->warning (_ ("cannot end phrasing slur")); | |
248 } | |
249 | |
250 vsize old_slurs = slurs_.size (); | |
251 for (vsize i = start_events_.size (); i--;) | |
252 { | |
253 Stream_event *ev = start_events_[i]; | |
254 string id = robust_scm2string (ev->get_property ("spanner-id"), ""); | |
255 Direction updown = to_dir (ev->get_property ("direction")); | |
256 | |
257 bool completed; | |
258 for (vsize j = slurs_.size (); !(completed = (j-- == 0));) | |
259 { | |
260 // Check if we already have a slur with the same spanner-id. | |
261 if (id == robust_scm2string (slurs_[j]->get_property ("spanner-id"), "
")) | |
262 { | |
263 if (j < old_slurs) | |
264 { | |
265 // We already have an old slur, so give a warning | |
266 // and completely ignore the new slur. | |
267 ev->origin ()->warning (_ ("already have phrasing slur")); | |
268 start_events_.erase (start_events_.begin () + i); | |
269 break; | |
270 } | |
271 | |
272 // If this slur event has no direction, it will not | |
273 // contribute anything new to the existing slur(s), so | |
274 // we can ignore it. | |
275 | |
276 if (!updown) | |
277 break; | |
278 | |
279 Stream_event *c = unsmob_stream_event (slurs_[j]->get_property ("c
ause")); | |
280 | |
281 if (!c) | |
282 { | |
283 slurs_[j]->programming_error ("phrasing slur without a cause")
; | |
284 continue; | |
285 } | |
286 | |
287 Direction slur_dir = to_dir (c->get_property ("direction")); | |
288 | |
289 // If the existing slur does not have a direction yet, | |
290 // we'd rather take the new one. | |
291 | |
292 if (!slur_dir) | |
293 { | |
294 slurs_[j]->suicide (); | |
295 slurs_.erase (slurs_.begin () + j); | |
296 continue; | |
297 } | |
298 | |
299 // If the existing slur has the same direction as ours, drop ours | |
300 | |
301 if (slur_dir == updown) | |
302 break; | |
303 } | |
304 } | |
305 // If the loop completed, our slur is new | |
306 if (completed) | |
307 { | |
308 Grob *slur = make_spanner ("PhrasingSlur", ev->self_scm ()); | |
309 slur->set_property ("spanner-id", ly_string2scm (id)); | |
310 if (updown) | |
311 set_grob_direction (slur, updown); | |
312 slurs_.push_back (slur); | |
313 } | |
314 } | |
315 } | |
316 | |
317 void | |
318 Phrasing_slur_engraver::stop_translation_timestep () | |
319 { | |
320 if (Grob *g = unsmob_grob (get_property ("currentCommandColumn"))) | |
321 { | |
322 for (vsize i = 0; i < end_slurs_.size (); i++) | |
323 Slur::add_extra_encompass (end_slurs_[i], g); | |
324 | |
325 if (!start_events_.size ()) | |
326 for (vsize i = 0; i < slurs_.size (); i++) | |
327 Slur::add_extra_encompass (slurs_[i], g); | |
328 } | |
329 | |
330 for (vsize i = 0; i < end_slurs_.size (); i++) | |
331 { | |
332 Spanner *s = dynamic_cast<Spanner *> (end_slurs_[i]); | |
333 if (!s->get_bound (RIGHT)) | |
334 s->set_bound (RIGHT, unsmob_grob (get_property ("currentMusicalColumn"))
); | |
335 announce_end_grob (s, SCM_EOL); | |
336 } | |
337 | |
338 for (vsize i = 0; i < objects_to_acknowledge_.size (); i++) | |
339 Slur::auxiliary_acknowledge_extra_object (objects_to_acknowledge_[i], slurs_
, end_slurs_); | |
340 | |
341 objects_to_acknowledge_.clear (); | |
342 end_slurs_.clear (); | |
343 start_events_.clear (); | |
344 stop_events_.clear (); | |
345 break_slur_ = 0; | |
346 } | 69 } |
347 | 70 |
348 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, inline_accidental); | 71 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, inline_accidental); |
349 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, fingering) | 72 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, fingering) |
350 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, note_column); | 73 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, note_column); |
351 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, slur); | 74 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, slur); |
352 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, script); | 75 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, script); |
353 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, dots); | 76 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, dots); |
354 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, text_script); | 77 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, text_script); |
355 ADD_END_ACKNOWLEDGER (Phrasing_slur_engraver, tie); | 78 ADD_END_ACKNOWLEDGER (Phrasing_slur_engraver, tie); |
356 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, tuplet_number); | 79 ADD_ACKNOWLEDGER (Phrasing_slur_engraver, tuplet_number); |
357 | 80 |
358 ADD_TRANSLATOR (Phrasing_slur_engraver, | 81 ADD_TRANSLATOR (Phrasing_slur_engraver, |
359 /* doc */ | 82 /* doc */ |
360 "Print phrasing slurs. Similar to @ref{Slur_engraver}.", | 83 "Print phrasing slurs. Similar to @ref{Slur_engraver}.", |
361 | 84 |
362 /* create */ | 85 /* create */ |
363 "PhrasingSlur ", | 86 "PhrasingSlur ", |
364 | 87 |
365 /* read */ | 88 /* read */ |
366 "", | 89 "", |
367 | 90 |
368 /* write */ | 91 /* write */ |
369 "" | 92 "" |
370 ); | 93 ); |
LEFT | RIGHT |