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 &);
86 TRANSLATOR_DECLARATIONS (Tie_engraver
);
90 Tie_engraver::derived_mark () const
92 Engraver::derived_mark ();
93 for (vsize i
= 0; i
< heads_to_tie_
.size (); i
++)
94 scm_gc_mark (heads_to_tie_
[i
].tie_definition_
);
97 Tie_engraver::Tie_engraver ()
103 IMPLEMENT_TRANSLATOR_LISTENER (Tie_engraver
, tie
);
105 Tie_engraver::listen_tie (Stream_event
*ev
)
107 ASSIGN_EVENT_ONCE (event_
, ev
);
110 void Tie_engraver::report_unterminated_tie (Head_event_tuple
const &tie_start
)
112 // If tie_from_chord_created is set, we have another note at the same
113 // moment that created a tie, so this is not necessarily an unterminated
114 // tie. Happens e.g. for <c e g>~ g
115 if (!tie_start
.tie_from_chord_created
)
116 tie_start
.head_
->warning (_("unterminated tie"));
120 Tie_engraver::process_music ()
123 for (vsize i
= 0; !busy
&& i
< heads_to_tie_
.size (); i
++)
124 busy
|= (heads_to_tie_
[i
].tie_event_
125 || heads_to_tie_
[i
].tie_stream_event_
);
128 context ()->set_property ("tieMelismaBusy", SCM_BOOL_T
);
132 Tie_engraver::acknowledge_note_head (Grob_info i
)
136 now_heads_
.push_back (h
);
137 for (vsize i
= heads_to_tie_
.size (); i
--;)
139 Grob
*th
= heads_to_tie_
[i
].head_
;
140 Stream_event
*right_ev
= unsmob_stream_event (h
->get_property ("cause"));
141 Stream_event
*left_ev
= unsmob_stream_event (th
->get_property ("cause"));
144 maybe should check positions too.
146 if (!right_ev
|| !left_ev
)
149 if (ly_is_equal (right_ev
->get_property ("pitch"),
150 left_ev
->get_property ("pitch")))
152 Grob
*p
= new Spanner (heads_to_tie_
[i
].tie_definition_
);
153 Moment end
= heads_to_tie_
[i
].end_moment_
;
155 SCM cause
= heads_to_tie_
[i
].tie_event_
156 ? heads_to_tie_
[i
].tie_event_
->self_scm ()
157 : heads_to_tie_
[i
].tie_stream_event_
->self_scm ();
159 announce_grob (p
, cause
);
160 Tie::set_head (p
, LEFT
, th
);
161 Tie::set_head (p
, RIGHT
, h
);
164 if (is_direction (unsmob_stream_event (cause
)->get_property ("direction")))
166 Direction d
= to_dir (unsmob_stream_event (cause
)->get_property ("direction"));
167 p
->set_property ("direction", scm_from_int (d
));
171 heads_to_tie_
.erase (heads_to_tie_
.begin () + i
);
173 // Prevent all other tied notes ending at the same moment (assume
174 // implicitly the notes have also started at the same moment!)
175 // from triggering an "unterminated tie" warning. Needed e.g. for
177 for (vsize j
= heads_to_tie_
.size (); j
--;)
179 if (heads_to_tie_
[j
].end_moment_
== end
)
180 heads_to_tie_
[j
].tie_from_chord_created
= true;
185 if (ties_
.size () && ! tie_column_
)
186 tie_column_
= make_spanner ("TieColumn", ties_
[0]->self_scm ());
189 for (vsize i
= ties_
.size (); i
--;)
190 Tie_column::add_tie (tie_column_
, ties_
[i
]);
194 Tie_engraver::start_translation_timestep ()
196 context ()->set_property ("tieMelismaBusy",
197 ly_bool2scm (heads_to_tie_
.size ()));
199 if (heads_to_tie_
.size () && !to_boolean (get_property ("tieWaitForNote")))
201 Moment now
= now_mom ();
202 for (vsize i
= heads_to_tie_
.size (); i
--; )
204 if (now
> heads_to_tie_
[i
].end_moment_
)
206 report_unterminated_tie (heads_to_tie_
[i
]);
207 heads_to_tie_
.erase (heads_to_tie_
.begin () + i
);
214 Tie_engraver::stop_translation_timestep ()
216 bool wait
= to_boolean (get_property ("tieWaitForNote"));
221 vector
<Head_event_tuple
>::iterator it
= heads_to_tie_
.begin ();
222 for (; it
< heads_to_tie_
.end (); it
++)
223 report_unterminated_tie (*it
);
224 heads_to_tie_
.clear ();
227 for (vsize i
= 0; i
< ties_
.size (); i
++)
228 typeset_tie (ties_
[i
]);
234 vector
<Head_event_tuple
> new_heads_to_tie
;
236 for (vsize i
= 0; i
< now_heads_
.size (); i
++)
238 Grob
*head
= now_heads_
[i
];
239 Stream_event
*left_ev
240 = unsmob_stream_event (head
->get_property ("cause"));
244 // may happen for ambitus
249 SCM left_articulations
= left_ev
->get_property ("articulations");
251 Stream_event
*tie_event
= 0;
252 Stream_event
*tie_stream_event
= event_
;
253 for (SCM s
= left_articulations
;
254 !tie_event
&& !tie_stream_event
&& scm_is_pair (s
);
257 Stream_event
*ev
= unsmob_stream_event (scm_car (s
));
261 if (ev
->in_event_class ("tie-event"))
265 if (left_ev
&& (tie_event
|| tie_stream_event
))
267 Head_event_tuple event_tup
;
270 = updated_grob_properties (context (), ly_symbol2scm ("Tie"));
272 event_tup
.head_
= head
;
273 event_tup
.tie_definition_
= start_definition
;
274 event_tup
.tie_event_
= tie_event
;
275 event_tup
.tie_stream_event_
= tie_stream_event
;
277 Moment end
= now_mom ();
280 end
.grace_part_
+= get_event_length (left_ev
).main_part_
;
284 end
+= get_event_length (left_ev
);
286 event_tup
.end_moment_
= end
;
288 new_heads_to_tie
.push_back (event_tup
);
292 if (!wait
&& new_heads_to_tie
.size ())
294 vector
<Head_event_tuple
>::iterator it
=heads_to_tie_
.begin ();
295 for (; it
< heads_to_tie_
.end (); it
++)
296 report_unterminated_tie (*it
);
297 heads_to_tie_
.clear ();
300 // hmmm, how to do with copy () ?
301 for (vsize i
= 0; i
< new_heads_to_tie
.size (); i
++)
302 heads_to_tie_
.push_back (new_heads_to_tie
[i
]);
309 Tie_engraver::typeset_tie (Grob
*her
)
311 if (! (Tie::head (her
, LEFT
) && Tie::head (her
, RIGHT
)))
312 warning (_ ("lonely tie"));
315 Drul_array
<Grob
*> new_head_drul
;
316 new_head_drul
[LEFT
] = Tie::head (her
, LEFT
);
317 new_head_drul
[RIGHT
] = Tie::head (her
, RIGHT
);
320 if (!Tie::head (her
, d
))
321 new_head_drul
[d
] = Tie::head (her
, (Direction
) - d
);
323 while (flip (&d
) != LEFT
);
325 Spanner
*sp
= dynamic_cast<Spanner
*> (her
);
326 sp
->set_bound (LEFT
, new_head_drul
[LEFT
]);
327 sp
->set_bound (RIGHT
, new_head_drul
[RIGHT
]);
330 ADD_ACKNOWLEDGER (Tie_engraver
, note_head
);
331 ADD_TRANSLATOR (Tie_engraver
,
333 "Generate ties between note heads of equal pitch.",