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 = robust_scm2moment (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"),
374 a->set_parent (support, Y_AXIS);
376 if (!accidental_placement_)
377 accidental_placement_ = make_item ("AccidentalPlacement",
379 Accidental_placement::add_accidental (accidental_placement_, a);
380 SCM accs = scm_cons (scm_int2num (pitch->get_alteration ()),
382 if (num == 2 && extra_natural_b)
383 accs = scm_cons (scm_int2num (0), accs);
385 /* TODO: add cautionary option in accidental. */
388 a->set_property ("cautionary", SCM_BOOL_T);
390 support->set_property ("accidental-grob", a->self_scm ());
392 a->set_property ("accidentals", accs);
393 accidentals_[i].accidental_ = a;
396 We add the accidentals to the support of the arpeggio,
397 so it is put left of the accidentals.
399 for (int i = 0; i < left_objects_.size (); i++)
400 Side_position_interface::add_support (left_objects_[i], a);
401 for (int i = 0; i < right_objects_.size (); i++)
402 Side_position_interface::add_support (a, right_objects_[i]);
409 Accidental_engraver::finalize ()
411 last_keysig_ = SCM_EOL;
415 Accidental_engraver::stop_translation_timestep ()
417 for (int j = ties_.size (); j--;)
419 Grob *r = Tie::head (ties_[j], RIGHT);
420 for (int i = accidentals_.size (); i--;)
421 if (accidentals_[i].head_ == r)
423 if (Grob *g = accidentals_[i].accidental_)
425 g->set_property ("tie", ties_[j]->self_scm ());
426 accidentals_[i].tied_ = true;
433 for (int i = accidentals_.size (); i--;)
435 int barnum = get_bar_number ();
437 Music *note = accidentals_[i].melodic_;
438 Context * origin = accidentals_[i].origin_;
440 Pitch *pitch = unsmob_pitch (note->get_property ("pitch"));
444 int n = pitch->get_notename ();
445 int o = pitch->get_octave ();
446 int a = pitch->get_alteration ();
447 SCM key = scm_cons (scm_int2num (o), scm_int2num (n));
450 && origin->where_defined (ly_symbol2scm ("localKeySignature")))
453 huh? we set props all the way to the top?
455 SCM localsig = origin->get_property ("localKeySignature");
457 if (accidentals_[i].tied_)
460 Remember an alteration that is different both from
461 that of the tied note and of the key signature.
463 localsig = ly_assoc_front_x
464 (localsig, key, scm_cons (SCM_BOOL_T, scm_int2num (barnum)));
471 not really really correct if there are more than one
472 noteheads with the same notename.
474 localsig = ly_assoc_front_x (localsig, key,
475 scm_cons (scm_int2num (a),
476 scm_int2num (barnum)));
481 origin->set_property ("localKeySignature", localsig);
483 origin = origin->get_parent_context ();
487 accidental_placement_ = 0;
488 accidentals_.clear ();
489 left_objects_.clear ();
490 right_objects_.clear ();
494 Accidental_engraver::acknowledge_grob (Grob_info info)
496 Music *note = info.music_cause ();
499 && note->is_mus_type ("note-event")
500 && Rhythmic_head::has_interface (info.grob_))
502 if (to_boolean ( get_property ("harmonicAccidentals"))
503 || !ly_c_equal_p (info.grob_->get_property ("style"),
504 ly_symbol2scm ("harmonic")))
507 Accidental_entry entry ;
508 entry.head_ = info.grob_;
509 entry.origin_trans_ = dynamic_cast<Engraver*> (info.origin_trans_);
510 entry.origin_ = info.origin_trans_->context ();
511 entry.melodic_ = note;
513 accidentals_.push (entry);
516 else if (Tie::has_interface (info.grob_))
517 ties_.push (dynamic_cast<Spanner*> (info.grob_));
518 else if (Arpeggio::has_interface (info.grob_))
519 left_objects_.push (info.grob_);
521 ->internal_has_interface (ly_symbol2scm ("finger-interface")))
522 left_objects_.push (info.grob_);
526 Accidental_engraver::process_music ()
528 SCM sig = get_property ("keySignature");
529 /* Detect key sig changes.
530 Update all parents and children. */
531 if (last_keysig_ != sig)
532 update_local_key_signature ();
535 ENTER_DESCRIPTION (Accidental_engraver,
537 "Catch note heads, ties and notices key-change events. "
538 "This engraver usually lives at Staff level, but "
539 "reads the settings for Accidental at @code{Voice} level, "
540 "so you can @code{\\override} them at @code{Voice}. "
546 "arpeggio-interface "
548 "rhythmic-head-interface "
554 "harmonicAccidentals "