]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
release: 1.5.27
[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 #include "warn.hh"
22
23 /**
24
25
26    FIXME: should not compute vertical positioning of accidentals, but
27    get them from the noteheads
28
29    The algorithm for accidentals should be documented, and made
30    tweakable.
31
32 */
33
34
35 struct Accidental_engraver : Engraver {
36   Item *key_item_p_;
37 protected:
38   TRANSLATOR_DECLARATIONS(Accidental_engraver);
39   virtual void process_music ();
40   virtual void acknowledge_grob (Grob_info);
41   virtual void stop_translation_timestep ();
42   virtual void initialize ();
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> head_l_arr_;
58   Link_array<Item> forced_l_arr_;
59   Link_array<Grob> tie_l_arr_;
60
61 };
62
63
64 Accidental_engraver::Accidental_engraver ()
65 {
66   key_item_p_ =0;
67   last_keysig_ = SCM_EOL;
68 }
69
70 void
71 Accidental_engraver::initialize ()
72 {
73   last_keysig_ = get_property ("keySignature");
74   daddy_trans_l_->set_property ("localKeySignature",  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, SCM curbarnum, SCM lazyness,
84                     bool ignore_octave_b)
85 {
86   Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
87   int n = pitch->notename_i_;
88   int o = pitch->octave_i_;
89   int a = pitch->alteration_i_;
90   int curbarnum_i = gh_scm2int(curbarnum);
91   int accbarnum_i = 0;
92   SCM prev;
93   if (ignore_octave_b)
94     prev = ly_assoc_cdr (gh_int2scm (n), sig);
95   else
96     prev = gh_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
97   /* should really be true unless prev==SCM_BOOL_F */
98   if(gh_pair_p(prev) && gh_pair_p(ly_cdr(prev))) {
99     accbarnum_i = gh_scm2int(ly_cddr(prev));
100     prev = gh_cons(ly_car(prev),ly_cadr(prev));
101   }
102   /* If an accidental was not found or the accidental was too old */
103   if (prev == SCM_BOOL_F ||
104       (gh_number_p(lazyness) && curbarnum_i>accbarnum_i+gh_scm2int(lazyness)))
105     prev = gh_assoc (gh_int2scm (n), sig);
106   SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
107
108   int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
109
110   int num;
111   if (a==p && !to_boolean (note_l->get_mus_property ("force-accidental")) && gh_number_p(prev_acc)) num=0;
112   else if ( (abs(a)<abs(p) || p*a<0) && a!=0 ) num=2;
113   else num=1;
114   
115   return a==p ? num : -num;
116 }
117
118 static int
119 number_accidentals (SCM localsig, Note_req * note_l, SCM accidentals_l,
120                     SCM curbarnum) {
121   int number=0;
122   int diff=0;
123   while(gh_pair_p(accidentals_l)) {
124     if(gh_pair_p(ly_car(accidentals_l))) {
125       SCM type = gh_caar(accidentals_l);
126       SCM lazyness = gh_cdar(accidentals_l);
127       bool measure_same_octave_b =
128         gh_eq_p(ly_symbol2scm("measure-same-octave"),type);
129       bool measure_any_octave_b =
130         gh_eq_p(ly_symbol2scm("measure-any-octave"),type);
131       if(measure_same_octave_b || measure_any_octave_b) {
132         int n = number_accidentals
133           (localsig,note_l,curbarnum,lazyness,measure_any_octave_b);
134         diff |= n<0;
135         number = max(number,abs(n));     
136       }
137       else warning(_f("unknown accidental typesetting: %s",
138                       ly_symbol2string(type).ch_C()));
139     }
140     else warning(_f("Accidental typesetting must be pair: %s",
141                       ly_scm2string(ly_car(accidentals_l)).ch_C()));
142     accidentals_l = ly_cdr(accidentals_l);
143   }
144   return diff ? -number : number;
145 }
146
147 void
148 Accidental_engraver::create_grobs ()
149 {
150   if (!key_item_p_ && mel_l_arr_.size ()) 
151     {
152       SCM localsig = get_property ("localKeySignature");
153       SCM accidentals_l =  get_property ("autoAccidentals");
154       SCM cautionaries_l =  get_property ("autoCautionaries");
155       SCM barnum = get_property ("currentBarNumber");
156
157       bool extra_natural_b = get_property ("extraNatural")==SCM_BOOL_T;
158       for (int i=0; i  < mel_l_arr_.size (); i++) 
159         {
160           Grob * support_l = head_l_arr_[i];
161           Note_req * note_l = mel_l_arr_[i];
162
163           int num = number_accidentals(localsig,note_l,accidentals_l,barnum);
164           int num_caut = number_accidentals(localsig,note_l,cautionaries_l,barnum);
165           bool cautionary = to_boolean (note_l->get_mus_property ("cautionary"));
166           if (abs(num_caut)>abs(num))
167             {
168               num=num_caut;
169               cautionary=true;
170             }
171           
172           bool different=num<0;
173           num=abs(num);
174
175           /* see if there's a tie that "changes" the accidental */
176           /* works because if there's a tie, the note to the left
177              is of the same pitch as the actual note */
178
179
180           Grob *tie_break_reminder = 0;
181           bool tie_changes = false;
182           for (int i=0; i < tie_l_arr_.size (); i++)
183             if (support_l == Tie::head (tie_l_arr_[i], RIGHT))
184               {
185                 tie_changes = different;
186                 /* Enable accidentals for broken tie
187
188                    We only want an accidental on a broken tie,
189                    if the tie changes the accidental.
190                    
191                    Maybe check property noTieBreakForceAccidental? */
192                 if (different)
193                   tie_break_reminder = tie_l_arr_[i];
194                 break;
195               }
196
197           if (num)
198             {
199               if (!key_item_p_) 
200                 {
201                   key_item_p_ = new Item (get_property ("Accidentals"));
202                   Local_key_item::set_interface (key_item_p_);
203
204                   Staff_symbol_referencer::set_interface (key_item_p_);
205                   SCM c0 = get_property ("centralCPosition");
206                   if (gh_number_p (c0))
207                     Staff_symbol_referencer::set_position (key_item_p_, gh_scm2int (c0));
208                          
209                   announce_grob (key_item_p_, 0);
210                 }
211
212               
213               Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
214                                          cautionary,
215                                          num==2 && extra_natural_b,
216                                          tie_break_reminder);
217               Side_position_interface::add_support (key_item_p_,support_l);
218               
219               support_l->set_grob_property ("accidentals", key_item_p_->self_scm ());
220             }
221           
222
223           /*
224             We should not record the accidental if it is the first
225             note and it is tied from the previous measure.
226
227             Checking whether it is tied also works mostly, but will it
228             always do the correct thing?
229            */
230           
231           Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
232           int n = pitch->notename_i_;
233           int o = pitch->octave_i_;
234           int a = pitch->alteration_i_;
235           SCM on = gh_cons (gh_int2scm (o), gh_int2scm (n));
236           if (tie_changes)
237             {
238               /*
239                 Remember an alteration that is different both from
240                 that of the tied note and of the key signature.
241                */
242               localsig = ly_assoc_front_x
243                 (localsig, on, gh_cons(SCM_BOOL_T,barnum));
244             }
245           else
246             {
247               /*
248                 not really really correct if there are more than one
249                 noteheads with the same notename.
250                */
251               localsig = ly_assoc_front_x
252                 (localsig, on, gh_cons(gh_int2scm (a),barnum)); 
253             }
254         }
255   
256       daddy_trans_l_->set_property ("localKeySignature",  localsig);
257     }
258   
259   
260   if (key_item_p_)
261     {
262       /*
263         We add the accidentals to the support of the arpeggio, so it is put left of the
264         accidentals. 
265         
266        */
267       for (int i=0;  i < arpeggios_.size ();  i++)
268         Side_position_interface::add_support (arpeggios_[i], key_item_p_);
269
270       arpeggios_.clear ();
271     }
272 }
273
274 void
275 Accidental_engraver::finalize ()
276 {
277
278 }
279
280 void
281 Accidental_engraver::stop_translation_timestep ()
282 {
283   if (key_item_p_)
284     {
285       for (int i=0; i < head_l_arr_.size (); i++)
286         Side_position_interface::add_support (key_item_p_,head_l_arr_[i]);
287
288       typeset_grob (key_item_p_);
289       key_item_p_ =0;
290     }
291
292
293   mel_l_arr_.clear ();
294   arpeggios_.clear ();
295   tie_l_arr_.clear ();
296   head_l_arr_.clear ();
297   forced_l_arr_.clear ();       
298 }
299
300 void
301 Accidental_engraver::acknowledge_grob (Grob_info info)
302 {
303   Note_req * note_l =  dynamic_cast <Note_req *> (info.music_cause ());
304
305   if (note_l && Rhythmic_head::has_interface (info.grob_l_))
306     {
307       mel_l_arr_.push (note_l);
308       head_l_arr_.push (info.grob_l_);
309     }
310   else if (Tie::has_interface (info.grob_l_))
311     {
312       tie_l_arr_.push (info.grob_l_);
313     }
314   else if (Arpeggio::has_interface (info.grob_l_))
315     {
316       arpeggios_.push (info.grob_l_); 
317     }
318   
319 }
320
321 /*
322   ugh. repeated deep_copy generates lots of garbage.
323  */
324 void
325 Accidental_engraver::process_music ()
326 {
327   SCM smp = get_property ("measurePosition");
328   Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
329
330   SCM sig = get_property ("keySignature");
331
332   /* Detect key sig changes. */
333   if (last_keysig_ != sig) 
334     {
335       daddy_trans_l_->set_property ("localKeySignature",  ly_deep_copy (sig));
336       last_keysig_ = sig;
337     }
338 }
339
340
341
342
343
344 ENTER_DESCRIPTION(Accidental_engraver,
345 /* descr */       "Make accidentals.  Catches note heads, ties and notices key-change
346 events.  Due to interaction with ties (which don't come together
347 with note heads), this needs to be in a context higher than Tie_engraver. FIXME",
348 /* creats*/       "Accidentals",
349 /* acks  */       "rhythmic-head-interface tie-interface arpeggio-interface",
350 /* reads */       "localKeySignature extraNatural autoAccidentals autoCautionaries",
351 /* write */       "localKeySignature");