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 String context = ly_symbol2string (ly_car (accidentals));
208 while (origin && !origin->is_alias_b (context))
209 origin = origin->daddy_trans_;
212 warning (_f ("Symbol is not a parent context: %s. Ignored",
213 context.to_str0 ()));
215 else warning (_f ("Accidental typesetting must be pair or context-name: %s",
216 ly_scm2string (ly_car (accidentals)).to_str0 ()));
218 accidentals = ly_cdr (accidentals);
220 return diff ? -number : number;
224 Accidental_engraver::process_acknowledged_grobs ()
226 if (accidentals_.size () && !accidentals_.top().done_)
228 //SCM localsig = get_property ("localKeySignature");
229 SCM accidentals = get_property ("autoAccidentals");
230 SCM cautionaries = get_property ("autoCautionaries");
231 SCM barnum = get_property ("currentBarNumber");
232 SCM smp = get_property("measurePosition");
233 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
234 if(mp.main_part_<Rational(0) && gh_number_p(barnum)) barnum = scm_int2num(gh_scm2int(barnum)-1);
235 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
236 for (int i = 0; i < accidentals_.size (); i++)
238 if (accidentals_[i].done_ )
240 accidentals_[i].done_ = true;
241 Grob * support = accidentals_[i].head_;
242 Music * note = accidentals_[i].melodic_;
243 Translator_group * origin = accidentals_[i].origin_;
245 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 = new Item (get_property ("Accidental"));
287 a->set_parent (support, Y_AXIS);
289 if (!accidental_placement_)
291 accidental_placement_ = new Item (get_property ("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_trans_;
397 Accidental_engraver::finalize ()
403 Accidental_engraver::stop_translation_timestep ()
405 for (int i = 0; i < accidentals_.size(); i++)
407 Grob *a = accidentals_[i].accidental_;
414 if (accidental_placement_)
415 typeset_grob(accidental_placement_);
416 accidental_placement_ = 00;
418 accidentals_.clear();
419 left_objects_.clear ();
420 right_objects_.clear ();
425 Accidental_engraver::acknowledge_grob (Grob_info info)
427 Music * note = info.music_cause ();
430 && note->is_mus_type("note-event")
431 && Rhythmic_head::has_interface (info.grob_))
433 Accidental_entry entry ;
434 entry.head_ = info.grob_;
435 entry.origin_ = info.origin_trans_->daddy_trans_;
436 entry.melodic_ = note;
438 accidentals_.push (entry);
440 else if (Tie::has_interface (info.grob_))
442 ties_.push (info.grob_);
444 else if (Arpeggio::has_interface (info.grob_))
446 left_objects_.push (info.grob_);
448 else if (info.grob_->internal_has_interface (ly_symbol2scm("finger-interface")))
450 left_objects_.push (info.grob_);
455 Accidental_engraver::process_music ()
457 SCM sig = get_property ("keySignature");
459 /* Detect key sig changes.
460 Update all parents and children
462 if (last_keysig_ != sig)
464 Translator_group * trans_ = daddy_trans_;
467 trans_ -> set_property ("localKeySignature", ly_deep_copy (sig));
468 trans_ = trans_->daddy_trans_;
470 set_property_on_children(daddy_trans_,"localKeySignature", sig);
480 ENTER_DESCRIPTION (Accidental_engraver,
481 "Make accidentals. Catches note heads, ties and notices key-change "
482 " events. Due to interaction with ties (which don't come together "
483 " with note heads), this needs to be in a context higher than Tie_engraver.",
486 "finger-interface rhythmic-head-interface tie-interface arpeggio-interface",
487 "localKeySignature extraNatural autoAccidentals autoCautionaries",
488 "localKeySignature");