]> 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--2003 Juergen Reuter <reuter@ipd.uka.de>
7  */
8
9 #include "mensural-ligature.hh"
10 #include "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: 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
33  * after the ligature.
34  *
35  * TODO: make spacing more robust: do not screw up spacing if user
36  * erroneously puts rest in ligature.
37  *
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.
46  *
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
50  * of Cologne's rules?
51  *
52  * TODO: dotted heads: when applying Franco of Cologne's mapping, put
53  * dots *above* (rather than after) affected ligature heads.
54  *
55  * TODO: prohibit multiple voices within a ligature.
56  *
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.
62  *
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
66  * pieces.
67  *
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
79  * others.
80  */
81 class Mensural_ligature_engraver : public Ligature_engraver
82 {
83
84 protected:
85   virtual Spanner *create_ligature_spanner ();
86   virtual void typeset_ligature (Spanner *ligature,
87                                  Array<Grob_info> primitives);
88
89 public:
90   TRANSLATOR_DECLARATIONS(Mensural_ligature_engraver);
91
92 private:
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);
100 };
101
102
103 Mensural_ligature_engraver::Mensural_ligature_engraver ()
104 {
105 }
106
107 Spanner *
108 Mensural_ligature_engraver::create_ligature_spanner ()
109 {
110   return new Spanner (get_property ("MensuralLigature"));
111 }
112
113 /*
114  * TODO: move this function to class Item?
115  */
116 void
117 Mensural_ligature_engraver::get_set_column (Item *item, Paper_column *column)
118 {
119   Item *parent = dynamic_cast<Item*> (item->get_parent (X_AXIS));
120   if (!parent)
121     {
122       programming_error ("failed tweaking paper column in ligature");
123       return;
124     }
125
126   String name = parent->name ();
127   if (!String::compare (name, "PaperColumn"))
128     {
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");
133            gh_pair_p (tail);
134            tail = ly_cdr (tail))
135         {
136           Item *sibling = unsmob_item (ly_car (tail));
137           if ((sibling) &&
138               (Staff_symbol_referencer::get_staff_symbol (sibling) == sl))
139             {
140               sibling->set_parent (column, X_AXIS);
141             }
142         }
143     }
144   else
145     {
146       get_set_column (parent, column);
147     }
148 }
149
150 /*
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.
155  */
156
157 /*
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
170  * simpler).
171  */
172 enum Ligature_input
173 {
174   // Ascending/Descending Longa/Brevis/Semibrevis/EndOfLigature
175   INPUT_AL = 0,
176   INPUT_DL,
177   INPUT_AB,
178   INPUT_DB,
179   INPUT_AS,
180   INPUT_DS,
181   INPUT_AE,
182   INPUT_DE,
183 };
184
185 /*
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.
192  */
193 enum Ligature_state
194 {
195   // aL = ascending Longa, dL descending Longa, B = Brevis, S =
196   // Semibrevis, SS = 2 Semibreves
197   STATE_START = 0,
198   STATE_aL,
199   STATE_dL,
200   STATE_B,
201   STATE_S,
202   STATE_SS,
203   STATE_ERROR,
204   STATE_END,
205 };
206
207 /*
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
211  * Cologne.
212  */
213 const int/*new state*/ transition_state[/*old state*/][8/*input*/] =
214 {
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
231 };
232
233 /*
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
238  * Franco of Cologne.
239  */
240 const int/*output*/ transition_output[/*old state*/][8/*input*/] =
241 {
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
258 };
259
260 int
261 Mensural_ligature_engraver::apply_transition (Array<Grob_info> primitives,
262                                               int state, int input, int i)
263 {
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;
271   switch (output)
272     {
273       case MLP_NONE:
274         // skip note head, expecting a primitive with two note heads
275         break;
276       case MLP_sc:
277       case MLP_ss:
278       case MLP_cs:
279         // primitive with single note head
280         if (!last_primitive)
281           {
282             programming_error ("last_primitive undefined");
283             break;
284           }
285         last_primitive->set_grob_property ("primitive", gh_int2scm (output));
286         break;
287       case MLP_BB:
288       case MLP_LB:
289         // primitive with two note heads
290         if (!last_primitive)
291           {
292             programming_error ("last_primitive undefined");
293             break;
294           }
295         if (!primitive)
296           {
297             programming_error ("primitive undefined");
298             break;
299           }
300         last_primitive->set_grob_property ("primitive", gh_int2scm (output));
301         primitive->set_grob_property ("primitive", gh_int2scm (MLP_NONE));
302         break;
303       case MLP_SS:
304         // delayed primitive with two note heads
305         if (!last_last_primitive)
306           {
307             programming_error ("last_last_primitive undefined");
308             break;
309           }
310         if (!last_primitive)
311           {
312             programming_error ("last_primitive undefined");
313             break;
314           }
315         last_last_primitive->set_grob_property ("primitive", gh_int2scm (output));
316         last_primitive->set_grob_property ("primitive", gh_int2scm (MLP_NONE));
317         break;
318       default:
319         programming_error (_f ("unexpected case fall-through"));
320         break;
321     }
322   return transition_state[state][input];
323 }
324
325 void
326 Mensural_ligature_engraver::transform_heads (Array<Grob_info> primitives)
327 {
328   if (primitives.size () < 2)
329     {
330       warning (_f ("ligature with less than 2 heads -> skipping"));
331       return;
332     }
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++) {
337     last_pitch = pitch;
338     have_last_pitch = have_pitch;
339     Grob_info info = primitives[i];
340     int duration_log =
341       Note_head::get_balltype (dynamic_cast<Item*> (info.grob_));
342
343     Music *nr = info.music_cause ();
344     
345     /*
346     ugh. why not simply check for pitch? 
347      */
348     if (!nr->is_mus_type ("note-event"))
349       {
350         info.music_cause ()->origin ()->warning (_f ("can not determine pitch of ligature primitive -> skipping"));
351         i++;
352         state = STATE_START;
353         have_pitch = 0;
354         continue;
355       }
356     else
357       {
358         pitch = *unsmob_pitch (nr->get_mus_property ("pitch"));
359         have_pitch = 1;
360       }
361
362     int delta_pitch;
363
364     if (!have_last_pitch)
365       {
366         delta_pitch = 0; // first pitch; delta undefined
367       }
368     else
369       {
370         delta_pitch = (pitch.steps () - last_pitch.steps ());
371         if (Pitch::compare (last_pitch, pitch) == 0)
372           {
373             info.music_cause ()->origin ()->warning (_f ("prime interval within ligature -> skipping"));
374             i++;
375             state = STATE_START;
376             have_pitch = 0;
377             continue;
378           }
379       }
380
381     if ((duration_log < -2) || (duration_log > 0))
382       {
383         info.music_cause ()->origin ()->warning (_f ("mensural ligature: duration none of L, B, S -> skipping"));
384         i++;
385         state = STATE_START;
386         have_pitch = 0;
387         continue;
388       }
389
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) { ... }
393   }
394
395   state = apply_transition (primitives, state, INPUT_AE, primitives.size ());
396   // TODO: if (state == STATE_ERROR) { ... }
397 }
398
399 void set_delta_pitch (Item *primitive, Grob_info info1, Grob_info info2)
400 {
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));
405 }
406
407 /*
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.
420  */
421 void
422 Mensural_ligature_engraver::propagate_properties (Spanner *ligature,
423                                                   Array<Grob_info> primitives)
424 {
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");
429
430   Real head_width =
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);
437
438   Real half_flexa_width = 0.5 * (flexa_width + thickness);
439
440   for (int i = 0; i < primitives.size (); i++)
441     {
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));
446       switch (output) {
447         case MLP_NONE:
448           primitive->set_grob_property ("head-width",
449                                         gh_double2scm (half_flexa_width));
450           break;
451         case MLP_sc:
452         case MLP_ss:
453         case MLP_cs:
454           primitive->set_grob_property ("head-width",
455                                         gh_double2scm (head_width));
456           break;
457         case MLP_BB:
458         case MLP_LB:
459         case MLP_SS:
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]);
466           break;
467         default:
468           programming_error (_f ("unexpected case fall-through"));
469           break;
470       }
471     }
472 }
473
474 void
475 Mensural_ligature_engraver::fold_up_primitives (Array<Grob_info> primitives)
476 {
477   Item *first = 0;
478   Real distance = 0;
479   for (int i = 0; i < primitives.size (); i++)
480     {
481       Item *current = dynamic_cast<Item*> (primitives[i].grob_);
482       if (i == 0)
483         {
484           first = current;
485         }
486
487       get_set_column (current, first->get_column ());
488
489       if (i > 0)
490         {
491 #if 0
492           Rod r;
493           r.distance_ = distance;
494           r.item_l_drul_[LEFT] = first;
495           r.item_l_drul_[RIGHT] = current;
496           r.add_to_cols ();
497 #endif
498           current->translate_axis (distance, X_AXIS);
499         }
500
501       distance +=
502         gh_scm2double (current->get_grob_property ("head-width")) -
503         gh_scm2double (current->get_grob_property ("thickness"));
504     }
505 }
506
507 void
508 Mensural_ligature_engraver::join_primitives (Array<Grob_info> primitives)
509 {
510   Pitch last_pitch;
511   for (int i = 0; i < primitives.size (); i++)
512     {
513       Grob_info info = primitives[i];
514       Pitch pitch = *unsmob_pitch (info.music_cause ()->get_mus_property ("pitch"));
515       if (i > 0)
516         {
517           Item *primitive = dynamic_cast<Item*> (info.grob_);
518           int output = gh_scm2int (primitive->get_grob_property ("primitive"));
519           if (output & MLP_ANY)
520             {
521               int delta_pitch = (pitch.steps () - last_pitch.steps ());
522               primitive->set_grob_property ("join-left",
523                                             gh_int2scm (delta_pitch));
524             }
525         }
526       last_pitch = pitch;
527     }
528 }
529
530 void
531 Mensural_ligature_engraver::typeset_ligature (Spanner *ligature,
532                                               Array<Grob_info> primitives)
533 {
534   transform_heads (primitives);
535   propagate_properties (ligature, primitives);
536   fold_up_primitives (primitives);
537   join_primitives (primitives);
538
539   for (int i = 0; i < primitives.size (); i++)
540     {
541       typeset_grob (primitives[i].grob_);
542     }
543 }
544
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",
550 /* reads */       "",
551 /* write */       "");