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