2 mensural-ligature-engraver.cc -- implement Mensural_ligature_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 2002--2003 Juergen Reuter <reuter@ipd.uka.de>
9 #include "mensural-ligature.hh"
10 #include "ligature-engraver.hh"
16 #include "paper-column.hh"
17 #include "note-column.hh"
18 #include "rhythmic-head.hh"
19 #include "note-head.hh"
20 #include "staff-symbol-referencer.hh"
21 #include "paper-def.hh"
22 #include "font-interface.hh"
25 * TODO: local accidentals: collect accidentals that occur within a
26 * ligature and put them before the ligature. If an accidental
27 * changes within a ligature, print a warning (user error) and ignore
28 * any further accidental for that pitch within that ligature
29 * (actually, in such a case, the user should split the ligature into
30 * two separate ligatures). Similarly, any object that, in ordinary
31 * notation, may be put to the left or to the right of a
32 * note-head/ligature-head, should be collected and put before or
35 * TODO: make spacing more robust: do not screw up spacing if user
36 * erroneously puts rest in ligature.
38 * TODO: My resources on Franco of Cologne's rules claim that his
39 * rules map ligature<->mensural timing in a non-ambigous way, but in
40 * fact, as presented in these resources, the rules become ambigous as
41 * soon as there appear durations other than breves within a ligature
42 * with more than two heads (ligatura ternaria etc.). Hence, the
43 * below implementation is an approximation of what I think the rules
44 * could look like if forced to be non-ambigous. This should be
45 * further investigated.
47 * TODO: The automat is quite complicated, and its design is error
48 * prone (and most probably, it behaves wrong for some very special
49 * cases). Maybe we can find a better paradigm for modelling Franco
52 * TODO: dotted heads: when applying Franco of Cologne's mapping, put
53 * dots *above* (rather than after) affected ligature heads.
55 * TODO: prohibit multiple voices within a ligature.
57 * TODO: for each ligature, add Rod that represents the total length
58 * of the ligature (to preemptively avoid collision with adjacent
59 * notes); or maybe just additionally create a mensural-ligature grob
60 * (via Mensural_ligature::brew_molecule(SCM)) that just consists of a
61 * bounding box around all primitives of the ligature.
63 * TODO: enhance robustness: in case of an illegal ligature (e.g. the
64 * user events for a ligature that contains a minima or STATE_ERROR
65 * is reached), automatically break the ligature into smaller, valid
68 * TODO: In the future, there will be further ligature engravers
69 * implemented, such as a Vaticana_ligature_engraver. There will be
70 * redundant code between these engravers and the
71 * Mensural_ligature_engraver. In particular these are functions
72 * set_column_, fold_up_primitives, join_primitives, and
73 * ackowledge_grob; further the code for handling accidentals. It is
74 * not appropriate to put these things into Ligature_engraver, since,
75 * for example, Ligature_bracket_engraver does not share any of this
76 * code. Hence, we might to introduce a further subclass of
77 * Ligature_engraver which serves as super class for
78 * Mensural_ligature_engraver, Vaticana_ligature_engraver, among
81 class Mensural_ligature_engraver : public Ligature_engraver
85 virtual Spanner *create_ligature_spanner ();
86 virtual void typeset_ligature (Spanner *ligature,
87 Array<Grob_info> primitives);
90 TRANSLATOR_DECLARATIONS(Mensural_ligature_engraver);
93 int apply_transition (Array<Grob_info> primitives,
94 int state, int input, int i);
95 void transform_heads (Array<Grob_info> primitives);
96 void propagate_properties (Spanner *ligature, Array<Grob_info> primitives);
97 void fold_up_primitives (Array<Grob_info> primitives);
98 void join_primitives (Array<Grob_info> primitives);
99 void get_set_column (Item *item, Paper_column *new_col);
103 Mensural_ligature_engraver::Mensural_ligature_engraver ()
108 Mensural_ligature_engraver::create_ligature_spanner ()
110 return new Spanner (get_property ("MensuralLigature"));
114 * TODO: move this function to class Item?
117 Mensural_ligature_engraver::get_set_column (Item *item, Paper_column *column)
119 Item *parent = dynamic_cast<Item*> (item->get_parent (X_AXIS));
122 programming_error ("failed tweaking paper column in ligature");
126 String name = parent->name ();
127 if (!String::compare (name, "PaperColumn"))
129 // Change column not only for targeted item (NoteColumn), but
130 // also for all associated grobs (NoteSpacing, SeparationItem).
131 Grob *sl = Staff_symbol_referencer::get_staff_symbol (item);
132 for (SCM tail = parent->get_grob_property ("elements");
134 tail = ly_cdr (tail))
136 Item *sibling = unsmob_item (ly_car (tail));
138 (Staff_symbol_referencer::get_staff_symbol (sibling) == sl))
140 sibling->set_parent (column, X_AXIS);
146 get_set_column (parent, column);
151 * The following lines implement a finite state automat. Given a
152 * sequence of durations (Longa, Brevis, Semibrevis) or
153 * end-of-ligature-event as input, the automat outputs a sequence of
154 * events for grobs that form a proper ligature.
158 * This enumeration represents the set of possible input values to the
159 * automat. There may (potentially) be any sequence of Longa, Brevis,
160 * and Semibrevis duration symbols fed into the automat, with a final
161 * EndOfLigature symbol to terminate the ligature. Other durations
162 * are explicitly prohibited. Depending on the note's pitch of the
163 * preceding and the current input, the melodic line may be ascending
164 * or descending. Per definition, the melodic line must either ascend
165 * or descend, because if the pitches were twice the same, the two
166 * notes would be merged into a single one (as long as not resulting
167 * in a prohibited duration). In the case of the EndOfLigature
168 * symbol, the melodic line is undefined (but we still have ascending
169 * and descending case for the sake of consistency, making the automat
174 // Ascending/Descending Longa/Brevis/Semibrevis/EndOfLigature
186 * This enumeration represents all possible internal states of the
187 * automat. Besides the generic states START, ERROR, and END, the
188 * remaining states L, B, S, and SS describe pending values from the
189 * sequence of input values that have not yet been transformed to
190 * proper output values, including the melodic direction
191 * (ascending/descending) for state L.
195 // aL = ascending Longa, dL descending Longa, B = Brevis, S =
196 // Semibrevis, SS = 2 Semibreves
208 * The following array represents the transitions of the automat:
209 * given some state and input, it maps to a new state, according (with
210 * the limitations as described above) to the rules of Franco of
213 const int/*new state*/ transition_state[/*old state*/][8/*input*/] =
215 {STATE_aL, STATE_dL, STATE_B, STATE_B,
216 STATE_S, STATE_S, STATE_ERROR, STATE_ERROR}, // was: STATE_START
217 {STATE_aL, STATE_dL, STATE_B, STATE_START,
218 STATE_ERROR, STATE_ERROR, STATE_END, STATE_END}, // was: STATE_aL
219 {STATE_aL, STATE_dL, STATE_B, STATE_START,
220 STATE_ERROR, STATE_ERROR, STATE_END, STATE_END}, // was: STATE_dL
221 {STATE_aL, STATE_dL, STATE_B, STATE_START,
222 STATE_ERROR, STATE_ERROR, STATE_END, STATE_END}, // was: STATE_B
223 {STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR,
224 STATE_SS, STATE_SS, STATE_ERROR, STATE_ERROR}, // was: STATE_S
225 {STATE_aL, STATE_dL, STATE_B, STATE_B,
226 STATE_S, STATE_S, STATE_END, STATE_END}, // was: STATE_SS
227 {STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR,
228 STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR}, // was: STATE_ERROR
229 {STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR,
230 STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR}, // was: STATE_END
234 * The following array represents the output of the automat while
235 * switching from one state to another: given some state and input, it
236 * maps to the output produced when switching to the next state,
237 * according (with the limitations as described above) to the rules of
240 const int/*output*/ transition_output[/*old state*/][8/*input*/] =
242 {MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE,
243 MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE}, // was: STATE_START
244 {MLP_sc, MLP_ss, MLP_sc, MLP_LB,
245 MLP_NONE, MLP_NONE, MLP_sc, MLP_sc}, // was: STATE_aL
246 {MLP_sc, MLP_ss, MLP_sc, MLP_LB,
247 MLP_NONE, MLP_NONE, MLP_ss, MLP_ss}, // was: STATE_dL
248 {MLP_ss, MLP_cs, MLP_ss, MLP_BB,
249 MLP_NONE, MLP_NONE, MLP_ss, MLP_ss} , // was: STATE_B
250 {MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE,
251 MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE}, // was: STATE_S
252 {MLP_SS, MLP_SS, MLP_SS, MLP_SS,
253 MLP_SS, MLP_SS, MLP_SS, MLP_SS} , // was: STATE_SS
254 {MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE,
255 MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE}, // was: STATE_ERROR
256 {MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE,
257 MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE}, // was: STATE_END
261 Mensural_ligature_engraver::apply_transition (Array<Grob_info> primitives,
262 int state, int input, int i)
264 int output = transition_output[state][input];
265 Item *last_last_primitive = (i > 1) ?
266 dynamic_cast<Item*> (primitives[i-2].grob_) : 0;
267 Item *last_primitive = (i > 0) ?
268 dynamic_cast<Item*> (primitives[i-1].grob_) : 0;
269 Item *primitive = (i < primitives.size ()) ?
270 dynamic_cast<Item*> (primitives[i].grob_) : 0;
274 // skip note head, expecting a primitive with two note heads
279 // primitive with single note head
282 programming_error ("last_primitive undefined");
285 last_primitive->set_grob_property ("primitive", gh_int2scm (output));
289 // primitive with two note heads
292 programming_error ("last_primitive undefined");
297 programming_error ("primitive undefined");
300 last_primitive->set_grob_property ("primitive", gh_int2scm (output));
301 primitive->set_grob_property ("primitive", gh_int2scm (MLP_NONE));
304 // delayed primitive with two note heads
305 if (!last_last_primitive)
307 programming_error ("last_last_primitive undefined");
312 programming_error ("last_primitive undefined");
315 last_last_primitive->set_grob_property ("primitive", gh_int2scm (output));
316 last_primitive->set_grob_property ("primitive", gh_int2scm (MLP_NONE));
319 programming_error (_f ("unexpected case fall-through"));
322 return transition_state[state][input];
326 Mensural_ligature_engraver::transform_heads (Array<Grob_info> primitives)
328 if (primitives.size () < 2)
330 warning (_f ("ligature with less than 2 heads -> skipping"));
333 int state = STATE_START;
334 Pitch last_pitch, pitch;
335 bool have_last_pitch = 0, have_pitch = 0;
336 for (int i = 0; i < primitives.size (); i++) {
338 have_last_pitch = have_pitch;
339 Grob_info info = primitives[i];
341 Note_head::get_balltype (dynamic_cast<Item*> (info.grob_));
343 Music *nr = info.music_cause ();
346 ugh. why not simply check for pitch?
348 if (!nr->is_mus_type ("note-event"))
350 info.music_cause ()->origin ()->warning (_f ("can not determine pitch of ligature primitive -> skipping"));
358 pitch = *unsmob_pitch (nr->get_mus_property ("pitch"));
364 if (!have_last_pitch)
366 delta_pitch = 0; // first pitch; delta undefined
370 delta_pitch = (pitch.steps () - last_pitch.steps ());
371 if (Pitch::compare (last_pitch, pitch) == 0)
373 info.music_cause ()->origin ()->warning (_f ("prime interval within ligature -> skipping"));
381 if ((duration_log < -2) || (duration_log > 0))
383 info.music_cause ()->origin ()->warning (_f ("mensural ligature: duration none of L, B, S -> skipping"));
390 int input = (duration_log + 2) * 2 + ((delta_pitch < 0) ? 1 : 0);
391 state = apply_transition (primitives, state, input, i);
392 // TODO: if (state == STATE_ERROR) { ... }
395 state = apply_transition (primitives, state, INPUT_AE, primitives.size ());
396 // TODO: if (state == STATE_ERROR) { ... }
399 void set_delta_pitch (Item *primitive, Grob_info info1, Grob_info info2)
401 Pitch pitch1 = *unsmob_pitch (info1.music_cause ()->get_mus_property ("pitch"));
402 Pitch pitch2 = *unsmob_pitch (info2.music_cause ()->get_mus_property ("pitch"));
403 int delta_pitch = (pitch2.steps () - pitch1.steps ());
404 primitive->set_grob_property ("delta-pitch", gh_int2scm (delta_pitch));
408 * A MensuralLigature grob consists of a bunch of LigatureHead grobs
409 * that are glued together. It (a) does not make sense to change
410 * properties like thickness or flexa-width from one head to the next
411 * within a ligature (this would totally screw up alignment), and (b)
412 * some of these properties (like flexa-width) are specific to
413 * e.g. the MensuralLigature (as in contrast to e.g. LigatureBracket),
414 * and therefore should not be handled in the generic LigatureHead
415 * (which is also used by LigatureBracket). Therefore, we let the
416 * user control these properties via the concrete Ligature grob (like
417 * MensuralLigature) and then copy these properties as necessary to
418 * each of the LigatureHead grobs. This is what
419 * propagate_properties() does.
422 Mensural_ligature_engraver::propagate_properties (Spanner *ligature,
423 Array<Grob_info> primitives)
425 SCM thickness_scm = ligature->get_grob_property ("thickness");
426 Real thickness = (thickness_scm != SCM_EOL) ?
427 gh_scm2double (thickness_scm) : 1.4;
428 thickness *= ligature->get_paper ()->get_var ("linethickness");
431 Font_interface::get_default_font (ligature)->
432 find_by_name ("noteheads--1mensural").extent (X_AXIS).length ();
433 SCM flexa_width_scm = ligature->get_grob_property ("flexa-width");
434 Real flexa_width = (flexa_width_scm != SCM_EOL) ?
435 gh_scm2double (flexa_width_scm) : 2.0;
436 flexa_width *= Staff_symbol_referencer::staff_space (ligature);
438 Real half_flexa_width = 0.5 * (flexa_width + thickness);
440 for (int i = 0; i < primitives.size (); i++)
442 Item *primitive = dynamic_cast<Item*> (primitives[i].grob_);
443 int output = gh_scm2int (primitive->get_grob_property ("primitive"));
444 primitive->set_grob_property ("thickness",
445 gh_double2scm (thickness));
448 primitive->set_grob_property ("head-width",
449 gh_double2scm (half_flexa_width));
454 primitive->set_grob_property ("head-width",
455 gh_double2scm (head_width));
460 primitive->set_grob_property ("head-width",
461 gh_double2scm (half_flexa_width));
462 primitive->set_grob_property ("flexa-width",
463 gh_double2scm (flexa_width));
464 set_delta_pitch (primitive,
465 primitives[i], primitives[i+1]);
468 programming_error (_f ("unexpected case fall-through"));
475 Mensural_ligature_engraver::fold_up_primitives (Array<Grob_info> primitives)
479 for (int i = 0; i < primitives.size (); i++)
481 Item *current = dynamic_cast<Item*> (primitives[i].grob_);
487 get_set_column (current, first->get_column ());
493 r.distance_ = distance;
494 r.item_l_drul_[LEFT] = first;
495 r.item_l_drul_[RIGHT] = current;
498 current->translate_axis (distance, X_AXIS);
502 gh_scm2double (current->get_grob_property ("head-width")) -
503 gh_scm2double (current->get_grob_property ("thickness"));
508 Mensural_ligature_engraver::join_primitives (Array<Grob_info> primitives)
511 for (int i = 0; i < primitives.size (); i++)
513 Grob_info info = primitives[i];
514 Pitch pitch = *unsmob_pitch (info.music_cause ()->get_mus_property ("pitch"));
517 Item *primitive = dynamic_cast<Item*> (info.grob_);
518 int output = gh_scm2int (primitive->get_grob_property ("primitive"));
519 if (output & MLP_ANY)
521 int delta_pitch = (pitch.steps () - last_pitch.steps ());
522 primitive->set_grob_property ("join-left",
523 gh_int2scm (delta_pitch));
531 Mensural_ligature_engraver::typeset_ligature (Spanner *ligature,
532 Array<Grob_info> primitives)
534 transform_heads (primitives);
535 propagate_properties (ligature, primitives);
536 fold_up_primitives (primitives);
537 join_primitives (primitives);
539 for (int i = 0; i < primitives.size (); i++)
541 typeset_grob (primitives[i].grob_);
545 ENTER_DESCRIPTION (Mensural_ligature_engraver,
546 /* descr */ "Handles Mensural_ligature_events by glueing special ligature heads together.",
547 /* creats*/ "MensuralLigature",
548 /* accepts */ "ligature-event abort-event",
549 /* acks */ "ligature-head-interface note-head-interface rest-interface",