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>
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 process_acknowledged_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.
71 Urgh. Since the accidentals depend on lots of variables, we have to
72 store all information before we can really create the accidentals.
74 Link_array<Grob> arpeggios_;
76 Grob * accidental_placement_;
82 Array<Accidental_entry> accidentals_;
84 Link_array<Grob> ties_;
90 static void set_property_on_children (Translator_group * trans, const char * sym, SCM val)
92 trans->set_property (sym, val);
93 for (SCM p = trans -> trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
94 Translator_group *trg = dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
95 set_property_on_children(trg,sym,ly_deep_copy(val));
99 Accidental_engraver::Accidental_engraver ()
101 accidental_placement_ = 0;
102 last_keysig_ = SCM_EOL;
106 Accidental_engraver::initialize ()
108 last_keysig_ = get_property ("keySignature");
110 Translator_group * trans_ = daddy_trans_;
113 trans_ -> set_property ("localKeySignature", ly_deep_copy (last_keysig_));
114 trans_ = trans_->daddy_trans_;
116 set_property_on_children (daddy_trans_,"localKeySignature", last_keysig_);
121 calculates the number of accidentals on basis of the current local key sig
123 Returns number of accidentals (0, 1 or 2).
124 Negative (-1 or -2) if accidental has changed.
128 number_accidentals (SCM sig, Music * note, Pitch *pitch, SCM curbarnum, SCM lazyness,
129 bool ignore_octave_b)
131 int n = pitch->notename_;
132 int o = pitch->octave_;
133 int a = pitch->alteration_;
134 int curbarnum_i = gh_scm2int (curbarnum);
139 prev = ly_assoc_cdr (scm_int2num (n), sig);
141 prev = gh_assoc (gh_cons (scm_int2num (o), scm_int2num (n)), sig);
143 /* should really be true unless prev == SCM_BOOL_F */
144 if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
146 accbarnum_i = gh_scm2int (ly_cddr (prev));
147 prev = gh_cons (ly_car (prev), ly_cadr (prev));
150 /* If an accidental was not found or the accidental was too old */
151 if (prev == SCM_BOOL_F ||
152 (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
153 prev = gh_assoc (scm_int2num (n), sig);
156 SCM prev_acc = (prev == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev);
158 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
161 if (a == p && gh_number_p (prev_acc))
163 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
168 return a == p ? num : -num;
172 number_accidentals (Music * note, Pitch *pitch, Translator_group * origin,
173 SCM accidentals, SCM curbarnum)
178 if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
179 warning (_f ("Accidental typesetting list must begin with context-name: %s",
180 ly_scm2string (ly_car (accidentals)).to_str0 ()));
182 while (gh_pair_p (accidentals) && origin)
184 // If pair then it is a new accidentals typesetting rule to be checked
185 if (gh_pair_p (ly_car (accidentals)))
187 SCM type = gh_caar (accidentals);
188 SCM lazyness = gh_cdar (accidentals);
189 SCM localsig = origin->get_property ("localKeySignature");
192 gh_eq_p (ly_symbol2scm ("same-octave"), type);
194 gh_eq_p (ly_symbol2scm ("any-octave"), type);
196 if (same_octave_b || any_octave_b)
198 int n = number_accidentals
199 (localsig, note, pitch, curbarnum, lazyness, any_octave_b);
200 diff = diff || (n < 0);
201 number = max (number, abs (n));
204 warning (_f ("unknown accidental typesetting: %s. Ignored",
205 ly_symbol2string (type).to_str0 ()));
210 if symbol then it is a context name. Scan parent contexts to find it.
212 else if (gh_symbol_p (ly_car (accidentals)))
214 String context = ly_symbol2string (ly_car (accidentals));
216 while (origin && !origin->is_alias_b (context))
217 origin = origin->daddy_trans_;
220 warning (_f ("Symbol is not a parent context: %s. Ignored",
221 context.to_str0 ()));
223 else warning (_f ("Accidental typesetting must be pair or context-name: %s",
224 ly_scm2string (ly_car (accidentals)).to_str0 ()));
226 accidentals = ly_cdr (accidentals);
228 return diff ? -number : number;
232 Accidental_engraver::process_acknowledged_grobs ()
234 if (accidentals_.size () && !accidentals_.top().done_)
236 //SCM localsig = get_property ("localKeySignature");
237 SCM accidentals = get_property ("autoAccidentals");
238 SCM cautionaries = get_property ("autoCautionaries");
239 SCM barnum = get_property ("currentBarNumber");
240 SCM smp = get_property("measurePosition");
241 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
242 if(mp.main_part_<Rational(0) && gh_number_p(barnum)) barnum = scm_int2num(gh_scm2int(barnum)-1);
243 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
244 for (int i = 0; i < accidentals_.size (); i++)
246 if (accidentals_[i].done_ )
248 accidentals_[i].done_ = true;
249 Grob * support = accidentals_[i].head_;
250 Music * note = accidentals_[i].melodic_;
251 Translator_group * origin = accidentals_[i].origin_;
253 Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
254 int num = number_accidentals (note, pitch, origin, accidentals, barnum);
255 int num_caut = number_accidentals (note, pitch, origin, cautionaries, barnum);
256 bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
258 if (abs (num_caut) > abs (num))
264 if(num==0 && to_boolean (note->get_mus_property ("force-accidental")))
267 bool different = num < 0;
270 /* see if there's a tie that "changes" the accidental */
271 /* works because if there's a tie, the note to the left
272 is of the same pitch as the actual note */
274 Grob *tie_break_reminder = 0;
275 bool tie_changes = false;
276 for (int j = 0; j < ties_.size (); j++)
277 if (support == Tie::head (ties_[j], RIGHT))
279 tie_changes = different;
281 /* Enable accidentals for broken tie
283 We only want an accidental on a broken tie,
284 if the tie changes the accidental.
286 Maybe check property noTieBreakForceAccidental? */
288 tie_break_reminder = ties_[j];
294 Grob * a = new Item (get_property ("Accidental"));
295 a->set_parent (support, Y_AXIS);
297 if (!accidental_placement_)
299 accidental_placement_ = new Item (get_property ("AccidentalPlacement"));
300 announce_grob (accidental_placement_, a->self_scm());
303 Accidental_placement::add_accidental (accidental_placement_, a);
304 announce_grob (a, SCM_EOL);
307 SCM accs = gh_cons (scm_int2num (pitch->alteration_), SCM_EOL);
308 if (num == 2 && extra_natural_b)
309 accs = gh_cons (scm_int2num (0), accs);
313 add cautionary option in accidental.
318 a->set_grob_property ("cautionary", SCM_BOOL_T);
321 if (tie_break_reminder)
324 a->set_grob_property ("tie", tie_break_reminder->self_scm());
328 support->set_grob_property ("accidental-grob", a->self_scm ());
330 a->set_grob_property ("accidentals", accs);
331 accidentals_[i].accidental_ = a;
333 We add the accidentals to the support of the arpeggio, so it is put left of the
337 for (int i = 0; i < arpeggios_.size (); i++)
338 Side_position_interface::add_support (arpeggios_[i], a);
343 We should not record the accidental if it is the first
344 note and it is tied from the previous measure.
346 Checking whether it is tied also works mostly, but will it
347 always do the correct thing?
351 int n = pitch->notename_;
352 int o = pitch->octave_;
353 int a = pitch->alteration_;
354 SCM on_s = gh_cons (scm_int2num (o), scm_int2num (n));
359 Perhaps only check translators mentioned in the auto-accidentals?
364 I'd be surprised if the impact of this would be
365 measurable. Anyway, it seems localsig doesn't change
366 every time-step, but a set_property() is done every
367 time. We could save on that, probably.
376 SCM localsig = origin->get_property ("localKeySignature");
380 Remember an alteration that is different both from
381 that of the tied note and of the key signature.
383 localsig = ly_assoc_front_x
384 (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
389 not really really correct if there are more than one
390 noteheads with the same notename.
392 localsig = ly_assoc_front_x
393 (localsig, on_s, gh_cons (scm_int2num (a), barnum));
395 origin->set_property ("localKeySignature", localsig);
396 origin = origin->daddy_trans_;
403 Accidental_engraver::finalize ()
409 Accidental_engraver::stop_translation_timestep ()
411 for (int i = 0; i < accidentals_.size(); i++)
413 Grob *a = accidentals_[i].accidental_;
420 if (accidental_placement_)
421 typeset_grob(accidental_placement_);
422 accidental_placement_ = 00;
424 accidentals_.clear();
430 Accidental_engraver::acknowledge_grob (Grob_info info)
432 Music * note = info.music_cause ();
435 && note->is_mus_type("note-event")
436 && Rhythmic_head::has_interface (info.grob_))
438 Accidental_entry entry ;
439 entry.head_ = info.grob_;
440 entry.origin_ = info.origin_trans_->daddy_trans_;
441 entry.melodic_ = note;
443 accidentals_.push (entry);
445 else if (Tie::has_interface (info.grob_))
447 ties_.push (info.grob_);
449 else if (Arpeggio::has_interface (info.grob_))
451 arpeggios_.push (info.grob_);
457 Accidental_engraver::process_music ()
459 SCM sig = get_property ("keySignature");
461 /* Detect key sig changes.
462 Update all parents and children
464 if (last_keysig_ != sig)
466 Translator_group * trans_ = daddy_trans_;
469 trans_ -> set_property ("localKeySignature", ly_deep_copy (sig));
470 trans_ = trans_->daddy_trans_;
472 set_property_on_children(daddy_trans_,"localKeySignature", sig);
482 ENTER_DESCRIPTION (Accidental_engraver,
483 "Make accidentals. Catches note heads, ties and notices key-change "
484 " events. Due to interaction with ties (which don't come together "
485 " with note heads), this needs to be in a context higher than Tie_engraver.",
488 "rhythmic-head-interface tie-interface arpeggio-interface",
489 "localKeySignature extraNatural autoAccidentals autoCautionaries",
490 "localKeySignature");