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>
13 #include "rhythmic-head.hh"
14 #include "engraver-group-engraver.hh"
15 #include "accidental-placement.hh"
16 #include "side-position-interface.hh"
17 #include "engraver.hh"
18 #include "arpeggio.hh"
21 #include "protected-scm.hh"
25 The algorithm for accidentals should be documented, and made
30 struct Accidental_entry {
39 Accidental_entry::Accidental_entry()
48 struct Accidental_engraver : Engraver {
50 TRANSLATOR_DECLARATIONS (Accidental_engraver);
51 virtual void process_music ();
52 virtual void acknowledge_grob (Grob_info);
53 virtual void stop_translation_timestep ();
54 virtual void initialize ();
55 virtual void process_acknowledged_grobs ();
56 virtual void finalize ();
59 Protected_scm last_keysig_;
62 Urgh. Since the accidentals depend on lots of variables, we have to
63 store all information before we can really create the accidentals.
65 Link_array<Grob> left_objects_;
66 Link_array<Grob> right_objects_;
68 Grob * accidental_placement_;
73 Array<Accidental_entry> accidentals_;
74 Link_array<Grob> ties_;
79 set_property_on_children (Context * trans, const char * sym, SCM val)
81 trans->set_property (sym, val);
82 for (SCM p = trans->context_list_; gh_pair_p (p); p = ly_cdr(p))
84 Context *trg = unsmob_context (ly_car (p));
85 set_property_on_children(trg, sym, ly_deep_copy(val));
89 Accidental_engraver::Accidental_engraver ()
91 accidental_placement_ = 0;
92 last_keysig_ = SCM_EOL;
96 Accidental_engraver::initialize ()
98 last_keysig_ = get_property ("keySignature");
100 Context * trans_ = daddy_context_;
103 trans_ -> set_property ("localKeySignature",
104 ly_deep_copy (last_keysig_));
105 trans_ = trans_->daddy_context_;
107 set_property_on_children (daddy_context_,"localKeySignature", last_keysig_);
112 calculates the number of accidentals on basis of the current local key sig
114 Returns number of accidentals (0, 1 or 2).
115 Negative (-1 or -2) if accidental has changed.
119 number_accidentals_from_sig (SCM sig, Music *, Pitch *pitch, SCM curbarnum, SCM lazyness,
120 bool ignore_octave_b)
122 int n = pitch->get_notename ();
123 int o = pitch->get_octave();
124 int a = pitch->get_alteration ();
125 int curbarnum_i = gh_scm2int (curbarnum);
130 prev = ly_assoc_cdr (scm_int2num (n), sig);
132 prev = scm_assoc (gh_cons (scm_int2num (o), scm_int2num (n)), sig);
134 /* should really be true unless prev == SCM_BOOL_F */
135 if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
137 accbarnum_i = gh_scm2int (ly_cddr (prev));
138 prev = gh_cons (ly_car (prev), ly_cadr (prev));
141 /* If an accidental was not found or the accidental was too old */
142 if (prev == SCM_BOOL_F ||
143 (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
144 prev = scm_assoc (scm_int2num (n), sig);
147 SCM prev_acc = (prev == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev);
149 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
152 if (a == p && gh_number_p (prev_acc))
154 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
159 return a == p ? num : -num;
163 number_accidentals (Music * note, Pitch *pitch, Context * origin,
164 SCM accidentals, SCM curbarnum)
169 if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
170 warning (_f ("Accidental typesetting list must begin with context-name: %s",
171 ly_scm2string (ly_car (accidentals)).to_str0 ()));
173 for (; gh_pair_p (accidentals) && origin; accidentals = gh_cdr (accidentals))
175 // If pair then it is a new accidentals typesetting rule to be checked
176 SCM rule = gh_car (accidentals);
177 if (gh_pair_p (rule))
179 SCM type = gh_car (rule);
180 SCM lazyness = gh_cdr (rule);
181 SCM localsig = origin->get_property ("localKeySignature");
184 gh_eq_p (ly_symbol2scm ("same-octave"), type);
186 gh_eq_p (ly_symbol2scm ("any-octave"), type);
188 if (same_octave_b || any_octave_b)
190 int n = number_accidentals_from_sig
191 (localsig, note, pitch, curbarnum, lazyness, any_octave_b);
192 diff = diff || (n < 0);
193 number = max (number, abs (n));
196 warning (_f ("unknown accidental typesetting: %s. Ignored",
197 ly_symbol2string (type).to_str0 ()));
202 if symbol then it is a context name. Scan parent contexts to find it.
204 else if (gh_symbol_p (rule))
206 Context * dad = origin;
207 while (dad && !dad->is_alias (rule))
208 dad = dad->daddy_context_;
213 else warning (_f ("Accidental rule must be pair or context-name; Found %s",
214 ly_scm2string (rule).to_str0 ()));
217 return diff ? -number : number;
221 Accidental_engraver::process_acknowledged_grobs ()
223 if (accidentals_.size () && !accidentals_.top().done_)
225 //SCM localsig = get_property ("localKeySignature");
226 SCM accidentals = get_property ("autoAccidentals");
227 SCM cautionaries = get_property ("autoCautionaries");
228 SCM barnum = get_property ("currentBarNumber");
229 SCM smp = get_property("measurePosition");
230 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
231 if(mp.main_part_<Rational(0) && gh_number_p(barnum)) barnum = scm_int2num(gh_scm2int(barnum)-1);
232 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
233 for (int i = 0; i < accidentals_.size (); i++)
235 if (accidentals_[i].done_ )
237 accidentals_[i].done_ = true;
238 Grob * support = accidentals_[i].head_;
239 Music * note = accidentals_[i].melodic_;
240 Context * origin = accidentals_[i].origin_;
242 Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
246 int num = number_accidentals (note, pitch, origin, accidentals, barnum);
247 int num_caut = number_accidentals (note, pitch, origin, cautionaries, barnum);
248 bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
250 if (abs (num_caut) > abs (num))
256 if(num==0 && to_boolean (note->get_mus_property ("force-accidental")))
259 bool different = num < 0;
262 /* see if there's a tie that "changes" the accidental */
263 /* works because if there's a tie, the note to the left
264 is of the same pitch as the actual note */
266 Grob *tie_break_reminder = 0;
267 bool tie_changes = false;
268 for (int j = 0; j < ties_.size (); j++)
269 if (support == Tie::head (ties_[j], RIGHT))
271 tie_changes = different;
273 /* Enable accidentals for broken tie
275 We only want an accidental on a broken tie,
276 if the tie changes the accidental.
278 Maybe check property noTieBreakForceAccidental? */
280 tie_break_reminder = ties_[j];
287 We construct the accidentals at the originating Voice
288 level, so that we get the property settings for
289 Accidental from the respective Voice.
291 Grob * a = make_item_from_properties (origin,
292 ly_symbol2scm ("Accidental"));
293 a->set_parent (support, Y_AXIS);
295 if (!accidental_placement_)
297 accidental_placement_ = make_item ("AccidentalPlacement");
298 announce_grob (accidental_placement_, a->self_scm());
301 Accidental_placement::add_accidental (accidental_placement_, a);
302 announce_grob (a, SCM_EOL);
305 SCM accs = gh_cons (scm_int2num (pitch->get_alteration ()), SCM_EOL);
306 if (num == 2 && extra_natural_b)
307 accs = gh_cons (scm_int2num (0), accs);
311 add cautionary option in accidental.
316 a->set_grob_property ("cautionary", SCM_BOOL_T);
319 if (tie_break_reminder)
322 a->set_grob_property ("tie", tie_break_reminder->self_scm());
326 support->set_grob_property ("accidental-grob", a->self_scm ());
328 a->set_grob_property ("accidentals", accs);
329 accidentals_[i].accidental_ = a;
331 We add the accidentals to the support of the arpeggio, so it is put left of the
335 for (int i = 0; i < left_objects_.size (); i++)
336 Side_position_interface::add_support (left_objects_[i], a);
337 for (int i = 0; i < right_objects_.size (); i++)
338 Side_position_interface::add_support (a, right_objects_[i]);
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->get_notename ();
352 int o = pitch->get_octave ();
353 int a = pitch->get_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_context_;
403 Accidental_engraver::finalize ()
406 Must reset, since Accidental_engraver is GCd.
408 last_keysig_ = SCM_EOL;
412 Accidental_engraver::stop_translation_timestep ()
414 for (int i = 0; i < accidentals_.size(); i++)
416 Grob *a = accidentals_[i].accidental_;
423 if (accidental_placement_)
424 typeset_grob(accidental_placement_);
425 accidental_placement_ = 00;
427 accidentals_.clear();
428 left_objects_.clear ();
429 right_objects_.clear ();
434 Accidental_engraver::acknowledge_grob (Grob_info info)
436 Music * note = info.music_cause ();
439 && note->is_mus_type ("note-event")
440 && Rhythmic_head::has_interface (info.grob_)
441 && !gh_equal_p (info.grob_->get_grob_property ("style"), ly_symbol2scm ("harmonic"))
444 Accidental_entry entry ;
445 entry.head_ = info.grob_;
446 entry.origin_ = info.origin_trans_->daddy_context_;
447 entry.melodic_ = note;
449 accidentals_.push (entry);
451 else if (Tie::has_interface (info.grob_))
453 ties_.push (info.grob_);
455 else if (Arpeggio::has_interface (info.grob_))
457 left_objects_.push (info.grob_);
459 else if (info.grob_->internal_has_interface (ly_symbol2scm("finger-interface")))
461 left_objects_.push (info.grob_);
466 Accidental_engraver::process_music ()
468 SCM sig = get_property ("keySignature");
470 /* Detect key sig changes.
471 Update all parents and children
473 if (last_keysig_ != sig)
475 Context * trans_ = daddy_context_;
478 trans_ -> set_property ("localKeySignature", ly_deep_copy (sig));
479 trans_ = trans_->daddy_context_;
481 set_property_on_children(daddy_context_,"localKeySignature", sig);
491 ENTER_DESCRIPTION (Accidental_engraver,
492 "Make accidentals. Catches note heads, ties and notices key-change "
493 "events. This engraver usually lives at Staff level, but "
494 "reads the settings for Accidental at @code{Voice} level, "
495 "so you can @code{\\override} them at @code{Voice}. "
500 "finger-interface rhythmic-head-interface tie-interface arpeggio-interface",
501 "localKeySignature extraNatural autoAccidentals autoCautionaries",
502 "localKeySignature");