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