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