* input/regression/clef-oct.ly: new file.
[lilypond.git] / lily / mensural-ligature-engraver.cc
blobf8786b43142723b9d4a8e946e94341a41675f5b4
1 /*
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>
7 */
9 #include "mensural-ligature.hh"
10 #include "coherent-ligature-engraver.hh"
11 #include "event.hh"
12 #include "warn.hh"
13 #include "item.hh"
14 #include "spanner.hh"
15 #include "rod.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: My resources on Franco of Cologne's rules claim that his
26 * rules map ligature<->mensural timing in a non-ambigous way, but in
27 * fact, as presented in these resources, the rules become ambigous as
28 * soon as there appear durations other than breves within a ligature
29 * with more than two heads (ligatura ternaria etc.). Hence, the
30 * below implementation is an approximation of what I think the rules
31 * could look like if forced to be non-ambigous. This should be
32 * further investigated.
34 * TODO: The automat is quite complicated, and its design is error
35 * prone (and most probably, it behaves wrong for some very special
36 * cases). Maybe we can find a better paradigm for modelling Franco
37 * of Cologne's rules?
39 * TODO: dotted heads: when applying Franco of Cologne's mapping, put
40 * dots *above* (rather than after) affected ligature heads.
42 * TODO: prohibit multiple voices within a ligature.
44 * TODO: enhance robustness: in case of an illegal ligature (e.g. the
45 * user events for a ligature that contains a minima or STATE_ERROR
46 * is reached), automatically break the ligature into smaller, valid
47 * pieces.
49 class Mensural_ligature_engraver : public Coherent_ligature_engraver
52 protected:
53 virtual Spanner *create_ligature_spanner ();
54 virtual void build_ligature (Spanner *ligature, Array<Grob_info> primitives);
56 public:
57 TRANSLATOR_DECLARATIONS(Mensural_ligature_engraver);
59 private:
60 int apply_transition (Array<Grob_info> primitives,
61 int state, int input, int i);
62 void transform_heads (Array<Grob_info> primitives);
63 void propagate_properties (Spanner *ligature, Array<Grob_info> primitives);
64 void fold_up_primitives (Array<Grob_info> primitives);
65 void join_primitives (Array<Grob_info> primitives);
69 Mensural_ligature_engraver::Mensural_ligature_engraver ()
73 Spanner *
74 Mensural_ligature_engraver::create_ligature_spanner ()
76 return new Spanner (get_property ("MensuralLigature"));
80 * The following lines implement a finite state automat. Given a
81 * sequence of durations (Longa, Brevis, Semibrevis) or
82 * end-of-ligature-event as input, the automat outputs a sequence of
83 * events for grobs that form a proper ligature.
87 * This enumeration represents the set of possible input values to the
88 * automat. There may (potentially) be any sequence of Longa, Brevis,
89 * and Semibrevis duration symbols fed into the automat, with a final
90 * EndOfLigature symbol to terminate the ligature. Other durations
91 * are explicitly prohibited. Depending on the note's pitch of the
92 * preceding and the current input, the melodic line may be ascending
93 * or descending. Per definition, the melodic line must either ascend
94 * or descend, because if the pitches were twice the same, the two
95 * notes would be merged into a single one (as long as not resulting
96 * in a prohibited duration). In the case of the EndOfLigature
97 * symbol, the melodic line is undefined (but we still have ascending
98 * and descending case for the sake of consistency, making the automat
99 * simpler).
101 enum Ligature_input
103 // Ascending/Descending Longa/Brevis/Semibrevis/EndOfLigature
104 INPUT_AL = 0,
105 INPUT_DL,
106 INPUT_AB,
107 INPUT_DB,
108 INPUT_AS,
109 INPUT_DS,
110 INPUT_AE,
111 INPUT_DE,
115 * This enumeration represents all possible internal states of the
116 * automat. Besides the generic states START, ERROR, and END, the
117 * remaining states L, B, S, and SS describe pending values from the
118 * sequence of input values that have not yet been transformed to
119 * proper output values, including the melodic direction
120 * (ascending/descending) for state L.
122 enum Ligature_state
124 // aL = ascending Longa, dL descending Longa, B = Brevis, S =
125 // Semibrevis, SS = 2 Semibreves
126 STATE_START = 0,
127 STATE_aL,
128 STATE_dL,
129 STATE_B,
130 STATE_S,
131 STATE_SS,
132 STATE_ERROR,
133 STATE_END,
137 * The following array represents the transitions of the automat:
138 * given some state and input, it maps to a new state, according (with
139 * the limitations as described above) to the rules of Franco of
140 * Cologne.
142 const int/*new state*/ transition_state[/*old state*/][8/*input*/] =
144 {STATE_aL, STATE_dL, STATE_B, STATE_B,
145 STATE_S, STATE_S, STATE_ERROR, STATE_ERROR}, // was: STATE_START
146 {STATE_aL, STATE_dL, STATE_B, STATE_START,
147 STATE_ERROR, STATE_ERROR, STATE_END, STATE_END}, // was: STATE_aL
148 {STATE_aL, STATE_dL, STATE_B, STATE_START,
149 STATE_ERROR, STATE_ERROR, STATE_END, STATE_END}, // was: STATE_dL
150 {STATE_aL, STATE_dL, STATE_B, STATE_START,
151 STATE_ERROR, STATE_ERROR, STATE_END, STATE_END}, // was: STATE_B
152 {STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR,
153 STATE_SS, STATE_SS, STATE_ERROR, STATE_ERROR}, // was: STATE_S
154 {STATE_aL, STATE_dL, STATE_B, STATE_B,
155 STATE_S, STATE_S, STATE_END, STATE_END}, // was: STATE_SS
156 {STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR,
157 STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR}, // was: STATE_ERROR
158 {STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR,
159 STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR}, // was: STATE_END
163 * The following array represents the output of the automat while
164 * switching from one state to another: given some state and input, it
165 * maps to the output produced when switching to the next state,
166 * according (with the limitations as described above) to the rules of
167 * Franco of Cologne.
169 const int/*output*/ transition_output[/*old state*/][8/*input*/] =
171 {MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE,
172 MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE}, // was: STATE_START
173 {MLP_sc, MLP_ss, MLP_sc, MLP_LB,
174 MLP_NONE, MLP_NONE, MLP_sc, MLP_sc}, // was: STATE_aL
175 {MLP_sc, MLP_ss, MLP_sc, MLP_LB,
176 MLP_NONE, MLP_NONE, MLP_ss, MLP_ss}, // was: STATE_dL
177 {MLP_ss, MLP_cs, MLP_ss, MLP_BB,
178 MLP_NONE, MLP_NONE, MLP_ss, MLP_ss} , // was: STATE_B
179 {MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE,
180 MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE}, // was: STATE_S
181 {MLP_SS, MLP_SS, MLP_SS, MLP_SS,
182 MLP_SS, MLP_SS, MLP_SS, MLP_SS} , // was: STATE_SS
183 {MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE,
184 MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE}, // was: STATE_ERROR
185 {MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE,
186 MLP_NONE, MLP_NONE, MLP_NONE, MLP_NONE}, // was: STATE_END
190 Mensural_ligature_engraver::apply_transition (Array<Grob_info> primitives,
191 int state, int input, int i)
193 int output = transition_output[state][input];
194 Item *last_last_primitive = (i > 1) ?
195 dynamic_cast<Item*> (primitives[i-2].grob_) : 0;
196 Item *last_primitive = (i > 0) ?
197 dynamic_cast<Item*> (primitives[i-1].grob_) : 0;
198 Item *primitive = (i < primitives.size ()) ?
199 dynamic_cast<Item*> (primitives[i].grob_) : 0;
200 switch (output)
202 case MLP_NONE:
203 // skip note head, expecting a primitive with two note heads
204 break;
205 case MLP_sc:
206 case MLP_ss:
207 case MLP_cs:
208 // primitive with single note head
209 if (!last_primitive)
211 programming_error ("last_primitive undefined");
212 break;
214 last_primitive->set_grob_property ("primitive", gh_int2scm (output));
215 break;
216 case MLP_BB:
217 case MLP_LB:
218 // primitive with two note heads
219 if (!last_primitive)
221 programming_error ("last_primitive undefined");
222 break;
224 if (!primitive)
226 programming_error ("primitive undefined");
227 break;
229 last_primitive->set_grob_property ("primitive", gh_int2scm (output));
230 primitive->set_grob_property ("primitive", gh_int2scm (MLP_NONE));
231 break;
232 case MLP_SS:
233 // delayed primitive with two note heads
234 if (!last_last_primitive)
236 programming_error ("last_last_primitive undefined");
237 break;
239 if (!last_primitive)
241 programming_error ("last_primitive undefined");
242 break;
244 last_last_primitive->set_grob_property ("primitive", gh_int2scm (output));
245 last_primitive->set_grob_property ("primitive", gh_int2scm (MLP_NONE));
246 break;
247 default:
248 programming_error (_f ("unexpected case fall-through"));
249 break;
251 return transition_state[state][input];
254 void
255 Mensural_ligature_engraver::transform_heads (Array<Grob_info> primitives)
257 if (primitives.size () < 2)
259 warning (_f ("ligature with less than 2 heads -> skipping"));
260 return;
262 int state = STATE_START;
263 Pitch last_pitch, pitch;
264 bool have_last_pitch = 0, have_pitch = 0;
265 for (int i = 0; i < primitives.size (); i++) {
266 last_pitch = pitch;
267 have_last_pitch = have_pitch;
268 Grob_info info = primitives[i];
269 int duration_log =
270 Note_head::get_balltype (dynamic_cast<Item*> (info.grob_));
272 Music *nr = info.music_cause ();
275 ugh. why not simply check for pitch?
277 if (!nr->is_mus_type ("note-event"))
279 info.music_cause ()->origin ()->warning (_f ("can not determine pitch of ligature primitive -> skipping"));
280 i++;
281 state = STATE_START;
282 have_pitch = 0;
283 continue;
285 else
287 pitch = *unsmob_pitch (nr->get_mus_property ("pitch"));
288 have_pitch = 1;
291 int delta_pitch;
293 if (!have_last_pitch)
295 delta_pitch = 0; // first pitch; delta undefined
297 else
299 delta_pitch = (pitch.steps () - last_pitch.steps ());
300 if (Pitch::compare (last_pitch, pitch) == 0)
302 info.music_cause ()->origin ()->warning (_f ("prime interval within ligature -> skipping"));
303 i++;
304 state = STATE_START;
305 have_pitch = 0;
306 continue;
310 if ((duration_log < -2) || (duration_log > 0))
312 info.music_cause ()->origin ()->warning (_f ("mensural ligature: duration none of L, B, S -> skipping"));
313 i++;
314 state = STATE_START;
315 have_pitch = 0;
316 continue;
319 int input = (duration_log + 2) * 2 + ((delta_pitch < 0) ? 1 : 0);
320 state = apply_transition (primitives, state, input, i);
321 // TODO: if (state == STATE_ERROR) { ... }
324 state = apply_transition (primitives, state, INPUT_AE, primitives.size ());
325 // TODO: if (state == STATE_ERROR) { ... }
329 * A MensuralLigature grob consists of a bunch of NoteHead grobs that
330 * are glued together. It (a) does not make sense to change
331 * properties like thickness or flexa-width from one head to the next
332 * within a ligature (this would totally screw up alignment), and (b)
333 * some of these properties (like flexa-width) are specific to
334 * e.g. the MensuralLigature (as in contrast to e.g. LigatureBracket),
335 * and therefore should not be handled in the NoteHead code (which is
336 * also used by LigatureBracket). Therefore, we let the user control
337 * these properties via the concrete Ligature grob (like
338 * MensuralLigature) and then copy these properties as necessary to
339 * each of the NoteHead grobs. This is what
340 * propagate_properties() does.
342 void
343 Mensural_ligature_engraver::propagate_properties (Spanner *ligature,
344 Array<Grob_info> primitives)
346 SCM thickness_scm = ligature->get_grob_property ("thickness");
347 Real thickness = (thickness_scm != SCM_EOL) ?
348 gh_scm2double (thickness_scm) : 1.4;
349 thickness *= ligature->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
351 Real head_width =
352 Font_interface::get_default_font (ligature)->
353 find_by_name ("noteheads--1mensural").extent (X_AXIS).length ();
354 SCM flexa_width_scm = ligature->get_grob_property ("flexa-width");
355 Real flexa_width = (flexa_width_scm != SCM_EOL) ?
356 gh_scm2double (flexa_width_scm) : 2.0;
357 flexa_width *= Staff_symbol_referencer::staff_space (ligature);
359 Real half_flexa_width = 0.5 * (flexa_width + thickness);
361 for (int i = 0; i < primitives.size (); i++)
363 Item *primitive = dynamic_cast<Item*> (primitives[i].grob_);
364 int output = gh_scm2int (primitive->get_grob_property ("primitive"));
365 primitive->set_grob_property ("thickness",
366 gh_double2scm (thickness));
367 switch (output) {
368 case MLP_NONE:
369 primitive->set_grob_property ("head-width",
370 gh_double2scm (half_flexa_width));
371 break;
372 case MLP_sc:
373 case MLP_ss:
374 case MLP_cs:
375 primitive->set_grob_property ("head-width",
376 gh_double2scm (head_width));
377 break;
378 case MLP_BB:
379 case MLP_LB:
380 case MLP_SS:
381 primitive->set_grob_property ("head-width",
382 gh_double2scm (half_flexa_width));
383 primitive->set_grob_property ("flexa-width",
384 gh_double2scm (flexa_width));
385 break;
386 default:
387 programming_error (_f ("unexpected case fall-through"));
388 break;
393 void
394 Mensural_ligature_engraver::fold_up_primitives (Array<Grob_info> primitives)
396 Item *first = 0;
397 Real distance = 0;
398 for (int i = 0; i < primitives.size (); i++)
400 Item *current = dynamic_cast<Item*> (primitives[i].grob_);
401 if (i == 0)
403 first = current;
406 get_set_column (current, first->get_column ());
408 if (i > 0)
410 #if 0
411 Rod r;
412 r.distance_ = distance;
413 r.item_l_drul_[LEFT] = first;
414 r.item_l_drul_[RIGHT] = current;
415 r.add_to_cols ();
416 #endif
417 current->translate_axis (distance, X_AXIS);
420 distance +=
421 gh_scm2double (current->get_grob_property ("head-width")) -
422 gh_scm2double (current->get_grob_property ("thickness"));
426 void
427 Mensural_ligature_engraver::join_primitives (Array<Grob_info> primitives)
429 Pitch last_pitch;
430 for (int i = 0; i < primitives.size (); i++)
432 Grob_info info = primitives[i];
433 Pitch pitch = *unsmob_pitch (info.music_cause ()->get_mus_property ("pitch"));
434 if (i > 0)
436 Item *primitive = dynamic_cast<Item*> (info.grob_);
437 int output = gh_scm2int (primitive->get_grob_property ("primitive"));
438 if (output & MLP_ANY)
440 int delta_pitch = (pitch.steps () - last_pitch.steps ());
441 primitive->set_grob_property ("join-left-amount",
442 gh_int2scm (delta_pitch));
445 last_pitch = pitch;
449 void
450 Mensural_ligature_engraver::build_ligature (Spanner *ligature,
451 Array<Grob_info> primitives)
453 transform_heads (primitives);
454 propagate_properties (ligature, primitives);
455 fold_up_primitives (primitives);
456 join_primitives (primitives);
459 ENTER_DESCRIPTION (Mensural_ligature_engraver,
460 /* descr */ "Handles Mensural_ligature_events by glueing special ligature heads together.",
461 /* creats*/ "MensuralLigature",
462 /* accepts */ "ligature-event abort-event",
463 /* acks */ "note-head-interface rest-interface",
464 /* reads */ "",
465 /* write */ "");