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"
24 FIXME: should not compute vertical positioning of accidentals, but
25 get them from the noteheads
27 The algorithm for accidentals should be documented, and made
32 struct Accidental_entry {
36 Translator_group *origin_;
41 Accidental_entry::Accidental_entry()
50 struct Accidental_engraver : Engraver {
52 TRANSLATOR_DECLARATIONS (Accidental_engraver);
53 virtual void process_music ();
54 virtual void acknowledge_grob (Grob_info);
55 virtual void stop_translation_timestep ();
56 virtual void initialize ();
57 virtual void process_acknowledged_grobs ();
58 virtual void finalize ();
64 This is not a property, and it is not protected. This poses a
65 very small risk of the value being GC'd from under us.
70 Urgh. Since the accidentals depend on lots of variables, we have to
71 store all information before we can really create the accidentals.
73 Link_array<Grob> left_objects_;
74 Link_array<Grob> right_objects_;
76 Grob * accidental_placement_;
81 Array<Accidental_entry> accidentals_;
82 Link_array<Grob> ties_;
86 static void set_property_on_children (Translator_group * trans, const char * sym, SCM val)
88 trans->set_property (sym, val);
89 for (SCM p = trans -> trans_group_list_; gh_pair_p (p); p = ly_cdr(p)) {
90 Translator_group *trg = dynamic_cast<Translator_group*> (unsmob_translator (ly_car (p)));
91 set_property_on_children(trg,sym,ly_deep_copy(val));
95 Accidental_engraver::Accidental_engraver ()
97 accidental_placement_ = 0;
98 last_keysig_ = SCM_EOL;
102 Accidental_engraver::initialize ()
104 last_keysig_ = get_property ("keySignature");
106 Translator_group * trans_ = daddy_trans_;
109 trans_ -> set_property ("localKeySignature", ly_deep_copy (last_keysig_));
110 trans_ = trans_->daddy_trans_;
112 set_property_on_children (daddy_trans_,"localKeySignature", last_keysig_);
117 calculates the number of accidentals on basis of the current local key sig
119 Returns number of accidentals (0, 1 or 2).
120 Negative (-1 or -2) if accidental has changed.
124 number_accidentals (SCM sig, Music * note, Pitch *pitch, SCM curbarnum, SCM lazyness,
125 bool ignore_octave_b)
127 int n = pitch->get_notename ();
128 int o = pitch->get_octave();
129 int a = pitch->get_alteration ();
130 int curbarnum_i = gh_scm2int (curbarnum);
135 prev = ly_assoc_cdr (scm_int2num (n), sig);
137 prev = scm_assoc (gh_cons (scm_int2num (o), scm_int2num (n)), sig);
139 /* should really be true unless prev == SCM_BOOL_F */
140 if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
142 accbarnum_i = gh_scm2int (ly_cddr (prev));
143 prev = gh_cons (ly_car (prev), ly_cadr (prev));
146 /* If an accidental was not found or the accidental was too old */
147 if (prev == SCM_BOOL_F ||
148 (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
149 prev = scm_assoc (scm_int2num (n), sig);
152 SCM prev_acc = (prev == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev);
154 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
157 if (a == p && gh_number_p (prev_acc))
159 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
164 return a == p ? num : -num;
168 number_accidentals (Music * note, Pitch *pitch, Translator_group * origin,
169 SCM accidentals, SCM curbarnum)
174 if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
175 warning (_f ("Accidental typesetting list must begin with context-name: %s",
176 ly_scm2string (ly_car (accidentals)).to_str0 ()));
178 while (gh_pair_p (accidentals) && origin)
180 // If pair then it is a new accidentals typesetting rule to be checked
181 if (gh_pair_p (ly_car (accidentals)))
183 SCM type = gh_caar (accidentals);
184 SCM lazyness = gh_cdar (accidentals);
185 SCM localsig = origin->get_property ("localKeySignature");
188 gh_eq_p (ly_symbol2scm ("same-octave"), type);
190 gh_eq_p (ly_symbol2scm ("any-octave"), type);
192 if (same_octave_b || any_octave_b)
194 int n = number_accidentals
195 (localsig, note, pitch, curbarnum, lazyness, any_octave_b);
196 diff = diff || (n < 0);
197 number = max (number, abs (n));
200 warning (_f ("unknown accidental typesetting: %s. Ignored",
201 ly_symbol2string (type).to_str0 ()));
206 if symbol then it is a context name. Scan parent contexts to find it.
208 else if (gh_symbol_p (ly_car (accidentals)))
210 String context = ly_symbol2string (ly_car (accidentals));
212 while (origin && !origin->is_alias_b (context))
213 origin = origin->daddy_trans_;
216 warning (_f ("Symbol is not a parent context: %s. Ignored",
217 context.to_str0 ()));
219 else warning (_f ("Accidental typesetting must be pair or context-name: %s",
220 ly_scm2string (ly_car (accidentals)).to_str0 ()));
222 accidentals = ly_cdr (accidentals);
224 return diff ? -number : number;
228 Accidental_engraver::process_acknowledged_grobs ()
230 if (accidentals_.size () && !accidentals_.top().done_)
232 //SCM localsig = get_property ("localKeySignature");
233 SCM accidentals = get_property ("autoAccidentals");
234 SCM cautionaries = get_property ("autoCautionaries");
235 SCM barnum = get_property ("currentBarNumber");
236 SCM smp = get_property("measurePosition");
237 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
238 if(mp.main_part_<Rational(0) && gh_number_p(barnum)) barnum = scm_int2num(gh_scm2int(barnum)-1);
239 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
240 for (int i = 0; i < accidentals_.size (); i++)
242 if (accidentals_[i].done_ )
244 accidentals_[i].done_ = true;
245 Grob * support = accidentals_[i].head_;
246 Music * note = accidentals_[i].melodic_;
247 Translator_group * origin = accidentals_[i].origin_;
249 Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
250 int num = number_accidentals (note, pitch, origin, accidentals, barnum);
251 int num_caut = number_accidentals (note, pitch, origin, cautionaries, barnum);
252 bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
254 if (abs (num_caut) > abs (num))
260 if(num==0 && to_boolean (note->get_mus_property ("force-accidental")))
263 bool different = num < 0;
266 /* see if there's a tie that "changes" the accidental */
267 /* works because if there's a tie, the note to the left
268 is of the same pitch as the actual note */
270 Grob *tie_break_reminder = 0;
271 bool tie_changes = false;
272 for (int j = 0; j < ties_.size (); j++)
273 if (support == Tie::head (ties_[j], RIGHT))
275 tie_changes = different;
277 /* Enable accidentals for broken tie
279 We only want an accidental on a broken tie,
280 if the tie changes the accidental.
282 Maybe check property noTieBreakForceAccidental? */
284 tie_break_reminder = ties_[j];
290 Grob * a = new Item (get_property ("Accidental"));
291 a->set_parent (support, Y_AXIS);
293 if (!accidental_placement_)
295 accidental_placement_ = new Item (get_property ("AccidentalPlacement"));
296 announce_grob (accidental_placement_, a->self_scm());
299 Accidental_placement::add_accidental (accidental_placement_, a);
300 announce_grob (a, SCM_EOL);
303 SCM accs = gh_cons (scm_int2num (pitch->get_alteration ()), SCM_EOL);
304 if (num == 2 && extra_natural_b)
305 accs = gh_cons (scm_int2num (0), accs);
309 add cautionary option in accidental.
314 a->set_grob_property ("cautionary", SCM_BOOL_T);
317 if (tie_break_reminder)
320 a->set_grob_property ("tie", tie_break_reminder->self_scm());
324 support->set_grob_property ("accidental-grob", a->self_scm ());
326 a->set_grob_property ("accidentals", accs);
327 accidentals_[i].accidental_ = a;
329 We add the accidentals to the support of the arpeggio, so it is put left of the
333 for (int i = 0; i < left_objects_.size (); i++)
334 Side_position_interface::add_support (left_objects_[i], a);
335 for (int i = 0; i < right_objects_.size (); i++)
336 Side_position_interface::add_support (a, right_objects_[i]);
341 We should not record the accidental if it is the first
342 note and it is tied from the previous measure.
344 Checking whether it is tied also works mostly, but will it
345 always do the correct thing?
349 int n = pitch->get_notename ();
350 int o = pitch->get_octave ();
351 int a = pitch->get_alteration ();
352 SCM on_s = gh_cons (scm_int2num (o), scm_int2num (n));
357 Perhaps only check translators mentioned in the auto-accidentals?
362 I'd be surprised if the impact of this would be
363 measurable. Anyway, it seems localsig doesn't change
364 every time-step, but a set_property() is done every
365 time. We could save on that, probably.
374 SCM localsig = origin->get_property ("localKeySignature");
378 Remember an alteration that is different both from
379 that of the tied note and of the key signature.
381 localsig = ly_assoc_front_x
382 (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
387 not really really correct if there are more than one
388 noteheads with the same notename.
390 localsig = ly_assoc_front_x
391 (localsig, on_s, gh_cons (scm_int2num (a), barnum));
393 origin->set_property ("localKeySignature", localsig);
394 origin = origin->daddy_trans_;
401 Accidental_engraver::finalize ()
407 Accidental_engraver::stop_translation_timestep ()
409 for (int i = 0; i < accidentals_.size(); i++)
411 Grob *a = accidentals_[i].accidental_;
418 if (accidental_placement_)
419 typeset_grob(accidental_placement_);
420 accidental_placement_ = 00;
422 accidentals_.clear();
423 left_objects_.clear ();
424 right_objects_.clear ();
429 Accidental_engraver::acknowledge_grob (Grob_info info)
431 Music * note = info.music_cause ();
434 && note->is_mus_type("note-event")
435 && Rhythmic_head::has_interface (info.grob_))
437 Accidental_entry entry ;
438 entry.head_ = info.grob_;
439 entry.origin_ = info.origin_trans_->daddy_trans_;
440 entry.melodic_ = note;
442 accidentals_.push (entry);
444 else if (Tie::has_interface (info.grob_))
446 ties_.push (info.grob_);
448 else if (Arpeggio::has_interface (info.grob_))
450 left_objects_.push (info.grob_);
452 else if (info.grob_->internal_has_interface (ly_symbol2scm("finger-interface")))
454 left_objects_.push (info.grob_);
459 Accidental_engraver::process_music ()
461 SCM sig = get_property ("keySignature");
463 /* Detect key sig changes.
464 Update all parents and children
466 if (last_keysig_ != sig)
468 Translator_group * trans_ = daddy_trans_;
471 trans_ -> set_property ("localKeySignature", ly_deep_copy (sig));
472 trans_ = trans_->daddy_trans_;
474 set_property_on_children(daddy_trans_,"localKeySignature", sig);
484 ENTER_DESCRIPTION (Accidental_engraver,
485 "Make accidentals. Catches note heads, ties and notices key-change "
486 " events. Due to interaction with ties (which don't come together "
487 " with note heads), this needs to be in a context higher than Tie_engraver.",
490 "finger-interface rhythmic-head-interface tie-interface arpeggio-interface",
491 "localKeySignature extraNatural autoAccidentals autoCautionaries",
492 "localKeySignature");