2 accidental-engraver.cc -- implement accidental_engraver
4 (c) 1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 Modified 2001-2002 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 "engraver-group-engraver.hh"
16 #include "staff-symbol-referencer.hh"
17 #include "side-position-interface.hh"
18 #include "engraver.hh"
19 #include "arpeggio.hh"
22 #include "translator-group.hh"
27 FIXME: should not compute vertical positioning of accidentals, but
28 get them from the noteheads
30 The algorithm for accidentals should be documented, and made
36 struct Accidental_engraver : Engraver {
39 TRANSLATOR_DECLARATIONS(Accidental_engraver);
40 virtual void process_music ();
41 virtual void acknowledge_grob (Grob_info);
42 virtual void stop_translation_timestep ();
43 virtual void initialize ();
44 virtual void create_grobs ();
45 virtual void finalize ();
48 // todo -> property (why? -rz)
52 Urgh. Since the accidentals depend on lots of variables, we have to
53 store all information before we can really create the accidentals.
55 Link_array<Grob> arpeggios_;
57 Link_array<Note_req> mel_l_arr_;
58 Link_array<Grob> head_l_arr_;
59 Link_array<Item> forced_l_arr_;
60 Link_array<Grob> tie_l_arr_;
61 Link_array<Translator_group> origin_l_arr_;
66 Accidental_engraver::Accidental_engraver ()
69 last_keysig_ = SCM_EOL;
73 Accidental_engraver::initialize ()
75 last_keysig_ = get_property ("keySignature");
77 Translator_group * trans_ = daddy_trans_l_;
79 trans_ -> set_property ("localKeySignature", ly_deep_copy(last_keysig_));
80 trans_ = trans_->daddy_trans_l_;
82 daddy_trans_l_->set_children_property("localKeySignature",last_keysig_);
86 /** calculates the number of accidentals on basis of the current local key sig
87 * (passed as argument)
88 * Returns number of accidentals (0, 1 or 2).
89 * Negative (-1 or -2) if accidental has changed.
91 // FIXME: Use references for SCM-values
93 number_accidentals (SCM sig, Note_req * note_l, SCM curbarnum, SCM lazyness,
96 Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
97 int n = pitch->notename_i_;
98 int o = pitch->octave_i_;
99 int a = pitch->alteration_i_;
100 int curbarnum_i = gh_scm2int(curbarnum);
104 prev = ly_assoc_cdr (gh_int2scm (n), sig);
106 prev = gh_assoc (gh_cons (gh_int2scm (o), gh_int2scm (n)), sig);
107 /* should really be true unless prev==SCM_BOOL_F */
108 if(gh_pair_p(prev) && gh_pair_p(ly_cdr(prev))) {
109 accbarnum_i = gh_scm2int(ly_cddr(prev));
110 prev = gh_cons(ly_car(prev),ly_cadr(prev));
112 /* If an accidental was not found or the accidental was too old */
113 if (prev == SCM_BOOL_F ||
114 (gh_number_p(lazyness) && curbarnum_i>accbarnum_i+gh_scm2int(lazyness)))
115 prev = gh_assoc (gh_int2scm (n), sig);
116 SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
118 int p = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
121 if (a==p && !to_boolean (note_l->get_mus_property ("force-accidental")) && gh_number_p(prev_acc)) num=0;
122 else if ( (abs(a)<abs(p) || p*a<0) && a!=0 ) num=2;
125 return a==p ? num : -num;
129 number_accidentals (Note_req * note_l, Translator_group * origin_l,
130 SCM accidentals_sl, SCM curbarnum_s) {
133 if(gh_pair_p(accidentals_sl) && !gh_symbol_p(ly_car(accidentals_sl)))
134 warning(_f("Accidental typesetting list must begin with context-name: %s",
135 ly_scm2string(ly_car(accidentals_sl)).ch_C()));
136 while(gh_pair_p(accidentals_sl) && origin_l) {
137 // If pair then it is a new accidentals typesetting rule to be checked
138 if(gh_pair_p(ly_car(accidentals_sl))) {
139 SCM type = gh_caar(accidentals_sl);
140 SCM lazyness = gh_cdar(accidentals_sl);
141 SCM localsig = origin_l->get_property ("localKeySignature");
143 gh_eq_p(ly_symbol2scm("same-octave"),type);
145 gh_eq_p(ly_symbol2scm("any-octave"),type);
146 if(same_octave_b || any_octave_b) {
147 int n = number_accidentals
148 (localsig,note_l,curbarnum_s,lazyness,any_octave_b);
150 number = max(number,abs(n));
152 else warning(_f("unknown accidental typesetting: %s. Ignored",
153 ly_symbol2string(type).ch_C()));
155 // if symbol then it is a context name. Scan parent contexts to find it.
156 else if (gh_symbol_p(ly_car(accidentals_sl))) {
157 String context = ly_symbol2string(ly_car(accidentals_sl));
158 while (origin_l && !origin_l->is_alias_b(context)) {
159 origin_l = origin_l->daddy_trans_l_;
162 warning(_f("Symbol is not a parent context: %s. Ignored",
165 else warning(_f("Accidental typesetting must be pair or context-name: %s",
166 ly_scm2string(ly_car(accidentals_sl)).ch_C()));
167 accidentals_sl = ly_cdr(accidentals_sl);
169 return diff ? -number : number;
173 Accidental_engraver::create_grobs ()
175 if (!key_item_p_ && mel_l_arr_.size ())
177 //SCM localsig = get_property ("localKeySignature");
178 SCM accidentals_sl = get_property ("autoAccidentals");
179 SCM cautionaries_sl = get_property ("autoCautionaries");
180 SCM barnum = get_property ("currentBarNumber");
182 bool extra_natural_b = get_property ("extraNatural")==SCM_BOOL_T;
183 for (int i=0; i < mel_l_arr_.size (); i++)
185 Grob * support_l = head_l_arr_[i];
186 Note_req * note_l = mel_l_arr_[i];
187 Translator_group * origin_l = origin_l_arr_[i];
189 int num = number_accidentals(note_l,origin_l,accidentals_sl,barnum);
190 int num_caut = number_accidentals(note_l,origin_l,cautionaries_sl,barnum);
191 bool cautionary = to_boolean (note_l->get_mus_property ("cautionary"));
192 if (abs(num_caut)>abs(num))
198 bool different=num<0;
201 /* see if there's a tie that "changes" the accidental */
202 /* works because if there's a tie, the note to the left
203 is of the same pitch as the actual note */
206 Grob *tie_break_reminder = 0;
207 bool tie_changes = false;
208 for (int j=0; j < tie_l_arr_.size (); j++)
209 if (support_l == Tie::head (tie_l_arr_[j], RIGHT))
211 tie_changes = different;
212 /* Enable accidentals for broken tie
214 We only want an accidental on a broken tie,
215 if the tie changes the accidental.
217 Maybe check property noTieBreakForceAccidental? */
219 tie_break_reminder = tie_l_arr_[j];
227 key_item_p_ = new Item (get_property ("Accidentals"));
228 Local_key_item::set_interface (key_item_p_);
230 Staff_symbol_referencer::set_interface (key_item_p_);
231 SCM c0 = get_property ("centralCPosition");
232 if (gh_number_p (c0))
233 Staff_symbol_referencer::set_position (key_item_p_, gh_scm2int (c0));
235 announce_grob(key_item_p_, SCM_EOL);
239 Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
241 num==2 && extra_natural_b,
243 Side_position_interface::add_support (key_item_p_,support_l);
245 support_l->set_grob_property ("accidentals-grob", key_item_p_->self_scm ());
250 We should not record the accidental if it is the first
251 note and it is tied from the previous measure.
253 Checking whether it is tied also works mostly, but will it
254 always do the correct thing?
257 Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
258 int n = pitch->notename_i_;
259 int o = pitch->octave_i_;
260 int a = pitch->alteration_i_;
261 SCM on_s = gh_cons (gh_int2scm (o), gh_int2scm (n));
265 Perhaps only check translators mentioned in the auto-accidentals?
268 Translator_group * trans_ = origin_l_arr_[i];
270 SCM localsig = trans_->get_property ("localKeySignature");
274 Remember an alteration that is different both from
275 that of the tied note and of the key signature.
277 localsig = ly_assoc_front_x
278 (localsig, on_s, gh_cons(SCM_BOOL_T,barnum));
283 not really really correct if there are more than one
284 noteheads with the same notename.
286 localsig = ly_assoc_front_x
287 (localsig, on_s, gh_cons(gh_int2scm (a),barnum));
289 trans_->set_property ("localKeySignature", localsig);
290 trans_ = trans_->daddy_trans_l_;
299 We add the accidentals to the support of the arpeggio, so it is put left of the
303 for (int i=0; i < arpeggios_.size (); i++)
304 Side_position_interface::add_support (arpeggios_[i], key_item_p_);
311 Accidental_engraver::finalize ()
317 Accidental_engraver::stop_translation_timestep ()
321 for (int i=0; i < head_l_arr_.size (); i++)
322 Side_position_interface::add_support (key_item_p_,head_l_arr_[i]);
324 typeset_grob (key_item_p_);
332 head_l_arr_.clear ();
333 forced_l_arr_.clear ();
334 origin_l_arr_.clear ();
338 Accidental_engraver::acknowledge_grob (Grob_info info)
340 Note_req * note_l = dynamic_cast <Note_req *> (info.music_cause ());
342 if (note_l && Rhythmic_head::has_interface (info.grob_l_))
344 mel_l_arr_.push (note_l);
345 head_l_arr_.push (info.grob_l_);
346 origin_l_arr_.push (info.origin_trans_l_->daddy_trans_l_);
348 else if (Tie::has_interface (info.grob_l_))
350 tie_l_arr_.push (info.grob_l_);
352 else if (Arpeggio::has_interface (info.grob_l_))
354 arpeggios_.push (info.grob_l_);
360 Accidental_engraver::process_music ()
363 SCM smp = get_property ("measurePosition");
364 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
367 SCM sig = get_property ("keySignature");
369 /* Detect key sig changes.
370 Update all parents and children
372 if (last_keysig_ != sig) {
373 Translator_group * trans_ = daddy_trans_l_;
375 trans_ -> set_property ("localKeySignature", ly_deep_copy(sig));
376 trans_ = trans_->daddy_trans_l_;
378 daddy_trans_l_->set_children_property("localKeySignature",sig);
388 ENTER_DESCRIPTION(Accidental_engraver,
389 /* The FIXME below is deprecated and should be removed. -rz */
390 /* descr */ "Make accidentals. Catches note heads, ties and notices key-change
391 events. Due to interaction with ties (which don't come together
392 with note heads), this needs to be in a context higher than Tie_engraver. FIXME",
393 /* creats*/ "Accidentals",
394 /* acks */ "rhythmic-head-interface tie-interface arpeggio-interface",
395 /* reads */ "localKeySignature extraNatural autoAccidentals autoCautionaries",
396 /* write */ "localKeySignature");