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 ();
63 virtual void derived_mark () const;
65 SCM last_keysig_; // ugh.
67 /* Urgh. Since the accidentals depend on lots of variables, we have
68 to store all information before we can really create the
70 Link_array<Grob> left_objects_;
71 Link_array<Grob> right_objects_;
73 Grob *accidental_placement_;
75 Array<Accidental_entry> accidentals_;
76 Link_array<Spanner> ties_;
83 ugh, it is not clear what properties are mutable and which
84 aren't. eg. localKeySignature is changed at runtime, which means
85 that references in grobs should always store ly_deep_copy ()s of
90 set_property_on_children (Context *trans, char const *sym, SCM val)
92 trans->set_property (sym, ly_deep_copy (val));
93 for (SCM p = trans->children_contexts (); scm_is_pair (p); p = scm_cdr (p))
95 Context *trg = unsmob_context (scm_car (p));
96 set_property_on_children (trg, sym, ly_deep_copy (val));
100 Accidental_engraver::Accidental_engraver ()
102 accidental_placement_ = 0;
103 last_keysig_ = SCM_EOL;
107 Accidental_engraver::derived_mark () const
109 scm_gc_mark (last_keysig_);
113 Accidental_engraver::update_local_key_signature ()
115 last_keysig_ = get_property ("keySignature");
116 set_property_on_children (context (), "localKeySignature", last_keysig_);
118 Context *trans = context ()->get_parent_context ();
120 /* Huh. Don't understand what this is good for. --hwn. */
121 while (trans && trans->where_defined (ly_symbol2scm ("localKeySignature")))
123 trans->set_property ("localKeySignature", ly_deep_copy (last_keysig_));
124 trans = trans->get_parent_context ();
129 Accidental_engraver::initialize ()
131 update_local_key_signature ();
135 /** Calculate the number of accidentals on basis of the current local key
136 sig (passed as argument)
138 * First check step+octave (taking into account barnumbers if necessary).
140 * Then check the global signature (only step).
142 Return number of accidentals (0, 1 or 2). */
145 recent_enough (int bar_number, SCM alteration_def, SCM laziness)
147 if (scm_is_number (alteration_def)
148 || laziness== SCM_BOOL_T)
151 return (bar_number <= scm_to_int (scm_cdr (alteration_def)) + scm_to_int (laziness));
155 extract_alteration (SCM alteration_def)
157 if (scm_is_number (alteration_def))
158 return scm_to_int (alteration_def);
159 else if (scm_is_pair (alteration_def))
160 return scm_to_int (scm_car (alteration_def));
161 else if (alteration_def == SCM_BOOL_F)
169 is_tied (SCM alteration_def)
171 return (alteration_def == SCM_BOOL_T)
172 || (scm_is_pair (alteration_def) && scm_car (alteration_def) == SCM_BOOL_T);
176 number_accidentals_from_sig (bool *different, SCM sig, Pitch *pitch,
177 int bar_number, SCM laziness, bool ignore_octave)
179 int n = pitch->get_notename ();
180 int o = pitch->get_octave ();
182 SCM previous_alteration = SCM_BOOL_F;
185 SCM from_same_octave = ly_assoc_get (scm_cons (scm_int2num (o),
186 scm_int2num (n)), sig, SCM_BOOL_F);
187 SCM from_key_signature = ly_assoc_get (scm_int2num (n), sig, SCM_BOOL_F);
188 SCM from_other_octaves = SCM_BOOL_F;
189 for (SCM s = sig ; scm_is_pair (s); s = scm_cdr (s))
191 SCM entry = scm_car (s);
192 if (scm_is_pair (scm_car (entry))
193 && scm_cdar (entry) == scm_int2num (n))
194 from_other_octaves = scm_cdr (entry);
198 if (from_same_octave != SCM_BOOL_F
199 && recent_enough (bar_number, from_same_octave, laziness))
201 previous_alteration = from_same_octave;
203 else if (ignore_octave
204 && from_other_octaves != SCM_BOOL_F
205 && recent_enough (bar_number, from_other_octaves, laziness))
207 previous_alteration = from_other_octaves;
209 else if (from_key_signature != SCM_BOOL_F)
211 previous_alteration = from_key_signature;
215 if (is_tied (previous_alteration))
222 int prev = extract_alteration (previous_alteration);
223 int alter = pitch->get_alteration ();
227 else if ((abs (alter) < abs (prev) || prev*alter < 0) && alter != 0)
229 *different = (alter != prev);
235 number_accidentals (bool *different,
236 Pitch *pitch, Context *origin,
237 SCM accidentals, int bar_number)
242 if (scm_is_pair (accidentals) && !scm_is_symbol (scm_car (accidentals)))
243 warning (_f ("Accidental typesetting list must begin with context-name: %s",
244 ly_scm2string (scm_car (accidentals)).to_str0 ()));
246 for (; scm_is_pair (accidentals) && origin;
247 accidentals = scm_cdr (accidentals))
249 // If pair then it is a new accidentals typesetting rule to be checked
250 SCM rule = scm_car (accidentals);
251 if (scm_is_pair (rule))
253 SCM type = scm_car (rule);
254 SCM laziness = scm_cdr (rule);
255 SCM localsig = origin->get_property ("localKeySignature");
258 scm_is_eq (ly_symbol2scm ("same-octave"), type);
260 scm_is_eq (ly_symbol2scm ("any-octave"), type);
262 if (same_octave_b || any_octave_b)
265 int n = number_accidentals_from_sig
266 (&d, localsig, pitch, bar_number, laziness, any_octave_b);
267 *different = *different || d;
268 number = max (number, n);
271 warning (_f ("ignoring unknown accidental: %s",
272 ly_symbol2string (type).to_str0 ()));
277 if symbol then it is a context name. Scan parent contexts to find it.
279 else if (scm_is_symbol (rule))
281 Context *dad = origin;
282 while (dad && !dad->is_alias (rule))
283 dad = dad->get_parent_context ();
288 else warning (_f ("Accidental rule must be pair or context-name; Found %s",
289 ly_scm2string (rule).to_str0 ()));
296 Accidental_engraver::get_bar_number ()
298 SCM barnum = get_property ("currentBarNumber");
299 SCM smp = get_property ("measurePosition");
301 int bn = robust_scm2int (barnum, 0);
303 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
304 if (mp.main_part_ < Rational (0))
311 Accidental_engraver::process_acknowledged_grobs ()
313 if (accidentals_.size () && !accidentals_.top ().done_)
315 SCM accidentals = get_property ("autoAccidentals");
316 SCM cautionaries = get_property ("autoCautionaries");
317 int barnum = get_bar_number ();
319 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
320 for (int i = 0; i < accidentals_.size (); i++)
322 if (accidentals_[i].done_ )
324 accidentals_[i].done_ = true;
325 Grob *support = accidentals_[i].head_;
326 Music *note = accidentals_[i].melodic_;
327 Context *origin = accidentals_[i].origin_;
329 Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
333 bool different = false;
334 bool different_caut = false;
336 int num = number_accidentals (&different,
338 accidentals, barnum);
339 int num_caut = number_accidentals (&different_caut,
341 cautionaries, barnum);
343 bool cautionary = to_boolean (note->get_property ("cautionary"));
348 different = different_caut;
352 if (num == 0 && to_boolean (note->get_property ("force-accidental")))
357 Can not look for ties: it's not guaranteed that they reach
364 We construct the accidentals at the originating Voice
365 level, so that we get the property settings for
366 Accidental from the respective Voice.
369 = make_item_from_properties (accidentals_[i].origin_trans_,
370 ly_symbol2scm ("Accidental"),
372 a->set_parent (support, Y_AXIS);
374 if (!accidental_placement_)
375 accidental_placement_ = make_item ("AccidentalPlacement",
377 Accidental_placement::add_accidental (accidental_placement_, a);
378 SCM accs = scm_cons (scm_int2num (pitch->get_alteration ()),
380 if (num == 2 && extra_natural_b)
381 accs = scm_cons (scm_int2num (0), accs);
383 /* TODO: add cautionary option in accidental. */
386 a->set_property ("cautionary", SCM_BOOL_T);
388 support->set_property ("accidental-grob", a->self_scm ());
390 a->set_property ("accidentals", accs);
391 accidentals_[i].accidental_ = a;
394 We add the accidentals to the support of the arpeggio,
395 so it is put left of the accidentals.
397 for (int i = 0; i < left_objects_.size (); i++)
398 Side_position_interface::add_support (left_objects_[i], a);
399 for (int i = 0; i < right_objects_.size (); i++)
400 Side_position_interface::add_support (a, right_objects_[i]);
407 Accidental_engraver::finalize ()
409 last_keysig_ = SCM_EOL;
413 Accidental_engraver::stop_translation_timestep ()
415 for (int j = ties_.size (); j--;)
417 Grob *r = Tie::head (ties_[j], RIGHT);
418 for (int i = accidentals_.size (); i--;)
419 if (accidentals_[i].head_ == r)
421 if (Grob *g = accidentals_[i].accidental_)
423 g->set_property ("tie", ties_[j]->self_scm ());
424 accidentals_[i].tied_ = true;
431 for (int i = accidentals_.size (); i--;)
433 int barnum = get_bar_number ();
435 Music *note = accidentals_[i].melodic_;
436 Context * origin = accidentals_[i].origin_;
438 Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
442 int n = pitch->get_notename ();
443 int o = pitch->get_octave ();
444 int a = pitch->get_alteration ();
445 SCM key = scm_cons (scm_int2num (o), scm_int2num (n));
448 && origin->where_defined (ly_symbol2scm ("localKeySignature")))
451 huh? we set props all the way to the top?
453 SCM localsig = origin->get_property ("localKeySignature");
455 if (accidentals_[i].tied_)
458 Remember an alteration that is different both from
459 that of the tied note and of the key signature.
461 localsig = ly_assoc_front_x
462 (localsig, key, scm_cons (SCM_BOOL_T, scm_int2num (barnum)));
469 not really really correct if there are more than one
470 noteheads with the same notename.
472 localsig = ly_assoc_front_x (localsig, key,
473 scm_cons (scm_int2num (a),
474 scm_int2num (barnum)));
479 origin->set_property ("localKeySignature", localsig);
481 origin = origin->get_parent_context ();
485 accidental_placement_ = 0;
486 accidentals_.clear ();
487 left_objects_.clear ();
488 right_objects_.clear ();
492 Accidental_engraver::acknowledge_grob (Grob_info info)
494 Music *note = info.music_cause ();
497 && note->is_mus_type ("note-event")
498 && Rhythmic_head::has_interface (info.grob_))
500 if (to_boolean ( get_property ("harmonicAccidentals"))
501 || !ly_c_equal_p (info.grob_->get_property ("style"),
502 ly_symbol2scm ("harmonic")))
505 Accidental_entry entry ;
506 entry.head_ = info.grob_;
507 entry.origin_trans_ = dynamic_cast<Engraver*> (info.origin_trans_);
508 entry.origin_ = info.origin_trans_->context ();
509 entry.melodic_ = note;
511 accidentals_.push (entry);
514 else if (Tie::has_interface (info.grob_))
515 ties_.push (dynamic_cast<Spanner*> (info.grob_));
516 else if (Arpeggio::has_interface (info.grob_))
517 left_objects_.push (info.grob_);
519 ->internal_has_interface (ly_symbol2scm ("finger-interface")))
520 left_objects_.push (info.grob_);
524 Accidental_engraver::process_music ()
526 SCM sig = get_property ("keySignature");
527 /* Detect key sig changes.
528 Update all parents and children. */
529 if (last_keysig_ != sig)
530 update_local_key_signature ();
533 ENTER_DESCRIPTION (Accidental_engraver,
535 "Catch note heads, ties and notices key-change events. "
536 "This engraver usually lives at Staff level, but "
537 "reads the settings for Accidental at @code{Voice} level, "
538 "so you can @code{\\override} them at @code{Voice}. "
544 "arpeggio-interface "
546 "rhythmic-head-interface "
552 "harmonicAccidentals "