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"
24 The algorithm for accidentals should be documented, and made
29 struct Accidental_entry {
33 Translator_group *origin_;
38 Accidental_entry::Accidental_entry()
47 struct Accidental_engraver : Engraver {
49 TRANSLATOR_DECLARATIONS (Accidental_engraver);
50 virtual void process_music ();
51 virtual void acknowledge_grob (Grob_info);
52 virtual void stop_translation_timestep ();
53 virtual void initialize ();
54 virtual void process_acknowledged_grobs ();
55 virtual void finalize ();
58 Protected_scm last_keysig_;
61 Urgh. Since the accidentals depend on lots of variables, we have to
62 store all information before we can really create the accidentals.
64 Link_array<Grob> left_objects_;
65 Link_array<Grob> right_objects_;
67 Grob * accidental_placement_;
72 Array<Accidental_entry> accidentals_;
73 Link_array<Grob> ties_;
78 set_property_on_children (Translator_group * trans, const char * sym, SCM val)
80 trans->set_property (sym, val);
81 for (SCM p = trans->trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
82 Translator_group *trg = dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
83 set_property_on_children(trg,sym,ly_deep_copy(val));
87 Accidental_engraver::Accidental_engraver ()
89 accidental_placement_ = 0;
90 last_keysig_ = SCM_EOL;
94 Accidental_engraver::initialize ()
96 last_keysig_ = get_property ("keySignature");
98 Translator_group * trans_ = daddy_trans_;
101 trans_ -> set_property ("localKeySignature", ly_deep_copy (last_keysig_));
102 trans_ = trans_->daddy_trans_;
104 set_property_on_children (daddy_trans_,"localKeySignature", last_keysig_);
109 calculates the number of accidentals on basis of the current local key sig
111 Returns number of accidentals (0, 1 or 2).
112 Negative (-1 or -2) if accidental has changed.
116 number_accidentals_from_sig (SCM sig, Music *, Pitch *pitch, SCM curbarnum, SCM lazyness,
117 bool ignore_octave_b)
119 int n = pitch->get_notename ();
120 int o = pitch->get_octave();
121 int a = pitch->get_alteration ();
122 int curbarnum_i = gh_scm2int (curbarnum);
127 prev = ly_assoc_cdr (scm_int2num (n), sig);
129 prev = scm_assoc (gh_cons (scm_int2num (o), scm_int2num (n)), sig);
131 /* should really be true unless prev == SCM_BOOL_F */
132 if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
134 accbarnum_i = gh_scm2int (ly_cddr (prev));
135 prev = gh_cons (ly_car (prev), ly_cadr (prev));
138 /* If an accidental was not found or the accidental was too old */
139 if (prev == SCM_BOOL_F ||
140 (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
141 prev = scm_assoc (scm_int2num (n), sig);
144 SCM prev_acc = (prev == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev);
146 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
149 if (a == p && gh_number_p (prev_acc))
151 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
156 return a == p ? num : -num;
160 number_accidentals (Music * note, Pitch *pitch, Translator_group * origin,
161 SCM accidentals, SCM curbarnum)
166 if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
167 warning (_f ("Accidental typesetting list must begin with context-name: %s",
168 ly_scm2string (ly_car (accidentals)).to_str0 ()));
170 for (; gh_pair_p (accidentals) && origin; accidentals = gh_cdr (accidentals))
172 // If pair then it is a new accidentals typesetting rule to be checked
173 SCM rule = gh_car (accidentals);
174 if (gh_pair_p (rule))
176 SCM type = gh_car (rule);
177 SCM lazyness = gh_cdr (rule);
178 SCM localsig = origin->get_property ("localKeySignature");
181 gh_eq_p (ly_symbol2scm ("same-octave"), type);
183 gh_eq_p (ly_symbol2scm ("any-octave"), type);
185 if (same_octave_b || any_octave_b)
187 int n = number_accidentals_from_sig
188 (localsig, note, pitch, curbarnum, lazyness, any_octave_b);
189 diff = diff || (n < 0);
190 number = max (number, abs (n));
193 warning (_f ("unknown accidental typesetting: %s. Ignored",
194 ly_symbol2string (type).to_str0 ()));
199 if symbol then it is a context name. Scan parent contexts to find it.
201 else if (gh_symbol_p (rule))
203 Translator_group * dad = origin;
204 while (dad && !dad->is_alias (rule))
205 dad = dad->daddy_trans_;
210 else warning (_f ("Accidental rule must be pair or context-name; Found %s",
211 ly_scm2string (rule).to_str0 ()));
214 return diff ? -number : number;
218 Accidental_engraver::process_acknowledged_grobs ()
220 if (accidentals_.size () && !accidentals_.top().done_)
222 //SCM localsig = get_property ("localKeySignature");
223 SCM accidentals = get_property ("autoAccidentals");
224 SCM cautionaries = get_property ("autoCautionaries");
225 SCM barnum = get_property ("currentBarNumber");
226 SCM smp = get_property("measurePosition");
227 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
228 if(mp.main_part_<Rational(0) && gh_number_p(barnum)) barnum = scm_int2num(gh_scm2int(barnum)-1);
229 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
230 for (int i = 0; i < accidentals_.size (); i++)
232 if (accidentals_[i].done_ )
234 accidentals_[i].done_ = true;
235 Grob * support = accidentals_[i].head_;
236 Music * note = accidentals_[i].melodic_;
237 Translator_group * origin = accidentals_[i].origin_;
239 Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
243 int num = number_accidentals (note, pitch, origin, accidentals, barnum);
244 int num_caut = number_accidentals (note, pitch, origin, cautionaries, barnum);
245 bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
247 if (abs (num_caut) > abs (num))
253 if(num==0 && to_boolean (note->get_mus_property ("force-accidental")))
256 bool different = num < 0;
259 /* see if there's a tie that "changes" the accidental */
260 /* works because if there's a tie, the note to the left
261 is of the same pitch as the actual note */
263 Grob *tie_break_reminder = 0;
264 bool tie_changes = false;
265 for (int j = 0; j < ties_.size (); j++)
266 if (support == Tie::head (ties_[j], RIGHT))
268 tie_changes = different;
270 /* Enable accidentals for broken tie
272 We only want an accidental on a broken tie,
273 if the tie changes the accidental.
275 Maybe check property noTieBreakForceAccidental? */
277 tie_break_reminder = ties_[j];
283 Grob * a = make_item ("Accidental");
284 a->set_parent (support, Y_AXIS);
286 if (!accidental_placement_)
288 accidental_placement_ = make_item ("AccidentalPlacement");
289 announce_grob (accidental_placement_, a->self_scm());
292 Accidental_placement::add_accidental (accidental_placement_, a);
293 announce_grob (a, SCM_EOL);
296 SCM accs = gh_cons (scm_int2num (pitch->get_alteration ()), SCM_EOL);
297 if (num == 2 && extra_natural_b)
298 accs = gh_cons (scm_int2num (0), accs);
302 add cautionary option in accidental.
307 a->set_grob_property ("cautionary", SCM_BOOL_T);
310 if (tie_break_reminder)
313 a->set_grob_property ("tie", tie_break_reminder->self_scm());
317 support->set_grob_property ("accidental-grob", a->self_scm ());
319 a->set_grob_property ("accidentals", accs);
320 accidentals_[i].accidental_ = a;
322 We add the accidentals to the support of the arpeggio, so it is put left of the
326 for (int i = 0; i < left_objects_.size (); i++)
327 Side_position_interface::add_support (left_objects_[i], a);
328 for (int i = 0; i < right_objects_.size (); i++)
329 Side_position_interface::add_support (a, right_objects_[i]);
334 We should not record the accidental if it is the first
335 note and it is tied from the previous measure.
337 Checking whether it is tied also works mostly, but will it
338 always do the correct thing?
342 int n = pitch->get_notename ();
343 int o = pitch->get_octave ();
344 int a = pitch->get_alteration ();
345 SCM on_s = gh_cons (scm_int2num (o), scm_int2num (n));
350 Perhaps only check translators mentioned in the auto-accidentals?
355 I'd be surprised if the impact of this would be
356 measurable. Anyway, it seems localsig doesn't change
357 every time-step, but a set_property() is done every
358 time. We could save on that, probably.
367 SCM localsig = origin->get_property ("localKeySignature");
371 Remember an alteration that is different both from
372 that of the tied note and of the key signature.
374 localsig = ly_assoc_front_x
375 (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
380 not really really correct if there are more than one
381 noteheads with the same notename.
383 localsig = ly_assoc_front_x
384 (localsig, on_s, gh_cons (scm_int2num (a), barnum));
386 origin->set_property ("localKeySignature", localsig);
387 origin = origin->daddy_trans_;
394 Accidental_engraver::finalize ()
396 last_keysig_ = SCM_EOL;
400 Accidental_engraver::stop_translation_timestep ()
402 for (int i = 0; i < accidentals_.size(); i++)
404 Grob *a = accidentals_[i].accidental_;
411 if (accidental_placement_)
412 typeset_grob(accidental_placement_);
413 accidental_placement_ = 00;
415 accidentals_.clear();
416 left_objects_.clear ();
417 right_objects_.clear ();
422 Accidental_engraver::acknowledge_grob (Grob_info info)
424 Music * note = info.music_cause ();
427 && note->is_mus_type("note-event")
428 && Rhythmic_head::has_interface (info.grob_))
430 Accidental_entry entry ;
431 entry.head_ = info.grob_;
432 entry.origin_ = info.origin_trans_->daddy_trans_;
433 entry.melodic_ = note;
435 accidentals_.push (entry);
437 else if (Tie::has_interface (info.grob_))
439 ties_.push (info.grob_);
441 else if (Arpeggio::has_interface (info.grob_))
443 left_objects_.push (info.grob_);
445 else if (info.grob_->internal_has_interface (ly_symbol2scm("finger-interface")))
447 left_objects_.push (info.grob_);
452 Accidental_engraver::process_music ()
454 SCM sig = get_property ("keySignature");
456 /* Detect key sig changes.
457 Update all parents and children
459 if (last_keysig_ != sig)
461 Translator_group * trans_ = daddy_trans_;
464 trans_ -> set_property ("localKeySignature", ly_deep_copy (sig));
465 trans_ = trans_->daddy_trans_;
467 set_property_on_children(daddy_trans_,"localKeySignature", sig);
477 ENTER_DESCRIPTION (Accidental_engraver,
478 "Make accidentals. Catches note heads, ties and notices key-change "
479 " events. Due to interaction with ties (which don't come together "
480 " with note heads), this needs to be in a context higher than Tie_engraver.",
483 "finger-interface rhythmic-head-interface tie-interface arpeggio-interface",
484 "localKeySignature extraNatural autoAccidentals autoCautionaries",
485 "localKeySignature");