]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
65b83a1b611ca39563e546f1102286229a61d83e
[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
94   bool different = !gh_equal_p (prev_acc , gh_int2scm (a));
95   int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
96
97   int num;
98   if (a==p && !to_boolean (note_l->get_mus_property ("force-accidental"))) num=0;
99   else if ( (abs(a)<abs(p) || p*a<0) && a!=0 ) num=2;
100   else num=1;
101   
102   return a==p ? num : -num;
103 }
104
105 void
106 Accidental_engraver::create_grobs ()
107 {
108   if (!key_item_p_ && mel_l_arr_.size ()) 
109     {
110       SCM localsig = get_property ("localKeySignature");
111       SCM lazysig = get_property ("lazyKeySignature");
112
113       for (int i=0; i  < mel_l_arr_.size (); i++) 
114         {
115           Grob * support_l = support_l_arr_[i];
116           Note_req * note_l = mel_l_arr_[i];
117
118           int local_num = number_accidentals(localsig,note_l);
119           bool local_diff = local_num<0; local_num = abs(local_num);
120           int lazy_num = number_accidentals(lazysig,note_l);
121           bool lazy_diff = lazy_num<0; lazy_num = abs(lazy_num);
122
123           int num = local_num;;
124           bool different= local_diff;
125           bool cautionary = to_boolean (note_l->get_mus_property ("cautionary"));
126           if (to_boolean (get_property ("noResetKey"))) {
127             num = lazy_num;
128             different = lazy_diff;
129           }
130           else if (gh_equal_p (get_property ("autoReminders"),ly_symbol2scm("cautionary"))
131                    || gh_equal_p (get_property ("autoReminders"),ly_symbol2scm("accidental"))) {
132             num = max(local_num,lazy_num);
133             if (gh_equal_p (get_property ("autoReminders"),ly_symbol2scm("cautionary"))
134                 && lazy_num>local_num)
135               cautionary = true;
136           }
137
138           /* see if there's a tie that "changes" the accidental */
139           /* works because if there's a tie, the note to the left
140              is of the same pitch as the actual note */
141
142
143           Grob *tie_break_reminder = 0;
144           bool tie_changes = false;
145           for (int i=0; i < tie_l_arr_.size (); i++)
146             if (support_l == Tie::head (tie_l_arr_[i], RIGHT))
147               {
148                 tie_changes = different;
149                 /* Enable accidentals for broken tie
150
151                    We only want an accidental on a broken tie,
152                    if the tie changes the accidental.
153                    
154                    Maybe check property noTieBreakForceAccidental? */
155                 if (different)
156                   tie_break_reminder = tie_l_arr_[i];
157                 break;
158               }
159
160           if (num)
161             {
162               if (!key_item_p_) 
163                 {
164                   key_item_p_ = new Item (get_property ("Accidentals"));
165                   Local_key_item::set_interface (key_item_p_);
166
167                   
168                   Staff_symbol_referencer::set_interface (key_item_p_);
169                   SCM c0 = get_property ("centralCPosition");
170                   if (gh_number_p (c0))
171                     Staff_symbol_referencer::set_position (key_item_p_, gh_scm2int (c0));
172                          
173                   announce_grob (key_item_p_, 0);
174                 }
175
176               
177               Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
178                                          cautionary,
179                                          num==2,
180                                          tie_break_reminder);
181               Side_position_interface::add_support (key_item_p_,support_l);
182             }
183           
184
185           /*
186             We should not record the accidental if it is the first
187             note and it is tied from the previous measure.
188
189             Checking whether it is tied also works mostly, but will it
190             always do the correct thing?
191             (???? -Rune )
192            */
193           
194           Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
195           int n = pitch->notename_i_;
196           int o = pitch->octave_i () ;
197           int a = pitch->alteration_i_;
198           SCM on = gh_cons (gh_int2scm (o), gh_int2scm (n));
199           bool forget = to_boolean (get_property ("forgetAccidentals"));
200           if (tie_changes)
201             {
202               /*
203                 Remember an alteration that is different both from
204                 that of the tied note and of the key signature.
205                */
206               localsig = scm_assoc_set_x (localsig, on, SCM_BOOL_T); 
207               lazysig = scm_assoc_set_x  (lazysig,  on, SCM_BOOL_T); 
208             }
209           else if (!forget)
210             {
211               /*
212                 not really really correct if there are more than one
213                 noteheads with the same notename.
214                */
215               localsig = scm_assoc_set_x (localsig, on, gh_int2scm (a)); 
216               lazysig = scm_assoc_set_x  (lazysig,  on, gh_int2scm (a)); 
217             }
218         }
219   
220       daddy_trans_l_->set_property ("localKeySignature",  localsig);
221       daddy_trans_l_->set_property ("lazyKeySignature",   lazysig);
222     }
223   
224
225   if (key_item_p_)
226     {
227       /*
228         Hmm. Which one has to be on the left?
229
230         On which left, code or paper?
231
232  (Arpeggios are engraved left of accidentals, of course.)
233        */
234       for (int i=0;  i < arpeggios_.size ();  i++)
235         Side_position_interface::add_support (arpeggios_[i], key_item_p_);
236
237       arpeggios_.clear ();
238     }
239 }
240
241 void
242 Accidental_engraver::finalize ()
243 {
244
245 }
246
247 void
248 Accidental_engraver::stop_translation_timestep ()
249 {
250   if (key_item_p_)
251     {
252       for (int i=0; i < support_l_arr_.size (); i++)
253         Side_position_interface::add_support (key_item_p_,support_l_arr_[i]);
254
255       typeset_grob (key_item_p_);
256       key_item_p_ =0;
257     }
258
259
260   mel_l_arr_.clear ();
261   arpeggios_.clear ();
262   tie_l_arr_.clear ();
263   support_l_arr_.clear ();
264   forced_l_arr_.clear ();       
265 }
266
267 void
268 Accidental_engraver::acknowledge_grob (Grob_info info)
269 {
270   Note_req * note_l =  dynamic_cast <Note_req *> (info.music_cause ());
271
272   if (note_l && Rhythmic_head::has_interface (info.grob_l_))
273     {
274       mel_l_arr_.push (note_l);
275       support_l_arr_.push (info.grob_l_);
276     }
277   else if (Tie::has_interface (info.grob_l_))
278     {
279       tie_l_arr_.push (info.grob_l_);
280     }
281   else if (Arpeggio::has_interface (info.grob_l_))
282     {
283       arpeggios_.push (info.grob_l_); 
284     }
285   
286 }
287
288 /*
289   ugh. repeated deep_copy generates lots of garbage.
290  */
291 void
292 Accidental_engraver::process_music ()
293 {
294   SCM smp = get_property ("measurePosition");
295   Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
296
297   SCM sig = get_property ("keySignature");
298
299   /*
300     Detect key sig changes. If we haven't found any, check if at start
301     of measure, and set localKeySignature anyhow.  */
302   if (last_keysig_ != sig) 
303     {
304       daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
305       daddy_trans_l_->set_property ("lazyKeySignature",  ly_deep_copy (sig));
306       last_keysig_ = sig;
307     }
308   else if (!mp.to_bool () )
309     {
310         daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
311     }
312 }
313
314
315
316
317
318 ENTER_DESCRIPTION(Accidental_engraver,
319 /* descr */       "Make accidentals.  Catches note heads, ties and notices key-change
320 events.  Due to interaction with ties (which don't come together
321 with note heads), this needs to be in a context higher than Tie_engraver. FIXME",
322 /* creats*/       "Accidentals",
323 /* acks  */       "rhythmic-head-interface tie-interface arpeggio-interface",
324 /* reads */       "localKeySignature forgetAccidentals noResetKey autoReminders",
325 /* write */       "");