2 accidental-engraver.cc -- implement accidental_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2005 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_;
78 ugh, it is not clear what properties are mutable and which
79 aren't. eg. localKeySignature is changed at runtime, which means
80 that references in grobs should always store ly_deep_copy ()s of
85 set_property_on_children (Context *trans, char const *sym, SCM val)
87 trans->set_property (sym, ly_deep_copy (val));
88 for (SCM p = trans->children_contexts (); scm_is_pair (p); p = scm_cdr (p))
90 Context *trg = unsmob_context (scm_car (p));
91 set_property_on_children (trg, sym, ly_deep_copy (val));
95 Accidental_engraver::Accidental_engraver ()
97 accidental_placement_ = 0;
98 last_keysig_ = SCM_EOL;
102 Accidental_engraver::derived_mark () const
104 scm_gc_mark (last_keysig_);
108 Accidental_engraver::update_local_key_signature ()
110 last_keysig_ = get_property ("keySignature");
111 set_property_on_children (context (), "localKeySignature", last_keysig_);
113 Context *trans = context ()->get_parent_context ();
115 /* Huh. Don't understand what this is good for. --hwn. */
116 while (trans && trans->where_defined (ly_symbol2scm ("localKeySignature")))
118 trans->set_property ("localKeySignature", ly_deep_copy (last_keysig_));
119 trans = trans->get_parent_context ();
124 Accidental_engraver::initialize ()
126 update_local_key_signature ();
129 /** Calculate the number of accidentals on basis of the current local key
130 sig (passed as argument)
132 * First check step+octave (taking into account barnumbers if necessary).
134 * Then check the global signature (only step).
136 Return number of accidentals (0, 1 or 2). */
139 recent_enough (int bar_number, SCM alteration_def, SCM laziness)
141 if (scm_is_number (alteration_def)
142 || laziness == SCM_BOOL_T)
145 return (bar_number <= scm_to_int (scm_cdr (alteration_def)) + scm_to_int (laziness));
149 extract_alteration (SCM alteration_def)
151 if (scm_is_number (alteration_def))
152 return scm_to_int (alteration_def);
153 else if (scm_is_pair (alteration_def))
154 return scm_to_int (scm_car (alteration_def));
155 else if (alteration_def == SCM_BOOL_F)
163 is_tied (SCM alteration_def)
165 return (alteration_def == SCM_BOOL_T)
166 || (scm_is_pair (alteration_def) && scm_car (alteration_def) == SCM_BOOL_T);
170 number_accidentals_from_sig (bool *different, SCM sig, Pitch *pitch,
171 int bar_number, SCM laziness, bool ignore_octave)
173 int n = pitch->get_notename ();
174 int o = pitch->get_octave ();
176 SCM previous_alteration = SCM_BOOL_F;
178 SCM from_same_octave = ly_assoc_get (scm_cons (scm_int2num (o),
179 scm_int2num (n)), sig, SCM_BOOL_F);
180 SCM from_key_signature = ly_assoc_get (scm_int2num (n), sig, SCM_BOOL_F);
181 SCM from_other_octaves = SCM_BOOL_F;
182 for (SCM s = sig; scm_is_pair (s); s = scm_cdr (s))
184 SCM entry = scm_car (s);
185 if (scm_is_pair (scm_car (entry))
186 && scm_cdar (entry) == scm_int2num (n))
187 from_other_octaves = scm_cdr (entry);
190 if (from_same_octave != SCM_BOOL_F
191 && recent_enough (bar_number, from_same_octave, laziness))
193 previous_alteration = from_same_octave;
195 else if (ignore_octave
196 && from_other_octaves != SCM_BOOL_F
197 && recent_enough (bar_number, from_other_octaves, laziness))
199 previous_alteration = from_other_octaves;
201 else if (from_key_signature != SCM_BOOL_F)
203 previous_alteration = from_key_signature;
207 if (is_tied (previous_alteration))
214 int prev = extract_alteration (previous_alteration);
215 int alter = pitch->get_alteration ();
219 else if ((abs (alter) < abs (prev) || prev * alter < 0) && alter != 0)
221 *different = (alter != prev);
227 number_accidentals (bool *different,
228 Pitch *pitch, Context *origin,
229 SCM accidentals, int bar_number)
234 if (scm_is_pair (accidentals) && !scm_is_symbol (scm_car (accidentals)))
235 warning (_f ("Accidental typesetting list must begin with context-name: %s",
236 ly_scm2string (scm_car (accidentals)).to_str0 ()));
238 for (; scm_is_pair (accidentals) && origin;
239 accidentals = scm_cdr (accidentals))
241 // If pair then it is a new accidentals typesetting rule to be checked
242 SCM rule = scm_car (accidentals);
243 if (scm_is_pair (rule))
245 SCM type = scm_car (rule);
246 SCM laziness = scm_cdr (rule);
247 SCM localsig = origin->get_property ("localKeySignature");
250 = scm_is_eq (ly_symbol2scm ("same-octave"), type);
252 = scm_is_eq (ly_symbol2scm ("any-octave"), type);
254 if (same_octave_b || any_octave_b)
257 int n = number_accidentals_from_sig
258 (&d, localsig, pitch, bar_number, laziness, any_octave_b);
259 *different = *different || d;
260 number = max (number, n);
263 warning (_f ("ignoring unknown accidental: %s",
264 ly_symbol2string (type).to_str0 ()));
268 if symbol then it is a context name. Scan parent contexts to find it.
270 else if (scm_is_symbol (rule))
272 Context *dad = origin;
273 while (dad && !dad->is_alias (rule))
274 dad = dad->get_parent_context ();
279 else warning (_f ("Accidental rule must be pair or context-name; Found %s",
280 ly_scm2string (rule).to_str0 ()));
287 Accidental_engraver::get_bar_number ()
289 SCM barnum = get_property ("currentBarNumber");
290 SCM smp = get_property ("measurePosition");
292 int bn = robust_scm2int (barnum, 0);
294 Moment mp = robust_scm2moment (smp, Moment (0));
295 if (mp.main_part_ < Rational (0))
302 Accidental_engraver::process_acknowledged_grobs ()
304 if (accidentals_.size () && !accidentals_.top ().done_)
306 SCM accidentals = get_property ("autoAccidentals");
307 SCM cautionaries = get_property ("autoCautionaries");
308 int barnum = get_bar_number ();
310 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
311 for (int i = 0; i < accidentals_.size (); i++)
313 if (accidentals_[i].done_)
315 accidentals_[i].done_ = true;
316 Grob *support = accidentals_[i].head_;
317 Music *note = accidentals_[i].melodic_;
318 Context *origin = accidentals_[i].origin_;
320 Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
324 bool different = false;
325 bool different_caut = false;
327 int num = number_accidentals (&different,
329 accidentals, barnum);
330 int num_caut = number_accidentals (&different_caut,
332 cautionaries, barnum);
334 bool cautionary = to_boolean (note->get_property ("cautionary"));
339 different = different_caut;
343 if (num == 0 && to_boolean (note->get_property ("force-accidental")))
347 Can not look for ties: it's not guaranteed that they reach
354 We construct the accidentals at the originating Voice
355 level, so that we get the property settings for
356 Accidental from the respective Voice.
359 = make_item_from_properties (accidentals_[i].origin_trans_,
360 ly_symbol2scm ("Accidental"),
363 a->set_parent (support, Y_AXIS);
365 if (!accidental_placement_)
366 accidental_placement_ = make_item ("AccidentalPlacement",
368 Accidental_placement::add_accidental (accidental_placement_, a);
369 SCM accs = scm_cons (scm_int2num (pitch->get_alteration ()),
371 if (num == 2 && extra_natural_b)
372 accs = scm_cons (scm_int2num (0), accs);
374 /* TODO: add cautionary option in accidental. */
377 a->set_property ("cautionary", SCM_BOOL_T);
379 support->set_property ("accidental-grob", a->self_scm ());
381 a->set_property ("accidentals", accs);
382 accidentals_[i].accidental_ = a;
385 We add the accidentals to the support of the arpeggio,
386 so it is put left of the accidentals.
388 for (int i = 0; i < left_objects_.size (); i++)
389 Side_position_interface::add_support (left_objects_[i], a);
390 for (int i = 0; i < right_objects_.size (); i++)
391 Side_position_interface::add_support (a, right_objects_[i]);
398 Accidental_engraver::finalize ()
400 last_keysig_ = SCM_EOL;
404 Accidental_engraver::stop_translation_timestep ()
406 for (int j = ties_.size (); j--;)
408 Grob *r = Tie::head (ties_[j], RIGHT);
409 for (int i = accidentals_.size (); i--;)
410 if (accidentals_[i].head_ == r)
412 if (Grob *g = accidentals_[i].accidental_)
414 g->set_property ("tie", ties_[j]->self_scm ());
415 accidentals_[i].tied_ = true;
422 for (int i = accidentals_.size (); i--;)
424 int barnum = get_bar_number ();
426 Music *note = accidentals_[i].melodic_;
427 Context *origin = accidentals_[i].origin_;
429 Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
433 int n = pitch->get_notename ();
434 int o = pitch->get_octave ();
435 int a = pitch->get_alteration ();
436 SCM key = scm_cons (scm_int2num (o), scm_int2num (n));
439 && origin->where_defined (ly_symbol2scm ("localKeySignature")))
442 huh? we set props all the way to the top?
444 SCM localsig = origin->get_property ("localKeySignature");
446 if (accidentals_[i].tied_)
449 Remember an alteration that is different both from
450 that of the tied note and of the key signature.
452 localsig = ly_assoc_front_x
453 (localsig, key, scm_cons (SCM_BOOL_T, scm_int2num (barnum)));
460 not really really correct if there are more than one
461 noteheads with the same notename.
463 localsig = ly_assoc_front_x (localsig, key,
464 scm_cons (scm_int2num (a),
465 scm_int2num (barnum)));
470 origin->set_property ("localKeySignature", localsig);
472 origin = origin->get_parent_context ();
476 accidental_placement_ = 0;
477 accidentals_.clear ();
478 left_objects_.clear ();
479 right_objects_.clear ();
483 Accidental_engraver::acknowledge_grob (Grob_info info)
485 Music *note = info.music_cause ();
488 && note->is_mus_type ("note-event")
489 && Rhythmic_head::has_interface (info.grob_))
491 if (to_boolean (get_property ("harmonicAccidentals"))
492 || !ly_c_equal_p (info.grob_->get_property ("style"),
493 ly_symbol2scm ("harmonic")))
496 Accidental_entry entry;
497 entry.head_ = info.grob_;
498 entry.origin_trans_ = dynamic_cast<Engraver *> (info.origin_trans_);
499 entry.origin_ = info.origin_trans_->context ();
500 entry.melodic_ = note;
502 accidentals_.push (entry);
505 else if (Tie::has_interface (info.grob_))
506 ties_.push (dynamic_cast<Spanner *> (info.grob_));
507 else if (Arpeggio::has_interface (info.grob_))
508 left_objects_.push (info.grob_);
510 ->internal_has_interface (ly_symbol2scm ("finger-interface")))
511 left_objects_.push (info.grob_);
515 Accidental_engraver::process_music ()
517 SCM sig = get_property ("keySignature");
518 /* Detect key sig changes.
519 Update all parents and children. */
520 if (last_keysig_ != sig)
521 update_local_key_signature ();
524 ADD_TRANSLATOR (Accidental_engraver,
526 "Catch note heads, ties and notices key-change events. "
527 "This engraver usually lives at Staff level, but "
528 "reads the settings for Accidental at @code{Voice} level, "
529 "so you can @code{\\override} them at @code{Voice}. ",
532 "arpeggio-interface "
534 "rhythmic-head-interface "
539 "harmonicAccidentals "
541 "localKeySignature");