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 int number_accidentals (SCM sig, Note_req *);
43 virtual void create_grobs ();
44 virtual void finalize ();
51 Urgh. Since the accidentals depend on lots of variables, we have to
52 store all information before we can really create the accidentals.
54 Link_array<Grob> arpeggios_;
56 Link_array<Note_req> mel_l_arr_;
57 Link_array<Grob> support_l_arr_;
58 Link_array<Item> forced_l_arr_;
59 Link_array<Grob> tie_l_arr_;
63 Accidental_engraver::Accidental_engraver ()
66 last_keysig_ = SCM_EOL;
70 Accidental_engraver::initialize ()
72 last_keysig_ = get_property ("keySignature");
73 daddy_trans_l_->set_property ("localKeySignature", last_keysig_);
74 daddy_trans_l_->set_property ("lazyKeySignature", last_keysig_);
77 /** calculates the number of accidentals on basis of the current local time sig
78 * (passed as argument).
79 * Returns number of accidentals (0, 1 or 2).
80 * Negative (-1 or -2) if accidental has changed.
83 Accidental_engraver::number_accidentals (SCM sig, Note_req * note_l)
85 int n = unsmob_pitch (note_l->get_mus_property ("pitch"))->notename_i_;
86 int o = unsmob_pitch (note_l->get_mus_property ("pitch"))->octave_i () ;
87 int a = unsmob_pitch (note_l->get_mus_property ("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?
192 int n = unsmob_pitch (note_l->get_mus_property ("pitch"))->notename_i_;
193 int o = unsmob_pitch (note_l->get_mus_property ("pitch"))->octave_i () ;
194 int a = unsmob_pitch (note_l->get_mus_property ("pitch"))->alteration_i_;
195 SCM ON = gh_cons (gh_int2scm (o), gh_int2scm (n));
196 bool forget = to_boolean (get_property ("forgetAccidentals"));
200 Remember an alteration that is different both from
201 that of the tied note and of the key signature.
204 localsig = scm_assoc_set_x (localsig, ON, SCM_BOOL_T);
205 lazysig = scm_assoc_set_x (lazysig, ON, SCM_BOOL_T);
210 not really really correct if there are more than one
211 noteheads with the same notename.
213 localsig = scm_assoc_set_x (localsig, ON, gh_int2scm (a));
214 lazysig = scm_assoc_set_x (lazysig, ON, gh_int2scm (a));
218 daddy_trans_l_->set_property ("localKeySignature", localsig);
219 daddy_trans_l_->set_property ("lazyKeySignature", lazysig);
226 Hmm. Which one has to be on the left?
228 On which left, code or paper?
230 (Arpeggios are engraved left of accidentals, of course.)
232 for (int i=0; i < arpeggios_.size (); i++)
233 Side_position_interface::add_support (arpeggios_[i], key_item_p_);
240 Accidental_engraver::finalize ()
246 Accidental_engraver::stop_translation_timestep ()
250 for (int i=0; i < support_l_arr_.size (); i++)
251 Side_position_interface::add_support (key_item_p_,support_l_arr_[i]);
253 typeset_grob (key_item_p_);
261 support_l_arr_.clear ();
262 forced_l_arr_.clear ();
266 Accidental_engraver::acknowledge_grob (Grob_info info)
268 Note_req * note_l = dynamic_cast <Note_req *> (info.req_l_);
270 if (note_l && Rhythmic_head::has_interface (info.grob_l_))
272 mel_l_arr_.push (note_l);
273 support_l_arr_.push (info.grob_l_);
275 else if (Tie::has_interface (info.grob_l_))
277 tie_l_arr_.push (info.grob_l_);
279 else if (Arpeggio::has_interface (info.grob_l_))
281 arpeggios_.push (info.grob_l_);
287 ugh. repeated deep_copy generates lots of garbage.
290 Accidental_engraver::process_music ()
292 SCM smp = get_property ("measurePosition");
293 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
295 SCM sig = get_property ("keySignature");
298 Detect key sig changes. If we haven't found any, check if at start
299 of measure, and set localKeySignature anyhow. */
300 if (last_keysig_ != sig)
302 daddy_trans_l_->set_property ("localKeySignature", ly_deep_copy (sig));
303 daddy_trans_l_->set_property ("lazyKeySignature", ly_deep_copy (sig));
306 else if (!mp.to_bool () )
308 daddy_trans_l_->set_property ("localKeySignature", ly_deep_copy (sig));
316 ENTER_DESCRIPTION(Accidental_engraver,
317 /* descr */ "Make accidentals. Catches note heads, ties and notices key-change
318 events. Due to interaction with ties (which don't come together
319 with note heads), this needs to be in a context higher than Tie_engraver. FIXME",
320 /* creats*/ "Accidentals",
321 /* acks */ "rhythmic-head-interface tie-interface arpeggio-interface",
322 /* reads */ "localKeySignature forgetAccidentals noResetKey",