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_;
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 key sig
78 * (passed as argument).
79 * Returns number of accidentals (0, 1 or 2).
80 * Negative (-1 or -2) if accidental has changed.
83 number_accidentals (SCM sig, Note_req * note_l, bool ignore_octave_b)
85 Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
86 int n = pitch->notename_i_;
87 int o = pitch->octave_i_;
88 int a = pitch->alteration_i_;
92 prev = ly_assoc_cdr (gh_int2scm (n), sig);
94 prev = gh_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
95 if (prev == SCM_BOOL_F)
96 prev = gh_assoc (gh_int2scm (n), sig);
97 SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
99 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
102 if (a==p && !to_boolean (note_l->get_mus_property ("force-accidental"))) num=0;
103 else if ( (abs(a)<abs(p) || p*a<0) && a!=0 ) num=2;
106 return a==p ? num : -num;
110 number_accidentals (SCM localsig, SCM lazysig, Note_req * note_l, SCM accidentals_l) {
113 if(gh_memq(ly_symbol2scm("same-octave"),accidentals_l)!=SCM_BOOL_F) {
114 int n = number_accidentals(localsig,note_l,false);
116 number = max(number,abs(n));
118 if(gh_memq(ly_symbol2scm("lazy-same-octave"),accidentals_l)!=SCM_BOOL_F) {
119 int n = number_accidentals(lazysig,note_l,false);
121 number = max(number,abs(n));
123 if(gh_memq(ly_symbol2scm("any-octave"),accidentals_l)!=SCM_BOOL_F) {
124 int n = number_accidentals(localsig,note_l,true);
126 number = max(number,abs(n));
128 if(gh_memq(ly_symbol2scm("lazy-any-octave"),accidentals_l)!=SCM_BOOL_F) {
129 int n = number_accidentals(lazysig,note_l,true);
131 number = max(number,abs(n));
133 return diff ? -number : number;
137 Accidental_engraver::create_grobs ()
139 if (!key_item_p_ && mel_l_arr_.size ())
141 SCM localsig = get_property ("localKeySignature");
142 SCM lazysig = get_property ("lazyKeySignature");
143 SCM accidentals_l = get_property ("autoAccidentals");
144 SCM cautionaries_l = get_property ("autoCautionaries");
146 bool extra_natural_b = get_property ("extraNatural")==SCM_BOOL_T;
148 for (int i=0; i < mel_l_arr_.size (); i++)
150 Grob * support_l = support_l_arr_[i];
151 Note_req * note_l = mel_l_arr_[i];
153 int num = number_accidentals(localsig,lazysig,note_l,accidentals_l);
154 int num_caut = number_accidentals(localsig,lazysig,note_l,cautionaries_l);
155 bool cautionary = to_boolean (note_l->get_mus_property ("cautionary"));
156 if (abs(num_caut)>abs(num)) {
160 bool different=num<0;
163 /* see if there's a tie that "changes" the accidental */
164 /* works because if there's a tie, the note to the left
165 is of the same pitch as the actual note */
168 Grob *tie_break_reminder = 0;
169 bool tie_changes = false;
170 for (int i=0; i < tie_l_arr_.size (); i++)
171 if (support_l == Tie::head (tie_l_arr_[i], RIGHT))
173 tie_changes = different;
174 /* Enable accidentals for broken tie
176 We only want an accidental on a broken tie,
177 if the tie changes the accidental.
179 Maybe check property noTieBreakForceAccidental? */
181 tie_break_reminder = tie_l_arr_[i];
189 key_item_p_ = new Item (get_property ("Accidentals"));
190 Local_key_item::set_interface (key_item_p_);
193 Staff_symbol_referencer::set_interface (key_item_p_);
194 SCM c0 = get_property ("centralCPosition");
195 if (gh_number_p (c0))
196 Staff_symbol_referencer::set_position (key_item_p_, gh_scm2int (c0));
198 announce_grob (key_item_p_, 0);
202 Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
204 num==2 && extra_natural_b,
206 Side_position_interface::add_support (key_item_p_,support_l);
211 We should not record the accidental if it is the first
212 note and it is tied from the previous measure.
214 Checking whether it is tied also works mostly, but will it
215 always do the correct thing?
216 FIXME: 2nd accidental after broken-tie accidental should be junked.
217 Remove broken-tie-support?
220 Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
221 int n = pitch->notename_i_;
222 int o = pitch->octave_i () ;
223 int a = pitch->alteration_i_;
224 SCM on = gh_cons (gh_int2scm (o), gh_int2scm (n));
225 bool forget = to_boolean (get_property ("forgetAccidentals"));
229 Remember an alteration that is different both from
230 that of the tied note and of the key signature.
232 localsig = ly_assoc_front_x (localsig, on, SCM_BOOL_T);
233 lazysig = ly_assoc_front_x (lazysig, on, SCM_BOOL_T);
238 not really really correct if there are more than one
239 noteheads with the same notename.
241 localsig = ly_assoc_front_x (localsig, on, gh_int2scm (a));
242 lazysig = ly_assoc_front_x (lazysig, on, gh_int2scm (a));
246 daddy_trans_l_->set_property ("localKeySignature", localsig);
247 daddy_trans_l_->set_property ("lazyKeySignature", lazysig);
254 Hmm. Which one has to be on the left?
256 On which left, code or paper?
258 (Arpeggios are engraved left of accidentals, of course.)
260 for (int i=0; i < arpeggios_.size (); i++)
261 Side_position_interface::add_support (arpeggios_[i], key_item_p_);
268 Accidental_engraver::finalize ()
274 Accidental_engraver::stop_translation_timestep ()
278 for (int i=0; i < support_l_arr_.size (); i++)
279 Side_position_interface::add_support (key_item_p_,support_l_arr_[i]);
281 typeset_grob (key_item_p_);
289 support_l_arr_.clear ();
290 forced_l_arr_.clear ();
294 Accidental_engraver::acknowledge_grob (Grob_info info)
296 Note_req * note_l = dynamic_cast <Note_req *> (info.music_cause ());
298 if (note_l && Rhythmic_head::has_interface (info.grob_l_))
300 mel_l_arr_.push (note_l);
301 support_l_arr_.push (info.grob_l_);
303 else if (Tie::has_interface (info.grob_l_))
305 tie_l_arr_.push (info.grob_l_);
307 else if (Arpeggio::has_interface (info.grob_l_))
309 arpeggios_.push (info.grob_l_);
315 ugh. repeated deep_copy generates lots of garbage.
318 Accidental_engraver::process_music ()
320 SCM smp = get_property ("measurePosition");
321 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
323 SCM sig = get_property ("keySignature");
326 Detect key sig changes. If we haven't found any, check if at start
327 of measure, and set localKeySignature anyhow. */
328 if (last_keysig_ != sig)
330 daddy_trans_l_->set_property ("localKeySignature", ly_deep_copy (sig));
331 daddy_trans_l_->set_property ("lazyKeySignature", ly_deep_copy (sig));
334 else if (!mp.to_bool () )
336 /* Use the old local sig as new lazy sig. This way the lazy sig will be one measure late */
337 if (get_property("oneMeasureLazy")==SCM_BOOL_T)
338 daddy_trans_l_->set_property ("lazyKeySignature",
339 daddy_trans_l_->get_property ("localKeySignature"));
340 daddy_trans_l_->set_property ("localKeySignature", ly_deep_copy (sig));
348 ENTER_DESCRIPTION(Accidental_engraver,
349 /* descr */ "Make accidentals. Catches note heads, ties and notices key-change
350 events. Due to interaction with ties (which don't come together
351 with note heads), this needs to be in a context higher than Tie_engraver. FIXME",
352 /* creats*/ "Accidentals",
353 /* acks */ "rhythmic-head-interface tie-interface arpeggio-interface",
354 /* reads */ "localKeySignature lazyKeySignature forgetAccidentals oneMeasureLazy extraNatural autoAccidentals autoCautionaries",