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 a->set_grob_property ("tie", tie_break_reminder->self_scm());
314 support_l->set_grob_property ("accidental-grob", a->self_scm ());
316 a->set_grob_property ("accidentals", accs);
317 accidental_arr_[i].accidental_ = a;
319 We add the accidentals to the support of the arpeggio, so it is put left of the
323 for (int i = 0; i < arpeggios_.size (); i++)
324 Side_position_interface::add_support (arpeggios_[i], a);
329 We should not record the accidental if it is the first
330 note and it is tied from the previous measure.
332 Checking whether it is tied also works mostly, but will it
333 always do the correct thing?
337 int n = pitch->notename_i_;
338 int o = pitch->octave_i_;
339 int a = pitch->alteration_i_;
340 SCM on_s = gh_cons (gh_int2scm (o), gh_int2scm (n));
345 Perhaps only check translators mentioned in the auto-accidentals?
350 I'd be surprised if the impact of this would be
351 measurable. Anyway, it seems localsig doesn't change
352 every time-step, but a set_property() is done every
353 time. We could save on that, probably.
362 SCM localsig = origin_l->get_property ("localKeySignature");
366 Remember an alteration that is different both from
367 that of the tied note and of the key signature.
369 localsig = ly_assoc_front_x
370 (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
375 not really really correct if there are more than one
376 noteheads with the same notename.
378 localsig = ly_assoc_front_x
379 (localsig, on_s, gh_cons (gh_int2scm (a), barnum));
381 origin_l->set_property ("localKeySignature", localsig);
382 origin_l = origin_l->daddy_trans_l_;
389 New_accidental_engraver::finalize ()
395 New_accidental_engraver::stop_translation_timestep ()
397 for (int i = 0; i < accidental_arr_.size(); i++)
399 Grob *a = accidental_arr_[i].accidental_;
406 if (accidental_placement_)
407 typeset_grob(accidental_placement_);
408 accidental_placement_ = 00;
410 accidental_arr_.clear();
416 New_accidental_engraver::acknowledge_grob (Grob_info info)
418 Note_req * note_l = dynamic_cast <Note_req *> (info.music_cause ());
420 if (note_l && Rhythmic_head::has_interface (info.grob_l_))
422 Accidental_entry entry ;
423 entry.head_ = info.grob_l_;
424 entry.origin_ = info.origin_trans_l_->daddy_trans_l_;
425 entry.melodic_ = note_l;
427 accidental_arr_.push (entry);
429 else if (Tie::has_interface (info.grob_l_))
431 tie_arr_.push (info.grob_l_);
433 else if (Arpeggio::has_interface (info.grob_l_))
435 arpeggios_.push (info.grob_l_);
441 New_accidental_engraver::process_music ()
443 SCM sig = get_property ("keySignature");
445 /* Detect key sig changes.
446 Update all parents and children
448 if (last_keysig_ != sig)
450 Translator_group * trans_ = daddy_trans_l_;
453 trans_ -> set_property ("localKeySignature", ly_deep_copy (sig));
454 trans_ = trans_->daddy_trans_l_;
456 daddy_trans_l_->set_children_property ("localKeySignature", sig);
466 ENTER_DESCRIPTION (New_accidental_engraver,
467 "Make accidentals. Catches note heads, ties and notices key-change
468 events. Due to interaction with ties (which don't come together
469 with note heads), this needs to be in a context higher than Tie_engraver.",
472 "rhythmic-head-interface tie-interface arpeggio-interface",
473 "localKeySignature extraNatural autoAccidentals autoCautionaries",
474 "localKeySignature");