2 accidental-engraver.cc -- implement accidental_engraver
4 (c) 1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 Modified 2001-2002 by Rune Zedeler <rz@daimi.au.dk>
8 #include "musical-request.hh"
9 #include "command-request.hh"
10 #include "local-key-item.hh"
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 "translator-group.hh"
26 FIXME: should not compute vertical positioning of accidentals, but
27 get them from the noteheads
29 The algorithm for accidentals should be documented, and made
34 struct Accidental_entry {
38 Translator_group *origin_;
43 Accidental_entry::Accidental_entry()
52 struct New_accidental_engraver : Engraver {
54 TRANSLATOR_DECLARATIONS (New_accidental_engraver);
55 virtual void process_music ();
56 virtual void acknowledge_grob (Grob_info);
57 virtual void stop_translation_timestep ();
58 virtual void initialize ();
59 virtual void create_grobs ();
60 virtual void finalize ();
66 This is not a property, and it is not protected. This poses a
67 very small risk of the value being GC'd from under us.
74 Urgh. Since the accidentals depend on lots of variables, we have to
75 store all information before we can really create the accidentals.
77 Link_array<Grob> arpeggios_;
79 Grob * accidental_placement_;
85 Array<Accidental_entry> accidental_arr_;
87 Link_array<Grob> tie_arr_;
93 New_accidental_engraver::New_accidental_engraver ()
95 accidental_placement_ = 0;
96 last_keysig_ = SCM_EOL;
100 New_accidental_engraver::initialize ()
102 last_keysig_ = get_property ("keySignature");
104 Translator_group * trans_ = daddy_trans_l_;
107 trans_ -> set_property ("localKeySignature", ly_deep_copy (last_keysig_));
108 trans_ = trans_->daddy_trans_l_;
110 daddy_trans_l_->set_children_property ("localKeySignature", last_keysig_);
115 calculates the number of accidentals on basis of the current local key sig
117 Returns number of accidentals (0, 1 or 2).
118 Negative (-1 or -2) if accidental has changed.
122 number_accidentals (SCM sig, Note_req * note_l, Pitch *pitch, SCM curbarnum, SCM lazyness,
123 bool ignore_octave_b)
125 int n = pitch->notename_i_;
126 int o = pitch->octave_i_;
127 int a = pitch->alteration_i_;
128 int curbarnum_i = gh_scm2int (curbarnum);
133 prev = ly_assoc_cdr (gh_int2scm (n), sig);
135 prev = gh_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
137 /* should really be true unless prev == SCM_BOOL_F */
138 if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
140 accbarnum_i = gh_scm2int (ly_cddr (prev));
141 prev = gh_cons (ly_car (prev), ly_cadr (prev));
144 /* If an accidental was not found or the accidental was too old */
145 if (prev == SCM_BOOL_F ||
146 (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
147 prev = gh_assoc (gh_int2scm (n), sig);
150 SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
152 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
156 && !to_boolean (note_l->get_mus_property ("force-accidental"))
157 && 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 (Note_req * note_l, Pitch *pitch, Translator_group * origin_l,
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)).ch_C ()));
178 while (gh_pair_p (accidentals) && origin_l)
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_l->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_l, 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).ch_C ()));
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_l && !origin_l->is_alias_b (context))
213 origin_l = origin_l->daddy_trans_l_;
216 warning (_f ("Symbol is not a parent context: %s. Ignored",
219 else warning (_f ("Accidental typesetting must be pair or context-name: %s",
220 ly_scm2string (ly_car (accidentals)).ch_C ()));
222 accidentals = ly_cdr (accidentals);
224 return diff ? -number : number;
228 New_accidental_engraver::create_grobs ()
230 if (accidental_arr_.size () && !accidental_arr_.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");
237 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
238 for (int i = 0; i < accidental_arr_.size (); i++)
240 if (accidental_arr_[i].done_ )
242 accidental_arr_[i].done_ = true;
243 Grob * support_l = accidental_arr_[i].head_;
244 Note_req * note_l = accidental_arr_[i].melodic_;
245 Translator_group * origin_l = accidental_arr_[i].origin_;
247 Pitch * pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
248 int num = number_accidentals (note_l, pitch, origin_l, accidentals, barnum);
249 int num_caut = number_accidentals (note_l, pitch, origin_l, cautionaries, barnum);
250 bool cautionary = to_boolean (note_l->get_mus_property ("cautionary"));
252 if (abs (num_caut) > abs (num))
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 < tie_arr_.size (); j++)
268 if (support_l == Tie::head (tie_arr_[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 = tie_arr_[j];
285 Grob * a = new Item (get_property ("Accidental"));
286 a->set_parent (support_l, 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 (gh_int2scm (pitch->alteration_i_), SCM_EOL);
299 if (num == 2 && extra_natural_b)
300 accs = gh_cons (gh_int2scm (0), accs);
304 add cautionary option in accidental.
307 if (tie_break_reminder)
310 support_l->set_grob_property ("accidental-grob", a->self_scm ());
312 a->set_grob_property ("accidentals", accs);
313 accidental_arr_[i].accidental_ = a;
315 We add the accidentals to the support of the arpeggio, so it is put left of the
319 for (int i = 0; i < arpeggios_.size (); i++)
320 Side_position_interface::add_support (arpeggios_[i], a);
325 We should not record the accidental if it is the first
326 note and it is tied from the previous measure.
328 Checking whether it is tied also works mostly, but will it
329 always do the correct thing?
333 int n = pitch->notename_i_;
334 int o = pitch->octave_i_;
335 int a = pitch->alteration_i_;
336 SCM on_s = gh_cons (gh_int2scm (o), gh_int2scm (n));
341 Perhaps only check translators mentioned in the auto-accidentals?
346 I'd be surprised if the impact of this would be
347 measurable. Anyway, it seems localsig doesn't change
348 every time-step, but a set_property() is done every
349 time. We could save on that, probably.
358 SCM localsig = origin_l->get_property ("localKeySignature");
362 Remember an alteration that is different both from
363 that of the tied note and of the key signature.
365 localsig = ly_assoc_front_x
366 (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
371 not really really correct if there are more than one
372 noteheads with the same notename.
374 localsig = ly_assoc_front_x
375 (localsig, on_s, gh_cons (gh_int2scm (a), barnum));
377 origin_l->set_property ("localKeySignature", localsig);
378 origin_l = origin_l->daddy_trans_l_;
385 New_accidental_engraver::finalize ()
391 New_accidental_engraver::stop_translation_timestep ()
393 for (int i = 0; i < accidental_arr_.size(); i++)
395 Grob *a = accidental_arr_[i].accidental_;
402 if (accidental_placement_)
403 typeset_grob(accidental_placement_);
404 accidental_placement_ = 00;
406 accidental_arr_.clear();
412 New_accidental_engraver::acknowledge_grob (Grob_info info)
414 Note_req * note_l = dynamic_cast <Note_req *> (info.music_cause ());
416 if (note_l && Rhythmic_head::has_interface (info.grob_l_))
418 Accidental_entry entry ;
419 entry.head_ = info.grob_l_;
420 entry.origin_ = info.origin_trans_l_->daddy_trans_l_;
421 entry.melodic_ = note_l;
423 accidental_arr_.push (entry);
425 else if (Tie::has_interface (info.grob_l_))
427 tie_arr_.push (info.grob_l_);
429 else if (Arpeggio::has_interface (info.grob_l_))
431 arpeggios_.push (info.grob_l_);
437 New_accidental_engraver::process_music ()
439 SCM sig = get_property ("keySignature");
441 /* Detect key sig changes.
442 Update all parents and children
444 if (last_keysig_ != sig)
446 Translator_group * trans_ = daddy_trans_l_;
449 trans_ -> set_property ("localKeySignature", ly_deep_copy (sig));
450 trans_ = trans_->daddy_trans_l_;
452 daddy_trans_l_->set_children_property ("localKeySignature", sig);
462 ENTER_DESCRIPTION (New_accidental_engraver,
463 "Make accidentals. Catches note heads, ties and notices key-change
464 events. Due to interaction with ties (which don't come together
465 with note heads), this needs to be in a context higher than Tie_engraver.",
468 "rhythmic-head-interface tie-interface arpeggio-interface",
469 "localKeySignature extraNatural autoAccidentals autoCautionaries",
470 "localKeySignature");