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 (SCM sig, Music * note, 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 while (gh_pair_p (accidentals) && origin)
176 // If pair then it is a new accidentals typesetting rule to be checked
177 if (gh_pair_p (ly_car (accidentals)))
179 SCM type = gh_caar (accidentals);
180 SCM lazyness = gh_cdar (accidentals);
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
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 (ly_car (accidentals)))
206 SCM context =ly_car (accidentals);
207 while (origin && !origin->is_alias_b (context))
208 origin = origin->daddy_trans_;
211 warning (_f ("Symbol is not a parent context: %s. Ignored",
212 ly_symbol2string (context).to_str0 ()));
214 else warning (_f ("Accidental typesetting must be pair or context-name: %s",
215 ly_scm2string (ly_car (accidentals)).to_str0 ()));
217 accidentals = ly_cdr (accidentals);
219 return diff ? -number : number;
223 Accidental_engraver::process_acknowledged_grobs ()
225 if (accidentals_.size () && !accidentals_.top().done_)
227 //SCM localsig = get_property ("localKeySignature");
228 SCM accidentals = get_property ("autoAccidentals");
229 SCM cautionaries = get_property ("autoCautionaries");
230 SCM barnum = get_property ("currentBarNumber");
231 SCM smp = get_property("measurePosition");
232 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
233 if(mp.main_part_<Rational(0) && gh_number_p(barnum)) barnum = scm_int2num(gh_scm2int(barnum)-1);
234 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
235 for (int i = 0; i < accidentals_.size (); i++)
237 if (accidentals_[i].done_ )
239 accidentals_[i].done_ = true;
240 Grob * support = accidentals_[i].head_;
241 Music * note = accidentals_[i].melodic_;
242 Translator_group * origin = accidentals_[i].origin_;
244 Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
245 int num = number_accidentals (note, pitch, origin, accidentals, barnum);
246 int num_caut = number_accidentals (note, pitch, origin, cautionaries, barnum);
247 bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
249 if (abs (num_caut) > abs (num))
255 if(num==0 && to_boolean (note->get_mus_property ("force-accidental")))
258 bool different = num < 0;
261 /* see if there's a tie that "changes" the accidental */
262 /* works because if there's a tie, the note to the left
263 is of the same pitch as the actual note */
265 Grob *tie_break_reminder = 0;
266 bool tie_changes = false;
267 for (int j = 0; j < ties_.size (); j++)
268 if (support == Tie::head (ties_[j], RIGHT))
270 tie_changes = different;
272 /* Enable accidentals for broken tie
274 We only want an accidental on a broken tie,
275 if the tie changes the accidental.
277 Maybe check property noTieBreakForceAccidental? */
279 tie_break_reminder = ties_[j];
285 Grob * a = new Item (get_property ("Accidental"));
286 a->set_parent (support, Y_AXIS);
288 if (!accidental_placement_)
290 accidental_placement_ = new Item (get_property ("AccidentalPlacement"));
291 announce_grob (accidental_placement_, a->self_scm());
294 Accidental_placement::add_accidental (accidental_placement_, a);
295 announce_grob (a, SCM_EOL);
298 SCM accs = gh_cons (scm_int2num (pitch->get_alteration ()), SCM_EOL);
299 if (num == 2 && extra_natural_b)
300 accs = gh_cons (scm_int2num (0), accs);
304 add cautionary option in accidental.
309 a->set_grob_property ("cautionary", SCM_BOOL_T);
312 if (tie_break_reminder)
315 a->set_grob_property ("tie", tie_break_reminder->self_scm());
319 support->set_grob_property ("accidental-grob", a->self_scm ());
321 a->set_grob_property ("accidentals", accs);
322 accidentals_[i].accidental_ = a;
324 We add the accidentals to the support of the arpeggio, so it is put left of the
328 for (int i = 0; i < left_objects_.size (); i++)
329 Side_position_interface::add_support (left_objects_[i], a);
330 for (int i = 0; i < right_objects_.size (); i++)
331 Side_position_interface::add_support (a, right_objects_[i]);
336 We should not record the accidental if it is the first
337 note and it is tied from the previous measure.
339 Checking whether it is tied also works mostly, but will it
340 always do the correct thing?
344 int n = pitch->get_notename ();
345 int o = pitch->get_octave ();
346 int a = pitch->get_alteration ();
347 SCM on_s = gh_cons (scm_int2num (o), scm_int2num (n));
352 Perhaps only check translators mentioned in the auto-accidentals?
357 I'd be surprised if the impact of this would be
358 measurable. Anyway, it seems localsig doesn't change
359 every time-step, but a set_property() is done every
360 time. We could save on that, probably.
369 SCM localsig = origin->get_property ("localKeySignature");
373 Remember an alteration that is different both from
374 that of the tied note and of the key signature.
376 localsig = ly_assoc_front_x
377 (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
382 not really really correct if there are more than one
383 noteheads with the same notename.
385 localsig = ly_assoc_front_x
386 (localsig, on_s, gh_cons (scm_int2num (a), barnum));
388 origin->set_property ("localKeySignature", localsig);
389 origin = origin->daddy_trans_;
396 Accidental_engraver::finalize ()
402 Accidental_engraver::stop_translation_timestep ()
404 for (int i = 0; i < accidentals_.size(); i++)
406 Grob *a = accidentals_[i].accidental_;
413 if (accidental_placement_)
414 typeset_grob(accidental_placement_);
415 accidental_placement_ = 00;
417 accidentals_.clear();
418 left_objects_.clear ();
419 right_objects_.clear ();
424 Accidental_engraver::acknowledge_grob (Grob_info info)
426 Music * note = info.music_cause ();
429 && note->is_mus_type("note-event")
430 && Rhythmic_head::has_interface (info.grob_))
432 Accidental_entry entry ;
433 entry.head_ = info.grob_;
434 entry.origin_ = info.origin_trans_->daddy_trans_;
435 entry.melodic_ = note;
437 accidentals_.push (entry);
439 else if (Tie::has_interface (info.grob_))
441 ties_.push (info.grob_);
443 else if (Arpeggio::has_interface (info.grob_))
445 left_objects_.push (info.grob_);
447 else if (info.grob_->internal_has_interface (ly_symbol2scm("finger-interface")))
449 left_objects_.push (info.grob_);
454 Accidental_engraver::process_music ()
456 SCM sig = get_property ("keySignature");
458 /* Detect key sig changes.
459 Update all parents and children
461 if (last_keysig_ != sig)
463 Translator_group * trans_ = daddy_trans_;
466 trans_ -> set_property ("localKeySignature", ly_deep_copy (sig));
467 trans_ = trans_->daddy_trans_;
469 set_property_on_children(daddy_trans_,"localKeySignature", sig);
479 ENTER_DESCRIPTION (Accidental_engraver,
480 "Make accidentals. Catches note heads, ties and notices key-change "
481 " events. Due to interaction with ties (which don't come together "
482 " with note heads), this needs to be in a context higher than Tie_engraver.",
485 "finger-interface rhythmic-head-interface tie-interface arpeggio-interface",
486 "localKeySignature extraNatural autoAccidentals autoCautionaries",
487 "localKeySignature");