2 accidental-engraver.cc -- implement accidental_engraver
4 (c) 1997--2003 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"));
244 int num = number_accidentals (note, pitch, origin, accidentals, barnum);
245 int num_caut = number_accidentals (note, pitch, origin, cautionaries, barnum);
246 bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
248 if (abs (num_caut) > abs (num))
254 if(num==0 && to_boolean (note->get_mus_property ("force-accidental")))
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 < ties_.size (); j++)
267 if (support == Tie::head (ties_[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 = ties_[j];
284 Grob * a = new Item (get_property ("Accidental"));
285 a->set_parent (support, 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 (scm_int2num (pitch->get_alteration ()), SCM_EOL);
298 if (num == 2 && extra_natural_b)
299 accs = gh_cons (scm_int2num (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->set_grob_property ("accidental-grob", a->self_scm ());
320 a->set_grob_property ("accidentals", accs);
321 accidentals_[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 < left_objects_.size (); i++)
328 Side_position_interface::add_support (left_objects_[i], a);
329 for (int i = 0; i < right_objects_.size (); i++)
330 Side_position_interface::add_support (a, right_objects_[i]);
335 We should not record the accidental if it is the first
336 note and it is tied from the previous measure.
338 Checking whether it is tied also works mostly, but will it
339 always do the correct thing?
343 int n = pitch->get_notename ();
344 int o = pitch->get_octave ();
345 int a = pitch->get_alteration ();
346 SCM on_s = gh_cons (scm_int2num (o), scm_int2num (n));
351 Perhaps only check translators mentioned in the auto-accidentals?
356 I'd be surprised if the impact of this would be
357 measurable. Anyway, it seems localsig doesn't change
358 every time-step, but a set_property() is done every
359 time. We could save on that, probably.
368 SCM localsig = origin->get_property ("localKeySignature");
372 Remember an alteration that is different both from
373 that of the tied note and of the key signature.
375 localsig = ly_assoc_front_x
376 (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
381 not really really correct if there are more than one
382 noteheads with the same notename.
384 localsig = ly_assoc_front_x
385 (localsig, on_s, gh_cons (scm_int2num (a), barnum));
387 origin->set_property ("localKeySignature", localsig);
388 origin = origin->daddy_trans_;
395 Accidental_engraver::finalize ()
397 last_keysig_ = SCM_EOL;
401 Accidental_engraver::stop_translation_timestep ()
403 for (int i = 0; i < accidentals_.size(); i++)
405 Grob *a = accidentals_[i].accidental_;
412 if (accidental_placement_)
413 typeset_grob(accidental_placement_);
414 accidental_placement_ = 00;
416 accidentals_.clear();
417 left_objects_.clear ();
418 right_objects_.clear ();
423 Accidental_engraver::acknowledge_grob (Grob_info info)
425 Music * note = info.music_cause ();
428 && note->is_mus_type("note-event")
429 && Rhythmic_head::has_interface (info.grob_))
431 Accidental_entry entry ;
432 entry.head_ = info.grob_;
433 entry.origin_ = info.origin_trans_->daddy_trans_;
434 entry.melodic_ = note;
436 accidentals_.push (entry);
438 else if (Tie::has_interface (info.grob_))
440 ties_.push (info.grob_);
442 else if (Arpeggio::has_interface (info.grob_))
444 left_objects_.push (info.grob_);
446 else if (info.grob_->internal_has_interface (ly_symbol2scm("finger-interface")))
448 left_objects_.push (info.grob_);
453 Accidental_engraver::process_music ()
455 SCM sig = get_property ("keySignature");
457 /* Detect key sig changes.
458 Update all parents and children
460 if (last_keysig_ != sig)
462 Translator_group * trans_ = daddy_trans_;
465 trans_ -> set_property ("localKeySignature", ly_deep_copy (sig));
466 trans_ = trans_->daddy_trans_;
468 set_property_on_children(daddy_trans_,"localKeySignature", sig);
478 ENTER_DESCRIPTION (Accidental_engraver,
479 "Make accidentals. Catches note heads, ties and notices key-change "
480 " events. Due to interaction with ties (which don't come together "
481 " with note heads), this needs to be in a context higher than Tie_engraver.",
484 "finger-interface rhythmic-head-interface tie-interface arpeggio-interface",
485 "localKeySignature extraNatural autoAccidentals autoCautionaries",
486 "localKeySignature");