2 accidental-engraver.cc -- implement accidental_engraver
4 (c) 1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 Modified 2001-2002 by Rune Zedeler <rz@daimi.au.dk>
8 #include "musical-request.hh"
9 #include "command-request.hh"
10 #include "local-key-item.hh"
13 #include "rhythmic-head.hh"
14 #include "engraver-group-engraver.hh"
16 #include "staff-symbol-referencer.hh"
17 #include "side-position-interface.hh"
18 #include "engraver.hh"
19 #include "arpeggio.hh"
22 #include "translator-group.hh"
27 FIXME: should not compute vertical positioning of accidentals, but
28 get them from the noteheads
30 The algorithm for accidentals should be documented, and made
36 struct Accidental_engraver : Engraver {
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 ();
51 This is not a property, and it is not protected. This poses a
52 very small risk of the value being GC'd from under us.
59 Urgh. Since the accidentals depend on lots of variables, we have to
60 store all information before we can really create the accidentals.
62 Link_array<Grob> arpeggios_;
64 Link_array<Note_req> mel_l_arr_;
65 Link_array<Grob> head_l_arr_;
66 Link_array<Item> forced_l_arr_;
67 Link_array<Grob> tie_l_arr_;
68 Link_array<Translator_group> origin_l_arr_;
73 Accidental_engraver::Accidental_engraver ()
76 last_keysig_ = SCM_EOL;
80 Accidental_engraver::initialize ()
82 last_keysig_ = get_property ("keySignature");
84 Translator_group * trans_ = daddy_trans_l_;
87 trans_ -> set_property ("localKeySignature", ly_deep_copy (last_keysig_));
88 trans_ = trans_->daddy_trans_l_;
90 daddy_trans_l_->set_children_property ("localKeySignature", last_keysig_);
95 calculates the number of accidentals on basis of the current local key sig
97 Returns number of accidentals (0, 1 or 2).
98 Negative (-1 or -2) if accidental has changed.
102 number_accidentals (SCM sig, Note_req * note_l, SCM curbarnum, SCM lazyness,
103 bool ignore_octave_b)
105 Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
106 int n = pitch->notename_i_;
107 int o = pitch->octave_i_;
108 int a = pitch->alteration_i_;
109 int curbarnum_i = gh_scm2int (curbarnum);
114 prev = ly_assoc_cdr (gh_int2scm (n), sig);
116 prev = gh_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
118 /* should really be true unless prev == SCM_BOOL_F */
119 if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
121 accbarnum_i = gh_scm2int (ly_cddr (prev));
122 prev = gh_cons (ly_car (prev), ly_cadr (prev));
125 /* If an accidental was not found or the accidental was too old */
126 if (prev == SCM_BOOL_F ||
127 (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
128 prev = gh_assoc (gh_int2scm (n), sig);
131 SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
133 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
137 && !to_boolean (note_l->get_mus_property ("force-accidental"))
138 && gh_number_p (prev_acc))
140 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
145 return a == p ? num : -num;
149 number_accidentals (Note_req * note_l, Translator_group * origin_l,
150 SCM accidentals, SCM curbarnum)
155 if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
156 warning (_f ("Accidental typesetting list must begin with context-name: %s",
157 ly_scm2string (ly_car (accidentals)).ch_C ()));
159 while (gh_pair_p (accidentals) && origin_l)
161 // If pair then it is a new accidentals typesetting rule to be checked
162 if (gh_pair_p (ly_car (accidentals)))
164 SCM type = gh_caar (accidentals);
165 SCM lazyness = gh_cdar (accidentals);
166 SCM localsig = origin_l->get_property ("localKeySignature");
169 gh_eq_p (ly_symbol2scm ("same-octave"), type);
171 gh_eq_p (ly_symbol2scm ("any-octave"), type);
173 if (same_octave_b || any_octave_b)
175 int n = number_accidentals
176 (localsig, note_l, curbarnum, lazyness, any_octave_b);
177 diff = diff || (n < 0);
178 number = max (number, abs (n));
181 warning (_f ("unknown accidental typesetting: %s. Ignored",
182 ly_symbol2string (type).ch_C ()));
187 if symbol then it is a context name. Scan parent contexts to find it.
189 else if (gh_symbol_p (ly_car (accidentals)))
191 String context = ly_symbol2string (ly_car (accidentals));
193 while (origin_l && !origin_l->is_alias_b (context))
194 origin_l = origin_l->daddy_trans_l_;
197 warning (_f ("Symbol is not a parent context: %s. Ignored",
200 else warning (_f ("Accidental typesetting must be pair or context-name: %s",
201 ly_scm2string (ly_car (accidentals)).ch_C ()));
203 accidentals = ly_cdr (accidentals);
205 return diff ? -number : number;
209 Accidental_engraver::create_grobs ()
211 if (!key_item_p_ && mel_l_arr_.size ())
213 //SCM localsig = get_property ("localKeySignature");
214 SCM accidentals = get_property ("autoAccidentals");
215 SCM cautionaries = get_property ("autoCautionaries");
216 SCM barnum = get_property ("currentBarNumber");
218 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
219 for (int i = 0; i < mel_l_arr_.size (); i++)
221 Grob * support_l = head_l_arr_[i];
222 Note_req * note_l = mel_l_arr_[i];
223 Translator_group * origin_l = origin_l_arr_[i];
225 int num = number_accidentals (note_l, origin_l, accidentals, barnum);
226 int num_caut = number_accidentals (note_l, origin_l, cautionaries, barnum);
227 bool cautionary = to_boolean (note_l->get_mus_property ("cautionary"));
229 if (abs (num_caut)>abs (num))
235 bool different = num < 0;
238 /* see if there's a tie that "changes" the accidental */
239 /* works because if there's a tie, the note to the left
240 is of the same pitch as the actual note */
242 Grob *tie_break_reminder = 0;
243 bool tie_changes = false;
244 for (int j = 0; j < tie_l_arr_.size (); j++)
245 if (support_l == Tie::head (tie_l_arr_[j], RIGHT))
247 tie_changes = different;
249 /* Enable accidentals for broken tie
251 We only want an accidental on a broken tie,
252 if the tie changes the accidental.
254 Maybe check property noTieBreakForceAccidental? */
256 tie_break_reminder = tie_l_arr_[j];
264 key_item_p_ = new Item (get_property ("Accidentals"));
266 SCM c0 = get_property ("centralCPosition");
267 if (gh_number_p (c0))
268 Staff_symbol_referencer::set_position (key_item_p_, gh_scm2int (c0));
270 announce_grob (key_item_p_, SCM_EOL);
274 Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
276 num == 2 && extra_natural_b,
278 Side_position_interface::add_support (key_item_p_, support_l);
280 support_l->set_grob_property ("accidental-grob", key_item_p_->self_scm ());
285 We should not record the accidental if it is the first
286 note and it is tied from the previous measure.
288 Checking whether it is tied also works mostly, but will it
289 always do the correct thing?
292 Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
293 int n = pitch->notename_i_;
294 int o = pitch->octave_i_;
295 int a = pitch->alteration_i_;
296 SCM on_s = gh_cons (gh_int2scm (o), gh_int2scm (n));
301 Perhaps only check translators mentioned in the auto-accidentals?
306 I'd be surprised if the impact of this would be
307 measurable. Anyway, it seems localsig doesn't change
308 every time-step, but a set_property() is done every
309 time. We could save on that, probably.
315 Translator_group * trans_ = origin_l_arr_[i];
318 SCM localsig = trans_->get_property ("localKeySignature");
322 Remember an alteration that is different both from
323 that of the tied note and of the key signature.
325 localsig = ly_assoc_front_x
326 (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
331 not really really correct if there are more than one
332 noteheads with the same notename.
334 localsig = ly_assoc_front_x
335 (localsig, on_s, gh_cons (gh_int2scm (a), barnum));
337 trans_->set_property ("localKeySignature", localsig);
338 trans_ = trans_->daddy_trans_l_;
347 We add the accidentals to the support of the arpeggio, so it is put left of the
351 for (int i = 0; i < arpeggios_.size (); i++)
352 Side_position_interface::add_support (arpeggios_[i], key_item_p_);
359 Accidental_engraver::finalize ()
365 Accidental_engraver::stop_translation_timestep ()
369 for (int i = 0; i < head_l_arr_.size (); i++)
370 Side_position_interface::add_support (key_item_p_, head_l_arr_[i]);
372 typeset_grob (key_item_p_);
380 head_l_arr_.clear ();
381 forced_l_arr_.clear ();
382 origin_l_arr_.clear ();
386 Accidental_engraver::acknowledge_grob (Grob_info info)
388 Note_req * note_l = dynamic_cast <Note_req *> (info.music_cause ());
390 if (note_l && Rhythmic_head::has_interface (info.grob_l_))
392 mel_l_arr_.push (note_l);
393 head_l_arr_.push (info.grob_l_);
394 origin_l_arr_.push (info.origin_trans_l_->daddy_trans_l_);
396 else if (Tie::has_interface (info.grob_l_))
398 tie_l_arr_.push (info.grob_l_);
400 else if (Arpeggio::has_interface (info.grob_l_))
402 arpeggios_.push (info.grob_l_);
408 Accidental_engraver::process_music ()
410 SCM sig = get_property ("keySignature");
412 /* Detect key sig changes.
413 Update all parents and children
415 if (last_keysig_ != sig)
417 Translator_group * trans_ = daddy_trans_l_;
420 trans_ -> set_property ("localKeySignature", ly_deep_copy (sig));
421 trans_ = trans_->daddy_trans_l_;
423 daddy_trans_l_->set_children_property ("localKeySignature", sig);
433 ENTER_DESCRIPTION (Accidental_engraver,
434 "Make accidentals. Catches note heads, ties and notices key-change
435 events. Due to interaction with ties (which don't come together
436 with note heads), this needs to be in a context higher than Tie_engraver.",
439 "rhythmic-head-interface tie-interface arpeggio-interface",
440 "localKeySignature extraNatural autoAccidentals autoCautionaries",
441 "localKeySignature");