2 tie-engraver.cc -- implement Tie_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 1998--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
10 #include "command-request.hh"
11 #include "rhythmic-head.hh"
12 #include "musical-request.hh"
14 #include "translator-group.hh"
16 #include "tie-column.hh"
18 #include "engraver.hh"
21 struct CHead_melodic_tuple
{
25 CHead_melodic_tuple ();
26 CHead_melodic_tuple (Grob
*, Melodic_req
*, Moment
);
27 static int pitch_compare (CHead_melodic_tuple
const &, CHead_melodic_tuple
const &);
28 static int time_compare (CHead_melodic_tuple
const &, CHead_melodic_tuple
const &);
31 inline int compare (CHead_melodic_tuple
const &a
, CHead_melodic_tuple
const &b
)
33 return CHead_melodic_tuple::time_compare (a
,b
);
38 Manufacture ties. Acknowledge noteheads, and put them into a
39 priority queue. If we have a Tie_req, connect the notes that finish
40 just at this time, and note that start at this time.
42 TODO: junk the pq; the PQ is overkill if we assume that no
43 different durations occur in parallel.
45 class Tie_engraver
: public Engraver
47 PQueue
<CHead_melodic_tuple
> past_notes_pq_
;
52 Array
<CHead_melodic_tuple
> now_heads_
;
53 Array
<CHead_melodic_tuple
> stopped_heads_
;
54 Link_array
<Grob
> tie_p_arr_
;
56 Spanner
* tie_column_p_
;
58 void set_melisma (bool);
61 virtual void start_translation_timestep ();
62 virtual void stop_translation_timestep ();
63 virtual void acknowledge_grob (Grob_info
);
64 virtual bool try_music (Music
*);
65 virtual void create_grobs ();
66 void typeset_tie (Grob
*);
68 VIRTUAL_COPY_CONS (Translator
);
74 Tie_engraver::Tie_engraver ()
82 Tie_engraver::try_music (Music
*m
)
84 if (Tie_req
* c
= dynamic_cast<Tie_req
*> (m
))
86 /* if (end_mom_ > now_mom ())
90 SCM m
= get_property ("automaticMelismata");
91 bool am
= gh_boolean_p (m
) &&gh_scm2bool (m
);
102 Tie_engraver::set_melisma (bool m
)
104 daddy_trans_l_
->set_property ("tieMelismaBusy", m
? SCM_BOOL_T
: SCM_BOOL_F
);
108 Tie_engraver::acknowledge_grob (Grob_info i
)
110 if (Rhythmic_head::has_interface (i
.elem_l_
))
112 Note_req
* m
= dynamic_cast<Note_req
* > (i
.req_l_
);
115 now_heads_
.push (CHead_melodic_tuple (i
.elem_l_
, m
, now_mom ()+ m
->length_mom ()));
121 Tie_engraver::create_grobs ()
125 now_heads_
.sort (CHead_melodic_tuple::pitch_compare
);
126 stopped_heads_
.sort (CHead_melodic_tuple::pitch_compare
);
128 SCM head_list
= SCM_EOL
;
130 int j
= stopped_heads_
.size ()-1;
131 int i
= now_heads_
.size ()-1;
133 while (i
>= 0 && j
>=0)
136 = Pitch::compare (*unsmob_pitch (now_heads_
[i
].req_l_
->get_mus_property ("pitch")),
137 *unsmob_pitch (stopped_heads_
[j
].req_l_
->get_mus_property ("pitch")));
141 (comp
< 0) ? j
-- : i
--;
146 head_list
= gh_cons (gh_cons (stopped_heads_
[j
].head_l_
->self_scm (),
147 now_heads_
[i
].head_l_
->self_scm ()),
150 past_notes_pq_
. insert (now_heads_
[i
]);
152 stopped_heads_
.del (j
);
158 SCM basic
= get_property ("Tie");
159 SCM sparse
= get_property ("sparseTies");
160 if (to_boolean (sparse
))
162 int i
= scm_ilength (head_list
);
167 SCM pair
= gh_list_ref (head_list
, gh_int2scm (i
/2));
169 Spanner
* p
= new Spanner (basic
);
171 Tie::set_interface (p
);
172 Tie::set_head (p
,LEFT
, dynamic_cast<Item
*> (unsmob_grob (gh_car (pair
))));
173 Tie::set_head (p
,RIGHT
, dynamic_cast<Item
*> (unsmob_grob (gh_cdr (pair
))));
176 announce_grob (p
, req_l_
);
178 else for (SCM s
= head_list
; gh_pair_p (s
); s
= gh_cdr (s
))
180 Grob
* p
= new Spanner (basic
);
181 Tie::set_interface (p
);
183 Tie::set_head (p
, LEFT
, dynamic_cast<Item
*> (unsmob_grob (gh_caar (s
))));
184 Tie::set_head (p
, RIGHT
, dynamic_cast<Item
*> (unsmob_grob (gh_cdar (s
))));
187 announce_grob (p
, req_l_
);
190 if (tie_p_arr_
.size () > 1 && !tie_column_p_
)
192 tie_column_p_
= new Spanner (get_property ("TieColumn"));
193 Tie_column::set_interface (tie_column_p_
);
194 for (int i
= tie_p_arr_
.size (); i
--;)
195 Tie_column::add_tie (tie_column_p_
,tie_p_arr_
[i
]);
196 announce_grob (tie_column_p_
, 0);
203 Tie_engraver::stop_translation_timestep ()
205 for (int i
=0; i
< now_heads_
.size (); i
++)
207 past_notes_pq_
.insert (now_heads_
[i
]);
212 we don't warn for no ties, since this happens naturally when you
213 use skipTypesetting. */
216 if (req_l_
&& !tie_p_arr_
.size ())
218 /* How to shut up this warning, when no notes appeared because
219 they were suicided by Thread_devnull_engraver? */
220 req_l_
->origin ()->warning (_ ("No ties were created!"));
224 for (int i
=0; i
< tie_p_arr_
.size (); i
++)
226 typeset_tie (tie_p_arr_
[i
]);
231 typeset_grob (tie_column_p_
);
237 Tie_engraver::typeset_tie (Grob
*her
)
239 if (! (Tie::head (her
,LEFT
) && Tie::head (her
,RIGHT
)))
240 warning (_ ("lonely tie"));
243 Drul_array
<Grob
*> new_head_drul
;
244 new_head_drul
[LEFT
] = Tie::head (her
,LEFT
);
245 new_head_drul
[RIGHT
] = Tie::head (her
,RIGHT
);
247 if (!Tie::head (her
,d
))
248 new_head_drul
[d
] = Tie::head (her
, (Direction
)-d
);
249 } while (flip (&d
) != LEFT
);
251 index_set_cell (her
->get_grob_property ("heads"), LEFT
, new_head_drul
[LEFT
]->self_scm ());
252 index_set_cell (her
->get_grob_property ("heads"), RIGHT
, new_head_drul
[RIGHT
]->self_scm ());
258 Tie_engraver::start_translation_timestep ()
260 SCM m
= get_property ("automaticMelismata");
266 Moment now
= now_mom ();
267 while (past_notes_pq_
.size () && past_notes_pq_
.front ().end_
< now
)
268 past_notes_pq_
.delmin ();
271 stopped_heads_
.clear ();
272 while (past_notes_pq_
.size ()
273 && past_notes_pq_
.front ().end_
== now
)
274 stopped_heads_
.push (past_notes_pq_
.get ());
278 ADD_THIS_TRANSLATOR (Tie_engraver
);
281 CHead_melodic_tuple::CHead_melodic_tuple ()
288 CHead_melodic_tuple::CHead_melodic_tuple (Grob
*h
, Melodic_req
*m
, Moment mom
)
296 signed compare, should use pitch<?
299 CHead_melodic_tuple::pitch_compare (CHead_melodic_tuple
const&h1
,
300 CHead_melodic_tuple
const &h2
)
302 SCM p1
= h1
.req_l_
->get_mus_property ("pitch");
303 SCM p2
= h2
.req_l_
->get_mus_property ("pitch");
305 return Pitch::compare (*unsmob_pitch (p1
),
310 CHead_melodic_tuple::time_compare (CHead_melodic_tuple
const&h1
,
311 CHead_melodic_tuple
const &h2
)
313 return (h1
.end_
- h2
.end_
).sign ();