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"
12 #include "rhythmic-head.hh"
13 #include "engraver-group-engraver.hh"
14 #include "accidental-placement.hh"
15 #include "side-position-interface.hh"
16 #include "engraver.hh"
17 #include "arpeggio.hh"
20 #include "translator-group.hh"
25 FIXME: should not compute vertical positioning of accidentals, but
26 get them from the noteheads
28 The algorithm for accidentals should be documented, and made
33 struct Accidental_entry {
37 Translator_group *origin_;
42 Accidental_entry::Accidental_entry()
51 struct Accidental_engraver : Engraver {
53 TRANSLATOR_DECLARATIONS (Accidental_engraver);
54 virtual void process_music ();
55 virtual void acknowledge_grob (Grob_info);
56 virtual void stop_translation_timestep ();
57 virtual void initialize ();
58 virtual void create_grobs ();
59 virtual void finalize ();
65 This is not a property, and it is not protected. This poses a
66 very small risk of the value being GC'd from under us.
73 Urgh. Since the accidentals depend on lots of variables, we have to
74 store all information before we can really create the accidentals.
76 Link_array<Grob> arpeggios_;
78 Grob * accidental_placement_;
84 Array<Accidental_entry> accidental_arr_;
86 Link_array<Grob> tie_arr_;
92 Accidental_engraver::Accidental_engraver ()
94 accidental_placement_ = 0;
95 last_keysig_ = SCM_EOL;
99 Accidental_engraver::initialize ()
101 last_keysig_ = get_property ("keySignature");
103 Translator_group * trans_ = daddy_trans_l_;
106 trans_ -> set_property ("localKeySignature", ly_deep_copy (last_keysig_));
107 trans_ = trans_->daddy_trans_l_;
109 daddy_trans_l_->set_children_property ("localKeySignature", last_keysig_);
114 calculates the number of accidentals on basis of the current local key sig
116 Returns number of accidentals (0, 1 or 2).
117 Negative (-1 or -2) if accidental has changed.
121 number_accidentals (SCM sig, Note_req * note_l, Pitch *pitch, SCM curbarnum, SCM lazyness,
122 bool ignore_octave_b)
124 int n = pitch->notename_i_;
125 int o = pitch->octave_i_;
126 int a = pitch->alteration_i_;
127 int curbarnum_i = gh_scm2int (curbarnum);
132 prev = ly_assoc_cdr (gh_int2scm (n), sig);
134 prev = gh_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
136 /* should really be true unless prev == SCM_BOOL_F */
137 if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
139 accbarnum_i = gh_scm2int (ly_cddr (prev));
140 prev = gh_cons (ly_car (prev), ly_cadr (prev));
143 /* If an accidental was not found or the accidental was too old */
144 if (prev == SCM_BOOL_F ||
145 (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
146 prev = gh_assoc (gh_int2scm (n), sig);
149 SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
151 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
155 && !to_boolean (note_l->get_mus_property ("force-accidental"))
156 && gh_number_p (prev_acc))
158 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
163 return a == p ? num : -num;
167 number_accidentals (Note_req * note_l, Pitch *pitch, Translator_group * origin_l,
168 SCM accidentals, SCM curbarnum)
173 if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
174 warning (_f ("Accidental typesetting list must begin with context-name: %s",
175 ly_scm2string (ly_car (accidentals)).ch_C ()));
177 while (gh_pair_p (accidentals) && origin_l)
179 // If pair then it is a new accidentals typesetting rule to be checked
180 if (gh_pair_p (ly_car (accidentals)))
182 SCM type = gh_caar (accidentals);
183 SCM lazyness = gh_cdar (accidentals);
184 SCM localsig = origin_l->get_property ("localKeySignature");
187 gh_eq_p (ly_symbol2scm ("same-octave"), type);
189 gh_eq_p (ly_symbol2scm ("any-octave"), type);
191 if (same_octave_b || any_octave_b)
193 int n = number_accidentals
194 (localsig, note_l, pitch, curbarnum, lazyness, any_octave_b);
195 diff = diff || (n < 0);
196 number = max (number, abs (n));
199 warning (_f ("unknown accidental typesetting: %s. Ignored",
200 ly_symbol2string (type).ch_C ()));
205 if symbol then it is a context name. Scan parent contexts to find it.
207 else if (gh_symbol_p (ly_car (accidentals)))
209 String context = ly_symbol2string (ly_car (accidentals));
211 while (origin_l && !origin_l->is_alias_b (context))
212 origin_l = origin_l->daddy_trans_l_;
215 warning (_f ("Symbol is not a parent context: %s. Ignored",
218 else warning (_f ("Accidental typesetting must be pair or context-name: %s",
219 ly_scm2string (ly_car (accidentals)).ch_C ()));
221 accidentals = ly_cdr (accidentals);
223 return diff ? -number : number;
227 Accidental_engraver::create_grobs ()
229 if (accidental_arr_.size () && !accidental_arr_.top().done_)
231 //SCM localsig = get_property ("localKeySignature");
232 SCM accidentals = get_property ("autoAccidentals");
233 SCM cautionaries = get_property ("autoCautionaries");
234 SCM barnum = get_property ("currentBarNumber");
236 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
237 for (int i = 0; i < accidental_arr_.size (); i++)
239 if (accidental_arr_[i].done_ )
241 accidental_arr_[i].done_ = true;
242 Grob * support_l = accidental_arr_[i].head_;
243 Note_req * note_l = accidental_arr_[i].melodic_;
244 Translator_group * origin_l = accidental_arr_[i].origin_;
246 Pitch * pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
247 int num = number_accidentals (note_l, pitch, origin_l, accidentals, barnum);
248 int num_caut = number_accidentals (note_l, pitch, origin_l, cautionaries, barnum);
249 bool cautionary = to_boolean (note_l->get_mus_property ("cautionary"));
251 if (abs (num_caut) > abs (num))
257 bool different = num < 0;
260 /* see if there's a tie that "changes" the accidental */
261 /* works because if there's a tie, the note to the left
262 is of the same pitch as the actual note */
264 Grob *tie_break_reminder = 0;
265 bool tie_changes = false;
266 for (int j = 0; j < tie_arr_.size (); j++)
267 if (support_l == Tie::head (tie_arr_[j], RIGHT))
269 tie_changes = different;
271 /* Enable accidentals for broken tie
273 We only want an accidental on a broken tie,
274 if the tie changes the accidental.
276 Maybe check property noTieBreakForceAccidental? */
278 tie_break_reminder = tie_arr_[j];
284 Grob * a = new Item (get_property ("Accidental"));
285 a->set_parent (support_l, Y_AXIS);
287 if (!accidental_placement_)
289 accidental_placement_ = new Item (get_property ("AccidentalPlacement"));
290 announce_grob (accidental_placement_, a->self_scm());
293 Accidental_placement::add_accidental (accidental_placement_, a);
294 announce_grob (a, SCM_EOL);
297 SCM accs = gh_cons (gh_int2scm (pitch->alteration_i_), SCM_EOL);
298 if (num == 2 && extra_natural_b)
299 accs = gh_cons (gh_int2scm (0), accs);
303 add cautionary option in accidental.
308 a->set_grob_property ("cautionary", SCM_BOOL_T);
311 if (tie_break_reminder)
314 a->set_grob_property ("tie", tie_break_reminder->self_scm());
318 support_l->set_grob_property ("accidental-grob", a->self_scm ());
320 a->set_grob_property ("accidentals", accs);
321 accidental_arr_[i].accidental_ = a;
323 We add the accidentals to the support of the arpeggio, so it is put left of the
327 for (int i = 0; i < arpeggios_.size (); i++)
328 Side_position_interface::add_support (arpeggios_[i], a);
333 We should not record the accidental if it is the first
334 note and it is tied from the previous measure.
336 Checking whether it is tied also works mostly, but will it
337 always do the correct thing?
341 int n = pitch->notename_i_;
342 int o = pitch->octave_i_;
343 int a = pitch->alteration_i_;
344 SCM on_s = gh_cons (gh_int2scm (o), gh_int2scm (n));
349 Perhaps only check translators mentioned in the auto-accidentals?
354 I'd be surprised if the impact of this would be
355 measurable. Anyway, it seems localsig doesn't change
356 every time-step, but a set_property() is done every
357 time. We could save on that, probably.
366 SCM localsig = origin_l->get_property ("localKeySignature");
370 Remember an alteration that is different both from
371 that of the tied note and of the key signature.
373 localsig = ly_assoc_front_x
374 (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
379 not really really correct if there are more than one
380 noteheads with the same notename.
382 localsig = ly_assoc_front_x
383 (localsig, on_s, gh_cons (gh_int2scm (a), barnum));
385 origin_l->set_property ("localKeySignature", localsig);
386 origin_l = origin_l->daddy_trans_l_;
393 Accidental_engraver::finalize ()
399 Accidental_engraver::stop_translation_timestep ()
401 for (int i = 0; i < accidental_arr_.size(); i++)
403 Grob *a = accidental_arr_[i].accidental_;
410 if (accidental_placement_)
411 typeset_grob(accidental_placement_);
412 accidental_placement_ = 00;
414 accidental_arr_.clear();
420 Accidental_engraver::acknowledge_grob (Grob_info info)
422 Note_req * note_l = dynamic_cast <Note_req *> (info.music_cause ());
424 if (note_l && Rhythmic_head::has_interface (info.grob_l_))
426 Accidental_entry entry ;
427 entry.head_ = info.grob_l_;
428 entry.origin_ = info.origin_trans_l_->daddy_trans_l_;
429 entry.melodic_ = note_l;
431 accidental_arr_.push (entry);
433 else if (Tie::has_interface (info.grob_l_))
435 tie_arr_.push (info.grob_l_);
437 else if (Arpeggio::has_interface (info.grob_l_))
439 arpeggios_.push (info.grob_l_);
445 Accidental_engraver::process_music ()
447 SCM sig = get_property ("keySignature");
449 /* Detect key sig changes.
450 Update all parents and children
452 if (last_keysig_ != sig)
454 Translator_group * trans_ = daddy_trans_l_;
457 trans_ -> set_property ("localKeySignature", ly_deep_copy (sig));
458 trans_ = trans_->daddy_trans_l_;
460 daddy_trans_l_->set_children_property ("localKeySignature", sig);
470 ENTER_DESCRIPTION (Accidental_engraver,
471 "Make accidentals. Catches note heads, ties and notices key-change
472 events. Due to interaction with ties (which don't come together
473 with note heads), this needs to be in a context higher than Tie_engraver.",
476 "rhythmic-head-interface tie-interface arpeggio-interface",
477 "localKeySignature extraNatural autoAccidentals autoCautionaries",
478 "localKeySignature");