2 accidental-engraver.cc -- implement accidental_engraver
4 (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
5 Modified 2001--2002 by Rune Zedeler <rz@daimi.au.dk>
12 #include "rhythmic-head.hh"
13 #include "engraver-group-engraver.hh"
14 #include "accidental-placement.hh"
15 #include "side-position-interface.hh"
16 #include "engraver.hh"
17 #include "arpeggio.hh"
20 #include "protected-scm.hh"
23 struct Accidental_entry {
33 Accidental_entry::Accidental_entry ()
43 struct Accidental_engraver : Engraver {
46 int get_bar_number ();
47 void update_local_key_signature ();
50 TRANSLATOR_DECLARATIONS (Accidental_engraver);
51 virtual void process_music ();
52 virtual void acknowledge_grob (Grob_info);
53 virtual void stop_translation_timestep ();
54 virtual void initialize ();
55 virtual void process_acknowledged_grobs ();
56 virtual void finalize ();
59 Protected_scm last_keysig_; // ugh.
62 Urgh. Since the accidentals depend on lots of variables, we have to
63 store all information before we can really create the accidentals.
65 Link_array<Grob> left_objects_;
66 Link_array<Grob> right_objects_;
68 Grob * accidental_placement_;
70 Array<Accidental_entry> accidentals_;
71 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, const char * sym, SCM val)
87 trans->set_property (sym, ly_deep_copy (val));
88 for (SCM p = trans->children_contexts (); ly_c_pair_p (p); p = ly_cdr (p))
90 Context *trg = unsmob_context (ly_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;
103 Accidental_engraver::update_local_key_signature ()
105 last_keysig_ = get_property ("keySignature");
106 set_property_on_children (context (), "localKeySignature", last_keysig_);
108 Context * trans = context ()->get_parent_context ();
111 Huh. Don't understand what this is good for. --hwn.
113 while (trans && trans->where_defined (ly_symbol2scm ("localKeySignature")))
115 trans->set_property ("localKeySignature",
116 ly_deep_copy (last_keysig_));
117 trans = trans->get_parent_context ();
122 Accidental_engraver::initialize ()
124 update_local_key_signature ();
129 Calculates 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).
138 Returns number of accidentals (0, 1 or 2).
143 number_accidentals_from_sig (bool *different,
144 SCM sig, Pitch *pitch, int curbarnum, SCM laziness,
147 int n = pitch->get_notename ();
148 int o = pitch->get_octave ();
149 int a = pitch->get_alteration ();
151 SCM prev_alt = SCM_BOOL_F;
156 = scm_assoc (scm_cons (scm_int2num (o), scm_int2num (n)), sig);
158 if (ly_c_pair_p (prev_local))
160 if (ly_c_pair_p (ly_cdr (prev_local))
161 && ly_c_number_p (laziness)
164 int barnum = ly_scm2int (ly_cddr (prev_local));
166 prev_local = scm_cons (ly_car (prev_local), ly_cadr (prev_local));
167 if (curbarnum <= barnum + ly_scm2int (laziness))
168 prev_alt = prev_local;
173 if (prev_alt == SCM_BOOL_F)
174 prev_alt = scm_assoc (scm_int2num (n), sig);
177 prev_alt = (prev_alt == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev_alt);
180 UGH. prev_acc can be #t in case of ties. What is this for?
183 int p = ly_c_number_p (prev_alt) ? ly_scm2int (prev_alt) : 0;
186 if (a == p && ly_c_number_p (prev_alt))
188 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
193 *different = (a != p);
198 number_accidentals (bool *different,
199 Pitch *pitch, Context * origin,
200 SCM accidentals, int curbarnum)
205 if (ly_c_pair_p (accidentals) && !ly_c_symbol_p (ly_car (accidentals)))
206 warning (_f ("Accidental typesetting list must begin with context-name: %s",
207 ly_scm2string (ly_car (accidentals)).to_str0 ()));
209 for (; ly_c_pair_p (accidentals) && origin; accidentals = ly_cdr (accidentals))
211 // If pair then it is a new accidentals typesetting rule to be checked
212 SCM rule = ly_car (accidentals);
213 if (ly_c_pair_p (rule))
215 SCM type = ly_car (rule);
216 SCM laziness = ly_cdr (rule);
217 SCM localsig = origin->get_property ("localKeySignature");
220 ly_c_eq_p (ly_symbol2scm ("same-octave"), type);
222 ly_c_eq_p (ly_symbol2scm ("any-octave"), type);
224 if (same_octave_b || any_octave_b)
227 int n = number_accidentals_from_sig
228 (&d, localsig, pitch, curbarnum, laziness, any_octave_b);
229 *different = *different || d;
230 number = max (number, n);
233 warning (_f ("ignoring unknown accidental: %s",
234 ly_symbol2string (type).to_str0 ()));
239 if symbol then it is a context name. Scan parent contexts to find it.
241 else if (ly_c_symbol_p (rule))
243 Context * dad = origin;
244 while (dad && !dad->is_alias (rule))
245 dad = dad->get_parent_context ();
250 else warning (_f ("Accidental rule must be pair or context-name; Found %s",
251 ly_scm2string (rule).to_str0 ()));
258 Accidental_engraver::get_bar_number ()
260 SCM barnum = get_property ("currentBarNumber");
261 SCM smp = get_property ("measurePosition");
263 int bn = robust_scm2int (barnum, 0);
265 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
266 if (mp.main_part_ < Rational (0))
273 Accidental_engraver::process_acknowledged_grobs ()
275 if (accidentals_.size () && !accidentals_.top ().done_)
277 SCM accidentals = get_property ("autoAccidentals");
278 SCM cautionaries = get_property ("autoCautionaries");
279 int barnum = get_bar_number ();
281 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
282 for (int i = 0; i < accidentals_.size (); i++)
284 if (accidentals_[i].done_ )
286 accidentals_[i].done_ = true;
287 Grob * support = accidentals_[i].head_;
288 Music * note = accidentals_[i].melodic_;
289 Context * origin = accidentals_[i].origin_;
291 Pitch * pitch = unsmob_pitch (note->get_property ("pitch"));
295 bool different = false;
296 bool different_caut = false;
298 int num = number_accidentals (&different,
300 accidentals, barnum);
301 int num_caut = number_accidentals (&different_caut,
303 cautionaries, barnum);
305 bool cautionary = to_boolean (note->get_property ("cautionary"));
310 different = different_caut;
314 if (num == 0 && to_boolean (note->get_property ("force-accidental")))
319 Can not look for ties: it's not guaranteed that they reach
326 We construct the accidentals at the originating Voice
327 level, so that we get the property settings for
328 Accidental from the respective Voice.
330 Grob * a = make_item_from_properties (origin->implementation (),
331 ly_symbol2scm ("Accidental"),
334 a->set_parent (support, Y_AXIS);
336 if (!accidental_placement_)
338 accidental_placement_ = make_item ("AccidentalPlacement", a->self_scm ());
341 Accidental_placement::add_accidental (accidental_placement_, a);
344 SCM accs = scm_cons (scm_int2num (pitch->get_alteration ()), SCM_EOL);
345 if (num == 2 && extra_natural_b)
346 accs = scm_cons (scm_int2num (0), accs);
350 add cautionary option in accidental.
355 a->set_property ("cautionary", SCM_BOOL_T);
359 support->set_property ("accidental-grob", a->self_scm ());
361 a->set_property ("accidentals", accs);
362 accidentals_[i].accidental_ = a;
365 We add the accidentals to the support of the arpeggio,
366 so it is put left of the accidentals.
368 for (int i = 0; i < left_objects_.size (); i++)
369 Side_position_interface::add_support (left_objects_[i], a);
370 for (int i = 0; i < right_objects_.size (); i++)
371 Side_position_interface::add_support (a, right_objects_[i]);
381 Accidental_engraver::finalize ()
383 last_keysig_ = SCM_EOL;
387 Accidental_engraver::stop_translation_timestep ()
389 for (int j = ties_.size (); j --; )
391 Grob * r = Tie::head (ties_[j], RIGHT);
392 for (int i = accidentals_.size (); i--;)
393 if (accidentals_[i].head_ == r)
395 if (Grob * g = accidentals_[i].accidental_)
397 g->set_property ("tie", ties_[j]->self_scm ());
398 accidentals_[i].tied_ = true;
406 for (int i = accidentals_.size (); i--;)
408 int barnum = get_bar_number ();
410 Music * note = accidentals_[i].melodic_;
411 Context * origin = accidentals_[i].origin_;
413 Pitch * pitch = unsmob_pitch (note->get_property ("pitch"));
417 int n = pitch->get_notename ();
418 int o = pitch->get_octave ();
419 int a = pitch->get_alteration ();
420 SCM key = scm_cons (scm_int2num (o), scm_int2num (n));
422 while (origin && origin->where_defined (ly_symbol2scm ("localKeySignature")))
425 huh? we set props all the way to the top?
427 SCM localsig = origin->get_property ("localKeySignature");
429 if (accidentals_[i].tied_)
432 Remember an alteration that is different both from
433 that of the tied note and of the key signature.
435 localsig = ly_assoc_front_x
436 (localsig, key, scm_cons (SCM_BOOL_T, scm_int2num (barnum)));
443 not really really correct if there are more than one
444 noteheads with the same notename.
446 localsig = ly_assoc_front_x
447 (localsig, key, scm_cons (scm_int2num (a), scm_int2num (barnum)));
453 origin->set_property ("localKeySignature", localsig);
455 origin = origin->get_parent_context ();
460 accidental_placement_ = 0;
462 accidentals_.clear ();
463 left_objects_.clear ();
464 right_objects_.clear ();
468 Accidental_engraver::acknowledge_grob (Grob_info info)
470 Music * note = info.music_cause ();
473 && note->is_mus_type ("note-event")
474 && Rhythmic_head::has_interface (info.grob_))
476 if (to_boolean ( get_property ("harmonicAccidentals"))
477 || !ly_c_equal_p (info.grob_->get_property ("style"),
478 ly_symbol2scm ("harmonic")))
481 Accidental_entry entry ;
482 entry.head_ = info.grob_;
483 entry.origin_ = info.origin_trans_->context ();
484 entry.melodic_ = note;
486 accidentals_.push (entry);
489 else if (Tie::has_interface (info.grob_))
491 ties_.push (dynamic_cast<Spanner*> (info.grob_));
493 else if (Arpeggio::has_interface (info.grob_))
495 left_objects_.push (info.grob_);
497 else if (info.grob_->internal_has_interface (ly_symbol2scm ("finger-interface")))
499 left_objects_.push (info.grob_);
504 Accidental_engraver::process_music ()
506 SCM sig = get_property ("keySignature");
508 /* Detect key sig changes.
509 Update all parents and children
511 if (last_keysig_ != sig)
513 update_local_key_signature ();
521 ENTER_DESCRIPTION (Accidental_engraver,
522 "Make accidentals. Catches note heads, ties and notices key-change "
523 "events. This engraver usually lives at Staff level, but "
524 "reads the settings for Accidental at @code{Voice} level, "
525 "so you can @code{\\override} them at @code{Voice}. "
530 "finger-interface rhythmic-head-interface tie-interface arpeggio-interface",
531 "localKeySignature harmonicAccidentals extraNatural autoAccidentals autoCautionaries",
532 "localKeySignature");