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 (); ly_c_pair_p (p); p = ly_cdr (p))
95 Context *trg = unsmob_context (ly_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). */
144 number_accidentals_from_sig (bool *different, SCM sig, Pitch *pitch,
145 int curbarnum, SCM laziness, bool ignore_octave)
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 && scm_is_number (laziness))
163 int barnum = scm_to_int (ly_cddr (prev_local));
165 prev_local = scm_cons (ly_car (prev_local), ly_cadr (prev_local));
166 if (curbarnum <= barnum + scm_to_int (laziness))
167 prev_alt = prev_local;
172 if (prev_alt == SCM_BOOL_F)
173 prev_alt = scm_assoc (scm_int2num (n), sig);
175 prev_alt = (prev_alt == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev_alt);
177 /* UGH. prev_acc can be #t in case of ties. What is this for? */
178 int p = scm_is_number (prev_alt) ? scm_to_int (prev_alt) : 0;
181 if (a == p && scm_is_number (prev_alt))
183 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
188 *different = (a != p);
193 number_accidentals (bool *different,
194 Pitch *pitch, Context *origin,
195 SCM accidentals, int curbarnum)
200 if (ly_c_pair_p (accidentals) && !scm_is_symbol (ly_car (accidentals)))
201 warning (_f ("Accidental typesetting list must begin with context-name: %s",
202 ly_scm2string (ly_car (accidentals)).to_str0 ()));
204 for (; ly_c_pair_p (accidentals) && origin;
205 accidentals = ly_cdr (accidentals))
207 // If pair then it is a new accidentals typesetting rule to be checked
208 SCM rule = ly_car (accidentals);
209 if (ly_c_pair_p (rule))
211 SCM type = ly_car (rule);
212 SCM laziness = ly_cdr (rule);
213 SCM localsig = origin->get_property ("localKeySignature");
216 scm_is_eq (ly_symbol2scm ("same-octave"), type);
218 scm_is_eq (ly_symbol2scm ("any-octave"), type);
220 if (same_octave_b || any_octave_b)
223 int n = number_accidentals_from_sig
224 (&d, localsig, pitch, curbarnum, laziness, any_octave_b);
225 *different = *different || d;
226 number = max (number, n);
229 warning (_f ("ignoring unknown accidental: %s",
230 ly_symbol2string (type).to_str0 ()));
235 if symbol then it is a context name. Scan parent contexts to find it.
237 else if (scm_is_symbol (rule))
239 Context *dad = origin;
240 while (dad && !dad->is_alias (rule))
241 dad = dad->get_parent_context ();
246 else warning (_f ("Accidental rule must be pair or context-name; Found %s",
247 ly_scm2string (rule).to_str0 ()));
254 Accidental_engraver::get_bar_number ()
256 SCM barnum = get_property ("currentBarNumber");
257 SCM smp = get_property ("measurePosition");
259 int bn = robust_scm2int (barnum, 0);
261 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
262 if (mp.main_part_ < Rational (0))
269 Accidental_engraver::process_acknowledged_grobs ()
271 if (accidentals_.size () && !accidentals_.top ().done_)
273 SCM accidentals = get_property ("autoAccidentals");
274 SCM cautionaries = get_property ("autoCautionaries");
275 int barnum = get_bar_number ();
277 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
278 for (int i = 0; i < accidentals_.size (); i++)
280 if (accidentals_[i].done_ )
282 accidentals_[i].done_ = true;
283 Grob *support = accidentals_[i].head_;
284 Music *note = accidentals_[i].melodic_;
285 Context *origin = accidentals_[i].origin_;
287 Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
291 bool different = false;
292 bool different_caut = false;
294 int num = number_accidentals (&different,
296 accidentals, barnum);
297 int num_caut = number_accidentals (&different_caut,
299 cautionaries, barnum);
301 bool cautionary = to_boolean (note->get_property ("cautionary"));
306 different = different_caut;
310 if (num == 0 && to_boolean (note->get_property ("force-accidental")))
315 Can not look for ties: it's not guaranteed that they reach
322 We construct the accidentals at the originating Voice
323 level, so that we get the property settings for
324 Accidental from the respective Voice.
327 = make_item_from_properties (accidentals_[i].origin_trans_,
328 ly_symbol2scm ("Accidental"),
330 a->set_parent (support, Y_AXIS);
332 if (!accidental_placement_)
333 accidental_placement_ = make_item ("AccidentalPlacement",
335 Accidental_placement::add_accidental (accidental_placement_, a);
336 SCM accs = scm_cons (scm_int2num (pitch->get_alteration ()),
338 if (num == 2 && extra_natural_b)
339 accs = scm_cons (scm_int2num (0), accs);
341 /* TODO: add cautionary option in accidental. */
344 a->set_property ("cautionary", SCM_BOOL_T);
346 support->set_property ("accidental-grob", a->self_scm ());
348 a->set_property ("accidentals", accs);
349 accidentals_[i].accidental_ = a;
352 We add the accidentals to the support of the arpeggio,
353 so it is put left of the accidentals.
355 for (int i = 0; i < left_objects_.size (); i++)
356 Side_position_interface::add_support (left_objects_[i], a);
357 for (int i = 0; i < right_objects_.size (); i++)
358 Side_position_interface::add_support (a, right_objects_[i]);
365 Accidental_engraver::finalize ()
367 last_keysig_ = SCM_EOL;
371 Accidental_engraver::stop_translation_timestep ()
373 for (int j = ties_.size (); j--;)
375 Grob *r = Tie::head (ties_[j], RIGHT);
376 for (int i = accidentals_.size (); i--;)
377 if (accidentals_[i].head_ == r)
379 if (Grob *g = accidentals_[i].accidental_)
381 g->set_property ("tie", ties_[j]->self_scm ());
382 accidentals_[i].tied_ = true;
389 for (int i = accidentals_.size (); i--;)
391 int barnum = get_bar_number ();
393 Music *note = accidentals_[i].melodic_;
394 Context * origin = accidentals_[i].origin_;
396 Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
400 int n = pitch->get_notename ();
401 int o = pitch->get_octave ();
402 int a = pitch->get_alteration ();
403 SCM key = scm_cons (scm_int2num (o), scm_int2num (n));
406 && origin->where_defined (ly_symbol2scm ("localKeySignature")))
409 huh? we set props all the way to the top?
411 SCM localsig = origin->get_property ("localKeySignature");
413 if (accidentals_[i].tied_)
416 Remember an alteration that is different both from
417 that of the tied note and of the key signature.
419 localsig = ly_assoc_front_x
420 (localsig, key, scm_cons (SCM_BOOL_T, scm_int2num (barnum)));
427 not really really correct if there are more than one
428 noteheads with the same notename.
430 localsig = ly_assoc_front_x (localsig, key,
431 scm_cons (scm_int2num (a),
432 scm_int2num (barnum)));
437 origin->set_property ("localKeySignature", localsig);
439 origin = origin->get_parent_context ();
443 accidental_placement_ = 0;
444 accidentals_.clear ();
445 left_objects_.clear ();
446 right_objects_.clear ();
450 Accidental_engraver::acknowledge_grob (Grob_info info)
452 Music *note = info.music_cause ();
455 && note->is_mus_type ("note-event")
456 && Rhythmic_head::has_interface (info.grob_))
458 if (to_boolean ( get_property ("harmonicAccidentals"))
459 || !ly_c_equal_p (info.grob_->get_property ("style"),
460 ly_symbol2scm ("harmonic")))
463 Accidental_entry entry ;
464 entry.head_ = info.grob_;
465 entry.origin_trans_ = dynamic_cast<Engraver*> (info.origin_trans_);
466 entry.origin_ = info.origin_trans_->context ();
467 entry.melodic_ = note;
469 accidentals_.push (entry);
472 else if (Tie::has_interface (info.grob_))
473 ties_.push (dynamic_cast<Spanner*> (info.grob_));
474 else if (Arpeggio::has_interface (info.grob_))
475 left_objects_.push (info.grob_);
477 ->internal_has_interface (ly_symbol2scm ("finger-interface")))
478 left_objects_.push (info.grob_);
482 Accidental_engraver::process_music ()
484 SCM sig = get_property ("keySignature");
485 /* Detect key sig changes.
486 Update all parents and children. */
487 if (last_keysig_ != sig)
488 update_local_key_signature ();
491 ENTER_DESCRIPTION (Accidental_engraver,
493 "Catch note heads, ties and notices key-change events. "
494 "This engraver usually lives at Staff level, but "
495 "reads the settings for Accidental at @code{Voice} level, "
496 "so you can @code{\\override} them at @code{Voice}. "
502 "arpeggio-interface "
504 "rhythmic-head-interface "
510 "harmonicAccidentals "