]> git.donarmstrong.com Git - lilypond.git/blob - lily/accidental-engraver.cc
release: 1.5.47
[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
229                   SCM c0 = get_property ("centralCPosition");
230                   if (gh_number_p (c0))
231                     Staff_symbol_referencer::set_position (key_item_p_, gh_scm2int (c0));
232                          
233                   announce_grob(key_item_p_, SCM_EOL);
234                 }
235
236               
237               Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
238                                          cautionary,
239                                          num==2 && extra_natural_b,
240                                          tie_break_reminder);
241               Side_position_interface::add_support (key_item_p_,support_l);
242               
243               support_l->set_grob_property ("accidentals-grob", key_item_p_->self_scm ());
244             }
245           
246
247           /*
248             We should not record the accidental if it is the first
249             note and it is tied from the previous measure.
250
251             Checking whether it is tied also works mostly, but will it
252             always do the correct thing?
253            */
254           
255           Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
256           int n = pitch->notename_i_;
257           int o = pitch->octave_i_;
258           int a = pitch->alteration_i_;
259           SCM on_s = gh_cons (gh_int2scm (o), gh_int2scm (n));
260
261           /*
262             TODO: Speed this up!
263             Perhaps only check translators mentioned in the auto-accidentals?
264             -rz
265           */
266           Translator_group * trans_ = origin_l_arr_[i];
267           while (trans_) {
268             SCM localsig = trans_->get_property ("localKeySignature");
269             if (tie_changes)
270               {
271                 /*
272                   Remember an alteration that is different both from
273                   that of the tied note and of the key signature.
274                 */
275                 localsig = ly_assoc_front_x
276                   (localsig, on_s, gh_cons(SCM_BOOL_T,barnum));
277               }
278             else
279               {
280                 /*
281                   not really really correct if there are more than one
282                   noteheads with the same notename.
283                 */
284                 localsig = ly_assoc_front_x
285                   (localsig, on_s, gh_cons(gh_int2scm (a),barnum)); 
286               }
287             trans_->set_property ("localKeySignature",  localsig);
288             trans_ = trans_->daddy_trans_l_;
289           }
290         }
291     }
292
293   
294   if (key_item_p_)
295     {
296       /*
297         We add the accidentals to the support of the arpeggio, so it is put left of the
298         accidentals. 
299         
300        */
301       for (int i=0;  i < arpeggios_.size ();  i++)
302         Side_position_interface::add_support (arpeggios_[i], key_item_p_);
303
304       arpeggios_.clear ();
305     }
306 }
307
308 void
309 Accidental_engraver::finalize ()
310 {
311
312 }
313
314 void
315 Accidental_engraver::stop_translation_timestep ()
316 {
317   if (key_item_p_)
318     {
319       for (int i=0; i < head_l_arr_.size (); i++)
320         Side_position_interface::add_support (key_item_p_,head_l_arr_[i]);
321
322       typeset_grob (key_item_p_);
323       key_item_p_ =0;
324     }
325
326
327   mel_l_arr_.clear ();
328   arpeggios_.clear ();
329   tie_l_arr_.clear ();
330   head_l_arr_.clear ();
331   forced_l_arr_.clear ();
332   origin_l_arr_.clear ();
333 }
334
335 void
336 Accidental_engraver::acknowledge_grob (Grob_info info)
337 {
338   Note_req * note_l =  dynamic_cast <Note_req *> (info.music_cause ());
339
340   if (note_l && Rhythmic_head::has_interface (info.grob_l_))
341     {
342       mel_l_arr_.push (note_l);
343       head_l_arr_.push (info.grob_l_);
344       origin_l_arr_.push (info.origin_trans_l_->daddy_trans_l_);
345     }
346   else if (Tie::has_interface (info.grob_l_))
347     {
348       tie_l_arr_.push (info.grob_l_);
349     }
350   else if (Arpeggio::has_interface (info.grob_l_))
351     {
352       arpeggios_.push (info.grob_l_); 
353     }
354   
355 }
356
357 void
358 Accidental_engraver::process_music ()
359 {
360 #if 0
361   SCM smp = get_property ("measurePosition");
362   Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
363 #endif
364   
365   SCM sig = get_property ("keySignature");
366
367   /* Detect key sig changes.
368      Update all parents and children
369      */
370   if (last_keysig_ != sig) {
371     Translator_group * trans_ = daddy_trans_l_;
372     while (trans_) {
373       trans_ -> set_property ("localKeySignature",  ly_deep_copy(sig));
374       trans_ = trans_->daddy_trans_l_;
375     }
376     daddy_trans_l_->set_children_property("localKeySignature",sig);
377
378     last_keysig_ = sig;
379   }
380 }
381
382
383
384
385
386 ENTER_DESCRIPTION(Accidental_engraver,
387 /* The FIXME below is deprecated and should be removed. -rz */
388 /* descr */       "Make accidentals.  Catches note heads, ties and notices key-change
389 events.  Due to interaction with ties (which don't come together
390 with note heads), this needs to be in a context higher than Tie_engraver. FIXME",
391 /* creats*/       "Accidentals",
392 /* acks  */       "rhythmic-head-interface tie-interface arpeggio-interface",
393 /* reads */       "localKeySignature extraNatural autoAccidentals autoCautionaries",
394 /* write */       "localKeySignature");