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"));
229 SCM c0 = get_property ("centralCPosition");
230 if (gh_number_p (c0))
231 Staff_symbol_referencer::set_position (key_item_p_, gh_scm2int (c0));
233 announce_grob(key_item_p_, SCM_EOL);
237 Local_key_item::add_pitch (key_item_p_, *unsmob_pitch (note_l->get_mus_property ("pitch")),
239 num==2 && extra_natural_b,
241 Side_position_interface::add_support (key_item_p_,support_l);
243 support_l->set_grob_property ("accidentals-grob", key_item_p_->self_scm ());
248 We should not record the accidental if it is the first
249 note and it is tied from the previous measure.
251 Checking whether it is tied also works mostly, but will it
252 always do the correct thing?
255 Pitch *pitch = unsmob_pitch (note_l->get_mus_property ("pitch"));
256 int n = pitch->notename_i_;
257 int o = pitch->octave_i_;
258 int a = pitch->alteration_i_;
259 SCM on_s = gh_cons (gh_int2scm (o), gh_int2scm (n));
263 Perhaps only check translators mentioned in the auto-accidentals?
266 Translator_group * trans_ = origin_l_arr_[i];
268 SCM localsig = trans_->get_property ("localKeySignature");
272 Remember an alteration that is different both from
273 that of the tied note and of the key signature.
275 localsig = ly_assoc_front_x
276 (localsig, on_s, gh_cons(SCM_BOOL_T,barnum));
281 not really really correct if there are more than one
282 noteheads with the same notename.
284 localsig = ly_assoc_front_x
285 (localsig, on_s, gh_cons(gh_int2scm (a),barnum));
287 trans_->set_property ("localKeySignature", localsig);
288 trans_ = trans_->daddy_trans_l_;
297 We add the accidentals to the support of the arpeggio, so it is put left of the
301 for (int i=0; i < arpeggios_.size (); i++)
302 Side_position_interface::add_support (arpeggios_[i], key_item_p_);
309 Accidental_engraver::finalize ()
315 Accidental_engraver::stop_translation_timestep ()
319 for (int i=0; i < head_l_arr_.size (); i++)
320 Side_position_interface::add_support (key_item_p_,head_l_arr_[i]);
322 typeset_grob (key_item_p_);
330 head_l_arr_.clear ();
331 forced_l_arr_.clear ();
332 origin_l_arr_.clear ();
336 Accidental_engraver::acknowledge_grob (Grob_info info)
338 Note_req * note_l = dynamic_cast <Note_req *> (info.music_cause ());
340 if (note_l && Rhythmic_head::has_interface (info.grob_l_))
342 mel_l_arr_.push (note_l);
343 head_l_arr_.push (info.grob_l_);
344 origin_l_arr_.push (info.origin_trans_l_->daddy_trans_l_);
346 else if (Tie::has_interface (info.grob_l_))
348 tie_l_arr_.push (info.grob_l_);
350 else if (Arpeggio::has_interface (info.grob_l_))
352 arpeggios_.push (info.grob_l_);
358 Accidental_engraver::process_music ()
361 SCM smp = get_property ("measurePosition");
362 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
365 SCM sig = get_property ("keySignature");
367 /* Detect key sig changes.
368 Update all parents and children
370 if (last_keysig_ != sig) {
371 Translator_group * trans_ = daddy_trans_l_;
373 trans_ -> set_property ("localKeySignature", ly_deep_copy(sig));
374 trans_ = trans_->daddy_trans_l_;
376 daddy_trans_l_->set_children_property("localKeySignature",sig);
386 ENTER_DESCRIPTION(Accidental_engraver,
387 /* The FIXME below is deprecated and should be removed. -rz */
388 /* descr */ "Make accidentals. Catches note heads, ties and notices key-change
389 events. Due to interaction with ties (which don't come together
390 with note heads), this needs to be in a context higher than Tie_engraver. FIXME",
391 /* creats*/ "Accidentals",
392 /* acks */ "rhythmic-head-interface tie-interface arpeggio-interface",
393 /* reads */ "localKeySignature extraNatural autoAccidentals autoCautionaries",
394 /* write */ "localKeySignature");