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