]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
cb87b5edb34a1ecc15691cf1289c170965e5e3c6
[lilypond.git] / lily / accidental-engraver.cc
1 /*
2   accidental-engraver.cc -- implement accidental_engraver
3
4   (c)  1997--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5   Modified 2001 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 "timing-translator.hh"
15 #include "engraver-group-engraver.hh"
16
17 #include "staff-symbol-referencer.hh"
18 #include "side-position-interface.hh"
19 #include "engraver.hh"
20 #include "arpeggio.hh"
21
22 /**
23
24
25    FIXME: should not compute vertical positioning of accidentals, but
26    get them from the noteheads
27
28    The algorithm for accidentals should be documented, and made
29    tweakable.
30
31 */
32
33
34 struct Accidental_engraver : Engraver {
35   Item *key_item_p_;
36 protected:
37   TRANSLATOR_DECLARATIONS(Accidental_engraver);
38   virtual void process_music ();
39   virtual void acknowledge_grob (Grob_info);
40   virtual void stop_translation_timestep ();
41   virtual void initialize ();
42   virtual void create_grobs ();
43   virtual void finalize ();
44 public:
45
46   // todo -> property
47   SCM last_keysig_;
48
49   /*
50     Urgh. Since the accidentals depend on lots of variables, we have to
51     store all information before we can really create the accidentals.
52    */
53   Link_array<Grob> arpeggios_;
54   
55   Link_array<Note_req> mel_l_arr_;
56   Link_array<Grob> support_l_arr_;
57   Link_array<Item> forced_l_arr_;
58   Link_array<Grob> tie_l_arr_;
59
60 };
61
62 Accidental_engraver::Accidental_engraver ()
63 {
64   key_item_p_ =0;
65   last_keysig_ = SCM_EOL;
66 }
67
68 void
69 Accidental_engraver::initialize ()
70 {
71   last_keysig_ = get_property ("keySignature");
72   daddy_trans_l_->set_property ("localKeySignature",  last_keysig_);  
73   daddy_trans_l_->set_property ("lazyKeySignature",   last_keysig_);  
74 }
75
76 /** calculates the number of accidentals on basis of the current local key sig
77   * (passed as argument).
78   * Returns number of accidentals (0, 1 or 2).
79   *   Negative (-1 or -2) if accidental has changed.
80   **/
81 static int
82 number_accidentals (SCM sig, Note_req * note_l)
83 {
84   Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
85   int n = pitch->notename_i_;
86   int o = pitch->octave_i () ;
87   int a = pitch->alteration_i_;
88   
89   SCM prev = scm_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
90   if (prev == SCM_BOOL_F)
91     prev = scm_assoc (gh_int2scm (n), sig);
92   SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
93   bool different = !gh_equal_p (prev_acc , gh_int2scm (a));
94   int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
95
96   int num;
97   if (a==p && !to_boolean (note_l->get_mus_property ("force-accidental"))) num=0;
98   else if ( (abs(a)<abs(p) || p*a<0) && a!=0 ) num=2;
99   else num=1;
100   
101   return a==p ? num : -num;
102 }
103
104 void
105 Accidental_engraver::create_grobs ()
106 {
107   if (!key_item_p_ && mel_l_arr_.size ()) 
108     {
109       SCM localsig = get_property ("localKeySignature");
110       SCM lazysig = get_property ("lazyKeySignature");
111
112       for (int i=0; i  < mel_l_arr_.size (); i++) 
113         {
114           Grob * support_l = support_l_arr_[i];
115           Note_req * note_l = mel_l_arr_[i];
116
117           int local_num = number_accidentals(localsig,note_l);
118           bool local_diff = local_num<0; local_num = abs(local_num);
119           int lazy_num = number_accidentals(lazysig,note_l);
120           bool lazy_diff = lazy_num<0; lazy_num = abs(lazy_num);
121
122           int num = local_num;;
123           bool different= local_diff;
124           bool cautionary = to_boolean (note_l->get_mus_property ("cautionary"));
125           if (to_boolean (get_property ("noResetKey"))) {
126             num = lazy_num;
127             different = lazy_diff;
128           }
129           else if (gh_equal_p (get_property ("autoReminders"),ly_symbol2scm("cautionary"))
130                    || gh_equal_p (get_property ("autoReminders"),ly_symbol2scm("accidental"))) {
131             num = max(local_num,lazy_num);
132             if (gh_equal_p (get_property ("autoReminders"),ly_symbol2scm("cautionary"))
133                 && lazy_num>local_num)
134               cautionary = true;
135           }
136
137           /* see if there's a tie that "changes" the accidental */
138           /* works because if there's a tie, the note to the left
139              is of the same pitch as the actual note */
140
141
142           Grob *tie_break_reminder = 0;
143           bool tie_changes = false;
144           for (int i=0; i < tie_l_arr_.size (); i++)
145             if (support_l == Tie::head (tie_l_arr_[i], RIGHT))
146               {
147                 tie_changes = different;
148                 /* Enable accidentals for broken tie
149
150                    We only want an accidental on a broken tie,
151                    if the tie changes the accidental.
152                    
153                    Maybe check property noTieBreakForceAccidental? */
154                 if (different)
155                   tie_break_reminder = tie_l_arr_[i];
156                 break;
157               }
158
159           if (num)
160             {
161               if (!key_item_p_) 
162                 {
163                   key_item_p_ = new Item (get_property ("Accidentals"));
164                   Local_key_item::set_interface (key_item_p_);
165
166                   
167                   Staff_symbol_referencer::set_interface (key_item_p_);
168                   SCM c0 = get_property ("centralCPosition");
169                   if (gh_number_p (c0))
170                     Staff_symbol_referencer::set_position (key_item_p_, gh_scm2int (c0));
171                          
172                   announce_grob (key_item_p_, 0);
173                 }
174
175               
176               Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
177                                          cautionary,
178                                          num==2,
179                                          tie_break_reminder);
180               Side_position_interface::add_support (key_item_p_,support_l);
181             }
182           
183
184           /*
185             We should not record the accidental if it is the first
186             note and it is tied from the previous measure.
187
188             Checking whether it is tied also works mostly, but will it
189             always do the correct thing?
190             (???? -Rune )
191            */
192           
193           Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
194           int n = pitch->notename_i_;
195           int o = pitch->octave_i () ;
196           int a = pitch->alteration_i_;
197           SCM on = gh_cons (gh_int2scm (o), gh_int2scm (n));
198           bool forget = to_boolean (get_property ("forgetAccidentals"));
199           if (tie_changes)
200             {
201               /*
202                 Remember an alteration that is different both from
203                 that of the tied note and of the key signature.
204                */
205               localsig = scm_assoc_set_x (localsig, on, SCM_BOOL_T); 
206               lazysig = scm_assoc_set_x  (lazysig,  on, SCM_BOOL_T); 
207             }
208           else if (!forget)
209             {
210               /*
211                 not really really correct if there are more than one
212                 noteheads with the same notename.
213                */
214               localsig = scm_assoc_set_x (localsig, on, gh_int2scm (a)); 
215               lazysig = scm_assoc_set_x  (lazysig,  on, gh_int2scm (a)); 
216             }
217         }
218   
219       daddy_trans_l_->set_property ("localKeySignature",  localsig);
220       daddy_trans_l_->set_property ("lazyKeySignature",   lazysig);
221     }
222   
223
224   if (key_item_p_)
225     {
226       /*
227         Hmm. Which one has to be on the left?
228
229         On which left, code or paper?
230
231  (Arpeggios are engraved left of accidentals, of course.)
232        */
233       for (int i=0;  i < arpeggios_.size ();  i++)
234         Side_position_interface::add_support (arpeggios_[i], key_item_p_);
235
236       arpeggios_.clear ();
237     }
238 }
239
240 void
241 Accidental_engraver::finalize ()
242 {
243
244 }
245
246 void
247 Accidental_engraver::stop_translation_timestep ()
248 {
249   if (key_item_p_)
250     {
251       for (int i=0; i < support_l_arr_.size (); i++)
252         Side_position_interface::add_support (key_item_p_,support_l_arr_[i]);
253
254       typeset_grob (key_item_p_);
255       key_item_p_ =0;
256     }
257
258
259   mel_l_arr_.clear ();
260   arpeggios_.clear ();
261   tie_l_arr_.clear ();
262   support_l_arr_.clear ();
263   forced_l_arr_.clear ();       
264 }
265
266 void
267 Accidental_engraver::acknowledge_grob (Grob_info info)
268 {
269   Note_req * note_l =  dynamic_cast <Note_req *> (info.req_l_);
270
271   if (note_l && Rhythmic_head::has_interface (info.grob_l_))
272     {
273       mel_l_arr_.push (note_l);
274       support_l_arr_.push (info.grob_l_);
275     }
276   else if (Tie::has_interface (info.grob_l_))
277     {
278       tie_l_arr_.push (info.grob_l_);
279     }
280   else if (Arpeggio::has_interface (info.grob_l_))
281     {
282       arpeggios_.push (info.grob_l_); 
283     }
284   
285 }
286
287 /*
288   ugh. repeated deep_copy generates lots of garbage.
289  */
290 void
291 Accidental_engraver::process_music ()
292 {
293   SCM smp = get_property ("measurePosition");
294   Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
295
296   SCM sig = get_property ("keySignature");
297
298   /*
299     Detect key sig changes. If we haven't found any, check if at start
300     of measure, and set localKeySignature anyhow.  */
301   if (last_keysig_ != sig) 
302     {
303       daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
304       daddy_trans_l_->set_property ("lazyKeySignature",  ly_deep_copy (sig));
305       last_keysig_ = sig;
306     }
307   else if (!mp.to_bool () )
308     {
309         daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
310     }
311 }
312
313
314
315
316
317 ENTER_DESCRIPTION(Accidental_engraver,
318 /* descr */       "Make accidentals.  Catches note heads, ties and notices key-change
319 events.  Due to interaction with ties (which don't come together
320 with note heads), this needs to be in a context higher than Tie_engraver. FIXME",
321 /* creats*/       "Accidentals",
322 /* acks  */       "rhythmic-head-interface tie-interface arpeggio-interface",
323 /* reads */       "localKeySignature forgetAccidentals noResetKey autoReminders",
324 /* write */       "");