2 accidental-engraver.cc -- implement accidental_engraver
4 (c) 1997--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 Modified 2001 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 "timing-translator.hh"
15 #include "engraver-group-engraver.hh"
17 #include "staff-symbol-referencer.hh"
18 #include "side-position-interface.hh"
19 #include "engraver.hh"
20 #include "arpeggio.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
34 struct Accidental_engraver : Engraver {
37 TRANSLATOR_DECLARATIONS(Accidental_engraver);
38 virtual void process_music ();
39 virtual void acknowledge_grob (Grob_info);
40 virtual void stop_translation_timestep ();
41 virtual void initialize ();
42 virtual void create_grobs ();
43 virtual void finalize ();
50 Urgh. Since the accidentals depend on lots of variables, we have to
51 store all information before we can really create the accidentals.
53 Link_array<Grob> arpeggios_;
55 Link_array<Note_req> mel_l_arr_;
56 Link_array<Grob> support_l_arr_;
57 Link_array<Item> forced_l_arr_;
58 Link_array<Grob> tie_l_arr_;
62 Accidental_engraver::Accidental_engraver ()
65 last_keysig_ = SCM_EOL;
69 Accidental_engraver::initialize ()
71 last_keysig_ = get_property ("keySignature");
72 daddy_trans_l_->set_property ("localKeySignature", last_keysig_);
73 daddy_trans_l_->set_property ("lazyKeySignature", last_keysig_);
76 /** calculates the number of accidentals on basis of the current local key sig
77 * (passed as argument).
78 * Returns number of accidentals (0, 1 or 2).
79 * Negative (-1 or -2) if accidental has changed.
82 number_accidentals (SCM sig, Note_req * note_l)
84 Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
85 int n = pitch->notename_i_;
86 int o = pitch->octave_i () ;
87 int a = pitch->alteration_i_;
89 SCM prev = scm_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
90 if (prev == SCM_BOOL_F)
91 prev = scm_assoc (gh_int2scm (n), sig);
92 SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
94 bool different = !gh_equal_p (prev_acc , gh_int2scm (a));
95 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
98 if (a==p && !to_boolean (note_l->get_mus_property ("force-accidental"))) num=0;
99 else if ( (abs(a)<abs(p) || p*a<0) && a!=0 ) num=2;
102 return a==p ? num : -num;
106 Accidental_engraver::create_grobs ()
108 if (!key_item_p_ && mel_l_arr_.size ())
110 SCM localsig = get_property ("localKeySignature");
111 SCM lazysig = get_property ("lazyKeySignature");
113 for (int i=0; i < mel_l_arr_.size (); i++)
115 Grob * support_l = support_l_arr_[i];
116 Note_req * note_l = mel_l_arr_[i];
118 int local_num = number_accidentals(localsig,note_l);
119 bool local_diff = local_num<0; local_num = abs(local_num);
120 int lazy_num = number_accidentals(lazysig,note_l);
121 bool lazy_diff = lazy_num<0; lazy_num = abs(lazy_num);
123 int num = local_num;;
124 bool different= local_diff;
125 bool cautionary = to_boolean (note_l->get_mus_property ("cautionary"));
126 if (to_boolean (get_property ("noResetKey"))) {
128 different = lazy_diff;
130 else if (gh_equal_p (get_property ("autoReminders"),ly_symbol2scm("cautionary"))
131 || gh_equal_p (get_property ("autoReminders"),ly_symbol2scm("accidental"))) {
132 num = max(local_num,lazy_num);
133 if (gh_equal_p (get_property ("autoReminders"),ly_symbol2scm("cautionary"))
134 && lazy_num>local_num)
138 /* see if there's a tie that "changes" the accidental */
139 /* works because if there's a tie, the note to the left
140 is of the same pitch as the actual note */
143 Grob *tie_break_reminder = 0;
144 bool tie_changes = false;
145 for (int i=0; i < tie_l_arr_.size (); i++)
146 if (support_l == Tie::head (tie_l_arr_[i], RIGHT))
148 tie_changes = different;
149 /* Enable accidentals for broken tie
151 We only want an accidental on a broken tie,
152 if the tie changes the accidental.
154 Maybe check property noTieBreakForceAccidental? */
156 tie_break_reminder = tie_l_arr_[i];
164 key_item_p_ = new Item (get_property ("Accidentals"));
165 Local_key_item::set_interface (key_item_p_);
168 Staff_symbol_referencer::set_interface (key_item_p_);
169 SCM c0 = get_property ("centralCPosition");
170 if (gh_number_p (c0))
171 Staff_symbol_referencer::set_position (key_item_p_, gh_scm2int (c0));
173 announce_grob (key_item_p_, 0);
177 Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
181 Side_position_interface::add_support (key_item_p_,support_l);
186 We should not record the accidental if it is the first
187 note and it is tied from the previous measure.
189 Checking whether it is tied also works mostly, but will it
190 always do the correct thing?
194 Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
195 int n = pitch->notename_i_;
196 int o = pitch->octave_i () ;
197 int a = pitch->alteration_i_;
198 SCM on = gh_cons (gh_int2scm (o), gh_int2scm (n));
199 bool forget = to_boolean (get_property ("forgetAccidentals"));
203 Remember an alteration that is different both from
204 that of the tied note and of the key signature.
206 localsig = scm_assoc_set_x (localsig, on, SCM_BOOL_T);
207 lazysig = scm_assoc_set_x (lazysig, on, SCM_BOOL_T);
212 not really really correct if there are more than one
213 noteheads with the same notename.
215 localsig = scm_assoc_set_x (localsig, on, gh_int2scm (a));
216 lazysig = scm_assoc_set_x (lazysig, on, gh_int2scm (a));
220 daddy_trans_l_->set_property ("localKeySignature", localsig);
221 daddy_trans_l_->set_property ("lazyKeySignature", lazysig);
228 Hmm. Which one has to be on the left?
230 On which left, code or paper?
232 (Arpeggios are engraved left of accidentals, of course.)
234 for (int i=0; i < arpeggios_.size (); i++)
235 Side_position_interface::add_support (arpeggios_[i], key_item_p_);
242 Accidental_engraver::finalize ()
248 Accidental_engraver::stop_translation_timestep ()
252 for (int i=0; i < support_l_arr_.size (); i++)
253 Side_position_interface::add_support (key_item_p_,support_l_arr_[i]);
255 typeset_grob (key_item_p_);
263 support_l_arr_.clear ();
264 forced_l_arr_.clear ();
268 Accidental_engraver::acknowledge_grob (Grob_info info)
270 Note_req * note_l = dynamic_cast <Note_req *> (info.music_cause ());
272 if (note_l && Rhythmic_head::has_interface (info.grob_l_))
274 mel_l_arr_.push (note_l);
275 support_l_arr_.push (info.grob_l_);
277 else if (Tie::has_interface (info.grob_l_))
279 tie_l_arr_.push (info.grob_l_);
281 else if (Arpeggio::has_interface (info.grob_l_))
283 arpeggios_.push (info.grob_l_);
289 ugh. repeated deep_copy generates lots of garbage.
292 Accidental_engraver::process_music ()
294 SCM smp = get_property ("measurePosition");
295 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
297 SCM sig = get_property ("keySignature");
300 Detect key sig changes. If we haven't found any, check if at start
301 of measure, and set localKeySignature anyhow. */
302 if (last_keysig_ != sig)
304 daddy_trans_l_->set_property ("localKeySignature", ly_deep_copy (sig));
305 daddy_trans_l_->set_property ("lazyKeySignature", ly_deep_copy (sig));
308 else if (!mp.to_bool () )
310 daddy_trans_l_->set_property ("localKeySignature", ly_deep_copy (sig));
318 ENTER_DESCRIPTION(Accidental_engraver,
319 /* descr */ "Make accidentals. Catches note heads, ties and notices key-change
320 events. Due to interaction with ties (which don't come together
321 with note heads), this needs to be in a context higher than Tie_engraver. FIXME",
322 /* creats*/ "Accidentals",
323 /* acks */ "rhythmic-head-interface tie-interface arpeggio-interface",
324 /* reads */ "localKeySignature forgetAccidentals noResetKey autoReminders",