]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
5b476a05cb8dd922f4fe61228761046c7a4832e9
[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-2002 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 #include "translator-group.hh"
23
24 /**
25
26
27    FIXME: should not compute vertical positioning of accidentals, but
28    get them from the noteheads
29
30    The algorithm for accidentals should be documented, and made
31    tweakable.
32
33 */
34
35
36 struct Accidental_engraver : Engraver {
37   Item *key_item_p_;
38 protected:
39   TRANSLATOR_DECLARATIONS(Accidental_engraver);
40   virtual void process_music ();
41   virtual void acknowledge_grob (Grob_info);
42   virtual void stop_translation_timestep ();
43   virtual void initialize ();
44   virtual void create_grobs ();
45   virtual void finalize ();
46 public:
47
48   // todo -> property   (why? -rz)
49   SCM last_keysig_;
50
51   /*
52     Urgh. Since the accidentals depend on lots of variables, we have to
53     store all information before we can really create the accidentals.
54    */
55   Link_array<Grob> arpeggios_;
56   
57   Link_array<Note_req> mel_l_arr_;
58   Link_array<Grob> head_l_arr_;
59   Link_array<Item> forced_l_arr_;
60   Link_array<Grob> tie_l_arr_;
61   Link_array<Translator_group> origin_l_arr_;
62
63 };
64
65
66 Accidental_engraver::Accidental_engraver ()
67 {
68   key_item_p_ =0;
69   last_keysig_ = SCM_EOL;
70 }
71
72 void
73 Accidental_engraver::initialize ()
74 {
75   last_keysig_ = get_property ("keySignature");
76
77   Translator_group * trans_ = daddy_trans_l_;
78   while (trans_) {
79     trans_ -> set_property ("localKeySignature",  ly_deep_copy(last_keysig_));
80     trans_ = trans_->daddy_trans_l_;
81   }
82   daddy_trans_l_->set_children_property("localKeySignature",last_keysig_);
83
84 }
85
86 /** calculates the number of accidentals on basis of the current local key sig
87   * (passed as argument)
88   * Returns number of accidentals (0, 1 or 2).
89   *   Negative (-1 or -2) if accidental has changed.
90   **/
91 // FIXME: Use references for SCM-values
92 static int
93 number_accidentals (SCM sig, Note_req * note_l, SCM curbarnum, SCM lazyness,
94                     bool ignore_octave_b)
95 {
96   Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
97   int n = pitch->notename_i_;
98   int o = pitch->octave_i_;
99   int a = pitch->alteration_i_;
100   int curbarnum_i = gh_scm2int(curbarnum);
101   int accbarnum_i = 0;
102   SCM prev;
103   if (ignore_octave_b)
104     prev = ly_assoc_cdr (gh_int2scm (n), sig);
105   else
106     prev = gh_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
107   /* should really be true unless prev==SCM_BOOL_F */
108   if(gh_pair_p(prev) && gh_pair_p(ly_cdr(prev))) {
109     accbarnum_i = gh_scm2int(ly_cddr(prev));
110     prev = gh_cons(ly_car(prev),ly_cadr(prev));
111   }
112   /* If an accidental was not found or the accidental was too old */
113   if (prev == SCM_BOOL_F ||
114       (gh_number_p(lazyness) && curbarnum_i>accbarnum_i+gh_scm2int(lazyness)))
115     prev = gh_assoc (gh_int2scm (n), sig);
116   SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
117
118   int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
119
120   int num;
121   if (a==p && !to_boolean (note_l->get_mus_property ("force-accidental")) && gh_number_p(prev_acc)) num=0;
122   else if ( (abs(a)<abs(p) || p*a<0) && a!=0 ) num=2;
123   else num=1;
124   
125   return a==p ? num : -num;
126 }
127
128 int
129 number_accidentals (Note_req * note_l, Translator_group * origin_l,
130                     SCM accidentals_sl, SCM curbarnum_s) {
131   int number=0;
132   int diff=0;
133   if(gh_pair_p(accidentals_sl) && !gh_symbol_p(ly_car(accidentals_sl)))
134     warning(_f("Accidental typesetting list must begin with context-name: %s",
135                ly_scm2string(ly_car(accidentals_sl)).ch_C()));
136   while(gh_pair_p(accidentals_sl) && origin_l) {
137     // If pair then it is a new accidentals typesetting rule to be checked
138     if(gh_pair_p(ly_car(accidentals_sl))) {
139       SCM type = gh_caar(accidentals_sl);
140       SCM lazyness = gh_cdar(accidentals_sl);
141       SCM localsig = origin_l->get_property ("localKeySignature");
142       bool same_octave_b =
143         gh_eq_p(ly_symbol2scm("same-octave"),type);
144       bool any_octave_b =
145         gh_eq_p(ly_symbol2scm("any-octave"),type);
146       if(same_octave_b || any_octave_b) {
147         int n = number_accidentals
148           (localsig,note_l,curbarnum_s,lazyness,any_octave_b);
149         diff |= n<0;
150         number = max(number,abs(n));     
151       }
152       else warning(_f("unknown accidental typesetting: %s. Ignored",
153                       ly_symbol2string(type).ch_C()));
154     }
155     // if symbol then it is a context name. Scan parent contexts to find it.
156     else if (gh_symbol_p(ly_car(accidentals_sl))) {
157       String context = ly_symbol2string(ly_car(accidentals_sl));
158       while (origin_l && !origin_l->is_alias_b(context)) {
159         origin_l = origin_l->daddy_trans_l_;
160       }
161       if (!origin_l)
162         warning(_f("Symbol is not a parent context: %s. Ignored",
163                    context.ch_C()));
164     }
165     else warning(_f("Accidental typesetting must be pair or context-name: %s",
166                       ly_scm2string(ly_car(accidentals_sl)).ch_C()));
167     accidentals_sl = ly_cdr(accidentals_sl);
168   }
169   return diff ? -number : number;
170 }
171
172 void
173 Accidental_engraver::create_grobs ()
174 {
175   if (!key_item_p_ && mel_l_arr_.size ()) 
176     {
177       //SCM localsig = get_property ("localKeySignature");
178       SCM accidentals_sl =  get_property ("autoAccidentals");
179       SCM cautionaries_sl =  get_property ("autoCautionaries");
180       SCM barnum = get_property ("currentBarNumber");
181
182       bool extra_natural_b = get_property ("extraNatural")==SCM_BOOL_T;
183       for (int i=0; i  < mel_l_arr_.size (); i++) 
184         {
185           Grob * support_l = head_l_arr_[i];
186           Note_req * note_l = mel_l_arr_[i];
187           Translator_group * origin_l = origin_l_arr_[i];
188
189           int num = number_accidentals(note_l,origin_l,accidentals_sl,barnum);
190           int num_caut = number_accidentals(note_l,origin_l,cautionaries_sl,barnum);
191           bool cautionary = to_boolean (note_l->get_mus_property ("cautionary"));
192           if (abs(num_caut)>abs(num))
193             {
194               num=num_caut;
195               cautionary=true;
196             }
197           
198           bool different=num<0;
199           num=abs(num);
200
201           /* see if there's a tie that "changes" the accidental */
202           /* works because if there's a tie, the note to the left
203              is of the same pitch as the actual note */
204
205
206           Grob *tie_break_reminder = 0;
207           bool tie_changes = false;
208           for (int j=0; j < tie_l_arr_.size (); j++)
209             if (support_l == Tie::head (tie_l_arr_[j], RIGHT))
210               {
211                 tie_changes = different;
212                 /* Enable accidentals for broken tie
213
214                    We only want an accidental on a broken tie,
215                    if the tie changes the accidental.
216                    
217                    Maybe check property noTieBreakForceAccidental? */
218                 if (different)
219                   tie_break_reminder = tie_l_arr_[j];
220                 break;
221               }
222
223           if (num)
224             {
225               if (!key_item_p_) 
226                 {
227                   key_item_p_ = new Item (get_property ("Accidentals"));
228                   Local_key_item::set_interface (key_item_p_);
229
230                   Staff_symbol_referencer::set_interface (key_item_p_);
231                   SCM c0 = get_property ("centralCPosition");
232                   if (gh_number_p (c0))
233                     Staff_symbol_referencer::set_position (key_item_p_, gh_scm2int (c0));
234                          
235                   announce_grob(key_item_p_, SCM_EOL);
236                 }
237
238               
239               Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
240                                          cautionary,
241                                          num==2 && extra_natural_b,
242                                          tie_break_reminder);
243               Side_position_interface::add_support (key_item_p_,support_l);
244               
245               support_l->set_grob_property ("accidentals-grob", key_item_p_->self_scm ());
246             }
247           
248
249           /*
250             We should not record the accidental if it is the first
251             note and it is tied from the previous measure.
252
253             Checking whether it is tied also works mostly, but will it
254             always do the correct thing?
255            */
256           
257           Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
258           int n = pitch->notename_i_;
259           int o = pitch->octave_i_;
260           int a = pitch->alteration_i_;
261           SCM on_s = gh_cons (gh_int2scm (o), gh_int2scm (n));
262
263           /*
264             TODO: Speed this up!
265             Perhaps only check translators mentioned in the auto-accidentals?
266             -rz
267           */
268           Translator_group * trans_ = origin_l_arr_[i];
269           while (trans_) {
270             SCM localsig = trans_->get_property ("localKeySignature");
271             if (tie_changes)
272               {
273                 /*
274                   Remember an alteration that is different both from
275                   that of the tied note and of the key signature.
276                 */
277                 localsig = ly_assoc_front_x
278                   (localsig, on_s, gh_cons(SCM_BOOL_T,barnum));
279               }
280             else
281               {
282                 /*
283                   not really really correct if there are more than one
284                   noteheads with the same notename.
285                 */
286                 localsig = ly_assoc_front_x
287                   (localsig, on_s, gh_cons(gh_int2scm (a),barnum)); 
288               }
289             trans_->set_property ("localKeySignature",  localsig);
290             trans_ = trans_->daddy_trans_l_;
291           }
292         }
293     }
294
295   
296   if (key_item_p_)
297     {
298       /*
299         We add the accidentals to the support of the arpeggio, so it is put left of the
300         accidentals. 
301         
302        */
303       for (int i=0;  i < arpeggios_.size ();  i++)
304         Side_position_interface::add_support (arpeggios_[i], key_item_p_);
305
306       arpeggios_.clear ();
307     }
308 }
309
310 void
311 Accidental_engraver::finalize ()
312 {
313
314 }
315
316 void
317 Accidental_engraver::stop_translation_timestep ()
318 {
319   if (key_item_p_)
320     {
321       for (int i=0; i < head_l_arr_.size (); i++)
322         Side_position_interface::add_support (key_item_p_,head_l_arr_[i]);
323
324       typeset_grob (key_item_p_);
325       key_item_p_ =0;
326     }
327
328
329   mel_l_arr_.clear ();
330   arpeggios_.clear ();
331   tie_l_arr_.clear ();
332   head_l_arr_.clear ();
333   forced_l_arr_.clear ();
334   origin_l_arr_.clear ();
335 }
336
337 void
338 Accidental_engraver::acknowledge_grob (Grob_info info)
339 {
340   Note_req * note_l =  dynamic_cast <Note_req *> (info.music_cause ());
341
342   if (note_l && Rhythmic_head::has_interface (info.grob_l_))
343     {
344       mel_l_arr_.push (note_l);
345       head_l_arr_.push (info.grob_l_);
346       origin_l_arr_.push (info.origin_trans_l_->daddy_trans_l_);
347     }
348   else if (Tie::has_interface (info.grob_l_))
349     {
350       tie_l_arr_.push (info.grob_l_);
351     }
352   else if (Arpeggio::has_interface (info.grob_l_))
353     {
354       arpeggios_.push (info.grob_l_); 
355     }
356   
357 }
358
359 void
360 Accidental_engraver::process_music ()
361 {
362 #if 0
363   SCM smp = get_property ("measurePosition");
364   Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
365 #endif
366   
367   SCM sig = get_property ("keySignature");
368
369   /* Detect key sig changes.
370      Update all parents and children
371      */
372   if (last_keysig_ != sig) {
373     Translator_group * trans_ = daddy_trans_l_;
374     while (trans_) {
375       trans_ -> set_property ("localKeySignature",  ly_deep_copy(sig));
376       trans_ = trans_->daddy_trans_l_;
377     }
378     daddy_trans_l_->set_children_property("localKeySignature",sig);
379
380     last_keysig_ = sig;
381   }
382 }
383
384
385
386
387
388 ENTER_DESCRIPTION(Accidental_engraver,
389 /* The FIXME below is deprecated and should be removed. -rz */
390 /* descr */       "Make accidentals.  Catches note heads, ties and notices key-change
391 events.  Due to interaction with ties (which don't come together
392 with note heads), this needs to be in a context higher than Tie_engraver. FIXME",
393 /* creats*/       "Accidentals",
394 /* acks  */       "rhythmic-head-interface tie-interface arpeggio-interface",
395 /* reads */       "localKeySignature extraNatural autoAccidentals autoCautionaries",
396 /* write */       "localKeySignature");