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