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"
14 #include "protected-scm.hh"
15 #include "rhythmic-head.hh"
16 #include "side-position-interface.hh"
20 class Accidental_entry
27 Engraver *origin_trans_;
34 Accidental_entry::Accidental_entry ()
44 class Accidental_engraver : public Engraver
47 int get_bar_number ();
48 void update_local_key_signature ();
51 TRANSLATOR_DECLARATIONS (Accidental_engraver);
52 virtual void process_music ();
53 virtual void acknowledge_grob (Grob_info);
54 virtual void stop_translation_timestep ();
55 virtual void initialize ();
56 virtual void process_acknowledged_grobs ();
57 virtual void finalize ();
59 virtual void derived_mark () const;
61 SCM last_keysig_; // ugh.
63 /* Urgh. Since the accidentals depend on lots of variables, we have
64 to store all information before we can really create the
66 Link_array<Grob> left_objects_;
67 Link_array<Grob> right_objects_;
69 Grob *accidental_placement_;
71 Array<Accidental_entry> accidentals_;
72 Link_array<Spanner> ties_;
79 ugh, it is not clear what properties are mutable and which
80 aren't. eg. localKeySignature is changed at runtime, which means
81 that references in grobs should always store ly_deep_copy ()s of
86 set_property_on_children (Context *trans, char const *sym, SCM val)
88 trans->set_property (sym, ly_deep_copy (val));
89 for (SCM p = trans->children_contexts (); scm_is_pair (p); p = scm_cdr (p))
91 Context *trg = unsmob_context (scm_car (p));
92 set_property_on_children (trg, sym, ly_deep_copy (val));
96 Accidental_engraver::Accidental_engraver ()
98 accidental_placement_ = 0;
99 last_keysig_ = SCM_EOL;
103 Accidental_engraver::derived_mark () const
105 scm_gc_mark (last_keysig_);
109 Accidental_engraver::update_local_key_signature ()
111 last_keysig_ = get_property ("keySignature");
112 set_property_on_children (context (), "localKeySignature", last_keysig_);
114 Context *trans = context ()->get_parent_context ();
116 /* Huh. Don't understand what this is good for. --hwn. */
117 while (trans && trans->where_defined (ly_symbol2scm ("localKeySignature")))
119 trans->set_property ("localKeySignature", ly_deep_copy (last_keysig_));
120 trans = trans->get_parent_context ();
125 Accidental_engraver::initialize ()
127 update_local_key_signature ();
131 /** Calculate the number of accidentals on basis of the current local key
132 sig (passed as argument)
134 * First check step+octave (taking into account barnumbers if necessary).
136 * Then check the global signature (only step).
138 Return number of accidentals (0, 1 or 2). */
141 recent_enough (int bar_number, SCM alteration_def, SCM laziness)
143 if (scm_is_number (alteration_def)
144 || laziness == SCM_BOOL_T)
147 return (bar_number <= scm_to_int (scm_cdr (alteration_def)) + scm_to_int (laziness));
151 extract_alteration (SCM alteration_def)
153 if (scm_is_number (alteration_def))
154 return scm_to_int (alteration_def);
155 else if (scm_is_pair (alteration_def))
156 return scm_to_int (scm_car (alteration_def));
157 else if (alteration_def == SCM_BOOL_F)
165 is_tied (SCM alteration_def)
167 return (alteration_def == SCM_BOOL_T)
168 || (scm_is_pair (alteration_def) && scm_car (alteration_def) == SCM_BOOL_T);
172 number_accidentals_from_sig (bool *different, SCM sig, Pitch *pitch,
173 int bar_number, SCM laziness, bool ignore_octave)
175 int n = pitch->get_notename ();
176 int o = pitch->get_octave ();
178 SCM previous_alteration = SCM_BOOL_F;
181 SCM from_same_octave = ly_assoc_get (scm_cons (scm_int2num (o),
182 scm_int2num (n)), sig, SCM_BOOL_F);
183 SCM from_key_signature = ly_assoc_get (scm_int2num (n), sig, SCM_BOOL_F);
184 SCM from_other_octaves = SCM_BOOL_F;
185 for (SCM s = sig ; scm_is_pair (s); s = scm_cdr (s))
187 SCM entry = scm_car (s);
188 if (scm_is_pair (scm_car (entry))
189 && scm_cdar (entry) == scm_int2num (n))
190 from_other_octaves = scm_cdr (entry);
194 if (from_same_octave != SCM_BOOL_F
195 && recent_enough (bar_number, from_same_octave, laziness))
197 previous_alteration = from_same_octave;
199 else if (ignore_octave
200 && from_other_octaves != SCM_BOOL_F
201 && recent_enough (bar_number, from_other_octaves, laziness))
203 previous_alteration = from_other_octaves;
205 else if (from_key_signature != SCM_BOOL_F)
207 previous_alteration = from_key_signature;
211 if (is_tied (previous_alteration))
218 int prev = extract_alteration (previous_alteration);
219 int alter = pitch->get_alteration ();
223 else if ((abs (alter) < abs (prev) || prev*alter < 0) && alter != 0)
225 *different = (alter != prev);
231 number_accidentals (bool *different,
232 Pitch *pitch, Context *origin,
233 SCM accidentals, int bar_number)
238 if (scm_is_pair (accidentals) && !scm_is_symbol (scm_car (accidentals)))
239 warning (_f ("Accidental typesetting list must begin with context-name: %s",
240 ly_scm2string (scm_car (accidentals)).to_str0 ()));
242 for (; scm_is_pair (accidentals) && origin;
243 accidentals = scm_cdr (accidentals))
245 // If pair then it is a new accidentals typesetting rule to be checked
246 SCM rule = scm_car (accidentals);
247 if (scm_is_pair (rule))
249 SCM type = scm_car (rule);
250 SCM laziness = scm_cdr (rule);
251 SCM localsig = origin->get_property ("localKeySignature");
254 scm_is_eq (ly_symbol2scm ("same-octave"), type);
256 scm_is_eq (ly_symbol2scm ("any-octave"), type);
258 if (same_octave_b || any_octave_b)
261 int n = number_accidentals_from_sig
262 (&d, localsig, pitch, bar_number, laziness, any_octave_b);
263 *different = *different || d;
264 number = max (number, n);
267 warning (_f ("ignoring unknown accidental: %s",
268 ly_symbol2string (type).to_str0 ()));
273 if symbol then it is a context name. Scan parent contexts to find it.
275 else if (scm_is_symbol (rule))
277 Context *dad = origin;
278 while (dad && !dad->is_alias (rule))
279 dad = dad->get_parent_context ();
284 else warning (_f ("Accidental rule must be pair or context-name; Found %s",
285 ly_scm2string (rule).to_str0 ()));
292 Accidental_engraver::get_bar_number ()
294 SCM barnum = get_property ("currentBarNumber");
295 SCM smp = get_property ("measurePosition");
297 int bn = robust_scm2int (barnum, 0);
299 Moment mp = robust_scm2moment (smp, Moment (0));
300 if (mp.main_part_ < Rational (0))
307 Accidental_engraver::process_acknowledged_grobs ()
309 if (accidentals_.size () && !accidentals_.top ().done_)
311 SCM accidentals = get_property ("autoAccidentals");
312 SCM cautionaries = get_property ("autoCautionaries");
313 int barnum = get_bar_number ();
315 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
316 for (int i = 0; i < accidentals_.size (); i++)
318 if (accidentals_[i].done_ )
320 accidentals_[i].done_ = true;
321 Grob *support = accidentals_[i].head_;
322 Music *note = accidentals_[i].melodic_;
323 Context *origin = accidentals_[i].origin_;
325 Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
329 bool different = false;
330 bool different_caut = false;
332 int num = number_accidentals (&different,
334 accidentals, barnum);
335 int num_caut = number_accidentals (&different_caut,
337 cautionaries, barnum);
339 bool cautionary = to_boolean (note->get_property ("cautionary"));
344 different = different_caut;
348 if (num == 0 && to_boolean (note->get_property ("force-accidental")))
353 Can not look for ties: it's not guaranteed that they reach
360 We construct the accidentals at the originating Voice
361 level, so that we get the property settings for
362 Accidental from the respective Voice.
365 = make_item_from_properties (accidentals_[i].origin_trans_,
366 ly_symbol2scm ("Accidental"),
370 a->set_parent (support, Y_AXIS);
372 if (!accidental_placement_)
373 accidental_placement_ = make_item ("AccidentalPlacement",
375 Accidental_placement::add_accidental (accidental_placement_, a);
376 SCM accs = scm_cons (scm_int2num (pitch->get_alteration ()),
378 if (num == 2 && extra_natural_b)
379 accs = scm_cons (scm_int2num (0), accs);
381 /* TODO: add cautionary option in accidental. */
384 a->set_property ("cautionary", SCM_BOOL_T);
386 support->set_property ("accidental-grob", a->self_scm ());
388 a->set_property ("accidentals", accs);
389 accidentals_[i].accidental_ = a;
392 We add the accidentals to the support of the arpeggio,
393 so it is put left of the accidentals.
395 for (int i = 0; i < left_objects_.size (); i++)
396 Side_position_interface::add_support (left_objects_[i], a);
397 for (int i = 0; i < right_objects_.size (); i++)
398 Side_position_interface::add_support (a, right_objects_[i]);
405 Accidental_engraver::finalize ()
407 last_keysig_ = SCM_EOL;
411 Accidental_engraver::stop_translation_timestep ()
413 for (int j = ties_.size (); j--;)
415 Grob *r = Tie::head (ties_[j], RIGHT);
416 for (int i = accidentals_.size (); i--;)
417 if (accidentals_[i].head_ == r)
419 if (Grob *g = accidentals_[i].accidental_)
421 g->set_property ("tie", ties_[j]->self_scm ());
422 accidentals_[i].tied_ = true;
429 for (int i = accidentals_.size (); i--;)
431 int barnum = get_bar_number ();
433 Music *note = accidentals_[i].melodic_;
434 Context * origin = accidentals_[i].origin_;
436 Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
440 int n = pitch->get_notename ();
441 int o = pitch->get_octave ();
442 int a = pitch->get_alteration ();
443 SCM key = scm_cons (scm_int2num (o), scm_int2num (n));
446 && origin->where_defined (ly_symbol2scm ("localKeySignature")))
449 huh? we set props all the way to the top?
451 SCM localsig = origin->get_property ("localKeySignature");
453 if (accidentals_[i].tied_)
456 Remember an alteration that is different both from
457 that of the tied note and of the key signature.
459 localsig = ly_assoc_front_x
460 (localsig, key, scm_cons (SCM_BOOL_T, scm_int2num (barnum)));
467 not really really correct if there are more than one
468 noteheads with the same notename.
470 localsig = ly_assoc_front_x (localsig, key,
471 scm_cons (scm_int2num (a),
472 scm_int2num (barnum)));
477 origin->set_property ("localKeySignature", localsig);
479 origin = origin->get_parent_context ();
483 accidental_placement_ = 0;
484 accidentals_.clear ();
485 left_objects_.clear ();
486 right_objects_.clear ();
490 Accidental_engraver::acknowledge_grob (Grob_info info)
492 Music *note = info.music_cause ();
495 && note->is_mus_type ("note-event")
496 && Rhythmic_head::has_interface (info.grob_))
498 if (to_boolean ( get_property ("harmonicAccidentals"))
499 || !ly_c_equal_p (info.grob_->get_property ("style"),
500 ly_symbol2scm ("harmonic")))
503 Accidental_entry entry ;
504 entry.head_ = info.grob_;
505 entry.origin_trans_ = dynamic_cast<Engraver*> (info.origin_trans_);
506 entry.origin_ = info.origin_trans_->context ();
507 entry.melodic_ = note;
509 accidentals_.push (entry);
512 else if (Tie::has_interface (info.grob_))
513 ties_.push (dynamic_cast<Spanner*> (info.grob_));
514 else if (Arpeggio::has_interface (info.grob_))
515 left_objects_.push (info.grob_);
517 ->internal_has_interface (ly_symbol2scm ("finger-interface")))
518 left_objects_.push (info.grob_);
522 Accidental_engraver::process_music ()
524 SCM sig = get_property ("keySignature");
525 /* Detect key sig changes.
526 Update all parents and children. */
527 if (last_keysig_ != sig)
528 update_local_key_signature ();
531 ADD_TRANSLATOR (Accidental_engraver,
533 "Catch note heads, ties and notices key-change events. "
534 "This engraver usually lives at Staff level, but "
535 "reads the settings for Accidental at @code{Voice} level, "
536 "so you can @code{\\override} them at @code{Voice}. "
542 "arpeggio-interface "
544 "rhythmic-head-interface "
550 "harmonicAccidentals "