]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
e9daee13c3f043368c995b8eb4673afe17e3db2b
[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 int  number_accidentals (SCM sig, Note_req *);
43   virtual void create_grobs ();
44   virtual void finalize ();
45 public:
46
47   // todo -> property
48   SCM last_keysig_;
49
50   /*
51     Urgh. Since the accidentals depend on lots of variables, we have to
52     store all information before we can really create the accidentals.
53    */
54   Link_array<Grob> arpeggios_;
55   
56   Link_array<Note_req> mel_l_arr_;
57   Link_array<Grob> support_l_arr_;
58   Link_array<Item> forced_l_arr_;
59   Link_array<Grob> tie_l_arr_;
60
61 };
62
63 Accidental_engraver::Accidental_engraver ()
64 {
65   key_item_p_ =0;
66   last_keysig_ = SCM_EOL;
67 }
68
69 void
70 Accidental_engraver::initialize ()
71 {
72   last_keysig_ = get_property ("keySignature");
73   daddy_trans_l_->set_property ("localKeySignature",  last_keysig_);  
74   daddy_trans_l_->set_property ("lazyKeySignature",   last_keysig_);  
75 }
76
77 /** calculates the number of accidentals on basis of the current local time sig
78   * (passed as argument).
79   * Returns number of accidentals (0, 1 or 2).
80   *   Negative (-1 or -2) if accidental has changed.
81   **/
82 int
83 Accidental_engraver::number_accidentals (SCM sig, Note_req * note_l)
84 {
85   int n = unsmob_pitch (note_l->get_mus_property ("pitch"))->notename_i_;
86   int o = unsmob_pitch (note_l->get_mus_property ("pitch"))->octave_i () ;
87   int a = unsmob_pitch (note_l->get_mus_property ("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           int n = unsmob_pitch (note_l->get_mus_property ("pitch"))->notename_i_;
193           int o = unsmob_pitch (note_l->get_mus_property ("pitch"))->octave_i () ;
194           int a = unsmob_pitch (note_l->get_mus_property ("pitch"))->alteration_i_;
195           SCM ON = gh_cons (gh_int2scm (o), gh_int2scm (n));
196           bool forget = to_boolean (get_property ("forgetAccidentals"));
197           if (tie_changes)
198             {
199               /*
200                 Remember an alteration that is different both from
201                 that of the tied note and of the key signature.
202                 (????? -Rune )
203                */
204               localsig = scm_assoc_set_x (localsig, ON, SCM_BOOL_T); 
205               lazysig = scm_assoc_set_x  (lazysig,  ON, SCM_BOOL_T); 
206             }
207           else if (!forget)
208             {
209               /*
210                 not really really correct if there are more than one
211                 noteheads with the same notename.
212                */
213               localsig = scm_assoc_set_x (localsig, ON, gh_int2scm (a)); 
214               lazysig = scm_assoc_set_x  (lazysig,  ON, gh_int2scm (a)); 
215             }
216         }
217   
218       daddy_trans_l_->set_property ("localKeySignature",  localsig);
219       daddy_trans_l_->set_property ("lazyKeySignature",   lazysig);
220     }
221   
222
223   if (key_item_p_)
224     {
225       /*
226         Hmm. Which one has to be on the left?
227
228         On which left, code or paper?
229
230  (Arpeggios are engraved left of accidentals, of course.)
231        */
232       for (int i=0;  i < arpeggios_.size ();  i++)
233         Side_position_interface::add_support (arpeggios_[i], key_item_p_);
234
235       arpeggios_.clear ();
236     }
237 }
238
239 void
240 Accidental_engraver::finalize ()
241 {
242
243 }
244
245 void
246 Accidental_engraver::stop_translation_timestep ()
247 {
248   if (key_item_p_)
249     {
250       for (int i=0; i < support_l_arr_.size (); i++)
251         Side_position_interface::add_support (key_item_p_,support_l_arr_[i]);
252
253       typeset_grob (key_item_p_);
254       key_item_p_ =0;
255     }
256
257
258   mel_l_arr_.clear ();
259   arpeggios_.clear ();
260   tie_l_arr_.clear ();
261   support_l_arr_.clear ();
262   forced_l_arr_.clear ();       
263 }
264
265 void
266 Accidental_engraver::acknowledge_grob (Grob_info info)
267 {
268   Note_req * note_l =  dynamic_cast <Note_req *> (info.req_l_);
269
270   if (note_l && Rhythmic_head::has_interface (info.grob_l_))
271     {
272       mel_l_arr_.push (note_l);
273       support_l_arr_.push (info.grob_l_);
274     }
275   else if (Tie::has_interface (info.grob_l_))
276     {
277       tie_l_arr_.push (info.grob_l_);
278     }
279   else if (Arpeggio::has_interface (info.grob_l_))
280     {
281       arpeggios_.push (info.grob_l_); 
282     }
283   
284 }
285
286 /*
287   ugh. repeated deep_copy generates lots of garbage.
288  */
289 void
290 Accidental_engraver::process_music ()
291 {
292   SCM smp = get_property ("measurePosition");
293   Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
294
295   SCM sig = get_property ("keySignature");
296
297   /*
298     Detect key sig changes. If we haven't found any, check if at start
299     of measure, and set localKeySignature anyhow.  */
300   if (last_keysig_ != sig) 
301     {
302       daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
303       daddy_trans_l_->set_property ("lazyKeySignature",  ly_deep_copy (sig));
304       last_keysig_ = sig;
305     }
306   else if (!mp.to_bool () )
307     {
308         daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
309     }
310 }
311
312
313
314
315
316 ENTER_DESCRIPTION(Accidental_engraver,
317 /* descr */       "Make accidentals.  Catches note heads, ties and notices key-change
318 events.  Due to interaction with ties (which don't come together
319 with note heads), this needs to be in a context higher than Tie_engraver. FIXME",
320 /* creats*/       "Accidentals",
321 /* acks  */       "rhythmic-head-interface tie-interface arpeggio-interface",
322 /* reads */       "localKeySignature forgetAccidentals noResetKey",
323 /* write */       "");