]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
* lily/engraver.cc (internal_make_item): centralize item/spanner
[lilypond.git] / lily / accidental-engraver.cc
1 /*
2   accidental-engraver.cc -- implement accidental_engraver
3
4   (c)  1997--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5   Modified 2001--2002 by Rune Zedeler <rz@daimi.au.dk>
6 */
7
8 #include "event.hh"
9
10 #include "item.hh"
11 #include "tie.hh"
12 #include "rhythmic-head.hh"
13 #include "engraver-group-engraver.hh"
14 #include "accidental-placement.hh"
15 #include "side-position-interface.hh"
16 #include "engraver.hh"
17 #include "arpeggio.hh"
18 #include "warn.hh"
19 #include "translator-group.hh"
20 #include "protected-scm.hh"
21
22 /**
23
24
25 FIXME: should not compute vertical positioning of accidentals, but
26 get them from the noteheads
27
28 The algorithm for accidentals should be documented, and made
29 tweakable.
30
31 */
32
33 struct Accidental_entry {
34   bool done_;
35   Music * melodic_;
36   Grob * accidental_;
37   Translator_group *origin_;
38   Grob*  head_;
39   Accidental_entry();
40 };
41
42 Accidental_entry::Accidental_entry()
43 {
44   done_ = false;
45   melodic_ =0;
46   accidental_ = 0;
47   origin_ = 0;
48   head_ = 0;
49 }
50
51 struct Accidental_engraver : Engraver {
52 protected:
53   TRANSLATOR_DECLARATIONS (Accidental_engraver);
54   virtual void process_music ();
55   virtual void acknowledge_grob (Grob_info);
56   virtual void stop_translation_timestep ();
57   virtual void initialize ();
58   virtual void process_acknowledged_grobs ();
59   virtual void finalize ();
60 public:
61
62   Protected_scm last_keysig_;
63
64   /*
65     Urgh. Since the accidentals depend on lots of variables, we have to
66     store all information before we can really create the accidentals.
67   */
68   Link_array<Grob> left_objects_;
69   Link_array<Grob> right_objects_;
70
71   Grob * accidental_placement_;
72
73   /*
74     The next 
75    */
76   Array<Accidental_entry> accidentals_;
77   Link_array<Grob> ties_;
78 };
79
80
81 static void
82 set_property_on_children (Translator_group * trans, const char * sym, SCM val)
83 {
84   trans->set_property (sym, val);
85   for (SCM p = trans->trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
86     Translator_group *trg =  dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
87     set_property_on_children(trg,sym,ly_deep_copy(val));
88   }
89 }
90
91 Accidental_engraver::Accidental_engraver ()
92 {
93   accidental_placement_ = 0;
94   last_keysig_ = SCM_EOL;
95 }
96
97 void
98 Accidental_engraver::initialize ()
99 {
100   last_keysig_ = get_property ("keySignature");
101
102   Translator_group * trans_ = daddy_trans_;
103   while (trans_)
104     {
105       trans_ -> set_property ("localKeySignature",  ly_deep_copy (last_keysig_));
106       trans_ = trans_->daddy_trans_;
107     }
108   set_property_on_children (daddy_trans_,"localKeySignature", last_keysig_);
109 }
110
111 /*
112
113 calculates the number of accidentals on basis of the current local key sig
114   (passed as argument)
115   Returns number of accidentals (0, 1 or 2).
116     Negative (-1 or -2) if accidental has changed.
117
118 */
119 static int
120 number_accidentals_from_sig (SCM sig, Music *, Pitch *pitch, SCM curbarnum, SCM lazyness, 
121                     bool ignore_octave_b)
122 {
123   int n = pitch->get_notename ();
124   int o = pitch->get_octave();
125   int a = pitch->get_alteration ();
126   int curbarnum_i = gh_scm2int (curbarnum);
127   int accbarnum_i = 0;
128
129   SCM prev;
130   if (ignore_octave_b)
131     prev = ly_assoc_cdr (scm_int2num (n), sig);
132   else
133     prev = scm_assoc (gh_cons (scm_int2num (o), scm_int2num (n)), sig);
134
135   /* should really be true unless prev == SCM_BOOL_F */
136   if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
137     {
138       accbarnum_i = gh_scm2int (ly_cddr (prev));
139       prev = gh_cons (ly_car (prev), ly_cadr (prev));
140     }
141   
142   /* If an accidental was not found or the accidental was too old */
143   if (prev == SCM_BOOL_F ||
144       (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
145     prev = scm_assoc (scm_int2num (n), sig);
146
147
148   SCM prev_acc = (prev == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev);
149
150   int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
151
152   int num;
153   if (a == p && gh_number_p (prev_acc))
154     num = 0;
155   else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
156     num = 2;
157   else
158     num = 1;
159   
160   return a == p ? num : -num;
161 }
162
163 static int
164 number_accidentals (Music * note, Pitch *pitch, Translator_group * origin, 
165                     SCM accidentals, SCM curbarnum)
166 {
167   int number = 0;
168
169   bool diff = false;
170   if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
171     warning (_f ("Accidental typesetting list must begin with context-name: %s", 
172                  ly_scm2string (ly_car (accidentals)).to_str0 ()));
173   
174   for (; gh_pair_p (accidentals) && origin; accidentals = gh_cdr (accidentals))
175     {
176       // If pair then it is a new accidentals typesetting rule to be checked
177       SCM rule = gh_car (accidentals);
178       if (gh_pair_p (rule))
179         {
180           SCM type = gh_car (rule);
181           SCM lazyness = gh_cdr (rule);
182           SCM localsig = origin->get_property ("localKeySignature");
183           
184           bool same_octave_b = 
185             gh_eq_p (ly_symbol2scm ("same-octave"), type);
186           bool any_octave_b = 
187             gh_eq_p (ly_symbol2scm ("any-octave"), type);
188
189           if (same_octave_b || any_octave_b)
190             {
191               int n = number_accidentals_from_sig
192                 (localsig, note, pitch, curbarnum, lazyness, any_octave_b);
193               diff = diff || (n < 0);
194               number = max (number, abs (n));     
195             }
196           else
197             warning (_f ("unknown accidental typesetting: %s. Ignored", 
198                          ly_symbol2string (type).to_str0 ()));
199         }
200       
201
202       /*
203         if symbol then it is a context name. Scan parent contexts to find it.
204       */
205       else if (gh_symbol_p (rule))
206         {
207           Translator_group * dad = origin;
208           while (dad && !dad->is_alias (rule))
209             dad = dad->daddy_trans_;
210       
211           if (dad)
212             origin = dad;
213         }
214       else warning (_f ("Accidental rule must be pair or context-name; Found %s", 
215                         ly_scm2string (rule).to_str0 ()));
216     }
217
218   return diff ? -number : number;
219 }
220
221 void
222 Accidental_engraver::process_acknowledged_grobs ()
223 {
224   if (accidentals_.size () && !accidentals_.top().done_)
225     {
226       //SCM localsig = get_property ("localKeySignature");
227       SCM accidentals =  get_property ("autoAccidentals");
228       SCM cautionaries =  get_property ("autoCautionaries");
229       SCM barnum = get_property ("currentBarNumber");
230       SCM smp = get_property("measurePosition");
231       Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
232       if(mp.main_part_<Rational(0) && gh_number_p(barnum)) barnum = scm_int2num(gh_scm2int(barnum)-1);
233       bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
234       for (int i = 0; i  < accidentals_.size (); i++) 
235         {
236           if (accidentals_[i].done_ )
237             continue;
238           accidentals_[i].done_  = true;
239           Grob * support = accidentals_[i].head_;
240           Music * note = accidentals_[i].melodic_;
241           Translator_group * origin = accidentals_[i].origin_;
242
243           Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
244           int num = number_accidentals (note, pitch, origin, accidentals, barnum);
245           int num_caut = number_accidentals (note, pitch, origin, cautionaries, barnum);
246           bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
247           
248           if (abs (num_caut) > abs (num))
249             {
250               num = num_caut;
251               cautionary = true;
252             }
253
254           if(num==0 && to_boolean (note->get_mus_property ("force-accidental")))
255              num=1;
256           
257           bool different = num < 0;
258           num = abs (num);
259
260           /* see if there's a tie that "changes" the accidental */
261           /* works because if there's a tie, the note to the left
262              is of the same pitch as the actual note */
263
264           Grob *tie_break_reminder = 0;
265           bool tie_changes = false;
266           for (int j = 0; j < ties_.size (); j++)
267             if (support == Tie::head (ties_[j], RIGHT))
268               {
269                 tie_changes = different;
270
271                 /* Enable accidentals for broken tie
272
273                 We only want an accidental on a broken tie, 
274                 if the tie changes the accidental.
275                    
276                 Maybe check property noTieBreakForceAccidental? */
277                 if (different)
278                   tie_break_reminder = ties_[j];
279                 break;
280               }
281
282           if (num)
283             {
284               Grob * a = make_item ("Accidental");
285               a->set_parent (support, Y_AXIS);
286
287               if (!accidental_placement_)
288                 {
289                   accidental_placement_ = make_item ("AccidentalPlacement");
290                   announce_grob (accidental_placement_, a->self_scm());
291                 }
292               
293               Accidental_placement::add_accidental (accidental_placement_, a);
294               announce_grob (a, SCM_EOL);
295
296               
297               SCM accs = gh_cons (scm_int2num (pitch->get_alteration ()), SCM_EOL);
298               if (num == 2 && extra_natural_b)
299                 accs = gh_cons (scm_int2num (0), accs);
300
301               /* TODO:
302
303               add cautionary option in accidental.
304                */
305
306               if (cautionary)
307                 {
308                   a->set_grob_property ("cautionary", SCM_BOOL_T);
309                 }
310               
311               if (tie_break_reminder)
312                 {
313                   // TODO.
314                   a->set_grob_property ("tie", tie_break_reminder->self_scm());
315                 }
316               
317               
318               support->set_grob_property ("accidental-grob", a->self_scm ());
319
320               a->set_grob_property ("accidentals", accs);
321               accidentals_[i].accidental_ = a;
322  /*
323         We add the accidentals to the support of the arpeggio, so it is put left of the
324         accidentals. 
325         
326       */
327               for (int i = 0;  i < left_objects_.size ();  i++)
328                 Side_position_interface::add_support (left_objects_[i], a);
329               for (int i = 0;  i < right_objects_.size ();  i++)
330                 Side_position_interface::add_support (a, right_objects_[i]);
331             }
332           
333
334           /*
335             We should not record the accidental if it is the first
336             note and it is tied from the previous measure.
337
338             Checking whether it is tied also works mostly, but will it
339             always do the correct thing?
340           */
341           
342
343           int n = pitch->get_notename ();
344           int o = pitch->get_octave ();
345           int a = pitch->get_alteration ();
346           SCM on_s = gh_cons (scm_int2num (o), scm_int2num (n));
347
348           /*
349             TODO: Speed this up!
350             
351             Perhaps only check translators mentioned in the auto-accidentals?
352             -rz
353
354             TODO: profile this.
355             
356             I'd be surprised if the impact of this would be
357             measurable.  Anyway, it seems localsig doesn't change
358             every time-step, but a set_property() is done every
359             time. We could save on that, probably.
360
361             --hwn.
362             
363             
364           */
365
366           while (origin)
367             {
368               SCM localsig = origin->get_property ("localKeySignature");
369               if (tie_changes)
370                 {
371                   /*
372                     Remember an alteration that is different both from
373                     that of the tied note and of the key signature.
374                   */
375                   localsig = ly_assoc_front_x
376                     (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
377                 }
378               else
379                 {
380                   /*
381                     not really really correct if there are more than one
382                     noteheads with the same notename.
383                   */
384                   localsig = ly_assoc_front_x
385                     (localsig, on_s, gh_cons (scm_int2num (a), barnum)); 
386                 }
387               origin->set_property ("localKeySignature",  localsig);
388               origin = origin->daddy_trans_;
389             }
390         }
391     }
392 }
393
394 void
395 Accidental_engraver::finalize ()
396 {
397   last_keysig_ = SCM_EOL;
398 }
399
400 void
401 Accidental_engraver::stop_translation_timestep ()
402 {
403   for (int i = 0; i < accidentals_.size(); i++)
404     {
405       Grob *a = accidentals_[i].accidental_;
406       if (a)
407         {
408           typeset_grob (a);
409         }
410     }
411
412   if (accidental_placement_)
413     typeset_grob(accidental_placement_);
414   accidental_placement_ = 00;
415   
416   accidentals_.clear();
417   left_objects_.clear ();
418   right_objects_.clear ();
419   ties_.clear ();
420 }
421
422 void
423 Accidental_engraver::acknowledge_grob (Grob_info info)
424 {
425   Music * note =  info.music_cause ();
426
427   if (note
428       && note->is_mus_type("note-event")
429       && Rhythmic_head::has_interface (info.grob_))
430     {
431       Accidental_entry entry ;
432       entry.head_ = info.grob_;
433       entry.origin_ = info.origin_trans_->daddy_trans_;
434       entry.melodic_ = note;
435
436       accidentals_.push (entry);
437     }
438   else if (Tie::has_interface (info.grob_))
439     {
440       ties_.push (info.grob_);
441     }
442   else if (Arpeggio::has_interface (info.grob_))
443     {
444       left_objects_.push (info.grob_); 
445     }
446   else if (info.grob_->internal_has_interface (ly_symbol2scm("finger-interface")))
447     {
448       left_objects_.push (info.grob_); 
449     }
450 }
451
452 void
453 Accidental_engraver::process_music ()
454 {
455   SCM sig = get_property ("keySignature");
456
457   /* Detect key sig changes.
458      Update all parents and children
459   */
460   if (last_keysig_ != sig)
461     {
462       Translator_group * trans_ = daddy_trans_;
463       while (trans_)
464         {
465           trans_ -> set_property ("localKeySignature",  ly_deep_copy (sig));
466           trans_ = trans_->daddy_trans_;
467         }
468       set_property_on_children(daddy_trans_,"localKeySignature", sig);
469
470       last_keysig_ = sig;
471     }
472 }
473
474
475
476
477
478 ENTER_DESCRIPTION (Accidental_engraver,
479 "Make accidentals.  Catches note heads, ties and notices key-change "
480 " events.  Due to interaction with ties (which don't come together "
481 " with note heads), this needs to be in a context higher than Tie_engraver.",
482                "Accidental",
483 /* accepts */     "",
484                "finger-interface rhythmic-head-interface tie-interface arpeggio-interface",
485                "localKeySignature extraNatural autoAccidentals autoCautionaries",
486                    "localKeySignature");