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-group-engraver.hh"
14 #include "engraver.hh"
17 #include "protected-scm.hh"
18 #include "rhythmic-head.hh"
19 #include "side-position-interface.hh"
25 class Accidental_entry
32 Engraver *origin_trans_;
39 Accidental_entry::Accidental_entry ()
49 class Accidental_engraver : public Engraver
52 int get_bar_number ();
53 void update_local_key_signature ();
56 TRANSLATOR_DECLARATIONS (Accidental_engraver);
57 virtual void process_music ();
58 virtual void acknowledge_grob (Grob_info);
59 virtual void stop_translation_timestep ();
60 virtual void initialize ();
61 virtual void process_acknowledged_grobs ();
62 virtual void finalize ();
65 Protected_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::update_local_key_signature ()
109 last_keysig_ = get_property ("keySignature");
110 set_property_on_children (context (), "localKeySignature", last_keysig_);
112 Context *trans = context ()->get_parent_context ();
114 /* Huh. Don't understand what this is good for. --hwn. */
115 while (trans && trans->where_defined (ly_symbol2scm ("localKeySignature")))
117 trans->set_property ("localKeySignature", ly_deep_copy (last_keysig_));
118 trans = trans->get_parent_context ();
123 Accidental_engraver::initialize ()
125 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). */
138 number_accidentals_from_sig (bool *different, SCM sig, Pitch *pitch,
139 int curbarnum, SCM laziness, bool ignore_octave)
141 int n = pitch->get_notename ();
142 int o = pitch->get_octave ();
143 int a = pitch->get_alteration ();
145 SCM prev_alt = SCM_BOOL_F;
150 = scm_assoc (scm_cons (scm_int2num (o), scm_int2num (n)), sig);
152 if (ly_c_pair_p (prev_local))
154 if (ly_c_pair_p (ly_cdr (prev_local))
155 && ly_c_number_p (laziness))
157 int barnum = ly_scm2int (ly_cddr (prev_local));
159 prev_local = scm_cons (ly_car (prev_local), ly_cadr (prev_local));
160 if (curbarnum <= barnum + ly_scm2int (laziness))
161 prev_alt = prev_local;
166 if (prev_alt == SCM_BOOL_F)
167 prev_alt = scm_assoc (scm_int2num (n), sig);
169 prev_alt = (prev_alt == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev_alt);
171 /* UGH. prev_acc can be #t in case of ties. What is this for? */
172 int p = ly_c_number_p (prev_alt) ? ly_scm2int (prev_alt) : 0;
175 if (a == p && ly_c_number_p (prev_alt))
177 else if ( (abs (a)<abs (p) || p*a<0) && a != 0 )
182 *different = (a != p);
187 number_accidentals (bool *different,
188 Pitch *pitch, Context *origin,
189 SCM accidentals, int curbarnum)
194 if (ly_c_pair_p (accidentals) && !ly_c_symbol_p (ly_car (accidentals)))
195 warning (_f ("Accidental typesetting list must begin with context-name: %s",
196 ly_scm2string (ly_car (accidentals)).to_str0 ()));
198 for (; ly_c_pair_p (accidentals) && origin;
199 accidentals = ly_cdr (accidentals))
201 // If pair then it is a new accidentals typesetting rule to be checked
202 SCM rule = ly_car (accidentals);
203 if (ly_c_pair_p (rule))
205 SCM type = ly_car (rule);
206 SCM laziness = ly_cdr (rule);
207 SCM localsig = origin->get_property ("localKeySignature");
210 ly_c_eq_p (ly_symbol2scm ("same-octave"), type);
212 ly_c_eq_p (ly_symbol2scm ("any-octave"), type);
214 if (same_octave_b || any_octave_b)
217 int n = number_accidentals_from_sig
218 (&d, localsig, pitch, curbarnum, laziness, any_octave_b);
219 *different = *different || d;
220 number = max (number, n);
223 warning (_f ("ignoring unknown accidental: %s",
224 ly_symbol2string (type).to_str0 ()));
229 if symbol then it is a context name. Scan parent contexts to find it.
231 else if (ly_c_symbol_p (rule))
233 Context *dad = origin;
234 while (dad && !dad->is_alias (rule))
235 dad = dad->get_parent_context ();
240 else warning (_f ("Accidental rule must be pair or context-name; Found %s",
241 ly_scm2string (rule).to_str0 ()));
248 Accidental_engraver::get_bar_number ()
250 SCM barnum = get_property ("currentBarNumber");
251 SCM smp = get_property ("measurePosition");
253 int bn = robust_scm2int (barnum, 0);
255 Moment mp = (unsmob_moment (smp)) ? *unsmob_moment (smp) : Moment (0);
256 if (mp.main_part_ < Rational (0))
263 Accidental_engraver::process_acknowledged_grobs ()
265 if (accidentals_.size () && !accidentals_.top ().done_)
267 SCM accidentals = get_property ("autoAccidentals");
268 SCM cautionaries = get_property ("autoCautionaries");
269 int barnum = get_bar_number ();
271 bool extra_natural_b = get_property ("extraNatural") == SCM_BOOL_T;
272 for (int i = 0; i < accidentals_.size (); i++)
274 if (accidentals_[i].done_ )
276 accidentals_[i].done_ = true;
277 Grob *support = accidentals_[i].head_;
278 Music *note = accidentals_[i].melodic_;
279 Context *origin = accidentals_[i].origin_;
281 Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
285 bool different = false;
286 bool different_caut = false;
288 int num = number_accidentals (&different,
290 accidentals, barnum);
291 int num_caut = number_accidentals (&different_caut,
293 cautionaries, barnum);
295 bool cautionary = to_boolean (note->get_property ("cautionary"));
300 different = different_caut;
304 if (num == 0 && to_boolean (note->get_property ("force-accidental")))
309 Can not look for ties: it's not guaranteed that they reach
316 We construct the accidentals at the originating Voice
317 level, so that we get the property settings for
318 Accidental from the respective Voice.
321 = make_item_from_properties (accidentals_[i].origin_trans_,
322 ly_symbol2scm ("Accidental"),
324 a->set_parent (support, Y_AXIS);
326 if (!accidental_placement_)
327 accidental_placement_ = make_item ("AccidentalPlacement",
329 Accidental_placement::add_accidental (accidental_placement_, a);
330 SCM accs = scm_cons (scm_int2num (pitch->get_alteration ()),
332 if (num == 2 && extra_natural_b)
333 accs = scm_cons (scm_int2num (0), accs);
335 /* TODO: add cautionary option in accidental. */
338 a->set_property ("cautionary", SCM_BOOL_T);
340 support->set_property ("accidental-grob", a->self_scm ());
342 a->set_property ("accidentals", accs);
343 accidentals_[i].accidental_ = a;
346 We add the accidentals to the support of the arpeggio,
347 so it is put left of the accidentals.
349 for (int i = 0; i < left_objects_.size (); i++)
350 Side_position_interface::add_support (left_objects_[i], a);
351 for (int i = 0; i < right_objects_.size (); i++)
352 Side_position_interface::add_support (a, right_objects_[i]);
359 Accidental_engraver::finalize ()
361 last_keysig_ = SCM_EOL;
365 Accidental_engraver::stop_translation_timestep ()
367 for (int j = ties_.size (); j--;)
369 Grob *r = Tie::head (ties_[j], RIGHT);
370 for (int i = accidentals_.size (); i--;)
371 if (accidentals_[i].head_ == r)
373 if (Grob *g = accidentals_[i].accidental_)
375 g->set_property ("tie", ties_[j]->self_scm ());
376 accidentals_[i].tied_ = true;
383 for (int i = accidentals_.size (); i--;)
385 int barnum = get_bar_number ();
387 Music *note = accidentals_[i].melodic_;
388 Context * origin = accidentals_[i].origin_;
390 Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
394 int n = pitch->get_notename ();
395 int o = pitch->get_octave ();
396 int a = pitch->get_alteration ();
397 SCM key = scm_cons (scm_int2num (o), scm_int2num (n));
400 && origin->where_defined (ly_symbol2scm ("localKeySignature")))
403 huh? we set props all the way to the top?
405 SCM localsig = origin->get_property ("localKeySignature");
407 if (accidentals_[i].tied_)
410 Remember an alteration that is different both from
411 that of the tied note and of the key signature.
413 localsig = ly_assoc_front_x
414 (localsig, key, scm_cons (SCM_BOOL_T, scm_int2num (barnum)));
421 not really really correct if there are more than one
422 noteheads with the same notename.
424 localsig = ly_assoc_front_x (localsig, key,
425 scm_cons (scm_int2num (a),
426 scm_int2num (barnum)));
431 origin->set_property ("localKeySignature", localsig);
433 origin = origin->get_parent_context ();
437 accidental_placement_ = 0;
438 accidentals_.clear ();
439 left_objects_.clear ();
440 right_objects_.clear ();
444 Accidental_engraver::acknowledge_grob (Grob_info info)
446 Music *note = info.music_cause ();
449 && note->is_mus_type ("note-event")
450 && Rhythmic_head::has_interface (info.grob_))
452 if (to_boolean ( get_property ("harmonicAccidentals"))
453 || !ly_c_equal_p (info.grob_->get_property ("style"),
454 ly_symbol2scm ("harmonic")))
457 Accidental_entry entry ;
458 entry.head_ = info.grob_;
459 entry.origin_trans_ = dynamic_cast<Engraver*> (info.origin_trans_);
460 entry.origin_ = info.origin_trans_->context ();
461 entry.melodic_ = note;
463 accidentals_.push (entry);
466 else if (Tie::has_interface (info.grob_))
467 ties_.push (dynamic_cast<Spanner*> (info.grob_));
468 else if (Arpeggio::has_interface (info.grob_))
469 left_objects_.push (info.grob_);
471 ->internal_has_interface (ly_symbol2scm ("finger-interface")))
472 left_objects_.push (info.grob_);
476 Accidental_engraver::process_music ()
478 SCM sig = get_property ("keySignature");
479 /* Detect key sig changes.
480 Update all parents and children. */
481 if (last_keysig_ != sig)
482 update_local_key_signature ();
485 ENTER_DESCRIPTION (Accidental_engraver,
487 "Catch note heads, ties and notices key-change events. "
488 "This engraver usually lives at Staff level, but "
489 "reads the settings for Accidental at @code{Voice} level, "
490 "so you can @code{\\override} them at @code{Voice}. "
496 "arpeggio-interface "
498 "rhythmic-head-interface "
504 "harmonicAccidentals "