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