]> git.donarmstrong.com Git - lilypond.git/blob - lily/new-accidental-engraver.cc
2003 -> 2004
[lilypond.git] / lily / new-accidental-engraver.cc
1 /*
2   new-accidental-engraver.cc -- implement new_accidental_engraver
3
4   (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5   Modified 2001--2004 by Rune Zedeler <rz@daimi.au.dk>
6
7   This is an experimental file - producing correct accidentals but
8   unfortunately ruining the spacing. -rz
9   
10 */
11
12 #include "event.hh"
13
14 #include "item.hh"
15 #include "tie.hh"
16 #include "rhythmic-head.hh"
17 #include "engraver-group-engraver.hh"
18 #include "accidental-placement.hh"
19 #include "side-position-interface.hh"
20 #include "engraver.hh"
21 #include "arpeggio.hh"
22 #include "warn.hh"
23
24 #include "translator-group.hh"
25
26 /**
27
28
29 FIXME: should not compute vertical positioning of accidentals, but
30 get them from the noteheads
31
32 The algorithm for accidentals should be documented, and made
33 tweakable.
34
35 */
36
37 struct New_accidental_entry {
38   int pass_done_;
39   int number_accidentals_;
40   int number_cautionaries_;
41   bool different_;
42   Music * melodic_;
43   Grob * accidental_;
44   Translator_group *origin_;
45   Grob*  head_;
46   New_accidental_entry();
47 };
48
49 New_accidental_entry::New_accidental_entry()
50 {
51   pass_done_ = 0;
52   number_accidentals_ = 0;
53   number_cautionaries_ = 0;
54   different_ = false;
55   melodic_ = 0;
56   accidental_ = 0;
57   origin_ = 0;
58   head_ = 0;
59 }
60
61 struct New_accidental_engraver : Engraver {
62 protected:
63   TRANSLATOR_DECLARATIONS (New_accidental_engraver);
64   virtual void process_music ();
65   virtual void acknowledge_grob (Grob_info);
66   virtual void stop_translation_timestep ();
67   virtual void process_acknowledged_grobs ();
68   virtual void finalize ();
69   virtual void process_grobs_first_pass ();
70   virtual void process_grobs_second_pass ();
71
72   public:
73
74   /*
75     TODO -> property.
76     
77     This is not a property, and it is not protected.  This poses a
78     very small risk of the value being GC'd from under us.
79   */
80   SCM last_keysig_;
81
82   /*
83     Urgh. Since the accidentals depend on lots of variables, we have to
84     store all information before we can really create the accidentals.
85   */
86   Link_array<Grob> arpeggios_;
87
88   Grob * accidental_placement_;
89   
90
91   /*
92     The next 
93    */
94   Array<New_accidental_entry> accidentals_;
95   
96   Link_array<Grob> ties_;
97
98
99 };
100
101
102 New_accidental_engraver::New_accidental_engraver ()
103 {
104   last_keysig_ = SCM_BOOL_F;
105   accidental_placement_ = 0;
106 }
107
108 /* inserts the source alist into the destination alist, erasing old entries.
109    result is: dest = merged
110 */ 
111 static SCM merge_alists_front_x (SCM src, SCM dest) {
112   if(gh_pair_p(src)) {
113     dest = merge_alists_front_x(ly_cdr(src),dest);
114     dest = ly_assoc_front_x(dest, ly_caar(src), ly_cdar(src));
115   }
116   return dest;
117 }
118
119 static void merge_property_on_children (Translator_group * trans,
120                                        const char * from_sym, const char * to_sym)
121 {
122   SCM from = trans->get_property(from_sym);
123   SCM to = trans->get_property(to_sym);
124   to = merge_alists_front_x(from, to);
125   trans->set_property (to_sym,  to);
126   trans->set_property (from_sym, SCM_EOL);
127   for (SCM p = trans -> trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
128     Translator_group *trg =  dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
129     merge_property_on_children(trg, from_sym, to_sym);
130   }
131 }
132
133 static void merge_property_on_family (Translator_group * trans,
134                                       const char * from_sym, const char * to_sym)
135 {
136   merge_property_on_children (trans, from_sym, to_sym);
137   trans = trans->daddy_trans_;
138   while (trans)
139     {
140       SCM from = trans->get_property(from_sym);
141       SCM to = trans->get_property(to_sym);
142       to = merge_alists_front_x(from, to);
143       trans->set_property (to_sym,  to);
144       trans->set_property (from_sym, SCM_EOL);
145       trans = trans->daddy_trans_;
146     }
147 }
148
149 static void set_property_on_children (Translator_group * trans, const char * sym, SCM val)
150 {
151   trans->set_property (sym, val);
152   for (SCM p = trans -> trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
153     Translator_group *trg =  dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
154     set_property_on_children(trg,sym,ly_deep_copy(val));
155   }
156 }
157
158 static void set_property_on_family(Translator_group * trans, const char * sym, SCM val)
159 {
160   set_property_on_children (trans, sym, val);
161   trans = trans->daddy_trans_;
162   while (trans)
163     {
164       trans -> set_property (sym,  ly_deep_copy (val));
165       trans = trans->daddy_trans_;
166     }
167 }
168
169 /*
170 calculates the number of accidentals on basis of the current local key sig
171   (passed as argument)
172   Returns number of accidentals (0, 1 or 2).
173     Negative (-1 or -2) if accidental has changed.
174
175 */
176 static int
177 number_accidentals (SCM sig, Music * note, Pitch *pitch, SCM curbarnum, SCM lazyness, 
178                     bool ignore_octave_b)
179 {
180   int n = pitch->get_notename ();
181   int o = pitch->get_octave ();
182   int a = pitch->get_alteration (); 
183   int curbarnum_i = gh_scm2int (curbarnum);
184   int accbarnum_i = 0;
185
186   SCM prev;
187   if (ignore_octave_b)
188     prev = ly_assoc_cdr (gh_int2scm (n), sig);
189   else
190     prev = scm_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
191
192   /* should really be true unless prev == SCM_BOOL_F */
193   if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
194     {
195       accbarnum_i = gh_scm2int (ly_cddr (prev));
196       prev = gh_cons (ly_car (prev), ly_cadr (prev));
197     }
198   
199   /* If an accidental was not found or the accidental was too old */
200   if (prev == SCM_BOOL_F ||
201       (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
202     prev = scm_assoc (gh_int2scm (n), sig);
203
204
205   SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
206
207   int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
208
209   int num;
210   if (a == p
211       && !to_boolean (note->get_mus_property ("force-accidental"))
212       && gh_number_p (prev_acc))
213     num = 0;
214   else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
215     num = 2;
216   else
217     num = 1;
218   
219   return a == p ? num : -num;
220 }
221
222 static int
223 number_accidentals (Music * note, Pitch *pitch, Translator_group * origin, 
224                     SCM accidentals, SCM curbarnum)
225 {
226   int number = 0;
227
228   bool diff = false;
229   if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
230     warning (_f ("Accidental typesetting list must begin with context-name: %s", 
231                  ly_scm2string (ly_car (accidentals)).to_str0 ()));
232   
233   while (gh_pair_p (accidentals) && origin)
234     {
235       // If pair then it is a new accidentals typesetting rule to be checked
236       if (gh_pair_p (ly_car (accidentals)))
237         {
238           SCM type = gh_caar (accidentals);
239           SCM lazyness = gh_cdar (accidentals);
240           SCM localsig = origin->get_property ("localKeySignature");
241           
242           bool same_octave_b = 
243             gh_eq_p (ly_symbol2scm ("same-octave"), type);
244           bool any_octave_b = 
245             gh_eq_p (ly_symbol2scm ("any-octave"), type);
246
247           if (same_octave_b || any_octave_b)
248             {
249               int n = number_accidentals
250                 (localsig, note, pitch, curbarnum, lazyness, any_octave_b);
251               diff = diff || (n < 0);
252               number = max (number, abs (n));     
253             }
254           else
255             warning (_f ("unknown accidental typesetting: %s. Ignored", 
256                          ly_symbol2string (type).to_str0 ()));
257         }
258       
259
260       /*
261         if symbol then it is a context name. Scan parent contexts to find it.
262       */
263       else if (gh_symbol_p (ly_car (accidentals)))
264         {
265           SCM context = ly_car (accidentals);
266           
267           while (origin && !origin->is_alias (context))
268             origin = origin->daddy_trans_;
269       
270           if (!origin)
271             warning (_f ("Symbol is not a parent context: %s. Ignored", 
272                          ly_symbol2string (context).to_str0 ()));
273         }
274       else warning (_f ("Accidental typesetting must be pair or context-name: %s", 
275                         ly_scm2string (ly_car (accidentals)).to_str0 ()));
276       
277       accidentals = ly_cdr (accidentals);
278     }
279   return diff ? -number : number;
280 }
281
282
283 /* 
284   Perhaps one should join the two functions into one function taking an
285   argument (pass).
286   OTOH even though code would be smaller, spaghetti-level would increase.
287 */
288 void
289 New_accidental_engraver::process_grobs_first_pass ()
290 {
291   SCM accidentals =  get_property ("autoAccidentals");
292   SCM cautionaries =  get_property ("autoCautionaries");
293   SCM barnum = get_property ("currentBarNumber");
294
295   for (int i = 0; i  < accidentals_.size (); i++) 
296     {
297       if (accidentals_[i].pass_done_ >= 1)
298         continue;
299       accidentals_[i].pass_done_  = 1;
300
301       Grob * support = accidentals_[i].head_;
302       Music * note = accidentals_[i].melodic_;
303       Translator_group * origin = accidentals_[i].origin_;
304       Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
305
306       int num;
307       num = number_accidentals (note, pitch, origin, accidentals, barnum);
308       accidentals_[i].number_accidentals_ = abs(num);
309       accidentals_[i].different_ = num<0;
310
311       num = number_accidentals (note, pitch, origin, cautionaries, barnum);
312       accidentals_[i].number_cautionaries_ = abs(num);
313       accidentals_[i].different_ = accidentals_[i].different_ || num<0;
314
315       bool tie_changes = false;
316       for (int j = 0; j < ties_.size (); j++)
317         if (support == Tie::head (ties_[j], RIGHT))
318           tie_changes = accidentals_[i].different_;
319       int n = pitch->get_notename ();
320       int o = pitch->get_octave ();
321       int a = pitch->get_alteration ();
322       SCM o_s = gh_int2scm (o);
323       SCM n_s = gh_int2scm (n);
324       SCM on_s = gh_cons (o_s,n_s);
325       
326       while (origin)
327         {
328           SCM sigch = origin->get_property ("localKeySignatureChanges");
329           SCM alt;
330           if (tie_changes)
331             /*
332               Remember an alteration that is different both from
333               that of the tied note and of the key signature.
334             */
335             alt = SCM_BOOL_T;
336           else
337             alt = gh_int2scm (a);
338           bool other_alt_same_oct = false;
339           bool other_alt_any_oct = false;
340           for (SCM j = sigch; gh_pair_p(j); j = ly_cdr(j)) {
341             SCM entry = ly_car(j);
342             /* if same notename has a different alt already recorded: */
343             if(gh_equal_p(ly_cdar(entry),n_s) && !gh_equal_p(ly_cadr(entry),alt))
344               {
345                 /* if it is also in same octave */
346                 if(gh_equal_p(ly_caar(entry),o_s))
347                   other_alt_same_oct = true;
348                 else
349                   other_alt_any_oct = true;
350               }
351           }
352           if(other_alt_same_oct)
353             alt = SCM_BOOL_T;
354           sigch = ly_assoc_front_x (sigch, on_s, gh_cons(alt,barnum)); 
355           if(other_alt_any_oct && !other_alt_same_oct) {
356             sigch = ly_assoc_front_x (sigch, on_s, gh_cons(SCM_BOOL_T,barnum));
357           }
358           origin->set_property ("localKeySignatureChanges",  sigch);
359           origin = origin->daddy_trans_;  
360         }
361     }
362 }
363
364 void
365 New_accidental_engraver::process_grobs_second_pass ()
366 {
367   SCM accidentals =  get_property ("autoAccidentals");
368   SCM cautionaries =  get_property ("autoCautionaries");
369   SCM barnum = get_property ("currentBarNumber");
370   
371   bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
372   for (int i = 0; i  < accidentals_.size (); i++) 
373     {
374       if (accidentals_[i].pass_done_ >= 2)
375         continue;
376       accidentals_[i].pass_done_  = 2;
377       Grob * support = accidentals_[i].head_;
378       Music * note = accidentals_[i].melodic_;
379       Translator_group * origin = accidentals_[i].origin_;
380       
381       Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
382
383       int num;
384       num = number_accidentals (note, pitch, origin, accidentals, barnum);
385       accidentals_[i].number_accidentals_ =
386         max (accidentals_[i].number_accidentals_, abs(num));
387       accidentals_[i].different_ = accidentals_[i].different_ || num<0;
388
389       num = number_accidentals (note, pitch, origin, cautionaries, barnum);
390       accidentals_[i].number_cautionaries_ =
391         max (accidentals_[i].number_cautionaries_, abs(num));
392       accidentals_[i].different_ = accidentals_[i].different_ || num<0;
393
394
395       bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
396       
397       if (accidentals_[i].number_cautionaries_ >accidentals_[i].number_accidentals_ )
398         {
399           num = accidentals_[i].number_cautionaries_;
400           cautionary = true;
401         }
402       else
403           num = accidentals_[i].number_accidentals_;
404
405       bool tie_changes = false;
406       Grob *tie_break_reminder = 0;
407       for (int j = 0; j < ties_.size (); j++)
408         if (support == Tie::head (ties_[j], RIGHT))
409           {
410             tie_changes = accidentals_[i].different_;
411             tie_break_reminder = ties_[j];
412           }
413       
414       if (num)
415         {
416           Grob * a = make_item ("Accidental");
417           a->set_parent (support, Y_AXIS);
418           
419           if (!accidental_placement_)
420             {
421               accidental_placement_ = make_item ("AccidentalPlacement");
422               announce_grob (accidental_placement_, a->self_scm());
423             }
424           
425           Accidental_placement::add_accidental (accidental_placement_, a);
426           announce_grob (a, SCM_EOL);
427           
428           
429           SCM accs = gh_cons (gh_int2scm (pitch->get_alteration ()), SCM_EOL);
430           if (num == 2 && extra_natural_b)
431             accs = gh_cons (gh_int2scm (0), accs);
432           
433           if (cautionary)
434             {
435               a->set_grob_property ("cautionary", SCM_BOOL_T);
436             }
437           
438           if (tie_break_reminder)
439             {
440               a->set_grob_property ("tie", tie_break_reminder->self_scm());
441             }
442           
443           
444           support->set_grob_property ("accidental-grob", a->self_scm ());
445           
446           a->set_grob_property ("accidentals", accs);
447           accidentals_[i].accidental_ = a;
448           /*
449             We add the accidentals to the support of the arpeggio, so it is
450             put left of the accidentals. 
451           */
452           for (int i = 0;  i < arpeggios_.size ();  i++)
453             Side_position_interface::add_support (arpeggios_[i], a);
454         }       
455     }
456 }
457
458 void
459 New_accidental_engraver::process_acknowledged_grobs ()
460 {
461   if (accidentals_.size () && accidentals_.top().pass_done_ < 1)
462     process_grobs_first_pass ();
463 }
464
465 void
466 New_accidental_engraver::finalize ()
467 {
468
469 }
470
471 void
472 New_accidental_engraver::stop_translation_timestep ()
473 {
474   merge_property_on_family(daddy_trans_, "localKeySignatureChanges", "localKeySignature");
475   if (accidentals_.size () && accidentals_.top().pass_done_ < 2)
476     process_grobs_second_pass ();
477
478   for (int i = 0; i < accidentals_.size(); i++)
479     {
480       Grob *a = accidentals_[i].accidental_;
481       if (a)
482         {
483           typeset_grob (a);
484         }
485     }
486
487   if (accidental_placement_)
488     typeset_grob(accidental_placement_);
489   accidental_placement_ = 00;
490
491   set_property_on_family(daddy_trans_, "localKeySignatureChanges", SCM_EOL);  
492   accidentals_.clear();
493   arpeggios_.clear ();
494   ties_.clear ();
495 }
496
497 void
498 New_accidental_engraver::acknowledge_grob (Grob_info info)
499 {
500   Music * note =  info.music_cause ();
501
502   if (note
503       && note->is_mus_type ("note-event")
504       && Rhythmic_head::has_interface (info.grob_))
505     {
506       New_accidental_entry entry ;
507       entry.head_ = info.grob_;
508       entry.origin_ = info.origin_trans_->daddy_trans_;
509       entry.melodic_ = note;
510
511       accidentals_.push (entry);
512     }
513   else if (Tie::has_interface (info.grob_))
514     {
515       ties_.push (info.grob_);
516     }
517   else if (Arpeggio::has_interface (info.grob_))
518     {
519       arpeggios_.push (info.grob_); 
520     }
521   
522 }
523
524 void
525 New_accidental_engraver::process_music ()
526 {
527   SCM sig = get_property ("keySignature");
528
529   /* Detect key sig changes.
530      Update all parents and children
531   */
532   if (last_keysig_ != sig)
533     {
534       set_property_on_family(daddy_trans_, "localKeySignature", sig);
535       set_property_on_family(daddy_trans_, "localKeySignatureChanges", SCM_EOL); //This souldn't be neccesary
536       last_keysig_ = sig;
537     }
538 }
539
540
541
542
543
544 ENTER_DESCRIPTION (New_accidental_engraver,
545 "Make accidentals.  Catches note heads, ties and notices key-change "
546 "events.  Due to interaction with ties (which don't come together "
547 "with note heads), this needs to be in a context higher than Tie_engraver.",
548                    
549                "Accidental",
550 /* accepts */     "",
551                "rhythmic-head-interface tie-interface arpeggio-interface",
552                "localKeySignature localKeySignatureChanges extraNatural autoAccidentals autoCautionaries",
553                    "localKeySignature localKeySignatureChanges");