2 accidental-engraver.cc -- implement accidental_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Modified 2001--2002 by Rune Zedeler <rz@daimi.au.dk>
10 #include "accidental-placement.hh"
11 #include "arpeggio.hh"
13 #include "engraver.hh"
16 #include "protected-scm.hh"
17 #include "rhythmic-head.hh"
18 #include "side-position-interface.hh"
24 class Accidental_entry
31 Engraver *origin_trans_;
38 Accidental_entry::Accidental_entry ()
48 class Accidental_engraver : public Engraver
51 int get_bar_number ();
52 void update_local_key_signature ();
55 TRANSLATOR_DECLARATIONS (Accidental_engraver);
56 virtual void process_music ();
57 virtual void acknowledge_grob (Grob_info);
58 virtual void stop_translation_timestep ();
59 virtual void initialize ();
60 virtual void process_acknowledged_grobs ();
61 virtual void finalize ();
64 Protected_scm last_keysig_; // ugh.
66 /* Urgh. Since the accidentals depend on lots of variables, we have
67 to store all information before we can really create the
69 Link_array<Grob> left_objects_;
70 Link_array<Grob> right_objects_;
72 Grob *accidental_placement_;
74 Array<Accidental_entry> accidentals_;
75 Link_array<Spanner> ties_;
82 ugh, it is not clear what properties are mutable and which
83 aren't. eg. localKeySignature is changed at runtime, which means
84 that references in grobs should always store ly_deep_copy ()s of
89 set_property_on_children (Context *trans, char const *sym, SCM val)
91 trans->set_property (sym, ly_deep_copy (val));
92 for (SCM p = trans->children_contexts (); ly_c_pair_p (p); p = ly_cdr (p))
94 Context *trg = unsmob_context (ly_car (p));
95 set_property_on_children (trg, sym, ly_deep_copy (val));
99 Accidental_engraver::Accidental_engraver ()
101 accidental_placement_ = 0;
102 last_keysig_ = SCM_EOL;
106 Accidental_engraver::update_local_key_signature ()
108 last_keysig_ = get_property ("keySignature");
109 set_property_on_children (context (), "localKeySignature", last_keysig_);
111 Context *trans = context ()->get_parent_context ();
113 /* Huh. Don't understand what this is good for. --hwn. */
114 while (trans && trans->where_defined (ly_symbol2scm ("localKeySignature")))
116 trans->set_property ("localKeySignature", ly_deep_copy (last_keysig_));
117 trans = trans->get_parent_context ();
122 Accidental_engraver::initialize ()
124 update_local_key_signature ();
128 /** Calculate the number of accidentals on basis of the current local key
129 sig (passed as argument)
131 * First check step+octave (taking into account barnumbers if necessary).
133 * Then check the global signature (only step).
135 Return number of accidentals (0, 1 or 2). */
137 number_accidentals_from_sig (bool *different, SCM sig, Pitch *pitch,
138 int curbarnum, SCM laziness, bool ignore_octave)
140 int n = pitch->get_notename ();
141 int o = pitch->get_octave ();
142 int a = pitch->get_alteration ();
144 SCM prev_alt = SCM_BOOL_F;
149 = scm_assoc (scm_cons (scm_int2num (o), scm_int2num (n)), sig);
151 if (ly_c_pair_p (prev_local))
153 if (ly_c_pair_p (ly_cdr (prev_local))
154 && ly_c_number_p (laziness))
156 int barnum = ly_scm2int (ly_cddr (prev_local));
158 prev_local = scm_cons (ly_car (prev_local), ly_cadr (prev_local));
159 if (curbarnum <= barnum + ly_scm2int (laziness))
160 prev_alt = prev_local;
165 if (prev_alt == SCM_BOOL_F)
166 prev_alt = scm_assoc (scm_int2num (n), sig);
168 prev_alt = (prev_alt == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev_alt);
170 /* UGH. prev_acc can be #t in case of ties. What is this for? */
171 int p = ly_c_number_p (prev_alt) ? ly_scm2int (prev_alt) : 0;
174 if (a == p && ly_c_number_p (prev_alt))
176 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
181 *different = (a != p);
186 number_accidentals (bool *different,
187 Pitch *pitch, Context *origin,
188 SCM accidentals, int curbarnum)
193 if (ly_c_pair_p (accidentals) && !ly_c_symbol_p (ly_car (accidentals)))
194 warning (_f ("Accidental typesetting list must begin with context-name: %s",
195 ly_scm2string (ly_car (accidentals)).to_str0 ()));
197 for (; ly_c_pair_p (accidentals) && origin;
198 accidentals = ly_cdr (accidentals))
200 // If pair then it is a new accidentals typesetting rule to be checked
201 SCM rule = ly_car (accidentals);
202 if (ly_c_pair_p (rule))
204 SCM type = ly_car (rule);
205 SCM laziness = ly_cdr (rule);
206 SCM localsig = origin->get_property ("localKeySignature");
209 ly_c_eq_p (ly_symbol2scm ("same-octave"), type);
211 ly_c_eq_p (ly_symbol2scm ("any-octave"), type);
213 if (same_octave_b || any_octave_b)
216 int n = number_accidentals_from_sig
217 (&d, localsig, pitch, curbarnum, laziness, any_octave_b);
218 *different = *different || d;
219 number = max (number, n);
222 warning (_f ("ignoring unknown accidental: %s",
223 ly_symbol2string (type).to_str0 ()));
228 if symbol then it is a context name. Scan parent contexts to find it.
230 else if (ly_c_symbol_p (rule))
232 Context *dad = origin;
233 while (dad && !dad->is_alias (rule))
234 dad = dad->get_parent_context ();
239 else warning (_f ("Accidental rule must be pair or context-name; Found %s",
240 ly_scm2string (rule).to_str0 ()));
247 Accidental_engraver::get_bar_number ()
249 SCM barnum = get_property ("currentBarNumber");
250 SCM smp = get_property ("measurePosition");
252 int bn = robust_scm2int (barnum, 0);
254 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
255 if (mp.main_part_ < Rational (0))
262 Accidental_engraver::process_acknowledged_grobs ()
264 if (accidentals_.size () && !accidentals_.top ().done_)
266 SCM accidentals = get_property ("autoAccidentals");
267 SCM cautionaries = get_property ("autoCautionaries");
268 int barnum = get_bar_number ();
270 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
271 for (int i = 0; i < accidentals_.size (); i++)
273 if (accidentals_[i].done_ )
275 accidentals_[i].done_ = true;
276 Grob *support = accidentals_[i].head_;
277 Music *note = accidentals_[i].melodic_;
278 Context *origin = accidentals_[i].origin_;
280 Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
284 bool different = false;
285 bool different_caut = false;
287 int num = number_accidentals (&different,
289 accidentals, barnum);
290 int num_caut = number_accidentals (&different_caut,
292 cautionaries, barnum);
294 bool cautionary = to_boolean (note->get_property ("cautionary"));
299 different = different_caut;
303 if (num == 0 && to_boolean (note->get_property ("force-accidental")))
308 Can not look for ties: it's not guaranteed that they reach
315 We construct the accidentals at the originating Voice
316 level, so that we get the property settings for
317 Accidental from the respective Voice.
320 = make_item_from_properties (accidentals_[i].origin_trans_,
321 ly_symbol2scm ("Accidental"),
323 a->set_parent (support, Y_AXIS);
325 if (!accidental_placement_)
326 accidental_placement_ = make_item ("AccidentalPlacement",
328 Accidental_placement::add_accidental (accidental_placement_, a);
329 SCM accs = scm_cons (scm_int2num (pitch->get_alteration ()),
331 if (num == 2 && extra_natural_b)
332 accs = scm_cons (scm_int2num (0), accs);
334 /* TODO: add cautionary option in accidental. */
337 a->set_property ("cautionary", SCM_BOOL_T);
339 support->set_property ("accidental-grob", a->self_scm ());
341 a->set_property ("accidentals", accs);
342 accidentals_[i].accidental_ = a;
345 We add the accidentals to the support of the arpeggio,
346 so it is put left of the accidentals.
348 for (int i = 0; i < left_objects_.size (); i++)
349 Side_position_interface::add_support (left_objects_[i], a);
350 for (int i = 0; i < right_objects_.size (); i++)
351 Side_position_interface::add_support (a, right_objects_[i]);
358 Accidental_engraver::finalize ()
360 last_keysig_ = SCM_EOL;
364 Accidental_engraver::stop_translation_timestep ()
366 for (int j = ties_.size (); j--;)
368 Grob *r = Tie::head (ties_[j], RIGHT);
369 for (int i = accidentals_.size (); i--;)
370 if (accidentals_[i].head_ == r)
372 if (Grob *g = accidentals_[i].accidental_)
374 g->set_property ("tie", ties_[j]->self_scm ());
375 accidentals_[i].tied_ = true;
382 for (int i = accidentals_.size (); i--;)
384 int barnum = get_bar_number ();
386 Music *note = accidentals_[i].melodic_;
387 Context * origin = accidentals_[i].origin_;
389 Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
393 int n = pitch->get_notename ();
394 int o = pitch->get_octave ();
395 int a = pitch->get_alteration ();
396 SCM key = scm_cons (scm_int2num (o), scm_int2num (n));
399 && origin->where_defined (ly_symbol2scm ("localKeySignature")))
402 huh? we set props all the way to the top?
404 SCM localsig = origin->get_property ("localKeySignature");
406 if (accidentals_[i].tied_)
409 Remember an alteration that is different both from
410 that of the tied note and of the key signature.
412 localsig = ly_assoc_front_x
413 (localsig, key, scm_cons (SCM_BOOL_T, scm_int2num (barnum)));
420 not really really correct if there are more than one
421 noteheads with the same notename.
423 localsig = ly_assoc_front_x (localsig, key,
424 scm_cons (scm_int2num (a),
425 scm_int2num (barnum)));
430 origin->set_property ("localKeySignature", localsig);
432 origin = origin->get_parent_context ();
436 accidental_placement_ = 0;
437 accidentals_.clear ();
438 left_objects_.clear ();
439 right_objects_.clear ();
443 Accidental_engraver::acknowledge_grob (Grob_info info)
445 Music *note = info.music_cause ();
448 && note->is_mus_type ("note-event")
449 && Rhythmic_head::has_interface (info.grob_))
451 if (to_boolean ( get_property ("harmonicAccidentals"))
452 || !ly_c_equal_p (info.grob_->get_property ("style"),
453 ly_symbol2scm ("harmonic")))
456 Accidental_entry entry ;
457 entry.head_ = info.grob_;
458 entry.origin_trans_ = dynamic_cast<Engraver*> (info.origin_trans_);
459 entry.origin_ = info.origin_trans_->context ();
460 entry.melodic_ = note;
462 accidentals_.push (entry);
465 else if (Tie::has_interface (info.grob_))
466 ties_.push (dynamic_cast<Spanner*> (info.grob_));
467 else if (Arpeggio::has_interface (info.grob_))
468 left_objects_.push (info.grob_);
470 ->internal_has_interface (ly_symbol2scm ("finger-interface")))
471 left_objects_.push (info.grob_);
475 Accidental_engraver::process_music ()
477 SCM sig = get_property ("keySignature");
478 /* Detect key sig changes.
479 Update all parents and children. */
480 if (last_keysig_ != sig)
481 update_local_key_signature ();
484 ENTER_DESCRIPTION (Accidental_engraver,
486 "Catch note heads, ties and notices key-change events. "
487 "This engraver usually lives at Staff level, but "
488 "reads the settings for Accidental at @code{Voice} level, "
489 "so you can @code{\\override} them at @code{Voice}. "
495 "arpeggio-interface "
497 "rhythmic-head-interface "
503 "harmonicAccidentals "