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