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];
286 Grob * a = make_item ("Accidental");
287 a->set_parent (support, Y_AXIS);
289 if (!accidental_placement_)
291 accidental_placement_ = make_item ("AccidentalPlacement");
292 announce_grob (accidental_placement_, a->self_scm());
295 Accidental_placement::add_accidental (accidental_placement_, a);
296 announce_grob (a, SCM_EOL);
299 SCM accs = gh_cons (scm_int2num (pitch->get_alteration ()), SCM_EOL);
300 if (num == 2 && extra_natural_b)
301 accs = gh_cons (scm_int2num (0), accs);
305 add cautionary option in accidental.
310 a->set_grob_property ("cautionary", SCM_BOOL_T);
313 if (tie_break_reminder)
316 a->set_grob_property ("tie", tie_break_reminder->self_scm());
320 support->set_grob_property ("accidental-grob", a->self_scm ());
322 a->set_grob_property ("accidentals", accs);
323 accidentals_[i].accidental_ = a;
325 We add the accidentals to the support of the arpeggio, so it is put left of the
329 for (int i = 0; i < left_objects_.size (); i++)
330 Side_position_interface::add_support (left_objects_[i], a);
331 for (int i = 0; i < right_objects_.size (); i++)
332 Side_position_interface::add_support (a, right_objects_[i]);
337 We should not record the accidental if it is the first
338 note and it is tied from the previous measure.
340 Checking whether it is tied also works mostly, but will it
341 always do the correct thing?
345 int n = pitch->get_notename ();
346 int o = pitch->get_octave ();
347 int a = pitch->get_alteration ();
348 SCM on_s = gh_cons (scm_int2num (o), scm_int2num (n));
353 Perhaps only check translators mentioned in the auto-accidentals?
358 I'd be surprised if the impact of this would be
359 measurable. Anyway, it seems localsig doesn't change
360 every time-step, but a set_property() is done every
361 time. We could save on that, probably.
370 SCM localsig = origin->get_property ("localKeySignature");
374 Remember an alteration that is different both from
375 that of the tied note and of the key signature.
377 localsig = ly_assoc_front_x
378 (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
383 not really really correct if there are more than one
384 noteheads with the same notename.
386 localsig = ly_assoc_front_x
387 (localsig, on_s, gh_cons (scm_int2num (a), barnum));
389 origin->set_property ("localKeySignature", localsig);
390 origin = origin->daddy_context_;
397 Accidental_engraver::finalize ()
400 Must reset, since Accidental_engraver is GCd.
402 last_keysig_ = SCM_EOL;
406 Accidental_engraver::stop_translation_timestep ()
408 for (int i = 0; i < accidentals_.size(); i++)
410 Grob *a = accidentals_[i].accidental_;
417 if (accidental_placement_)
418 typeset_grob(accidental_placement_);
419 accidental_placement_ = 00;
421 accidentals_.clear();
422 left_objects_.clear ();
423 right_objects_.clear ();
428 Accidental_engraver::acknowledge_grob (Grob_info info)
430 Music * note = info.music_cause ();
433 && note->is_mus_type ("note-event")
434 && Rhythmic_head::has_interface (info.grob_)
435 && !gh_equal_p (info.grob_->get_grob_property ("style"), ly_symbol2scm ("harmonic"))
438 Accidental_entry entry ;
439 entry.head_ = info.grob_;
440 entry.origin_ = info.origin_trans_->daddy_context_;
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 left_objects_.push (info.grob_);
453 else if (info.grob_->internal_has_interface (ly_symbol2scm("finger-interface")))
455 left_objects_.push (info.grob_);
460 Accidental_engraver::process_music ()
462 SCM sig = get_property ("keySignature");
464 /* Detect key sig changes.
465 Update all parents and children
467 if (last_keysig_ != sig)
469 Context * trans_ = daddy_context_;
472 trans_ -> set_property ("localKeySignature", ly_deep_copy (sig));
473 trans_ = trans_->daddy_context_;
475 set_property_on_children(daddy_context_,"localKeySignature", sig);
485 ENTER_DESCRIPTION (Accidental_engraver,
486 "Make accidentals. Catches note heads, ties and notices key-change "
487 " events. Due to interaction with ties (which don't come together "
488 " with note heads), this needs to be in a context higher than Tie_engraver.",
491 "finger-interface rhythmic-head-interface tie-interface arpeggio-interface",
492 "localKeySignature extraNatural autoAccidentals autoCautionaries",
493 "localKeySignature");