]> git.donarmstrong.com Git - lilypond.git/blob - lily/mensural-ligature-engraver.cc
* VERSION (MY_PATCH_LEVEL): make 1.7.0
[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 Juergen Reuter <reuter@ipd.uka.de>
7  */
8
9 #include "mensural-ligature.hh"
10 #include "ligature-engraver.hh"
11 #include "musical-request.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 requests 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   Real distance_;
84   Array<Grob_info> primitives_;
85
86 protected:
87   virtual void acknowledge_grob (Grob_info);
88   virtual void try_stop_ligature ();
89   virtual Spanner *create_ligature_spanner ();
90
91 public:
92   TRANSLATOR_DECLARATIONS(Mensural_ligature_engraver);
93
94 private:
95   int apply_transition (int state, int input, int i);
96   void transform_heads ();
97   void propagate_properties ();
98   void fold_up_primitives ();
99   void join_primitives ();
100   void get_set_column (Item *item, Paper_column *new_col);
101 };
102
103
104 Mensural_ligature_engraver::Mensural_ligature_engraver ()
105 {
106   distance_ = 0;
107 }
108
109 Spanner *
110 Mensural_ligature_engraver::create_ligature_spanner ()
111 {
112   distance_ = 0;
113   return new Spanner (get_property ("MensuralLigature"));
114 }
115
116 /*
117  * TODO: move this function to class Item?
118  */
119 void
120 Mensural_ligature_engraver::get_set_column (Item *item, Paper_column *column)
121 {
122   Item *parent = dynamic_cast<Item*> (item->get_parent (X_AXIS));
123   if (!parent)
124     {
125       programming_error ("failed tweaking paper column in ligature");
126       return;
127     }
128
129   String name = parent->name ();
130   if (!String::compare (name, "PaperColumn"))
131     {
132       // Change column not only for targeted item (NoteColumn), but
133       // also for all associated grobs (NoteSpacing, SeparationItem).
134       Grob *sl = Staff_symbol_referencer::get_staff_symbol (item);
135       for (SCM tail = parent->get_grob_property ("elements");
136            gh_pair_p (tail);
137            tail = ly_cdr (tail))
138         {
139           Item *sibling = unsmob_item (ly_car (tail));
140           if ((sibling) &&
141               (Staff_symbol_referencer::get_staff_symbol (sibling) == sl))
142             {
143               sibling->set_parent (column, X_AXIS);
144             }
145         }
146     }
147   else
148     {
149       get_set_column (parent, column);
150     }
151 }
152
153 /*
154  * The following lines implement a finite state automat.  Given a
155  * sequence of durations (Longa, Brevis, Semibrevis) or
156  * end-of-ligature-request as input, the automat outputs a sequence of
157  * requests for grobs that form a proper ligature.
158  */
159
160 /*
161  * This enumeration represents the set of possible input values to the
162  * automat.  There may (potentially) be any sequence of Longa, Brevis,
163  * and Semibrevis duration symbols fed into the automat, with a final
164  * EndOfLigature symbol to terminate the ligature.  Other durations
165  * are explicitly prohibited.  Depending on the note's pitch of the
166  * preceding and the current input, the melodic line may be ascending
167  * or descending.  Per definition, the melodic line must either ascend
168  * or descend, because if the pitches were twice the same, the two
169  * notes would be merged into a single one (as long as not resulting
170  * in a prohibited duration).  In the case of the EndOfLigature
171  * symbol, the melodic line is undefined (but we still have ascending
172  * and descending case for the sake of consistency, making the automat
173  * simpler).
174  */
175 enum Ligature_input
176 {
177   // Ascending/Descending Longa/Brevis/Semibrevis/EndOfLigature
178   INPUT_AL = 0,
179   INPUT_DL,
180   INPUT_AB,
181   INPUT_DB,
182   INPUT_AS,
183   INPUT_DS,
184   INPUT_AE,
185   INPUT_DE,
186 };
187
188 /*
189  * This enumeration represents all possible internal states of the
190  * automat.  Besides the generic states START, ERROR, and END, the
191  * remaining states L, B, S, and SS describe pending values from the
192  * sequence of input values that have not yet been transformed to
193  * proper output values, including the melodic direction
194  * (ascending/descending) for state L.
195  */
196 enum Ligature_state
197 {
198   // aL = ascending Longa, dL descending Longa, B = Brevis, S =
199   // Semibrevis, SS = 2 Semibreves
200   STATE_START = 0,
201   STATE_aL,
202   STATE_dL,
203   STATE_B,
204   STATE_S,
205   STATE_SS,
206   STATE_ERROR,
207   STATE_END,
208 };
209
210 /*
211  * The following array represents the transitions of the automat:
212  * given some state and input, it maps to a new state, according (with
213  * the limitations as described above) to the rules of Franco of
214  * Cologne.
215  */
216 const int/*new state*/ transition_state[/*old state*/][8/*input*/] =
217 {
218   {STATE_aL,    STATE_dL,    STATE_B,     STATE_B,
219    STATE_S,     STATE_S,     STATE_ERROR, STATE_ERROR}, // was: STATE_START
220   {STATE_aL,    STATE_dL,    STATE_B,     STATE_START,
221    STATE_ERROR, STATE_ERROR, STATE_END,   STATE_END},   // was: STATE_aL
222   {STATE_aL,    STATE_dL,    STATE_B,     STATE_START,
223    STATE_ERROR, STATE_ERROR, STATE_END,   STATE_END},   // was: STATE_dL
224   {STATE_aL,    STATE_dL,    STATE_B,     STATE_START,
225    STATE_ERROR, STATE_ERROR, STATE_END,   STATE_END},   // was: STATE_B
226   {STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR,
227    STATE_SS,    STATE_SS,    STATE_ERROR, STATE_ERROR}, // was: STATE_S
228   {STATE_aL,    STATE_dL,    STATE_B,     STATE_B,
229    STATE_S,     STATE_S,     STATE_END,   STATE_END},   // was: STATE_SS
230   {STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR,
231    STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR}, // was: STATE_ERROR
232   {STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR,
233    STATE_ERROR, STATE_ERROR, STATE_ERROR, STATE_ERROR}, // was: STATE_END
234 };
235
236 /*
237  * The following array represents the output of the automat while
238  * switching from one state to another: given some state and input, it
239  * maps to the output produced when switching to the next state,
240  * according (with the limitations as described above) to the rules of
241  * Franco of Cologne.
242  */
243 const int/*output*/ transition_output[/*old state*/][8/*input*/] =
244 {
245   {MLP_NONE,  MLP_NONE,  MLP_NONE,  MLP_NONE,
246    MLP_NONE,  MLP_NONE,  MLP_NONE,  MLP_NONE}, // was: STATE_START
247   {MLP_sc,    MLP_ss,    MLP_sc,    MLP_LB,
248    MLP_NONE,  MLP_NONE,  MLP_sc,    MLP_sc},   // was: STATE_aL
249   {MLP_sc,    MLP_ss,    MLP_sc,    MLP_LB,
250    MLP_NONE,  MLP_NONE,  MLP_ss,    MLP_ss},   // was: STATE_dL
251   {MLP_ss,    MLP_cs,    MLP_ss,    MLP_BB,
252    MLP_NONE,  MLP_NONE,  MLP_ss,    MLP_ss} ,  // was: STATE_B
253   {MLP_NONE,  MLP_NONE,  MLP_NONE,  MLP_NONE,
254    MLP_NONE,  MLP_NONE,  MLP_NONE,  MLP_NONE}, // was: STATE_S
255   {MLP_SS,    MLP_SS,    MLP_SS,    MLP_SS,
256    MLP_SS,    MLP_SS,    MLP_SS,    MLP_SS} ,  // was: STATE_SS
257   {MLP_NONE,  MLP_NONE,  MLP_NONE,  MLP_NONE,
258    MLP_NONE,  MLP_NONE,  MLP_NONE,  MLP_NONE}, // was: STATE_ERROR
259   {MLP_NONE,  MLP_NONE,  MLP_NONE,  MLP_NONE,
260    MLP_NONE,  MLP_NONE,  MLP_NONE,  MLP_NONE}, // was: STATE_END
261 };
262
263 int
264 Mensural_ligature_engraver::apply_transition (int state, int input, int i)
265 {
266   int output = transition_output[state][input];
267   Item *last_last_primitive = (i > 1) ?
268     dynamic_cast<Item*> (primitives_[i-2].grob_) : 0;
269   Item *last_primitive = (i > 0) ?
270     dynamic_cast<Item*> (primitives_[i-1].grob_) : 0;
271   Item *primitive = (i < primitives_.size ()) ?
272     dynamic_cast<Item*> (primitives_[i].grob_) : 0;
273   switch (output)
274     {
275       case MLP_NONE:
276         // skip note head, expecting a primitive with two note heads
277         break;
278       case MLP_sc:
279       case MLP_ss:
280       case MLP_cs:
281         // primitive with single note head
282         if (!last_primitive)
283           {
284             programming_error ("last_primitive undefined");
285             break;
286           }
287         last_primitive->set_grob_property ("primitive", gh_int2scm (output));
288         break;
289       case MLP_BB:
290       case MLP_LB:
291         // primitive with two note heads
292         if (!last_primitive)
293           {
294             programming_error ("last_primitive undefined");
295             break;
296           }
297         if (!primitive)
298           {
299             programming_error ("primitive undefined");
300             break;
301           }
302         last_primitive->set_grob_property ("primitive", gh_int2scm (output));
303         primitive->set_grob_property ("primitive", gh_int2scm (MLP_NONE));
304         break;
305       case MLP_SS:
306         // delayed primitive with two note heads
307         if (!last_last_primitive)
308           {
309             programming_error ("last_last_primitive undefined");
310             break;
311           }
312         if (!last_primitive)
313           {
314             programming_error ("last_primitive undefined");
315             break;
316           }
317         last_last_primitive->set_grob_property ("primitive", gh_int2scm (output));
318         last_primitive->set_grob_property ("primitive", gh_int2scm (MLP_NONE));
319         break;
320       default:
321         programming_error (_f ("unexpected case fall-through"));
322         break;
323     }
324   return transition_state[state][input];
325 }
326
327 void
328 Mensural_ligature_engraver::transform_heads ()
329 {
330   if (primitives_.size () < 2)
331     {
332       warning (_f ("ligature with less than 2 heads -> skipping"));
333       return;
334     }
335   int state = STATE_START;
336   Pitch last_pitch, pitch;
337   bool have_last_pitch = 0, have_pitch = 0;
338   for (int i = 0; i < primitives_.size (); i++) {
339     last_pitch = pitch;
340     have_last_pitch = have_pitch;
341     Grob_info info = primitives_[i];
342     int duration_log =
343       Note_head::get_balltype (dynamic_cast<Item*> (info.grob_));
344     Note_req *nr = dynamic_cast<Note_req*> (info.music_cause ());
345     if (!nr)
346       {
347         info.music_cause ()->origin ()->warning (_f ("can not determine pitch of ligature primitive -> skipping"));
348         i++;
349         state = STATE_START;
350         have_pitch = 0;
351         continue;
352       }
353     else
354       {
355         pitch = *unsmob_pitch (nr->get_mus_property ("pitch"));
356         have_pitch = 1;
357       }
358
359     int delta_pitch;
360
361     if (!have_last_pitch)
362       {
363         delta_pitch = 0; // first pitch; delta undefined
364       }
365     else
366       {
367         delta_pitch = (pitch.steps () - last_pitch.steps ());
368         if (Pitch::compare (last_pitch, pitch) == 0)
369           {
370             info.music_cause ()->origin ()->warning (_f ("prime interval within ligature -> skipping"));
371             i++;
372             state = STATE_START;
373             have_pitch = 0;
374             continue;
375           }
376       }
377
378     if ((duration_log < -2) || (duration_log > 0))
379       {
380         info.music_cause ()->origin ()->warning (_f ("mensural ligature: duration none of L, B, S -> skipping"));
381         i++;
382         state = STATE_START;
383         have_pitch = 0;
384         continue;
385       }
386
387     int input = (duration_log + 2) * 2 + ((delta_pitch < 0) ? 1 : 0);
388     state = apply_transition (state, input, i);
389     // TODO: if (state == STATE_ERROR) { ... }
390   }
391
392   state = apply_transition (state, INPUT_AE, primitives_.size ());
393   // TODO: if (state == STATE_ERROR) { ... }
394 }
395
396 void set_delta_pitch (Item *primitive, Grob_info info1, Grob_info info2)
397 {
398   Note_req *nr1 = dynamic_cast<Note_req*> (info1.music_cause ());
399   Note_req *nr2 = dynamic_cast<Note_req*> (info2.music_cause ());
400   Pitch pitch1 = *unsmob_pitch (nr1->get_mus_property ("pitch"));
401   Pitch pitch2 = *unsmob_pitch (nr2->get_mus_property ("pitch"));
402   int delta_pitch = (pitch2.steps () - pitch1.steps ());
403   primitive->set_grob_property ("delta-pitch", gh_int2scm (delta_pitch));
404 }
405
406 /*
407  * A MensuralLigature grob consists of a bunch of LigatureHead grobs
408  * that are glued together.  It (a) does make sense to change
409  * properties like thickness or flexa-width from one head to the next
410  * within a ligature (this would totally screw up alignment), and (b)
411  * some of these properties (like flexa-width) are specific to
412  * e.g. the MensuralLigature (as in contrast to e.g. LigatureBracket),
413  * and therefore should not be handled in the generic LigatureHead
414  * (which is also used by LigatureBracket).  Therefore, we let the
415  * user control these properties via the concrete Ligature grob (like
416  * MensuralLigature) and then copy these properties as necessary to
417  * each of the LigatureHead grobs.  This is what
418  * propagate_properties() does.
419  */
420 void
421 Mensural_ligature_engraver::propagate_properties ()
422 {
423   SCM thickness_scm =
424     finished_ligature_->get_grob_property ("thickness");
425   Real thickness = (thickness_scm != SCM_EOL) ?
426     gh_scm2double (thickness_scm) : 1.4;
427   thickness *= finished_ligature_->get_paper ()->get_var ("linethickness");
428
429   Real head_width =
430     Font_interface::get_default_font (finished_ligature_)->
431     find_by_name ("noteheads--1mensural").extent (X_AXIS).length ();
432   SCM flexa_width_scm =
433     finished_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 (finished_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 ()
476 {
477   Item *first = 0;
478   for (int i = 0; i < primitives_.size (); i++)
479     {
480       Item *current = dynamic_cast<Item*> (primitives_[i].grob_);
481       if (i == 0)
482         {
483           first = current;
484         }
485
486       get_set_column (current, first->get_column ());
487
488       if (i > 0)
489         {
490 #if 0
491           Rod r;
492           r.distance_ = distance_;
493           r.item_l_drul_[LEFT] = first;
494           r.item_l_drul_[RIGHT] = current;
495           r.add_to_cols ();
496 #endif
497           current->translate_axis (distance_, X_AXIS);
498         }
499
500       distance_ +=
501         gh_scm2double (current->get_grob_property ("head-width")) -
502         gh_scm2double (current->get_grob_property ("thickness"));
503     }
504 }
505
506 void
507 Mensural_ligature_engraver::join_primitives ()
508 {
509   Pitch last_pitch;
510   for (int i = 0; i < primitives_.size (); i++)
511     {
512       Grob_info info = primitives_[i];
513       Note_req *nr = dynamic_cast<Note_req*> (info.music_cause ());
514       Pitch pitch = *unsmob_pitch (nr->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::try_stop_ligature ()
532 {
533   if (finished_ligature_)
534     {
535       transform_heads ();
536       propagate_properties ();
537       fold_up_primitives ();
538       join_primitives ();
539
540       for (int i = 0; i < primitives_.size (); i++)
541         {
542           typeset_grob (primitives_[i].grob_);
543         }
544
545       primitives_.clear ();
546       finished_ligature_ = 0;
547     }
548 }
549
550 void
551 Mensural_ligature_engraver::acknowledge_grob (Grob_info info)
552 {
553   Ligature_engraver::acknowledge_grob (info);
554   if (ligature_)
555     {
556       if (Note_head::has_interface (info.grob_))
557         {
558           primitives_.push (info);
559         }
560     }
561 }
562
563 ENTER_DESCRIPTION (Mensural_ligature_engraver,
564 /* descr */       "Handles Mensural_ligature_requests by glueing special ligature heads together.",
565 /* creats*/       "MensuralLigature",
566 /* acks  */       "ligature-head-interface note-head-interface rest-interface",
567 /* reads */       "",
568 /* write */       "");