]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
patch::: 1.5.25.rz1: accidentals 1.5.25.rz1
[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> support_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
159       for (int i=0; i  < mel_l_arr_.size (); i++) 
160         {
161           Grob * support_l = support_l_arr_[i];
162           Note_req * note_l = mel_l_arr_[i];
163
164           int num = number_accidentals(localsig,note_l,accidentals_l,barnum);
165           int num_caut = number_accidentals(localsig,note_l,cautionaries_l,barnum);
166           bool cautionary = to_boolean (note_l->get_mus_property ("cautionary"));
167           if (abs(num_caut)>abs(num)) {
168             num=num_caut;
169             cautionary=true;
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                   
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           
220
221           /*
222             We should not record the accidental if it is the first
223             note and it is tied from the previous measure.
224
225             Checking whether it is tied also works mostly, but will it
226             always do the correct thing?
227            */
228           
229           Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
230           int n = pitch->notename_i_;
231           int o = pitch->octave_i_;
232           int a = pitch->alteration_i_;
233           SCM on = gh_cons (gh_int2scm (o), gh_int2scm (n));
234           if (tie_changes)
235             {
236               /*
237                 Remember an alteration that is different both from
238                 that of the tied note and of the key signature.
239                */
240               localsig = ly_assoc_front_x
241                 (localsig, on, gh_cons(SCM_BOOL_T,barnum));
242             }
243           else
244             {
245               /*
246                 not really really correct if there are more than one
247                 noteheads with the same notename.
248                */
249               localsig = ly_assoc_front_x
250                 (localsig, on, gh_cons(gh_int2scm (a),barnum)); 
251             }
252         }
253   
254       daddy_trans_l_->set_property ("localKeySignature",  localsig);
255     }
256   
257   
258   if (key_item_p_)
259     {
260       /*
261         Hmm. Which one has to be on the left?
262
263         On which left, code or paper?
264
265  (Arpeggios are engraved left of accidentals, of course.)
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 < support_l_arr_.size (); i++)
286         Side_position_interface::add_support (key_item_p_,support_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   support_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       support_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");