2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1998--2009 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 noteheads, 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_
;
56 tie_definition_
= SCM_EOL
;
58 tie_stream_event_
= 0;
62 class Tie_engraver
: public Engraver
65 vector
<Grob
*> now_heads_
;
66 vector
<Head_event_tuple
> heads_to_tie_
;
72 void stop_translation_timestep ();
73 virtual void derived_mark () const;
74 void start_translation_timestep ();
75 DECLARE_ACKNOWLEDGER (note_head
);
76 DECLARE_TRANSLATOR_LISTENER (tie
);
77 void process_music ();
78 void typeset_tie (Grob
*);
80 TRANSLATOR_DECLARATIONS (Tie_engraver
);
84 Tie_engraver::derived_mark () const
86 Engraver::derived_mark ();
87 for (vsize i
= 0; i
< heads_to_tie_
.size (); i
++)
88 scm_gc_mark (heads_to_tie_
[i
].tie_definition_
);
91 Tie_engraver::Tie_engraver ()
97 IMPLEMENT_TRANSLATOR_LISTENER (Tie_engraver
, tie
);
99 Tie_engraver::listen_tie (Stream_event
*ev
)
101 ASSIGN_EVENT_ONCE (event_
, ev
);
105 Tie_engraver::process_music ()
108 for (vsize i
= 0; !busy
&& i
< heads_to_tie_
.size (); i
++)
109 busy
|= (heads_to_tie_
[i
].tie_event_
110 || heads_to_tie_
[i
].tie_stream_event_
);
113 context ()->set_property ("tieMelismaBusy", SCM_BOOL_T
);
117 Tie_engraver::acknowledge_note_head (Grob_info i
)
121 now_heads_
.push_back (h
);
122 for (vsize i
= heads_to_tie_
.size (); i
--;)
124 Grob
*th
= heads_to_tie_
[i
].head_
;
125 Stream_event
*right_ev
= unsmob_stream_event (h
->get_property ("cause"));
126 Stream_event
*left_ev
= unsmob_stream_event (th
->get_property ("cause"));
129 maybe should check positions too.
131 if (!right_ev
|| !left_ev
)
134 if (ly_is_equal (right_ev
->get_property ("pitch"),
135 left_ev
->get_property ("pitch")))
137 Grob
*p
= new Spanner (heads_to_tie_
[i
].tie_definition_
);
139 SCM cause
= heads_to_tie_
[i
].tie_event_
140 ? heads_to_tie_
[i
].tie_event_
->self_scm ()
141 : heads_to_tie_
[i
].tie_stream_event_
->self_scm ();
143 announce_grob (p
, cause
);
144 Tie::set_head (p
, LEFT
, th
);
145 Tie::set_head (p
, RIGHT
, h
);
148 if (is_direction (unsmob_stream_event (cause
)->get_property ("direction")))
150 Direction d
= to_dir (unsmob_stream_event (cause
)->get_property ("direction"));
151 p
->set_property ("direction", scm_from_int (d
));
155 heads_to_tie_
.erase (heads_to_tie_
.begin () + i
);
159 if (ties_
.size () && ! tie_column_
)
160 tie_column_
= make_spanner ("TieColumn", ties_
[0]->self_scm ());
163 for (vsize i
= ties_
.size (); i
--;)
164 Tie_column::add_tie (tie_column_
, ties_
[i
]);
168 Tie_engraver::start_translation_timestep ()
170 context ()->set_property ("tieMelismaBusy",
171 ly_bool2scm (heads_to_tie_
.size ()));
173 if (heads_to_tie_
.size () && !to_boolean (get_property ("tieWaitForNote")))
175 Moment now
= now_mom ();
176 for (vsize i
= heads_to_tie_
.size (); i
--; )
178 if (now
> heads_to_tie_
[i
].end_moment_
)
179 heads_to_tie_
.erase (heads_to_tie_
.begin () + i
);
185 Tie_engraver::stop_translation_timestep ()
187 bool wait
= to_boolean (get_property ("tieWaitForNote"));
191 heads_to_tie_
.clear ();
193 for (vsize i
= 0; i
< ties_
.size (); i
++)
194 typeset_tie (ties_
[i
]);
200 vector
<Head_event_tuple
> new_heads_to_tie
;
202 for (vsize i
= 0; i
< now_heads_
.size (); i
++)
204 Grob
*head
= now_heads_
[i
];
205 Stream_event
*left_ev
206 = unsmob_stream_event (head
->get_property ("cause"));
210 // may happen for ambituses
215 SCM left_articulations
= left_ev
->get_property ("articulations");
217 Stream_event
*tie_event
= 0;
218 Stream_event
*tie_stream_event
= event_
;
219 for (SCM s
= left_articulations
;
220 !tie_event
&& !tie_stream_event
&& scm_is_pair (s
);
223 Stream_event
*ev
= unsmob_stream_event (scm_car (s
));
227 if (ev
->in_event_class ("tie-event"))
231 if (left_ev
&& (tie_event
|| tie_stream_event
))
233 Head_event_tuple event_tup
;
236 = updated_grob_properties (context (), ly_symbol2scm ("Tie"));
238 event_tup
.head_
= head
;
239 event_tup
.tie_definition_
= start_definition
;
240 event_tup
.tie_event_
= tie_event
;
241 event_tup
.tie_stream_event_
= tie_stream_event
;
243 Moment end
= now_mom ();
246 end
.grace_part_
+= get_event_length (left_ev
).main_part_
;
250 end
+= get_event_length (left_ev
);
252 event_tup
.end_moment_
= end
;
254 new_heads_to_tie
.push_back (event_tup
);
258 if (!wait
&& new_heads_to_tie
.size ())
259 heads_to_tie_
.clear ();
261 // hmmm, how to do with copy () ?
262 for (vsize i
= 0; i
< new_heads_to_tie
.size (); i
++)
263 heads_to_tie_
.push_back (new_heads_to_tie
[i
]);
270 Tie_engraver::typeset_tie (Grob
*her
)
272 if (! (Tie::head (her
, LEFT
) && Tie::head (her
, RIGHT
)))
273 warning (_ ("lonely tie"));
276 Drul_array
<Grob
*> new_head_drul
;
277 new_head_drul
[LEFT
] = Tie::head (her
, LEFT
);
278 new_head_drul
[RIGHT
] = Tie::head (her
, RIGHT
);
281 if (!Tie::head (her
, d
))
282 new_head_drul
[d
] = Tie::head (her
, (Direction
) - d
);
284 while (flip (&d
) != LEFT
);
286 Spanner
*sp
= dynamic_cast<Spanner
*> (her
);
287 sp
->set_bound (LEFT
, new_head_drul
[LEFT
]);
288 sp
->set_bound (RIGHT
, new_head_drul
[RIGHT
]);
291 ADD_ACKNOWLEDGER (Tie_engraver
, note_head
);
292 ADD_TRANSLATOR (Tie_engraver
,
294 "Generate ties between note heads of equal pitch.",