]> git.donarmstrong.com Git - lilypond.git/blob - lily/mensural-ligature-engraver.cc
new file, move from
[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--2004 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 "output-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 make_spanner ("MensuralLigature", SCM_EOL);
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_property ("primitive", scm_int2num (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_property ("primitive", scm_int2num (output));
230         primitive->set_property ("primitive", scm_int2num (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_property ("primitive", scm_int2num (output));
245         last_primitive->set_property ("primitive", scm_int2num (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_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   Real thickness = robust_scm2double (ligature->get_property ("thickness"), 1.4);
347   thickness *= ligature->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
348
349   Real head_width =
350     Font_interface::get_default_font (ligature)->
351     find_by_name ("noteheads--1mensural").extent (X_AXIS).length ();
352     Real flexa_width = robust_scm2double (ligature->get_property ("flexa-width"), 2);
353   flexa_width *= Staff_symbol_referencer::staff_space (ligature);
354
355   Real half_flexa_width = 0.5 * (flexa_width + thickness);
356
357   for (int i = 0; i < primitives.size (); i++)
358     {
359       Item *primitive = dynamic_cast<Item*> (primitives[i].grob_);
360       int output = ly_scm2int (primitive->get_property ("primitive"));
361       primitive->set_property ("thickness",
362                                     scm_make_real (thickness));
363       switch (output) {
364         case MLP_NONE:
365           primitive->set_property ("head-width",
366                                         scm_make_real (half_flexa_width));
367           break;
368         case MLP_sc:
369         case MLP_ss:
370         case MLP_cs:
371           primitive->set_property ("head-width",
372                                         scm_make_real (head_width));
373           break;
374         case MLP_BB:
375         case MLP_LB:
376         case MLP_SS:
377           primitive->set_property ("head-width",
378                                         scm_make_real (half_flexa_width));
379           primitive->set_property ("flexa-width",
380                                         scm_make_real (flexa_width));
381           break;
382         default:
383           programming_error (_f ("unexpected case fall-through"));
384           break;
385       }
386     }
387 }
388
389 void
390 Mensural_ligature_engraver::fold_up_primitives (Array<Grob_info> primitives)
391 {
392   Item *first = 0;
393   Real distance = 0;
394   for (int i = 0; i < primitives.size (); i++)
395     {
396       Item *current = dynamic_cast<Item*> (primitives[i].grob_);
397       if (i == 0)
398         {
399           first = current;
400         }
401
402       get_set_column (current, first->get_column ());
403
404       if (i > 0)
405         {
406           current->translate_axis (distance, X_AXIS);
407         }
408
409       distance +=
410         ly_scm2double (current->get_property ("head-width")) -
411         ly_scm2double (current->get_property ("thickness"));
412     }
413 }
414
415 void
416 Mensural_ligature_engraver::join_primitives (Array<Grob_info> primitives)
417 {
418   Pitch last_pitch;
419   for (int i = 0; i < primitives.size (); i++)
420     {
421       Grob_info info = primitives[i];
422       Pitch pitch = *unsmob_pitch (info.music_cause ()->get_property ("pitch"));
423       if (i > 0)
424         {
425           Item *primitive = dynamic_cast<Item*> (info.grob_);
426           int output = ly_scm2int (primitive->get_property ("primitive"));
427           if (output & MLP_ANY)
428             {
429               int delta_pitch = (pitch.steps () - last_pitch.steps ());
430               primitive->set_property ("join-left-amount",
431                                             scm_int2num (delta_pitch));
432             }
433         }
434       last_pitch = pitch;
435     }
436 }
437
438 void
439 Mensural_ligature_engraver::build_ligature (Spanner *ligature,
440                                             Array<Grob_info> primitives)
441 {
442   transform_heads (primitives);
443   propagate_properties (ligature, primitives);
444   fold_up_primitives (primitives);
445   join_primitives (primitives);
446 }
447
448 ENTER_DESCRIPTION (Mensural_ligature_engraver,
449 /* descr */       "Handles Mensural_ligature_events by glueing special ligature heads together.",
450 /* creats*/       "MensuralLigature",
451 /* accepts */     "ligature-event",
452 /* acks  */      "note-head-interface rest-interface",
453 /* reads */       "",
454 /* write */       "");