]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
adb0d18d3447f7aebfd1d7ec1b26220f868b497c
[lilypond.git] / lily / accidental-engraver.cc
1 /*
2   accidental-engraver.cc -- implement accidental_engraver
3
4   (c)  1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5   Modified 2001-2002 by Rune Zedeler <rz@daimi.au.dk>
6 */
7
8 #include "musical-request.hh"
9 #include "command-request.hh"
10 #include "local-key-item.hh"
11 #include "item.hh"
12 #include "tie.hh"
13 #include "rhythmic-head.hh"
14 #include "engraver-group-engraver.hh"
15
16 #include "staff-symbol-referencer.hh"
17 #include "side-position-interface.hh"
18 #include "engraver.hh"
19 #include "arpeggio.hh"
20 #include "warn.hh"
21
22 #include "translator-group.hh"
23
24 /**
25
26
27 FIXME: should not compute vertical positioning of accidentals, but
28 get them from the noteheads
29
30 The algorithm for accidentals should be documented, and made
31 tweakable.
32
33 */
34
35
36 struct Accidental_engraver : Engraver {
37   Item *key_item_p_;
38 protected:
39   TRANSLATOR_DECLARATIONS (Accidental_engraver);
40   virtual void process_music ();
41   virtual void acknowledge_grob (Grob_info);
42   virtual void stop_translation_timestep ();
43   virtual void initialize ();
44   virtual void create_grobs ();
45   virtual void finalize ();
46 public:
47
48   /*
49     TODO -> property.
50     
51     This is not a property, and it is not protected.  This poses a
52     very small risk of the value being GC'd from under us.
53
54     Oh well.
55   */
56   SCM last_keysig_;
57
58   /*
59     Urgh. Since the accidentals depend on lots of variables, we have to
60     store all information before we can really create the accidentals.
61   */
62   Link_array<Grob> arpeggios_;
63   
64   Link_array<Note_req> mel_l_arr_;
65   Link_array<Grob> head_l_arr_;
66   Link_array<Item> forced_l_arr_;
67   Link_array<Grob> tie_l_arr_;
68   Link_array<Translator_group> origin_l_arr_;
69
70 };
71
72
73 Accidental_engraver::Accidental_engraver ()
74 {
75   key_item_p_ = 0;
76   last_keysig_ = SCM_EOL;
77 }
78
79 void
80 Accidental_engraver::initialize ()
81 {
82   last_keysig_ = get_property ("keySignature");
83
84   Translator_group * trans_ = daddy_trans_l_;
85   while (trans_)
86     {
87       trans_ -> set_property ("localKeySignature",  ly_deep_copy (last_keysig_));
88       trans_ = trans_->daddy_trans_l_;
89     }
90   daddy_trans_l_->set_children_property ("localKeySignature", last_keysig_);
91 }
92
93 /*
94
95 calculates the number of accidentals on basis of the current local key sig
96   (passed as argument)
97   Returns number of accidentals (0, 1 or 2).
98     Negative (-1 or -2) if accidental has changed.
99
100 */
101 static int
102 number_accidentals (SCM sig, Note_req * note_l, SCM curbarnum, SCM lazyness, 
103                     bool ignore_octave_b)
104 {
105   Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
106   int n = pitch->notename_i_;
107   int o = pitch->octave_i_;
108   int a = pitch->alteration_i_;
109   int curbarnum_i = gh_scm2int (curbarnum);
110   int accbarnum_i = 0;
111
112   SCM prev;
113   if (ignore_octave_b)
114     prev = ly_assoc_cdr (gh_int2scm (n), sig);
115   else
116     prev = gh_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
117
118   /* should really be true unless prev == SCM_BOOL_F */
119   if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
120     {
121       accbarnum_i = gh_scm2int (ly_cddr (prev));
122       prev = gh_cons (ly_car (prev), ly_cadr (prev));
123     }
124   
125   /* If an accidental was not found or the accidental was too old */
126   if (prev == SCM_BOOL_F ||
127       (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
128     prev = gh_assoc (gh_int2scm (n), sig);
129
130
131   SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
132
133   int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
134
135   int num;
136   if (a == p
137       && !to_boolean (note_l->get_mus_property ("force-accidental"))
138       && gh_number_p (prev_acc))
139     num = 0;
140   else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
141     num = 2;
142   else
143     num = 1;
144   
145   return a == p ? num : -num;
146 }
147
148 static int
149 number_accidentals (Note_req * note_l, Translator_group * origin_l, 
150                     SCM accidentals, SCM curbarnum)
151 {
152   int number = 0;
153
154   bool diff = false;
155   if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
156     warning (_f ("Accidental typesetting list must begin with context-name: %s", 
157                  ly_scm2string (ly_car (accidentals)).ch_C ()));
158   
159   while (gh_pair_p (accidentals) && origin_l)
160     {
161       // If pair then it is a new accidentals typesetting rule to be checked
162       if (gh_pair_p (ly_car (accidentals)))
163         {
164           SCM type = gh_caar (accidentals);
165           SCM lazyness = gh_cdar (accidentals);
166           SCM localsig = origin_l->get_property ("localKeySignature");
167           
168           bool same_octave_b = 
169             gh_eq_p (ly_symbol2scm ("same-octave"), type);
170           bool any_octave_b = 
171             gh_eq_p (ly_symbol2scm ("any-octave"), type);
172
173           if (same_octave_b || any_octave_b)
174             {
175               int n = number_accidentals
176                 (localsig, note_l, curbarnum, lazyness, any_octave_b);
177               diff = diff || (n < 0);
178               number = max (number, abs (n));     
179             }
180           else
181             warning (_f ("unknown accidental typesetting: %s. Ignored", 
182                          ly_symbol2string (type).ch_C ()));
183         }
184       
185
186       /*
187         if symbol then it is a context name. Scan parent contexts to find it.
188       */
189       else if (gh_symbol_p (ly_car (accidentals)))
190         {
191           String context = ly_symbol2string (ly_car (accidentals));
192           
193           while (origin_l && !origin_l->is_alias_b (context))
194             origin_l = origin_l->daddy_trans_l_;
195       
196           if (!origin_l)
197             warning (_f ("Symbol is not a parent context: %s. Ignored", 
198                          context.ch_C ()));
199         }
200       else warning (_f ("Accidental typesetting must be pair or context-name: %s", 
201                         ly_scm2string (ly_car (accidentals)).ch_C ()));
202       
203       accidentals = ly_cdr (accidentals);
204     }
205   return diff ? -number : number;
206 }
207
208 void
209 Accidental_engraver::create_grobs ()
210 {
211   if (!key_item_p_ && mel_l_arr_.size ()) 
212     {
213       //SCM localsig = get_property ("localKeySignature");
214       SCM accidentals =  get_property ("autoAccidentals");
215       SCM cautionaries =  get_property ("autoCautionaries");
216       SCM barnum = get_property ("currentBarNumber");
217
218       bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
219       for (int i = 0; i  < mel_l_arr_.size (); i++) 
220         {
221           Grob * support_l = head_l_arr_[i];
222           Note_req * note_l = mel_l_arr_[i];
223           Translator_group * origin_l = origin_l_arr_[i];
224
225           int num = number_accidentals (note_l, origin_l, accidentals, barnum);
226           int num_caut = number_accidentals (note_l, origin_l, cautionaries, barnum);
227           bool cautionary = to_boolean (note_l->get_mus_property ("cautionary"));
228           
229           if (abs (num_caut)>abs (num))
230             {
231               num = num_caut;
232               cautionary = true;
233             }
234           
235           bool different = num < 0;
236           num = abs (num);
237
238           /* see if there's a tie that "changes" the accidental */
239           /* works because if there's a tie, the note to the left
240              is of the same pitch as the actual note */
241
242           Grob *tie_break_reminder = 0;
243           bool tie_changes = false;
244           for (int j = 0; j < tie_l_arr_.size (); j++)
245             if (support_l == Tie::head (tie_l_arr_[j], RIGHT))
246               {
247                 tie_changes = different;
248
249                 /* Enable accidentals for broken tie
250
251                 We only want an accidental on a broken tie, 
252                 if the tie changes the accidental.
253                    
254                 Maybe check property noTieBreakForceAccidental? */
255                 if (different)
256                   tie_break_reminder = tie_l_arr_[j];
257                 break;
258               }
259
260           if (num)
261             {
262               if (!key_item_p_) 
263                 {
264                   key_item_p_ = new Item (get_property ("Accidentals"));
265
266                   SCM c0 = get_property ("centralCPosition");
267                   if (gh_number_p (c0))
268                     Staff_symbol_referencer::set_position (key_item_p_, gh_scm2int (c0));
269                          
270                   announce_grob (key_item_p_, SCM_EOL);
271                 }
272
273               
274               Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")), 
275                                          cautionary, 
276                                          num == 2 && extra_natural_b, 
277                                          tie_break_reminder);
278               Side_position_interface::add_support (key_item_p_, support_l);
279               
280               support_l->set_grob_property ("accidental-grob", key_item_p_->self_scm ());
281             }
282           
283
284           /*
285             We should not record the accidental if it is the first
286             note and it is tied from the previous measure.
287
288             Checking whether it is tied also works mostly, but will it
289             always do the correct thing?
290           */
291           
292           Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
293           int n = pitch->notename_i_;
294           int o = pitch->octave_i_;
295           int a = pitch->alteration_i_;
296           SCM on_s = gh_cons (gh_int2scm (o), gh_int2scm (n));
297
298           /*
299             TODO: Speed this up!
300             
301             Perhaps only check translators mentioned in the auto-accidentals?
302             -rz
303
304             TODO: profile this.
305             
306             I'd be surprised if the impact of this would be
307             measurable.  Anyway, it seems localsig doesn't change
308             every time-step, but a set_property() is done every
309             time. We could save on that, probably.
310
311             --hwn.
312             
313             
314           */
315           Translator_group * trans_ = origin_l_arr_[i];
316           while (trans_)
317             {
318               SCM localsig = trans_->get_property ("localKeySignature");
319               if (tie_changes)
320                 {
321                   /*
322                     Remember an alteration that is different both from
323                     that of the tied note and of the key signature.
324                   */
325                   localsig = ly_assoc_front_x
326                     (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
327                 }
328               else
329                 {
330                   /*
331                     not really really correct if there are more than one
332                     noteheads with the same notename.
333                   */
334                   localsig = ly_assoc_front_x
335                     (localsig, on_s, gh_cons (gh_int2scm (a), barnum)); 
336                 }
337               trans_->set_property ("localKeySignature",  localsig);
338               trans_ = trans_->daddy_trans_l_;
339             }
340         }
341     }
342
343   
344   if (key_item_p_)
345     {
346       /*
347         We add the accidentals to the support of the arpeggio, so it is put left of the
348         accidentals. 
349         
350       */
351       for (int i = 0;  i < arpeggios_.size ();  i++)
352         Side_position_interface::add_support (arpeggios_[i], key_item_p_);
353
354       arpeggios_.clear ();
355     }
356 }
357
358 void
359 Accidental_engraver::finalize ()
360 {
361
362 }
363
364 void
365 Accidental_engraver::stop_translation_timestep ()
366 {
367   if (key_item_p_)
368     {
369       for (int i = 0; i < head_l_arr_.size (); i++)
370         Side_position_interface::add_support (key_item_p_, head_l_arr_[i]);
371
372       typeset_grob (key_item_p_);
373       key_item_p_ = 0;
374     }
375
376
377   mel_l_arr_.clear ();
378   arpeggios_.clear ();
379   tie_l_arr_.clear ();
380   head_l_arr_.clear ();
381   forced_l_arr_.clear ();
382   origin_l_arr_.clear ();
383 }
384
385 void
386 Accidental_engraver::acknowledge_grob (Grob_info info)
387 {
388   Note_req * note_l =  dynamic_cast <Note_req *> (info.music_cause ());
389
390   if (note_l && Rhythmic_head::has_interface (info.grob_l_))
391     {
392       mel_l_arr_.push (note_l);
393       head_l_arr_.push (info.grob_l_);
394       origin_l_arr_.push (info.origin_trans_l_->daddy_trans_l_);
395     }
396   else if (Tie::has_interface (info.grob_l_))
397     {
398       tie_l_arr_.push (info.grob_l_);
399     }
400   else if (Arpeggio::has_interface (info.grob_l_))
401     {
402       arpeggios_.push (info.grob_l_); 
403     }
404 }
405
406 void
407 Accidental_engraver::process_music ()
408 {
409   SCM sig = get_property ("keySignature");
410
411   /* Detect key sig changes.
412      Update all parents and children
413   */
414   if (last_keysig_ != sig)
415     {
416       Translator_group * trans_ = daddy_trans_l_;
417       while (trans_)
418         {
419           trans_ -> set_property ("localKeySignature",  ly_deep_copy (sig));
420           trans_ = trans_->daddy_trans_l_;
421         }
422       daddy_trans_l_->set_children_property ("localKeySignature", sig);
423
424       last_keysig_ = sig;
425     }
426 }
427
428
429
430
431
432 ENTER_DESCRIPTION (Accidental_engraver,
433 "Make accidentals.  Catches note heads, ties and notices key-change
434 events.  Due to interaction with ties (which don't come together
435 with note heads), this needs to be in a context higher than Tie_engraver.",
436                    
437                "Accidentals",
438                "rhythmic-head-interface tie-interface arpeggio-interface",
439                "localKeySignature extraNatural autoAccidentals autoCautionaries",
440                    "localKeySignature");