]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
2003 -> 2004
[lilypond.git] / lily / accidental-engraver.cc
1 /*
2   accidental-engraver.cc -- implement accidental_engraver
3
4   (c) 1997--2004 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           if (!pitch)
245             continue;
246           
247           int num = number_accidentals (note, pitch, origin, accidentals, barnum);
248           int num_caut = number_accidentals (note, pitch, origin, cautionaries, barnum);
249           bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
250           
251           if (abs (num_caut) > abs (num))
252             {
253               num = num_caut;
254               cautionary = true;
255             }
256
257           if(num==0 && to_boolean (note->get_mus_property ("force-accidental")))
258              num=1;
259           
260           bool different = num < 0;
261           num = abs (num);
262
263           /* see if there's a tie that "changes" the accidental */
264           /* works because if there's a tie, the note to the left
265              is of the same pitch as the actual note */
266
267           Grob *tie_break_reminder = 0;
268           bool tie_changes = false;
269           for (int j = 0; j < ties_.size (); j++)
270             if (support == Tie::head (ties_[j], RIGHT))
271               {
272                 tie_changes = different;
273
274                 /* Enable accidentals for broken tie
275
276                 We only want an accidental on a broken tie, 
277                 if the tie changes the accidental.
278                    
279                 Maybe check property noTieBreakForceAccidental? */
280                 if (different)
281                   tie_break_reminder = ties_[j];
282                 break;
283               }
284
285           if (num)
286             {
287               Grob * a = make_item ("Accidental");
288               a->set_parent (support, Y_AXIS);
289
290               if (!accidental_placement_)
291                 {
292                   accidental_placement_ = make_item ("AccidentalPlacement");
293                   announce_grob (accidental_placement_, a->self_scm());
294                 }
295               
296               Accidental_placement::add_accidental (accidental_placement_, a);
297               announce_grob (a, SCM_EOL);
298
299               
300               SCM accs = gh_cons (scm_int2num (pitch->get_alteration ()), SCM_EOL);
301               if (num == 2 && extra_natural_b)
302                 accs = gh_cons (scm_int2num (0), accs);
303
304               /* TODO:
305
306               add cautionary option in accidental.
307                */
308
309               if (cautionary)
310                 {
311                   a->set_grob_property ("cautionary", SCM_BOOL_T);
312                 }
313               
314               if (tie_break_reminder)
315                 {
316                   // TODO.
317                   a->set_grob_property ("tie", tie_break_reminder->self_scm());
318                 }
319               
320               
321               support->set_grob_property ("accidental-grob", a->self_scm ());
322
323               a->set_grob_property ("accidentals", accs);
324               accidentals_[i].accidental_ = a;
325  /*
326         We add the accidentals to the support of the arpeggio, so it is put left of the
327         accidentals. 
328         
329       */
330               for (int i = 0;  i < left_objects_.size ();  i++)
331                 Side_position_interface::add_support (left_objects_[i], a);
332               for (int i = 0;  i < right_objects_.size ();  i++)
333                 Side_position_interface::add_support (a, right_objects_[i]);
334             }
335           
336
337           /*
338             We should not record the accidental if it is the first
339             note and it is tied from the previous measure.
340
341             Checking whether it is tied also works mostly, but will it
342             always do the correct thing?
343           */
344           
345
346           int n = pitch->get_notename ();
347           int o = pitch->get_octave ();
348           int a = pitch->get_alteration ();
349           SCM on_s = gh_cons (scm_int2num (o), scm_int2num (n));
350
351           /*
352             TODO: Speed this up!
353             
354             Perhaps only check translators mentioned in the auto-accidentals?
355             -rz
356
357             TODO: profile this.
358             
359             I'd be surprised if the impact of this would be
360             measurable.  Anyway, it seems localsig doesn't change
361             every time-step, but a set_property() is done every
362             time. We could save on that, probably.
363
364             --hwn.
365             
366             
367           */
368
369           while (origin)
370             {
371               SCM localsig = origin->get_property ("localKeySignature");
372               if (tie_changes)
373                 {
374                   /*
375                     Remember an alteration that is different both from
376                     that of the tied note and of the key signature.
377                   */
378                   localsig = ly_assoc_front_x
379                     (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
380                 }
381               else
382                 {
383                   /*
384                     not really really correct if there are more than one
385                     noteheads with the same notename.
386                   */
387                   localsig = ly_assoc_front_x
388                     (localsig, on_s, gh_cons (scm_int2num (a), barnum)); 
389                 }
390               origin->set_property ("localKeySignature",  localsig);
391               origin = origin->daddy_trans_;
392             }
393         }
394     }
395 }
396
397 void
398 Accidental_engraver::finalize ()
399 {
400   last_keysig_ = SCM_EOL;
401 }
402
403 void
404 Accidental_engraver::stop_translation_timestep ()
405 {
406   for (int i = 0; i < accidentals_.size(); i++)
407     {
408       Grob *a = accidentals_[i].accidental_;
409       if (a)
410         {
411           typeset_grob (a);
412         }
413     }
414
415   if (accidental_placement_)
416     typeset_grob(accidental_placement_);
417   accidental_placement_ = 00;
418   
419   accidentals_.clear();
420   left_objects_.clear ();
421   right_objects_.clear ();
422   ties_.clear ();
423 }
424
425 void
426 Accidental_engraver::acknowledge_grob (Grob_info info)
427 {
428   Music * note =  info.music_cause ();
429
430   if (note
431       && note->is_mus_type("note-event")
432       && Rhythmic_head::has_interface (info.grob_))
433     {
434       Accidental_entry entry ;
435       entry.head_ = info.grob_;
436       entry.origin_ = info.origin_trans_->daddy_trans_;
437       entry.melodic_ = note;
438
439       accidentals_.push (entry);
440     }
441   else if (Tie::has_interface (info.grob_))
442     {
443       ties_.push (info.grob_);
444     }
445   else if (Arpeggio::has_interface (info.grob_))
446     {
447       left_objects_.push (info.grob_); 
448     }
449   else if (info.grob_->internal_has_interface (ly_symbol2scm("finger-interface")))
450     {
451       left_objects_.push (info.grob_); 
452     }
453 }
454
455 void
456 Accidental_engraver::process_music ()
457 {
458   SCM sig = get_property ("keySignature");
459
460   /* Detect key sig changes.
461      Update all parents and children
462   */
463   if (last_keysig_ != sig)
464     {
465       Translator_group * trans_ = daddy_trans_;
466       while (trans_)
467         {
468           trans_ -> set_property ("localKeySignature",  ly_deep_copy (sig));
469           trans_ = trans_->daddy_trans_;
470         }
471       set_property_on_children(daddy_trans_,"localKeySignature", sig);
472
473       last_keysig_ = sig;
474     }
475 }
476
477
478
479
480
481 ENTER_DESCRIPTION (Accidental_engraver,
482 "Make accidentals.  Catches note heads, ties and notices key-change "
483 " events.  Due to interaction with ties (which don't come together "
484 " with note heads), this needs to be in a context higher than Tie_engraver.",
485                "Accidental",
486 /* accepts */     "",
487                "finger-interface rhythmic-head-interface tie-interface arpeggio-interface",
488                "localKeySignature extraNatural autoAccidentals autoCautionaries",
489                    "localKeySignature");