2 accidental-engraver.cc -- implement accidental_engraver
4 (c) 1997--2004 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"
19 #include "translator-group.hh"
20 #include "protected-scm.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 ();
62 Protected_scm last_keysig_;
65 Urgh. Since the accidentals depend on lots of variables, we have to
66 store all information before we can really create the accidentals.
68 Link_array<Grob> left_objects_;
69 Link_array<Grob> right_objects_;
71 Grob * accidental_placement_;
76 Array<Accidental_entry> accidentals_;
77 Link_array<Grob> ties_;
82 set_property_on_children (Translator_group * trans, const char * sym, SCM val)
84 trans->set_property (sym, val);
85 for (SCM p = trans->trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
86 Translator_group *trg = dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
87 set_property_on_children(trg,sym,ly_deep_copy(val));
91 Accidental_engraver::Accidental_engraver ()
93 accidental_placement_ = 0;
94 last_keysig_ = SCM_EOL;
98 Accidental_engraver::initialize ()
100 last_keysig_ = get_property ("keySignature");
102 Translator_group * trans_ = daddy_trans_;
105 trans_ -> set_property ("localKeySignature", ly_deep_copy (last_keysig_));
106 trans_ = trans_->daddy_trans_;
108 set_property_on_children (daddy_trans_,"localKeySignature", last_keysig_);
113 calculates the number of accidentals on basis of the current local key sig
115 Returns number of accidentals (0, 1 or 2).
116 Negative (-1 or -2) if accidental has changed.
120 number_accidentals_from_sig (SCM sig, Music *, Pitch *pitch, SCM curbarnum, SCM lazyness,
121 bool ignore_octave_b)
123 int n = pitch->get_notename ();
124 int o = pitch->get_octave();
125 int a = pitch->get_alteration ();
126 int curbarnum_i = gh_scm2int (curbarnum);
131 prev = ly_assoc_cdr (scm_int2num (n), sig);
133 prev = scm_assoc (gh_cons (scm_int2num (o), scm_int2num (n)), sig);
135 /* should really be true unless prev == SCM_BOOL_F */
136 if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
138 accbarnum_i = gh_scm2int (ly_cddr (prev));
139 prev = gh_cons (ly_car (prev), ly_cadr (prev));
142 /* If an accidental was not found or the accidental was too old */
143 if (prev == SCM_BOOL_F ||
144 (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
145 prev = scm_assoc (scm_int2num (n), sig);
148 SCM prev_acc = (prev == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev);
150 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
153 if (a == p && gh_number_p (prev_acc))
155 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
160 return a == p ? num : -num;
164 number_accidentals (Music * note, Pitch *pitch, Translator_group * origin,
165 SCM accidentals, SCM curbarnum)
170 if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
171 warning (_f ("Accidental typesetting list must begin with context-name: %s",
172 ly_scm2string (ly_car (accidentals)).to_str0 ()));
174 for (; gh_pair_p (accidentals) && origin; accidentals = gh_cdr (accidentals))
176 // If pair then it is a new accidentals typesetting rule to be checked
177 SCM rule = gh_car (accidentals);
178 if (gh_pair_p (rule))
180 SCM type = gh_car (rule);
181 SCM lazyness = gh_cdr (rule);
182 SCM localsig = origin->get_property ("localKeySignature");
185 gh_eq_p (ly_symbol2scm ("same-octave"), type);
187 gh_eq_p (ly_symbol2scm ("any-octave"), type);
189 if (same_octave_b || any_octave_b)
191 int n = number_accidentals_from_sig
192 (localsig, note, pitch, curbarnum, lazyness, any_octave_b);
193 diff = diff || (n < 0);
194 number = max (number, abs (n));
197 warning (_f ("unknown accidental typesetting: %s. Ignored",
198 ly_symbol2string (type).to_str0 ()));
203 if symbol then it is a context name. Scan parent contexts to find it.
205 else if (gh_symbol_p (rule))
207 Translator_group * dad = origin;
208 while (dad && !dad->is_alias (rule))
209 dad = dad->daddy_trans_;
214 else warning (_f ("Accidental rule must be pair or context-name; Found %s",
215 ly_scm2string (rule).to_str0 ()));
218 return diff ? -number : number;
222 Accidental_engraver::process_acknowledged_grobs ()
224 if (accidentals_.size () && !accidentals_.top().done_)
226 //SCM localsig = get_property ("localKeySignature");
227 SCM accidentals = get_property ("autoAccidentals");
228 SCM cautionaries = get_property ("autoCautionaries");
229 SCM barnum = get_property ("currentBarNumber");
230 SCM smp = get_property("measurePosition");
231 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
232 if(mp.main_part_<Rational(0) && gh_number_p(barnum)) barnum = scm_int2num(gh_scm2int(barnum)-1);
233 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
234 for (int i = 0; i < accidentals_.size (); i++)
236 if (accidentals_[i].done_ )
238 accidentals_[i].done_ = true;
239 Grob * support = accidentals_[i].head_;
240 Music * note = accidentals_[i].melodic_;
241 Translator_group * origin = accidentals_[i].origin_;
243 Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
247 int num = number_accidentals (note, pitch, origin, accidentals, barnum);
248 int num_caut = number_accidentals (note, pitch, origin, cautionaries, barnum);
249 bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
251 if (abs (num_caut) > abs (num))
257 if(num==0 && to_boolean (note->get_mus_property ("force-accidental")))
260 bool different = num < 0;
263 /* see if there's a tie that "changes" the accidental */
264 /* works because if there's a tie, the note to the left
265 is of the same pitch as the actual note */
267 Grob *tie_break_reminder = 0;
268 bool tie_changes = false;
269 for (int j = 0; j < ties_.size (); j++)
270 if (support == Tie::head (ties_[j], RIGHT))
272 tie_changes = different;
274 /* Enable accidentals for broken tie
276 We only want an accidental on a broken tie,
277 if the tie changes the accidental.
279 Maybe check property noTieBreakForceAccidental? */
281 tie_break_reminder = ties_[j];
287 Grob * a = make_item ("Accidental");
288 a->set_parent (support, Y_AXIS);
290 if (!accidental_placement_)
292 accidental_placement_ = make_item ("AccidentalPlacement");
293 announce_grob (accidental_placement_, a->self_scm());
296 Accidental_placement::add_accidental (accidental_placement_, a);
297 announce_grob (a, SCM_EOL);
300 SCM accs = gh_cons (scm_int2num (pitch->get_alteration ()), SCM_EOL);
301 if (num == 2 && extra_natural_b)
302 accs = gh_cons (scm_int2num (0), accs);
306 add cautionary option in accidental.
311 a->set_grob_property ("cautionary", SCM_BOOL_T);
314 if (tie_break_reminder)
317 a->set_grob_property ("tie", tie_break_reminder->self_scm());
321 support->set_grob_property ("accidental-grob", a->self_scm ());
323 a->set_grob_property ("accidentals", accs);
324 accidentals_[i].accidental_ = a;
326 We add the accidentals to the support of the arpeggio, so it is put left of the
330 for (int i = 0; i < left_objects_.size (); i++)
331 Side_position_interface::add_support (left_objects_[i], a);
332 for (int i = 0; i < right_objects_.size (); i++)
333 Side_position_interface::add_support (a, right_objects_[i]);
338 We should not record the accidental if it is the first
339 note and it is tied from the previous measure.
341 Checking whether it is tied also works mostly, but will it
342 always do the correct thing?
346 int n = pitch->get_notename ();
347 int o = pitch->get_octave ();
348 int a = pitch->get_alteration ();
349 SCM on_s = gh_cons (scm_int2num (o), scm_int2num (n));
354 Perhaps only check translators mentioned in the auto-accidentals?
359 I'd be surprised if the impact of this would be
360 measurable. Anyway, it seems localsig doesn't change
361 every time-step, but a set_property() is done every
362 time. We could save on that, probably.
371 SCM localsig = origin->get_property ("localKeySignature");
375 Remember an alteration that is different both from
376 that of the tied note and of the key signature.
378 localsig = ly_assoc_front_x
379 (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
384 not really really correct if there are more than one
385 noteheads with the same notename.
387 localsig = ly_assoc_front_x
388 (localsig, on_s, gh_cons (scm_int2num (a), barnum));
390 origin->set_property ("localKeySignature", localsig);
391 origin = origin->daddy_trans_;
398 Accidental_engraver::finalize ()
400 last_keysig_ = SCM_EOL;
404 Accidental_engraver::stop_translation_timestep ()
406 for (int i = 0; i < accidentals_.size(); i++)
408 Grob *a = accidentals_[i].accidental_;
415 if (accidental_placement_)
416 typeset_grob(accidental_placement_);
417 accidental_placement_ = 00;
419 accidentals_.clear();
420 left_objects_.clear ();
421 right_objects_.clear ();
426 Accidental_engraver::acknowledge_grob (Grob_info info)
428 Music * note = info.music_cause ();
431 && note->is_mus_type("note-event")
432 && Rhythmic_head::has_interface (info.grob_))
434 Accidental_entry entry ;
435 entry.head_ = info.grob_;
436 entry.origin_ = info.origin_trans_->daddy_trans_;
437 entry.melodic_ = note;
439 accidentals_.push (entry);
441 else if (Tie::has_interface (info.grob_))
443 ties_.push (info.grob_);
445 else if (Arpeggio::has_interface (info.grob_))
447 left_objects_.push (info.grob_);
449 else if (info.grob_->internal_has_interface (ly_symbol2scm("finger-interface")))
451 left_objects_.push (info.grob_);
456 Accidental_engraver::process_music ()
458 SCM sig = get_property ("keySignature");
460 /* Detect key sig changes.
461 Update all parents and children
463 if (last_keysig_ != sig)
465 Translator_group * trans_ = daddy_trans_;
468 trans_ -> set_property ("localKeySignature", ly_deep_copy (sig));
469 trans_ = trans_->daddy_trans_;
471 set_property_on_children(daddy_trans_,"localKeySignature", sig);
481 ENTER_DESCRIPTION (Accidental_engraver,
482 "Make accidentals. Catches note heads, ties and notices key-change "
483 " events. Due to interaction with ties (which don't come together "
484 " with note heads), this needs to be in a context higher than Tie_engraver.",
487 "finger-interface rhythmic-head-interface tie-interface arpeggio-interface",
488 "localKeySignature extraNatural autoAccidentals autoCautionaries",
489 "localKeySignature");