2 accidental-engraver.cc -- implement accidental_engraver
4 (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 Modified 2001--2002 by Rune Zedeler <rz@daimi.au.dk>
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 "protected-scm.hh"
24 struct Accidental_entry {
33 Accidental_entry::Accidental_entry()
42 struct Accidental_engraver : Engraver {
44 TRANSLATOR_DECLARATIONS (Accidental_engraver);
45 virtual void process_music ();
46 virtual void acknowledge_grob (Grob_info);
47 virtual void stop_translation_timestep ();
48 virtual void initialize ();
49 virtual void process_acknowledged_grobs ();
50 virtual void finalize ();
53 Protected_scm last_keysig_;
56 Urgh. Since the accidentals depend on lots of variables, we have to
57 store all information before we can really create the accidentals.
59 Link_array<Grob> left_objects_;
60 Link_array<Grob> right_objects_;
62 Grob * accidental_placement_;
67 Array<Accidental_entry> accidentals_;
68 Link_array<Grob> ties_;
73 set_property_on_children (Context * trans, const char * sym, SCM val)
75 trans->set_property (sym, val);
76 for (SCM p = trans->context_list_; gh_pair_p (p); p = ly_cdr(p))
78 Context *trg = unsmob_context (ly_car (p));
79 set_property_on_children(trg, sym, ly_deep_copy(val));
83 Accidental_engraver::Accidental_engraver ()
85 accidental_placement_ = 0;
86 last_keysig_ = SCM_EOL;
90 Accidental_engraver::initialize ()
92 last_keysig_ = get_property ("keySignature");
94 Context * trans_ = daddy_context_;
97 trans_ -> set_property ("localKeySignature",
98 ly_deep_copy (last_keysig_));
99 trans_ = trans_->daddy_context_;
101 set_property_on_children (daddy_context_,"localKeySignature", last_keysig_);
106 calculates the number of accidentals on basis of the current local key sig
108 Returns number of accidentals (0, 1 or 2).
109 Negative (-1 or -2) if accidental has changed.
113 number_accidentals_from_sig (SCM sig, Music *, Pitch *pitch, SCM curbarnum, SCM lazyness,
114 bool ignore_octave_b)
116 int n = pitch->get_notename ();
117 int o = pitch->get_octave();
118 int a = pitch->get_alteration ();
119 int curbarnum_i = gh_scm2int (curbarnum);
124 prev = ly_assoc_cdr (scm_int2num (n), sig);
126 prev = scm_assoc (gh_cons (scm_int2num (o), scm_int2num (n)), sig);
128 /* should really be true unless prev == SCM_BOOL_F */
129 if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
131 accbarnum_i = gh_scm2int (ly_cddr (prev));
132 prev = gh_cons (ly_car (prev), ly_cadr (prev));
135 /* If an accidental was not found or the accidental was too old */
136 if (prev == SCM_BOOL_F ||
137 (gh_number_p (lazyness) && curbarnum_i > accbarnum_i + gh_scm2int (lazyness)))
138 prev = scm_assoc (scm_int2num (n), sig);
141 SCM prev_acc = (prev == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev);
143 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
146 if (a == p && gh_number_p (prev_acc))
148 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
153 return a == p ? num : -num;
157 number_accidentals (Music * note, Pitch *pitch, Context * origin,
158 SCM accidentals, SCM curbarnum)
163 if (gh_pair_p (accidentals) && !gh_symbol_p (ly_car (accidentals)))
164 warning (_f ("Accidental typesetting list must begin with context-name: %s",
165 ly_scm2string (ly_car (accidentals)).to_str0 ()));
167 for (; gh_pair_p (accidentals) && origin; accidentals = gh_cdr (accidentals))
169 // If pair then it is a new accidentals typesetting rule to be checked
170 SCM rule = gh_car (accidentals);
171 if (gh_pair_p (rule))
173 SCM type = gh_car (rule);
174 SCM lazyness = gh_cdr (rule);
175 SCM localsig = origin->get_property ("localKeySignature");
178 gh_eq_p (ly_symbol2scm ("same-octave"), type);
180 gh_eq_p (ly_symbol2scm ("any-octave"), type);
182 if (same_octave_b || any_octave_b)
184 int n = number_accidentals_from_sig
185 (localsig, note, pitch, curbarnum, lazyness, any_octave_b);
186 diff = diff || (n < 0);
187 number = max (number, abs (n));
190 warning (_f ("unknown accidental typesetting: %s. Ignored",
191 ly_symbol2string (type).to_str0 ()));
196 if symbol then it is a context name. Scan parent contexts to find it.
198 else if (gh_symbol_p (rule))
200 Context * dad = origin;
201 while (dad && !dad->is_alias (rule))
202 dad = dad->daddy_context_;
207 else warning (_f ("Accidental rule must be pair or context-name; Found %s",
208 ly_scm2string (rule).to_str0 ()));
211 return diff ? -number : number;
215 Accidental_engraver::process_acknowledged_grobs ()
217 if (accidentals_.size () && !accidentals_.top().done_)
219 //SCM localsig = get_property ("localKeySignature");
220 SCM accidentals = get_property ("autoAccidentals");
221 SCM cautionaries = get_property ("autoCautionaries");
222 SCM barnum = get_property ("currentBarNumber");
223 SCM smp = get_property("measurePosition");
224 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
225 if(mp.main_part_<Rational(0) && gh_number_p(barnum)) barnum = scm_int2num(gh_scm2int(barnum)-1);
226 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
227 for (int i = 0; i < accidentals_.size (); i++)
229 if (accidentals_[i].done_ )
231 accidentals_[i].done_ = true;
232 Grob * support = accidentals_[i].head_;
233 Music * note = accidentals_[i].melodic_;
234 Context * origin = accidentals_[i].origin_;
236 Pitch * pitch = unsmob_pitch (note->get_mus_property ("pitch"));
240 int num = number_accidentals (note, pitch, origin, accidentals, barnum);
241 int num_caut = number_accidentals (note, pitch, origin, cautionaries, barnum);
242 bool cautionary = to_boolean (note->get_mus_property ("cautionary"));
244 if (abs (num_caut) > abs (num))
250 if(num==0 && to_boolean (note->get_mus_property ("force-accidental")))
253 bool different = num < 0;
256 /* See if there's a tie that makes the accidental disappear */
257 Grob *tie_break_reminder = 0;
258 bool tie_changes = false;
259 for (int j = 0; j < ties_.size (); j++)
260 if (support == Tie::head (ties_[j], RIGHT))
262 tie_changes = different;
264 tie_break_reminder = ties_[j];
271 We construct the accidentals at the originating Voice
272 level, so that we get the property settings for
273 Accidental from the respective Voice.
275 Grob * a = make_item_from_properties (origin,
276 ly_symbol2scm ("Accidental"));
277 a->set_parent (support, Y_AXIS);
279 if (!accidental_placement_)
281 accidental_placement_ = make_item ("AccidentalPlacement");
282 announce_grob (accidental_placement_, a->self_scm());
285 Accidental_placement::add_accidental (accidental_placement_, a);
286 announce_grob (a, SCM_EOL);
289 SCM accs = gh_cons (scm_int2num (pitch->get_alteration ()), SCM_EOL);
290 if (num == 2 && extra_natural_b)
291 accs = gh_cons (scm_int2num (0), accs);
295 add cautionary option in accidental.
300 a->set_grob_property ("cautionary", SCM_BOOL_T);
303 if (tie_break_reminder)
306 a->set_grob_property ("tie", tie_break_reminder->self_scm());
310 support->set_grob_property ("accidental-grob", a->self_scm ());
312 a->set_grob_property ("accidentals", accs);
313 accidentals_[i].accidental_ = a;
316 We add the accidentals to the support of the arpeggio,
317 so it is put left of the accidentals.
319 for (int i = 0; i < left_objects_.size (); i++)
320 Side_position_interface::add_support (left_objects_[i], a);
321 for (int i = 0; i < right_objects_.size (); i++)
322 Side_position_interface::add_support (a, right_objects_[i]);
327 We should not record the accidental if it is the first
328 note and it is tied from the previous measure.
330 Checking whether it is tied also works mostly, but will it
331 always do the correct thing?
335 int n = pitch->get_notename ();
336 int o = pitch->get_octave ();
337 int a = pitch->get_alteration ();
338 SCM on_s = gh_cons (scm_int2num (o), scm_int2num (n));
343 huh? we set props all the way to the top?
345 SCM localsig = origin->get_property ("localKeySignature");
350 Remember an alteration that is different both from
351 that of the tied note and of the key signature.
353 localsig = ly_assoc_front_x
354 (localsig, on_s, gh_cons (SCM_BOOL_T, barnum));
361 not really really correct if there are more than one
362 noteheads with the same notename.
364 localsig = ly_assoc_front_x
365 (localsig, on_s, gh_cons (scm_int2num (a), barnum));
371 origin->set_property ("localKeySignature", localsig);
372 origin = origin->daddy_context_;
379 Accidental_engraver::finalize ()
382 Must reset, since Accidental_engraver is GCd.
384 last_keysig_ = SCM_EOL;
388 Accidental_engraver::stop_translation_timestep ()
390 for (int i = 0; i < accidentals_.size(); i++)
392 Grob *a = accidentals_[i].accidental_;
399 if (accidental_placement_)
400 typeset_grob(accidental_placement_);
401 accidental_placement_ = 00;
403 accidentals_.clear();
404 left_objects_.clear ();
405 right_objects_.clear ();
410 Accidental_engraver::acknowledge_grob (Grob_info info)
412 Music * note = info.music_cause ();
415 && note->is_mus_type ("note-event")
416 && Rhythmic_head::has_interface (info.grob_)
417 && !gh_equal_p (info.grob_->get_grob_property ("style"), ly_symbol2scm ("harmonic"))
420 Accidental_entry entry ;
421 entry.head_ = info.grob_;
422 entry.origin_ = info.origin_trans_->daddy_context_;
423 entry.melodic_ = note;
425 accidentals_.push (entry);
427 else if (Tie::has_interface (info.grob_))
429 ties_.push (info.grob_);
431 else if (Arpeggio::has_interface (info.grob_))
433 left_objects_.push (info.grob_);
435 else if (info.grob_->internal_has_interface (ly_symbol2scm("finger-interface")))
437 left_objects_.push (info.grob_);
442 Accidental_engraver::process_music ()
444 SCM sig = get_property ("keySignature");
446 /* Detect key sig changes.
447 Update all parents and children
449 if (last_keysig_ != sig)
451 Context * trans_ = daddy_context_;
454 trans_ -> set_property ("localKeySignature", ly_deep_copy (sig));
455 trans_ = trans_->daddy_context_;
457 set_property_on_children(daddy_context_,"localKeySignature", sig);
467 ENTER_DESCRIPTION (Accidental_engraver,
468 "Make accidentals. Catches note heads, ties and notices key-change "
469 "events. This engraver usually lives at Staff level, but "
470 "reads the settings for Accidental at @code{Voice} level, "
471 "so you can @code{\\override} them at @code{Voice}. "
476 "finger-interface rhythmic-head-interface tie-interface arpeggio-interface",
477 "localKeySignature extraNatural autoAccidentals autoCautionaries",
478 "localKeySignature");