]> git.donarmstrong.com Git - lilypond.git/blob - lily/mensural-ligature-engraver.cc
* input/regression/breathing-sign.ly: tiny fixes
[lilypond.git] / lily / mensural-ligature-engraver.cc
1 /*
2   mensural-ligature-engraver.cc -- implement Mensural_ligature_engraver
3   
4   source file of the GNU LilyPond music typesetter
5   
6   (c) 2002--2003 Juergen Reuter <reuter@ipd.uka.de>
7  */
8
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"
23
24 /*
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.
33  *
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?
38  *
39  * TODO: dotted heads: when applying Franco of Cologne's mapping, put
40  * dots *above* (rather than after) affected ligature heads.
41  *
42  * TODO: prohibit multiple voices within a ligature.
43  *
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.
48  */
49 class Mensural_ligature_engraver : public Coherent_ligature_engraver
50 {
51
52 protected:
53   virtual Spanner *create_ligature_spanner ();
54   virtual void build_ligature (Spanner *ligature, Array<Grob_info> primitives);
55
56 public:
57   TRANSLATOR_DECLARATIONS(Mensural_ligature_engraver);
58
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);
66 };
67
68
69 Mensural_ligature_engraver::Mensural_ligature_engraver ()
70 {
71 }
72
73 Spanner *
74 Mensural_ligature_engraver::create_ligature_spanner ()
75 {
76   return new Spanner (get_property ("MensuralLigature"));
77 }
78
79 /*
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.
84  */
85
86 /*
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).
100  */
101 enum Ligature_input
102 {
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,
112 };
113
114 /*
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.
121  */
122 enum Ligature_state
123 {
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,
134 };
135
136 /*
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.
141  */
142 const int/*new state*/ transition_state[/*old state*/][8/*input*/] =
143 {
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
160 };
161
162 /*
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.
168  */
169 const int/*output*/ transition_output[/*old state*/][8/*input*/] =
170 {
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
187 };
188
189 int
190 Mensural_ligature_engraver::apply_transition (Array<Grob_info> primitives,
191                                               int state, int input, int i)
192 {
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)
201     {
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)
210           {
211             programming_error ("last_primitive undefined");
212             break;
213           }
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)
220           {
221             programming_error ("last_primitive undefined");
222             break;
223           }
224         if (!primitive)
225           {
226             programming_error ("primitive undefined");
227             break;
228           }
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)
235           {
236             programming_error ("last_last_primitive undefined");
237             break;
238           }
239         if (!last_primitive)
240           {
241             programming_error ("last_primitive undefined");
242             break;
243           }
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;
250     }
251   return transition_state[state][input];
252 }
253
254 void
255 Mensural_ligature_engraver::transform_heads (Array<Grob_info> primitives)
256 {
257   if (primitives.size () < 2)
258     {
259       warning (_f ("ligature with less than 2 heads -> skipping"));
260       return;
261     }
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_));
271
272     Music *nr = info.music_cause ();
273     
274     /*
275     ugh. why not simply check for pitch? 
276      */
277     if (!nr->is_mus_type ("note-event"))
278       {
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;
284       }
285     else
286       {
287         pitch = *unsmob_pitch (nr->get_mus_property ("pitch"));
288         have_pitch = 1;
289       }
290
291     int delta_pitch;
292
293     if (!have_last_pitch)
294       {
295         delta_pitch = 0; // first pitch; delta undefined
296       }
297     else
298       {
299         delta_pitch = (pitch.steps () - last_pitch.steps ());
300         if (Pitch::compare (last_pitch, pitch) == 0)
301           {
302             info.music_cause ()->origin ()->warning (_f ("prime interval within ligature -> skipping"));
303             i++;
304             state = STATE_START;
305             have_pitch = 0;
306             continue;
307           }
308       }
309
310     if ((duration_log < -2) || (duration_log > 0))
311       {
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;
317       }
318
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) { ... }
322   }
323
324   state = apply_transition (primitives, state, INPUT_AE, primitives.size ());
325   // TODO: if (state == STATE_ERROR) { ... }
326 }
327
328 /*
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.
341  */
342 void
343 Mensural_ligature_engraver::propagate_properties (Spanner *ligature,
344                                                   Array<Grob_info> primitives)
345 {
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"));
350
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);
358
359   Real half_flexa_width = 0.5 * (flexa_width + thickness);
360
361   for (int i = 0; i < primitives.size (); i++)
362     {
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;
389       }
390     }
391 }
392
393 void
394 Mensural_ligature_engraver::fold_up_primitives (Array<Grob_info> primitives)
395 {
396   Item *first = 0;
397   Real distance = 0;
398   for (int i = 0; i < primitives.size (); i++)
399     {
400       Item *current = dynamic_cast<Item*> (primitives[i].grob_);
401       if (i == 0)
402         {
403           first = current;
404         }
405
406       get_set_column (current, first->get_column ());
407
408       if (i > 0)
409         {
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);
418         }
419
420       distance +=
421         gh_scm2double (current->get_grob_property ("head-width")) -
422         gh_scm2double (current->get_grob_property ("thickness"));
423     }
424 }
425
426 void
427 Mensural_ligature_engraver::join_primitives (Array<Grob_info> primitives)
428 {
429   Pitch last_pitch;
430   for (int i = 0; i < primitives.size (); i++)
431     {
432       Grob_info info = primitives[i];
433       Pitch pitch = *unsmob_pitch (info.music_cause ()->get_mus_property ("pitch"));
434       if (i > 0)
435         {
436           Item *primitive = dynamic_cast<Item*> (info.grob_);
437           int output = gh_scm2int (primitive->get_grob_property ("primitive"));
438           if (output & MLP_ANY)
439             {
440               int delta_pitch = (pitch.steps () - last_pitch.steps ());
441               primitive->set_grob_property ("join-left-amount",
442                                             gh_int2scm (delta_pitch));
443             }
444         }
445       last_pitch = pitch;
446     }
447 }
448
449 void
450 Mensural_ligature_engraver::build_ligature (Spanner *ligature,
451                                             Array<Grob_info> primitives)
452 {
453   transform_heads (primitives);
454   propagate_properties (ligature, primitives);
455   fold_up_primitives (primitives);
456   join_primitives (primitives);
457 }
458
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 */       "");