2 tie-engraver.cc -- implement Tie_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 1998--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
12 #include "international.hh"
14 #include "note-head.hh"
15 #include "protected-scm.hh"
17 #include "staff-symbol-referencer.hh"
18 #include "stream-event.hh"
19 #include "tie-column.hh"
23 #include "translator.icc"
26 Manufacture ties. Acknowledge noteheads, and put them into a
27 priority queue. If we have a TieEvent, connect the notes that finish
28 just at this time, and note that start at this time.
30 TODO: Remove the dependency on musical info. We should tie on the
31 basis of position and duration-log of the heads (not of the events).
34 struct Head_event_tuple
39 Stream_event
*tie_stream_event_
;
40 Stream_event
*tie_event_
;
45 tie_definition_
= SCM_EOL
;
47 tie_stream_event_
= 0;
51 class Tie_engraver
: public Engraver
54 vector
<Grob
*> now_heads_
;
55 vector
<Head_event_tuple
> heads_to_tie_
;
61 void stop_translation_timestep ();
62 virtual void derived_mark () const;
63 void start_translation_timestep ();
64 DECLARE_ACKNOWLEDGER (note_head
);
65 DECLARE_TRANSLATOR_LISTENER (tie
);
66 void process_music ();
67 void typeset_tie (Grob
*);
69 TRANSLATOR_DECLARATIONS (Tie_engraver
);
73 Tie_engraver::derived_mark () const
75 Engraver::derived_mark ();
76 for (vsize i
= 0; i
< heads_to_tie_
.size (); i
++)
77 scm_gc_mark (heads_to_tie_
[i
].tie_definition_
);
80 Tie_engraver::Tie_engraver ()
86 IMPLEMENT_TRANSLATOR_LISTENER (Tie_engraver
, tie
);
88 Tie_engraver::listen_tie (Stream_event
*ev
)
90 ASSIGN_EVENT_ONCE (event_
, ev
);
94 Tie_engraver::process_music ()
97 for (vsize i
= 0; !busy
&& i
< heads_to_tie_
.size (); i
++)
98 busy
|= (heads_to_tie_
[i
].tie_event_
99 || heads_to_tie_
[i
].tie_stream_event_
);
102 context ()->set_property ("tieMelismaBusy", SCM_BOOL_T
);
106 Tie_engraver::acknowledge_note_head (Grob_info i
)
110 now_heads_
.push_back (h
);
111 for (vsize i
= heads_to_tie_
.size (); i
--;)
113 Grob
*th
= heads_to_tie_
[i
].head_
;
114 Stream_event
*right_ev
= unsmob_stream_event (h
->get_property ("cause"));
115 Stream_event
*left_ev
= unsmob_stream_event (th
->get_property ("cause"));
118 maybe should check positions too.
120 if (!right_ev
|| !left_ev
)
123 if (ly_is_equal (right_ev
->get_property ("pitch"),
124 left_ev
->get_property ("pitch")))
126 Grob
*p
= new Spanner (heads_to_tie_
[i
].tie_definition_
);
128 SCM cause
= heads_to_tie_
[i
].tie_event_
129 ? heads_to_tie_
[i
].tie_event_
->self_scm ()
130 : heads_to_tie_
[i
].tie_stream_event_
->self_scm ();
132 announce_grob (p
, cause
);
133 Tie::set_head (p
, LEFT
, th
);
134 Tie::set_head (p
, RIGHT
, h
);
137 if (is_direction (unsmob_stream_event (cause
)->get_property ("direction")))
139 Direction d
= to_dir (unsmob_stream_event (cause
)->get_property ("direction"));
140 p
->set_property ("direction", scm_from_int (d
));
144 heads_to_tie_
.erase (heads_to_tie_
.begin () + i
);
148 if (ties_
.size () && ! tie_column_
)
149 tie_column_
= make_spanner ("TieColumn", ties_
[0]->self_scm ());
152 for (vsize i
= ties_
.size (); i
--;)
153 Tie_column::add_tie (tie_column_
, ties_
[i
]);
157 Tie_engraver::start_translation_timestep ()
159 context ()->set_property ("tieMelismaBusy",
160 ly_bool2scm (heads_to_tie_
.size ()));
162 if (heads_to_tie_
.size () && !to_boolean (get_property ("tieWaitForNote")))
164 Moment now
= now_mom ();
165 for (vsize i
= heads_to_tie_
.size (); i
--; )
167 if (now
> heads_to_tie_
[i
].end_moment_
)
168 heads_to_tie_
.erase (heads_to_tie_
.begin () + i
);
174 Tie_engraver::stop_translation_timestep ()
176 bool wait
= to_boolean (get_property ("tieWaitForNote"));
180 heads_to_tie_
.clear ();
182 for (vsize i
= 0; i
< ties_
.size (); i
++)
183 typeset_tie (ties_
[i
]);
189 vector
<Head_event_tuple
> new_heads_to_tie
;
191 for (vsize i
= 0; i
< now_heads_
.size (); i
++)
193 Grob
*head
= now_heads_
[i
];
194 Stream_event
*left_ev
195 = unsmob_stream_event (head
->get_property ("cause"));
199 // may happen for ambituses
204 SCM left_articulations
= left_ev
->get_property ("articulations");
206 Stream_event
*tie_event
= 0;
207 Stream_event
*tie_stream_event
= event_
;
208 for (SCM s
= left_articulations
;
209 !tie_event
&& !tie_stream_event
&& scm_is_pair (s
);
212 Stream_event
*ev
= unsmob_stream_event (scm_car (s
));
216 if (ev
->in_event_class ("tie-event"))
220 if (left_ev
&& (tie_event
|| tie_stream_event
))
222 Head_event_tuple event_tup
;
225 = updated_grob_properties (context (), ly_symbol2scm ("Tie"));
227 event_tup
.head_
= head
;
228 event_tup
.tie_definition_
= start_definition
;
229 event_tup
.tie_event_
= tie_event
;
230 event_tup
.tie_stream_event_
= tie_stream_event
;
232 Moment end
= now_mom ();
235 end
.grace_part_
+= get_event_length (left_ev
).main_part_
;
239 end
+= get_event_length (left_ev
);
241 event_tup
.end_moment_
= end
;
243 new_heads_to_tie
.push_back (event_tup
);
247 if (!wait
&& new_heads_to_tie
.size ())
248 heads_to_tie_
.clear ();
250 // hmmm, how to do with copy () ?
251 for (vsize i
= 0; i
< new_heads_to_tie
.size (); i
++)
252 heads_to_tie_
.push_back (new_heads_to_tie
[i
]);
259 Tie_engraver::typeset_tie (Grob
*her
)
261 if (! (Tie::head (her
, LEFT
) && Tie::head (her
, RIGHT
)))
262 warning (_ ("lonely tie"));
265 Drul_array
<Grob
*> new_head_drul
;
266 new_head_drul
[LEFT
] = Tie::head (her
, LEFT
);
267 new_head_drul
[RIGHT
] = Tie::head (her
, RIGHT
);
270 if (!Tie::head (her
, d
))
271 new_head_drul
[d
] = Tie::head (her
, (Direction
) - d
);
273 while (flip (&d
) != LEFT
);
275 Spanner
*sp
= dynamic_cast<Spanner
*> (her
);
276 sp
->set_bound (LEFT
, new_head_drul
[LEFT
]);
277 sp
->set_bound (RIGHT
, new_head_drul
[RIGHT
]);
280 ADD_ACKNOWLEDGER (Tie_engraver
, note_head
);
281 ADD_TRANSLATOR (Tie_engraver
,
283 "Generate ties between note heads of equal pitch.",