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