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