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);
93 bool different = !gh_equal_p (prev_acc , gh_int2scm (a));
94 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
97 if (a==p && !to_boolean (note_l->get_mus_property ("force-accidental"))) num=0;
98 else if ( (abs(a)<abs(p) || p*a<0) && a!=0 ) num=2;
101 return a==p ? num : -num;
105 Accidental_engraver::create_grobs ()
107 if (!key_item_p_ && mel_l_arr_.size ())
109 SCM localsig = get_property ("localKeySignature");
110 SCM lazysig = get_property ("lazyKeySignature");
112 for (int i=0; i < mel_l_arr_.size (); i++)
114 Grob * support_l = support_l_arr_[i];
115 Note_req * note_l = mel_l_arr_[i];
117 int local_num = number_accidentals(localsig,note_l);
118 bool local_diff = local_num<0; local_num = abs(local_num);
119 int lazy_num = number_accidentals(lazysig,note_l);
120 bool lazy_diff = lazy_num<0; lazy_num = abs(lazy_num);
122 int num = local_num;;
123 bool different= local_diff;
124 bool cautionary = to_boolean (note_l->get_mus_property ("cautionary"));
125 if (to_boolean (get_property ("noResetKey"))) {
127 different = lazy_diff;
129 else if (gh_equal_p (get_property ("autoReminders"),ly_symbol2scm("cautionary"))
130 || gh_equal_p (get_property ("autoReminders"),ly_symbol2scm("accidental"))) {
131 num = max(local_num,lazy_num);
132 if (gh_equal_p (get_property ("autoReminders"),ly_symbol2scm("cautionary"))
133 && lazy_num>local_num)
137 /* see if there's a tie that "changes" the accidental */
138 /* works because if there's a tie, the note to the left
139 is of the same pitch as the actual note */
142 Grob *tie_break_reminder = 0;
143 bool tie_changes = false;
144 for (int i=0; i < tie_l_arr_.size (); i++)
145 if (support_l == Tie::head (tie_l_arr_[i], RIGHT))
147 tie_changes = different;
148 /* Enable accidentals for broken tie
150 We only want an accidental on a broken tie,
151 if the tie changes the accidental.
153 Maybe check property noTieBreakForceAccidental? */
155 tie_break_reminder = tie_l_arr_[i];
163 key_item_p_ = new Item (get_property ("Accidentals"));
164 Local_key_item::set_interface (key_item_p_);
167 Staff_symbol_referencer::set_interface (key_item_p_);
168 SCM c0 = get_property ("centralCPosition");
169 if (gh_number_p (c0))
170 Staff_symbol_referencer::set_position (key_item_p_, gh_scm2int (c0));
172 announce_grob (key_item_p_, 0);
176 Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
180 Side_position_interface::add_support (key_item_p_,support_l);
185 We should not record the accidental if it is the first
186 note and it is tied from the previous measure.
188 Checking whether it is tied also works mostly, but will it
189 always do the correct thing?
193 Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
194 int n = pitch->notename_i_;
195 int o = pitch->octave_i () ;
196 int a = pitch->alteration_i_;
197 SCM on = gh_cons (gh_int2scm (o), gh_int2scm (n));
198 bool forget = to_boolean (get_property ("forgetAccidentals"));
202 Remember an alteration that is different both from
203 that of the tied note and of the key signature.
205 localsig = scm_assoc_set_x (localsig, on, SCM_BOOL_T);
206 lazysig = scm_assoc_set_x (lazysig, on, SCM_BOOL_T);
211 not really really correct if there are more than one
212 noteheads with the same notename.
214 localsig = scm_assoc_set_x (localsig, on, gh_int2scm (a));
215 lazysig = scm_assoc_set_x (lazysig, on, gh_int2scm (a));
219 daddy_trans_l_->set_property ("localKeySignature", localsig);
220 daddy_trans_l_->set_property ("lazyKeySignature", lazysig);
227 Hmm. Which one has to be on the left?
229 On which left, code or paper?
231 (Arpeggios are engraved left of accidentals, of course.)
233 for (int i=0; i < arpeggios_.size (); i++)
234 Side_position_interface::add_support (arpeggios_[i], key_item_p_);
241 Accidental_engraver::finalize ()
247 Accidental_engraver::stop_translation_timestep ()
251 for (int i=0; i < support_l_arr_.size (); i++)
252 Side_position_interface::add_support (key_item_p_,support_l_arr_[i]);
254 typeset_grob (key_item_p_);
262 support_l_arr_.clear ();
263 forced_l_arr_.clear ();
267 Accidental_engraver::acknowledge_grob (Grob_info info)
269 Note_req * note_l = dynamic_cast <Note_req *> (info.req_l_);
271 if (note_l && Rhythmic_head::has_interface (info.grob_l_))
273 mel_l_arr_.push (note_l);
274 support_l_arr_.push (info.grob_l_);
276 else if (Tie::has_interface (info.grob_l_))
278 tie_l_arr_.push (info.grob_l_);
280 else if (Arpeggio::has_interface (info.grob_l_))
282 arpeggios_.push (info.grob_l_);
288 ugh. repeated deep_copy generates lots of garbage.
291 Accidental_engraver::process_music ()
293 SCM smp = get_property ("measurePosition");
294 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
296 SCM sig = get_property ("keySignature");
299 Detect key sig changes. If we haven't found any, check if at start
300 of measure, and set localKeySignature anyhow. */
301 if (last_keysig_ != sig)
303 daddy_trans_l_->set_property ("localKeySignature", ly_deep_copy (sig));
304 daddy_trans_l_->set_property ("lazyKeySignature", ly_deep_copy (sig));
307 else if (!mp.to_bool () )
309 daddy_trans_l_->set_property ("localKeySignature", ly_deep_copy (sig));
317 ENTER_DESCRIPTION(Accidental_engraver,
318 /* descr */ "Make accidentals. Catches note heads, ties and notices key-change
319 events. Due to interaction with ties (which don't come together
320 with note heads), this needs to be in a context higher than Tie_engraver. FIXME",
321 /* creats*/ "Accidentals",
322 /* acks */ "rhythmic-head-interface tie-interface arpeggio-interface",
323 /* reads */ "localKeySignature forgetAccidentals noResetKey autoReminders",